#include "pch.hpp"
//===========================================================================//
// File:	 ImageHlp.cpp													 //
// Project:  GameOS					                                         //
// Contents: Routines to interface with ImageHlp							 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 03/30/98  JTR    Created													 //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "ErrorHandler.hpp"
#include "Globals.hpp"
#include "Windows.hpp"
#include "debugger.hpp"
#include "DxRasterizer.hpp"
#include "Time.hpp"
#include <time.h>


extern DWORD GetVMSize();
extern DWORD PDBMemory;
bool WindowsNT;
HANDLE MainThreadHandle=0;
HANDLE WatchDogTimerThread=0;
HANDLE KillWatchdogEvent=0;
CONTEXT WatchDogContext;
bool FromWatchDog=0;
void* MainModuleBase;
extern char SpewOptions[256];
BYTE UppertoLowerTable[256];
extern bool gShowAllocations;
extern BYTE LeadByteTable[];
//
// Imagehlp.cpp variables
//
typedef BOOL (__stdcall *SYMCLEANUPPROC)( HANDLE );
typedef BOOL (__stdcall *SYMINITIALIZEPROC)( HANDLE, LPSTR, BOOL );
typedef BOOL (__stdcall *SYMSTACKWALKPROC)( DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID, PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE );
typedef LPVOID (__stdcall *SYMFUNCTIONTABLEACCESSPROC)( HANDLE, DWORD );
typedef DWORD (__stdcall *SYMGETMODULEBASEPROC)( HANDLE, DWORD );
typedef BOOL (__stdcall *SYMGETSYMFROMADDRPROC)( HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL );
typedef BOOL (__stdcall *SYMSETOPTIONSPROC)( DWORD );
typedef BOOL (__stdcall *SYMGETLINEFROMADDRPROC)( HANDLE, DWORD, PDWORD, PIMAGEHLP_LINE );
typedef BOOL (__stdcall *SYMLOADMODULEPROC)( HANDLE, HANDLE, LPSTR, LPSTR, DWORD, DWORD );
typedef BOOL (__stdcall *SYMUNLOADMODULEPROC)( HANDLE, DWORD );

static SYMFUNCTIONTABLEACCESSPROC	_SymFunctionTableAccess;
static SYMSTACKWALKPROC				_StackWalk;
static SYMGETMODULEBASEPROC			_SymGetModuleBase;
static SYMGETLINEFROMADDRPROC		_SymGetLineFromAddr;
static SYMGETSYMFROMADDRPROC		_SymGetSymFromAddr;
static SYMLOADMODULEPROC			_SymLoadModule;
static SYMINITIALIZEPROC			_SymInitialize;
static SYMSETOPTIONSPROC			_SymSetOptions;
static SYMUNLOADMODULEPROC			_SymUnloadModule;

HMODULE hModImagehlp=0;
HMODULE hModMsdbi=0;

extern char BugNotes[MAX_BUG_NOTES];
extern DWORD gEnableSpew;
extern DWORD gDisableSound;
extern void GetProcessorInfo();
extern DWORD gLoadAllSymbols;

//
// Buffer for symbol names
//
static BYTE sym[ sizeof(IMAGEHLP_SYMBOL) + 512 ];
static PIMAGEHLP_SYMBOL psym = (PIMAGEHLP_SYMBOL)sym;
//
// Last offset from GetSymbolFromAddress
//
DWORD		LastOffset;

//
// Current thread ID
//
DWORD		ThreadId=0;


//
// Find the imagehlp.dll and link to functions used
//
bool LoadImageHlp()
{
//
// Setup ignore list
//
	pIgnore=0;
//
// Find the base address of the main program module
//
	MEMORY_BASIC_INFORMATION mbi;
	VirtualQuery( &LoadImageHlp, &mbi, sizeof(mbi) );
	MainModuleBase=mbi.AllocationBase;
//
// Load the imagehlp library
//
	char tempPath[MAX_PATH];

	strcpy( tempPath, AssetsDirectory1 );
	strcat( tempPath, "\\assets\\binaries\\imagehlp.dll" );

	hModImagehlp = LoadLibrary( tempPath );
	if ( !hModImagehlp )
	{
		strcpy( tempPath, AssetsDirectory2 );
		strcat( tempPath, "\\imagehlp.dll" );

		hModImagehlp = LoadLibrary(tempPath);
		if (!hModImagehlp)
			return 0;
	}

	strcpy( ImageHelpPath, tempPath );

	_SymInitialize = (SYMINITIALIZEPROC)GetProcAddress( hModImagehlp, "SymInitialize" );
	if ( !_SymInitialize )
		return FALSE;

	_StackWalk = (SYMSTACKWALKPROC)GetProcAddress( hModImagehlp, "StackWalk" );
	if ( !_StackWalk )
		return FALSE;

	_SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC) GetProcAddress( hModImagehlp, "SymFunctionTableAccess" );
	if ( !_SymFunctionTableAccess )
		return FALSE;

	_SymGetModuleBase=(SYMGETMODULEBASEPROC)GetProcAddress( hModImagehlp, "SymGetModuleBase");
	if ( !_SymGetModuleBase )
		return FALSE;

	_SymGetSymFromAddr=(SYMGETSYMFROMADDRPROC)GetProcAddress( hModImagehlp, "SymGetSymFromAddr" );
	if ( !_SymGetSymFromAddr )
		return FALSE;

	_SymGetLineFromAddr=(SYMGETLINEFROMADDRPROC)GetProcAddress( hModImagehlp, "SymGetLineFromAddr" );
	if ( !_SymGetLineFromAddr )
		return FALSE;

	_SymSetOptions=(SYMSETOPTIONSPROC)GetProcAddress( hModImagehlp, "SymSetOptions" );
	if ( !_SymSetOptions )
		return FALSE;

	_SymLoadModule=(SYMLOADMODULEPROC)GetProcAddress( hModImagehlp, "SymLoadModule" );
	if ( !_SymLoadModule )
		return FALSE;

	_SymUnloadModule=(SYMUNLOADMODULEPROC)GetProcAddress( hModImagehlp, "SymUnloadModule" );
	if ( !_SymUnloadModule )
		return FALSE;

	return TRUE;
}




int __stdcall gosReadMemory( void* hProcess, DWORD lpBaseAddress, void* lpBuffer, DWORD nSize, DWORD* lpNumberOfBytesRead )
{
	if( IsBadReadPtr( (void*)lpBaseAddress, nSize ))
		return FALSE;
	
	memcpy( lpBuffer, (void*)lpBaseAddress, nSize );

	if( lpNumberOfBytesRead )
		*lpNumberOfBytesRead=nSize;

	return TRUE;
}



//
// Initialize the stack walking function
//
void InitStackWalk( STACKFRAME* sf, CONTEXT* Context )
{
	memset( sf, 0, sizeof(STACKFRAME) );
	sf->AddrPC.Offset		= Context->Eip;
	sf->AddrPC.Mode			= AddrModeFlat;
	sf->AddrStack.Offset	= Context->Esp;
	sf->AddrStack.Mode		= AddrModeFlat;
	sf->AddrFrame.Offset	= Context->Ebp;
	sf->AddrFrame.Mode		= AddrModeFlat;
	if( hModMsdbi )
	{
		sf->FuncTableEntry=_SymFunctionTableAccess( GetCurrentProcess(), Context->Eip );
	}
}

//
// Walk the stack, return new address 
//
int WalkStack( STACKFRAME* sf )
{
	if( hModMsdbi && _StackWalk(	IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), sf, 0, gosReadMemory, _SymFunctionTableAccess, _SymGetModuleBase, 0 ))
	{
		if( sf->AddrPC.Offset < 0x400000 )
			return 0;
		return sf->AddrPC.Offset;
	}
	else
		return 0;
}




//
// Return the "file(line number)" for a given address
//
char* GetLocationFromAddress( IMAGEHLP_LINE* ImageHlp_pline, char* Buffer, int Address )
{
	memset( ImageHlp_pline, 0, sizeof(ImageHlp_pline) );
	ImageHlp_pline->SizeOfStruct = sizeof(IMAGEHLP_LINE);

	DWORD disp=0;

	if( hModMsdbi )
	{
		for( int off=0; off<1024; off++ )
		{
			if( _SymGetLineFromAddr( GetCurrentProcess(), Address-off, &disp, ImageHlp_pline ) )
			{
				wsprintf( Buffer,"%s(%d)",ImageHlp_pline->FileName,ImageHlp_pline->LineNumber);
				return Buffer;
			}
		}
	}
	return "";
}



//
// Return the symbol nearest an address
//
char* GetSymbolFromAddress( char* Buffer, int Address )
{
	psym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
	psym->Flags = 0;
	psym->MaxNameLength = 512;
	psym->Name[0] = 0;
		
	if( hModMsdbi && _SymGetSymFromAddr( GetCurrentProcess(), Address, &LastOffset, psym ) )
	{
		MEMORY_BASIC_INFORMATION mbi;
		char ModuleName[MAX_PATH];
//
// Check to see if in current program or another module
//
		if( VirtualQuery( (void*)Address, &mbi, sizeof(mbi) ) )
		{
			if( mbi.AllocationBase && mbi.AllocationBase!=MainModuleBase )
			{
				if( GetModuleFileName( (HMODULE)mbi.AllocationBase, ModuleName, MAX_PATH ) )
				{
					char drive[_MAX_DRIVE];
					char dir[_MAX_DIR];
					char fname[_MAX_FNAME];
					char ext[_MAX_EXT];
   					_splitpath( ModuleName, drive, dir, fname, ext );
					for( int t0=0; t0<strlen(fname); t0++ )
						if( fname[t0]>='a' && fname[t0]<='z' )
							fname[t0]=(char)(fname[t0]-32);
					char Temp[256];
					sprintf( Temp, "%s! %s",fname,psym->Name );
					strcpy( psym->Name, Temp );
				}
			}
		}
//
// Return function name
//
		strcat( psym->Name, "()" );	// Add () after function

		if( LastOffset )			// If a displacement, display it after symbol
		{
			char Buffer[30];
			wsprintf( Buffer, "+0x%X",LastOffset );
			strcat(psym->Name, Buffer);
		}
		strcpy( Buffer, psym->Name );
		return Buffer;
	}
	else
	{
		MEMORY_BASIC_INFORMATION mbi;
		char ModuleName[MAX_PATH];

		if( !VirtualQuery( (void*)Address, &mbi, sizeof(mbi) ) )
			return "";

		DWORD hMod=(DWORD)mbi.AllocationBase;

		if( 0==hMod || 0==GetModuleFileName( (HMODULE)hMod, ModuleName, MAX_PATH ) )
			return "";

		PIMAGE_DOS_HEADER pDosHdr=(PIMAGE_DOS_HEADER)hMod;
		PIMAGE_NT_HEADERS pNtHdr=(PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
		PIMAGE_SECTION_HEADER pSection=IMAGE_FIRST_SECTION( pNtHdr );

		DWORD rva=Address-hMod;

		for( int i=0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++ )
		{
			DWORD sectionStart = pSection->VirtualAddress;
			DWORD sectionEnd = sectionStart +  pSection->SizeOfRawData > pSection->Misc.VirtualSize ? pSection->SizeOfRawData : pSection->Misc.VirtualSize;

			if( (rva>=sectionStart) && (rva<=sectionEnd) )
			{
				char drive[_MAX_DRIVE];
				char dir[_MAX_DIR];
				char fname[_MAX_FNAME];
				char ext[_MAX_EXT];
   				_splitpath( ModuleName, drive, dir, fname, ext );
				int offset = rva - sectionStart;
				for( int t0=0; t0<strlen(fname); t0++ )
					if( fname[t0]>='a' && fname[t0]<='z' )
						fname[t0]=(char)(fname[t0]-32);
				sprintf( Buffer, "%s! (+0x%X) ",fname,offset );				
				return Buffer;
			}
		}
	}
	return "";
}




//
// Null routine, used when Game does not support something the GameOS requires
//
void Null()
{
}
char* NullChar()
{
	return 0;
}


void GetEnvironmentSettings( char* CommandLine )
{
//
// Start exception handler with blank notes
//
	BugNotes[0]=0; 
	BugNotes[1]=0; 
//
// Setup Environment defaults
//
	DWORD temp=Environment.MemoryManager;
	memset( &Environment, 0, sizeof(Environment) );		// Clear environment variables as default
	Environment.MemoryManager=temp;
	Environment.GetGameInformation=NullChar;
	Environment.InitializeGameEngine=Null;
	Environment.DoGameLogic=Null;
	Environment.UpdateRenderers=Null;
	Environment.TerminateGameEngine=Null;
	Environment.Key_FullScreen=KEY_F4;
	Environment.Key_SwitchMonitors=KEY_F5;
	Environment.Key_Exit=KEY_ESCAPE;
	Environment.applicationName="Default";
	Environment.screenWidth=640;
	Environment.screenHeight=480;
	Environment.bitDepth=16;
	Environment.MegMemoryRequired=64;
//
// Update the environment from the application (Needed so PATH's are updated)
//
	GetGameOSEnvironment( CommandLine );				// Get information from Game
//
// Command line override to turn on all spews?
//
	if( gEnableSpew==1 )
	{
		Environment.debugLog="*DebugLog.txt";
		Environment.spew=SpewOptions;
		Environment.TimeStampSpew=1;
	}
	else if( gEnableSpew==2 )
	{
		Environment.debugLog="DebugLog.txt";
		Environment.spew=SpewOptions;
		Environment.TimeStampSpew=1;
	}
	if( gShowAllocations )
	{
		Environment.debugLog="DebugLog.txt";
		Environment.spew="GameOS_Memory*";
	}
//
// Make sure a version number is specified, if not use date this file was compiled
//
	static char Buffer[64];
	char ToDay[]=__DATE__;
	int Month=0;
	tm newtime;
	memset( &newtime,0,sizeof(newtime) );
	for( int t0=0; t0<12; t0++ )
	{
		newtime.tm_mon=t0;
		newtime.tm_mday=1;
		if( 0==strnicmp( &ToDay[0], asctime(&newtime)+4, 3) )		// Find month
			Month=t0+1;
	}	
	wsprintf( Buffer, "00.00.%02d.%02d00", Month, atoi(&ToDay[4]) );
//
//
//
	if( 0==Environment.version || *Environment.version==0 )
		Environment.version=Buffer;
//
// Make sure game name is a reasonable length (all sorts of fields may overflow otherwise)
//
	gosASSERT( Environment.applicationName && strlen(Environment.applicationName)<32 );
//
// Make sure command line is a reasonable length
//
	if( strlen(CommandLine)>127 )
		STOP(( "Command line too long" ));
//
// Save game name
//
	strcpy( ApplicationName, Environment.applicationName );
//
// Create the full name of the registry key
//
	wsprintf( RegistryKey,"Software\\Microsoft\\Microsoft Games\\%s",ApplicationName );
	if( Environment.registryVersion && *Environment.registryVersion )
	{
		strcat( RegistryKey, "\\" ); 
		strcat( RegistryKey, Environment.registryVersion ); 
	}
//
// If Debug or Profile build add copyright to title
//
	static char Title[256];
	strcpy( Title,Environment.applicationName );
	#if defined(_DEBUG)
		strcat( Title," DEBUG (not for release)  " );
	#elif defined(_ARMOR)
		strcat( Title," ARMOR (not for release)  " );
	#elif defined(LAB_ONLY)
		strcat( Title," PROFILE (not for release)  " );
	#endif
	#ifdef LAB_ONLY
		strcat( Title, Environment.version );
	#endif
	Environment.applicationName=Title;
//
// Check some Environment values are sensible
//
	gosASSERT( Environment.MegMemoryRequired < 1024 );									// 1 Gig is a sensible maximum!
	gosASSERT( Environment.bitDepth==16 || Environment.bitDepth==32 );
	gosASSERT(	(Environment.screenWidth==640 && Environment.screenHeight==480) ||
				(Environment.screenWidth==512 && Environment.screenHeight==384) ||
				(Environment.screenWidth==800 && Environment.screenHeight==600) ||
				(Environment.screenWidth==960 && Environment.screenHeight==720) ||		// Max resolution for 4Meg cards
				(Environment.screenWidth==1024 && Environment.screenHeight==768) ||
				(Environment.screenWidth==1280 && Environment.screenHeight==1024) ||
				(Environment.screenWidth==1600 && Environment.screenHeight==1200) );
}







DWORD LastFrameNumber=12345678;
DWORD TriedToStop=0;


void BadError()
{
	TriedToStop=2;
	FromWatchDog=1;
	AllowFail=0;
	ProcessingError=0;
	status=Running;
	STOP(( "Watchdog interupt was triggered" ));
}



#ifdef _DEBUG
//========================================================================
//========================================================================
typedef struct tagTHREADNAME_INFO
{
	DWORD	dwType;		// must be 0x1000
	char *	szName;		// pointer to name (in user addr space)
	DWORD	dwThreadID;	// thread ID (-1=caller thread)
	DWORD	dwFlags;	// reserved for future use, must be zero
} THREADNAME_INFO;


//========================================================================
//========================================================================
void gosSetThreadName(DWORD dwThreadID, char *szThreadName)
{
	THREADNAME_INFO info;

	info.dwType		= 0x1000;
	info.szName		= szThreadName;
	info.dwThreadID	= dwThreadID;
	info.dwFlags	= 0;

	__try
	{
		RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
	}
	__except (-1)
	{
	}
}
#else
void gosSetThreadName(DWORD dwThreadID, char *szThreadName)
{
}
#endif




//
// Watchdog timer - check for SCROLL LOCK keys every 5 seconds
//
DWORD WINAPI WatchDogThread( LPVOID ThreadParam )
{
    while( WaitForSingleObject( KillWatchdogEvent, 5000 ) == WAIT_TIMEOUT )
	{
		if( !ProcessingError && WatchDogFrame==LastFrameNumber )
		{
			if( GetAsyncKeyState(VK_SCROLL)&0x8000 )
			{
				OutputDebugString( "GameOS - trying to break into app\n" );
				CONTEXT CurrentContext;
				SuspendThread( MainThreadHandle );
				memset( &CurrentContext, 0, sizeof(CurrentContext) );
				CurrentContext.ContextFlags=0xffffffff;
				GetThreadContext( MainThreadHandle, &CurrentContext );
				WatchDogContext=CurrentContext;

				if( TriedToStop==1 )
				{
//
// Forcing main thread into exception handler didn't work - try a message box.
//
					char Buffer[512];
					char Buffer1[256];
					char Buffer2[256];
					IMAGEHLP_LINE pline;
					sprintf( Buffer, "Forcing a break at address %s (0x%x)\n\nLocation=%s", GetSymbolFromAddress(Buffer1, CurrentContext.Eip), CurrentContext.Eip, GetLocationFromAddress( &pline, Buffer2, CurrentContext.Eip) );
					MessageBox( hWindow, Buffer, "Break into Game", MB_OK );
					status=AfterExit;
					FatalExit(1);
				}
				else
				{
//
// Try and force thread to exception handler
//
					TriedToStop=1;
					CurrentContext.Eip=(DWORD)&BadError;
					SetThreadContext( MainThreadHandle, &CurrentContext );
					ResumeThread( MainThreadHandle );
				}
			}
		}
		LastFrameNumber=WatchDogFrame;
	}
//
// Otherwise end thread
//
	return 0;
}






//
// Get ImageHlp.dll ready for use
//
void InitExceptionHandler( char* CommandLine )
{
//
// In release mode always show process memory useage and grab screen shots
//
	#ifdef _ARMOR
		ProcessMemorySize=0;
	#else
		ProcessMemorySize=1;
	#endif
//
// Make sure SPEW only works on current thread
//
	ThreadId=GetCurrentThreadId();
//
// Name main thread
//
	gosSetThreadName( ThreadId, "Main" );
//
// Save the handle of the main thread
//
	DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &MainThreadHandle, 0, 0, DUPLICATE_SAME_ACCESS );
//
// Setup lower case translation table
//
	for( int t0=0; t0<256; t0++ )
		UppertoLowerTable[t0]=(BYTE)((t0>='A' && t0<='Z') ? t0+32 : t0 );
//
// Setup a 'watchdog' timer (If 5 seconds go by and GameOS has not executed, SCROLL LOCK will break into program)
//
	if (Platform!=Platform_DLL)
	{
		FromWatchDog=0;
		LoggingThreadID=0;
		#ifdef LAB_ONLY
		DWORD ID;
		KillWatchdogEvent=CreateEvent( NULL, false, false, NULL );
		WatchDogTimerThread=CreateThread(NULL, 0, WatchDogThread, 0, 0, &ID );
		gosSetThreadName( ID, "Watchdog" );
		#endif
	}
//
// Environment settings contain default paths
//
	GetEnvironmentSettings( CommandLine );
//
// Record screen settings
//
	GlobalWidth=Environment.screenWidth;
	GlobalHeight=Environment.screenHeight;
	GlobalZBuffer=Environment.disableZBuffer;
	GlobalFullScreen=Environment.fullScreen;
//
// Disable sound globally?
//
	if( gDisableSound )
		Environment.soundDisable=1;						// Disable sound if switch set
#if 0 // by jcem
//
// Now find a good working directory
//
	char TempDirectory[MAX_PATH+1];
	int Len=GetCurrentDirectory( MAX_PATH, TempDirectory );
	if( TempDirectory[Len-1]=='\\' )
	{
		TempDirectory[Len-1]=0;
		Len--;
	}
//
// In case the app was started with a command line and the current directory does not contain the assets
// If the .exe directory does, change to it.
//
	char PathBuffer[256];
	strcpy( PathBuffer, TempDirectory );
	strcat( PathBuffer, "\\blade.dll" );
	if( !gos_DoesFileExist( PathBuffer ) )
	{

		strcpy( PathBuffer, TempDirectory );
		strcat( PathBuffer, "\\Assets\\Binaries\\blade.dll" );
		if( !gos_DoesFileExist( PathBuffer ) )
		{
			char drive[_MAX_PATH];
			char dir[_MAX_DIR];
			char fname[_MAX_FNAME];
			char ext[_MAX_EXT];
			_splitpath( _pgmptr, drive, dir, fname, ext );
			strcpy( PathBuffer, drive );
			strcat( PathBuffer, dir );
			SetCurrentDirectory( PathBuffer );			
			Len=GetCurrentDirectory( MAX_PATH, TempDirectory );
			if( TempDirectory[Len-1]=='\\' )
			{
				TempDirectory[Len-1]=0;
				Len--;
			}
		}
	}
//
// Now check to make sure we did not start in debug\release\profile etc.... - move back a level
//
	if( !(Environment.directoryPath && 0==stricmp( Environment.directoryPath, "!")) )
	{
		if( Len>=6 && 0==stricmp( &TempDirectory[ Len-6 ], "\\debug" ))
		{
			TempDirectory[ Len-6 ]=0;
			SetCurrentDirectory( TempDirectory );
		}
		if( Len>=16 && 0==stricmp( &TempDirectory[ Len-16 ], "\\assets\\binaries" ))
		{
			TempDirectory[ Len-16 ]=0;
			SetCurrentDirectory( TempDirectory );
		}
		if( Len>=9 && 0==stricmp( &TempDirectory[ Len-9 ], "\\truetime" ))
		{
			TempDirectory[ Len-9 ]=0;
			SetCurrentDirectory( TempDirectory );
		}
		if( Len>=8 && 0==stricmp( &TempDirectory[ Len-8 ], "\\profile" ))
		{
			TempDirectory[ Len-8 ]=0;
			SetCurrentDirectory( TempDirectory );
		}
		if( Len>=8 && 0==stricmp( &TempDirectory[ Len-6 ], "\\armor" ))
		{
			TempDirectory[ Len-6 ]=0;
			SetCurrentDirectory( TempDirectory );
		}
		if( Len>=8 && 0==stricmp( &TempDirectory[ Len-8 ], "\\release" ))
		{
			TempDirectory[ Len-8 ]=0;
			SetCurrentDirectory( TempDirectory );
		}
//
// Allow the application to start in multiple directories
//
		Len=GetCurrentDirectory( MAX_PATH, TempDirectory );
		if( TempDirectory[Len-1]=='\\' )
		{
			TempDirectory[Len-1]=0;
			Len--;
		}
		if( Environment.directoryPath && *Environment.directoryPath )
		{
			int done=0;
			int StartCompare=0;
			int EndCompare=strchr(Environment.directoryPath,';') ? strchr(Environment.directoryPath,';')-Environment.directoryPath : strlen(Environment.directoryPath);

			do
			{
				if( Len>(EndCompare-StartCompare) && 0==_strnicmp( &TempDirectory[ Len-(EndCompare-StartCompare) ], &Environment.directoryPath[StartCompare], EndCompare-StartCompare ))
				{
					if( Environment.directoryPath[StartCompare]!=';' )
					{
						TempDirectory[ Len-(EndCompare-StartCompare) ]=0;
						SetCurrentDirectory( TempDirectory );
						done=1;
					}
				}
				if( !done )
				{
					StartCompare=EndCompare+1;
					EndCompare=strchr(&Environment.directoryPath[StartCompare],';') ? strchr(&Environment.directoryPath[StartCompare],';')-Environment.directoryPath : strlen(Environment.directoryPath);
				}
			} while( !done && Environment.directoryPath[StartCompare-1] );

		}
	}
//
// Setup search paths for imagehlp.dll, dsetup.dll and font.tga
//
// 1. Search current directory + assets\binaries or assets\graphics
// 2. Current directory
//
	Len=
#endif // by jcem
		GetCurrentDirectory( MAX_PATH, AssetsDirectory1 );
	strcpy(AssetsDirectory2,_pgmptr);

	_mbslwr((BYTE*)AssetsDirectory2);
	_mbslwr((BYTE*)AssetsDirectory1);
	//
	// Chop the executable name off the end of AssetsDirectory2
	// 
	char * lastBackslash = AssetsDirectory2;
	char * temp = AssetsDirectory2;
	while (*temp != 0)
	{
		if (temp[1] && LeadByteTable[*(unsigned char*)temp]) // jcem - check last-byte + '\0' char 
			temp += 2;
		else
		{
			if (*temp == '\\')
				lastBackslash = temp;
			temp++;
		}
	}

	*lastBackslash = 0;
#if 1 // by jcem
	strcpy(AssetsDirectory1, AssetsDirectory2);
	SetCurrentDirectory(AssetsDirectory2);
#endif // by jcem
//
// Now setup the exception handler
//
	gos_MathExceptions( 0, 0 );					// No exceptions, double precision
//
// Attempt to load dbghelp.dll file (required for imagehlp.dll)
//
	char msBuffer[256];
	strcpy( msBuffer, AssetsDirectory1 );
	strcat( msBuffer, "\\Assets\\Binaries\\dbghelp.dll" );

	hModMsdbi = LoadLibrary( msBuffer );
	if ( !hModMsdbi )
	{
		strcpy( msBuffer, AssetsDirectory2 );
		strcat( msBuffer, "\\dbghelp.dll" );
		hModMsdbi = LoadLibrary( msBuffer );
	}
//
// Attempt to load imagehlp.dll
//
	if( hModMsdbi )
	{
		if( !LoadImageHlp() )
		{
			FreeLibrary( hModMsdbi );
			hModMsdbi=0;
		}
	}
//
// Set default options
//
	if( hModMsdbi )
	{
		_SymSetOptions(0);
		_SymSetOptions(SYMOPT_UNDNAME|SYMOPT_LOAD_LINES|SYMOPT_OMAP_FIND_NEAREST);
		char drive[_MAX_PATH];
		char dir[_MAX_DIR];
		char fname[_MAX_FNAME];
		char ext[_MAX_EXT];
		_splitpath( _pgmptr, drive, dir, fname, ext );
		strcat( drive, dir );
		strcat( drive, ";" );
		strcat( drive, AssetsDirectory1 );
		strcat( drive, "\\assets\\binaries\\" );
//
// See how much memory used before PDB's loaded
//
		DWORD tempPDBMemory=GetVMSize();
//
// Open .PDB file
//
		if( !_SymInitialize( GetCurrentProcess(), drive, gLoadAllSymbols ))
		{
			MessageBox( hWindow, "Could not Initialize .PDB file", ApplicationName, MB_OK|MB_ICONEXCLAMATION );
		}
		_SymLoadModule( GetCurrentProcess(), NULL, _pgmptr, NULL, NULL, NULL );
//
// See how much memory used for all PDB's loaded
//
		PDBMemory=GetVMSize()-tempPDBMemory;
		if( PDBMemory>0x80000000 )				// Already loaded (under MSVC) means incorrect numbers
			PDBMemory=0;
	}
	else
	{
		PDBMemory=0;
	}
//
// Make all windows errors come to the exception handler
//
	SetErrorMode( SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS );
//
// Find if NT or 9x
//
	OSVERSIONINFO osinfo;
	osinfo.dwOSVersionInfoSize=sizeof(osinfo);
	GetVersionEx( &osinfo );
	WindowsNT=osinfo.dwPlatformId==VER_PLATFORM_WIN32_NT;
//
// Win NT 4.0 / 3.51 not supported
//
	if( WindowsNT && osinfo.dwMajorVersion<5 )
	{
		MessageBox( hWindow, "Error", "Windows 2000 and above required", MB_OK|MB_ICONEXCLAMATION );
		status=AfterExit;
		exit(1);
	}
//
// Init functions that just setup variables
//
	GetProcessorInfo();									// Info on processor running on
//
// Start logging things
//
	InitRunLog();
}


//
// Remove ImageHlp.dll from memory
//
void DestroyImageHlp()
{
	if( WatchDogTimerThread )
	{
        SetEvent( KillWatchdogEvent );
        WaitForSingleObject( WatchDogTimerThread, INFINITE );
		CloseHandle( WatchDogTimerThread );
		CloseHandle( KillWatchdogEvent );
		WatchDogTimerThread=0;
	}
	if( hModMsdbi )
	{
		FreeLibrary( hModMsdbi );
		hModMsdbi=0;
	}
	if (MainThreadHandle)
	{
		CloseHandle(MainThreadHandle);
		MainThreadHandle = 0;
	}
	if( hModImagehlp )
	{
		_SymUnloadModule( GetCurrentProcess(), NULL );
		FreeLibrary( hModImagehlp );
		hModImagehlp=0;
	}
}














																								
																												