#include "GPEMap.h"
  #include <stdio.h>
#include <malloc.h>
  
//
//	Internal Image Data structures
//
#pragma pack(push,1)
struct ImageDebugDirectory
{
    unsigned int	m_Characteristics;
    unsigned int	m_TimeDateStamp;
    unsigned short	m_MajorVersion;
    unsigned short	m_MinorVersion;
    unsigned int	m_Type;
    unsigned int	m_SizeOfData;
    unsigned int	m_AddressOfRawData;
    unsigned int	m_PointerToRawData;				//	The important bit - pointer to the debug directory's data
													//	in the PE file
};	//ImageDebugDirectory - Analagous to IMAGE_DEBUG_DIRECTORY with all the unnecessary stuff
	//chopped out
struct ImageDosStubHeader
{
	unsigned short	m_Magic;						//	DOS signature ('MZ')
	unsigned short	m_Filler[29];					//	Don't need to know this stuff (see MSDN for details)
    long			m_LFA;							//	File address of new exe header
}; //ImageDosStubHeader - Analagous to IMAGE_DOS_HEADER, with all the useless crap chopped out
struct ImageDataDirectory
{
  	unsigned long	m_VAddr;						//	Data virtual offset from m_pMapAddress
	unsigned long	m_Size;							//	Size of the data
}; //ImageDataDirectory - Analogous to IMAGE_DATA_DIRECTORY (this is an internal structure in <winnt.h>)
struct ImageNTHeaders
{
      unsigned long			m_Signature;			//	PE file signature ('PE\0\0')
	unsigned short			m_FileHdrFiller[10];	//	Don't need to know this shit (see MSDN for details)
	unsigned short			m_OpFileHdrFiller[48];	//	Don't need to know this shit (see MSDN for details)
	ImageDataDirectory		m_DataDirectory[16];	//	PE data directories (resources, debug data, etc.)
													//	(debug is at offset 144 from m_OpFileHdrFiller - i.e.
													//	position 6 in this array - see MSDN help for
													//	IMAGE_DATA_DIRECTORY)
};	//ImageNTHeaders - Analagous to IMAGE_NT_HEADERS in <dbghelp.h>, except with all the unnecessary
	//stuff chopped out
  
//
//	Internal CV Data structures
//
  struct CVSignature
{
	char				m_Signature[4];				//	CV signature ('NB11' or 'NB09')
	unsigned int		m_FilePos;					//	File position (CV base file address)
}; //CVSignature - CodeView type and file position reference
struct CVSubSection
{
    unsigned short		m_SubSection;				//	Subsection type (sst...)
    unsigned short		m_ModuleIndex;				//	Module index
    int					m_LFO;						//	Large file offset of subsection
    unsigned int		m_SubSectionSize;			//	Number of bytes in subsection
}; //CVSubSection - CodeView subsection
  struct CVDirHeader
{
	unsigned short		m_DirHeader;				//	Length of this structure
	unsigned short		m_DirEntry;					//	Number of bytes in each directory entry
	unsigned int		m_NumDirEntries;			//	Number of directory entries
	int					m_NextDir;					//	Offset from the base of next directory (unused)
	unsigned int		m_Flags;					//	Status flags
}; //CVDirHeader - Section directory header
struct CVSrcModuleHeader
{
	unsigned short	m_NumFiles;						//	Number of source files contained in this module
	unsigned short	m_NumSegs;						//	Number of base segments in this module
}; //CVSrcModuleHeader - Source module (.obj) header
struct CVSymTableHeader
{
	unsigned short	m_SymHashFnc;					//	Symbol hash function (must be 12)
	unsigned short	m_AddrHashFnc;					//	Address hash function (must be 10)
	unsigned int	m_NumSymBytes;					//	Number of bytes in the upcoming $$SYMBOL table
	unsigned int	m_NumSymHashBytes;				//	Number of bytes in the symbol hash table
	unsigned int	m_NumAddrHashBytes;				//	Number of bytes in the address hash table
}; //CVSymTableHeader
struct CVSymData
{
	unsigned int	m_TypeID;						//	Type identifier for the symbol
	unsigned int	m_Offset;						//	Offset of the symbol
	unsigned short	m_Segment;						//	Symbol segment
	unsigned char	m_NameLen;						//	Length of the symbol name
	char			m_Name[512];					//	Symbol name (not really 512 characters, but I only
													//	ever use this structure to reinterpret the data
													//	pointer, never as a stack or heap object)
}; //CVSymData - variable names in the $$SYMBOL table (S_LDATA32 and S_GDATA32)
struct CVSymProcStart
{
	unsigned int	m_Parent;						//	
	unsigned int	m_End;
	unsigned int	m_Next;
	unsigned int	m_ProcLength;
	unsigned int	m_DbgStart;
	unsigned int	m_DbgEnd;
	unsigned int	m_ProcType;
	unsigned int	m_Offset;
	unsigned short	m_Segment;
	unsigned char	m_Flags;
	unsigned char	m_NameLen;
	char			m_Name[512];
}; //CVSymProcStart - proc start in a $$SYMBOL table (S_LPROC32 and S_GPROC32)
struct CVSymProcRef
{
	unsigned int	m_Checksum;
	unsigned int	m_SymOffset;
	unsigned short	m_Module;
}; //CVSymProcRef - proc reference in global $$SYMBOL tables (S_PROCREF)
struct CVAddrHashOffset
{
	unsigned int	m_CurSymOffset;
	unsigned int	m_CurMemOffset;
}; //CVAddrHashOffset - used in the address sort table in global symbol tables
#pragma pack(pop)
 
 
  //
//	CV Enums
//
enum ModuleType
{
	//	Module identifiers
	eMT_sstModule			=	0x120,
	eMT_sstTypes			=	0x121,
	eMT_sstPublic			=	0x122,
	eMT_sstPublicSym		=	0x123,
	eMT_sstSymbols			=	0x124,
	eMT_sstAlignSym			=	0x125,
	eMT_sstSrcLnSeg			=	0x126,
	eMT_sstSrcModule		=	0x127,
	eMT_sstLibraries		=	0x128,
	eMT_sstGlobalSym		=	0x129,
	eMT_sstGlobalPub		=	0x12a,
	eMT_sstGlobalTypes		=	0x12b,
	eMT_sstMPC				=	0x12c,
	eMT_sstSegMap			=	0x12d,
	eMT_sstSegName			=	0x12e,
	eMT_sstPreComp			=	0x12f,
	eMT_unused				=	0x130,
	eMT_sstOffsetMap16		=	0x131,
	eMT_sstOffsetMap32		=	0x132,
	eMT_sstFileIndex		=	0x133,
	eMT_sstStaticSym		=	0x134,
  	//	Module type range
	eMT_lastModule,
	eMT_firstModule			= eMT_sstModule,
  	//	Maximum module types
	eModuleTypes			= eMT_lastModule - eMT_firstModule
  }; //ModuleType
  enum SType
{
	//	Symbol type identifiers (ripped from MSDN)
	S_COMPILE		= 0x0001,
	S_SSEARCH		= 0x0005,
	S_END			= 0x0006,
	S_SKIP			= 0x0007,
	S_CVRESERVE		= 0x0008,
	S_OBJNAME		= 0x0009,
	S_ENDARG		= 0x000a,
	S_COBOLUDT		= 0x000b,
	S_MANYREG		= 0x000c,
	S_RETURN		= 0x000d,
	S_ENTRYTHIS		= 0x000e,
	S_REGISTER		= 0x1001,
	S_CONSTANT		= 0x1002,
	S_UDT			= 0x1003,
	S_COBOLUDT2		= 0x1004,
	S_MANYREG2		= 0x1005,
	S_BPREL32		= 0x1006,
	S_LDATA32		= 0x1007,
	S_GDATA32		= 0x1008,
	S_PUB32			= 0x1009,
	S_LPROC32		= 0x100a,
	S_GPROC32		= 0x100b,
	S_THUNK32		= 0x0206,
	S_BLOCK32		= 0x0207,
	S_WITH32		= 0x0208,
	S_LABEL32		= 0x0209,
	S_CEXMODEL32	= 0x020a,
	S_VFTTABLE32	= 0x100c,
	S_REGREL32		= 0x100d,
	S_LTHREAD32		= 0x100e,
	S_GTHREAD32		= 0x100f,
	S_LPROCMIPS		= 0x1010,
	S_GPROCMIPS		= 0x1011,
	S_PROCREF		= 0x0400,
	S_DATAREF		= 0x0401,
	S_ALIGN			= 0x0402,
  	S_FIRST			= S_COMPILE,
	S_LAST			= S_ALIGN
};
 
 
 
 
 
 
  //
//	Global Utility Functions
//
TCHAR * _StringifyOSError(const int BufferSize,TCHAR * pBuffer,const unsigned long ErrCode = -1)
{
	//	_StringifyOSError fills up to BufferSize character in pBuffer with either the stringified
	//	version either GetLastError(), or ErrCode if it's not -1
	const unsigned long	ActErrCode	= ErrCode == -1 ? GetLastError() : ErrCode;
  	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
				  NULL,
				  ActErrCode,
				  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
				  LPTSTR(pBuffer),
				  BufferSize,
				  NULL);
  	//	FormatMessage seems to add newlines to the end of the string buffer. This is unacceptable!
	{
		const int	NewBufLen = strlen(pBuffer);
  		if (pBuffer[NewBufLen - 1] == _T('\n'))
		{
			pBuffer[NewBufLen - 1] = _T('\0');
		}
  		if (pBuffer[NewBufLen - 2] == _T('\r'))
		{
			pBuffer[NewBufLen - 2] = _T('\0');
		}
	}
  	return pBuffer;
} //_StringifyOSError
const unsigned char * _GetDataPtr(const unsigned char * & pDataPtr,const unsigned int NumBytes)
{
	//	_GetDataPtr - Stores pDataPtr, increments pDataPtr by NumByte, then returns the stored pDataPtr
	//	(useful for parsing the memory mapped file).
	const unsigned char * pOldDataPtr = pDataPtr;
	pDataPtr += NumBytes;
	return pOldDataPtr;
} //_GetDataPtr
template <class T> const T * _GetCastDataPtr(const unsigned char * & pDataPtr,const int Count = 1,T * pDummy = 0)
{
	//	_GetCastDataPtr - Templated version of _GetDataPtr - Count specifies the number of T's expected at pDataPtr
	return (const T*)_GetDataPtr(pDataPtr,sizeof(T) * Count);
} //_GetCastDataPtr
unsigned int _ConvertAddrToMap(const void * pAddr,const unsigned int SystemPageSize)
{
	//	_ConvertAddrToMap - Convert pAddr to the map file using VirtualQuery
	MEMORY_BASIC_INFORMATION	tmpMem;
	VirtualQuery(pAddr,&tmpMem,sizeof(tmpMem));
  	//	The difference in the allocation base and the base address tells us by how much pAddr was rounded down
	unsigned int Mod = (((unsigned int)tmpMem.BaseAddress - (unsigned int)tmpMem.AllocationBase) / SystemPageSize) - 1;
  	return ((unsigned int)(pAddr) - (unsigned int)tmpMem.BaseAddress) + (Mod * SystemPageSize);
} //_ConvertAddrToMap
 
 
  //
//	GPEMap Methods
//
  GPEMap::GPEMap()
{
  	Init();
  	const int	MaxModuleFilenameLen = 512;
	TCHAR		ModulePEFile[MaxModuleFilenameLen];
  	if (GetModuleFileName(0,ModulePEFile,MaxModuleFilenameLen) > 0)
	{
		//	Try to open the pe file and process it
		if (OpenPEFile(ModulePEFile) && ProcessPEFile())
		{
			m_bOK = true;
		}
	}
	else
	{
		const int	OSErrLen = 512;
		TCHAR		OSErr[OSErrLen];
		OutputErrorF(_T("GPEMap::GPEMap - GetModuleFilename failed (OS Error = '%s')"),
					 _StringifyOSError(OSErrLen,OSErr));
	}
  } //GPEMap::GPEMap
  GPEMap::GPEMap(const TCHAR * pPEFileToMap)
{
  	Init();
  	//	Try to open the pe file and process it
	if (OpenPEFile(pPEFileToMap) && ProcessPEFile())
	{
		m_bOK = true;
	}
  } //GPEMap::GPEMap
void GPEMap::Init()
{
	//	Initialize file mapping members
	m_bOK				= false;
	m_hFile				= 0;
	m_hMapFile			= 0;
	m_pMapAddress		= 0;
  	//	COFF
	m_pCOFFDbgDir		= 0;
  	//	CodeView
	m_pCVDbgDir			= 0;
	m_pCVGlobalSym		= 0;
	m_pCVSrcMods		= 0;
	m_pCVAlignSym		= 0;
	m_NumCVSrcMods		= 0;
	m_NumCVAlignSyms	= 0;
  	//	Initialize the system page size
	{
		SYSTEM_INFO	tmpSysInfo;
		GetSystemInfo(&tmpSysInfo);
		m_SystemPageSize = tmpSysInfo.dwPageSize;
	}
} //GPEMap::Init
bool GPEMap::OpenPEFile(const TCHAR * pPEFileToMap)
{
  	//	OpePEFile - Opens pPEFileToMap in a standard file, then creates a memory map of it. The base address
	//	of the map is stored in m_pMapAddress
	//	OS error storage
	const int	OSErrLen	= 256;
	TCHAR		OSErr[OSErrLen];
  	//	Open up the PE file
	OFSTRUCT	FileInfo;
	m_hFile = (HANDLE)OpenFile(pPEFileToMap,&FileInfo,OF_READ);
	if (m_hFile != HANDLE(HFILE_ERROR))
	{
  		//	Create a file mapping 
		m_hMapFile = CreateFileMapping(HANDLE(m_hFile),			//	Current file handle. 
									   NULL,					//	Default security
									   PAGE_READONLY,			//	Read/write permission. 
									   0,						//	Max. object size
									   0,						//	Size of hFile
									   _T("EXEMap"));			//	Name of mapping object. 
 
		if (m_hMapFile == NULL)
		{ 
			OutputErrorF(_T("GPEMap::GPEMap - Failed to open map file (OS Error = '%s')"),
						 _StringifyOSError(OSErrLen,OSErr));
		}
		else
		{
			//	Create a view of the file
			m_pMapAddress = (unsigned char*)MapViewOfFile(m_hMapFile,		//	Handle to mapping object
														  FILE_MAP_READ,	//	Read/write permission
														  0,				//	Max. object size
														  0,				//	Size of hFile
														  0);				//	Map entire file
			if (m_pMapAddress == NULL) 
			{ 
				OutputErrorF(_T("GPEMap::GPEMap - Could not map view of file (OS Error = '%s')"),
							 _StringifyOSError(OSErrLen,OSErr)); 
			}
			else
			{
				return true;
			}
  		}
  	}
	else
	{
		OutputErrorF(_T("GPEMap::GPEMap - Failed to open PE file (OS Error = '%s'"),
					 _StringifyOSError(OSErrLen,OSErr));
	}
  	return false;
} //GPEMap::OpenPEFile
bool GPEMap::ProcessPEFile()
{
	//	ProcessPEFile - Sets up precalculated file pointers for quicker symbol lookup. At the moment,
	//	only the CodeView debug format is supported
	//	Access the debug directories
	unsigned long	DataSize;
	const unsigned char *	pDirEntry;
	
	//	Get a pointer to the first debug directory.
	{
		//	Initial structure = a dos stub header, then image nt headers, which contains the pointers
		//	to the image directories (resources, debug data, import and export tables, and other stuff).
		//	The debug directories should be at position 6 (offset 144) in this array
		const unsigned char *		pDataPtr	= m_pMapAddress;
		const ImageDosStubHeader *	pDosHdr		= _GetCastDataPtr<ImageDosStubHeader>(pDataPtr);
		const ImageNTHeaders *		pNT			= (ImageNTHeaders*)(m_pMapAddress + pDosHdr->m_LFA);
		pDirEntry								= m_pMapAddress + pNT->m_DataDirectory[6].m_VAddr;
		DataSize								= pNT->m_DataDirectory[6].m_Size;
	}
  	//	If we got some debug directory data...
	if ((pDirEntry != m_pMapAddress) && (DataSize > 0))
	{
		//	... process it
		const ImageDebugDirectory  *	pDebugDir		= (ImageDebugDirectory*)pDirEntry;
		const int						NumDebugDirs	= int(DataSize) / sizeof(ImageDebugDirectory);
  		for (int i = 0; i < NumDebugDirs; i++, pDebugDir++)
		{
			switch (pDebugDir->m_Type)
			{
				//	Unprocessed debug directories
				case IMAGE_DEBUG_TYPE_UNKNOWN	:
				case IMAGE_DEBUG_TYPE_FPO		:
				case IMAGE_DEBUG_TYPE_MISC		:
				case IMAGE_DEBUG_TYPE_EXCEPTION	:
				case IMAGE_DEBUG_TYPE_FIXUP		:
				case IMAGE_DEBUG_TYPE_BORLAND	:	break;
  				//	Processed debug directories
				case IMAGE_DEBUG_TYPE_COFF		:
					ProcessCOFFDbgDir(pDebugDir);	//	TODO...
					break;
  				case IMAGE_DEBUG_TYPE_CODEVIEW	:
					ProcessCVDbgDir(pDebugDir);
					break;
			};
		}
	}
  	return UsingCOFF() || UsingCV();
} //GPEMap::ProcessPEFile
void GPEMap::ClosePEFile()
{
	//	Close down the file handles and stuff
	if (m_pMapAddress)
	{
		UnmapViewOfFile(m_pMapAddress);
		CloseHandle(m_hMapFile);
		CloseHandle(m_hFile);
  		m_pMapAddress	= 0;
		m_hMapFile		= 0;
		m_hFile			= 0;
	}
  } //GPEMap::ClosePEFile
void GPEMap::ProcessCVDbgDir(const ImageDebugDirectory * pDebugDir)
{
	//assert(UsingCV());
	
	//	Verify the CV signature before we do anything else
	m_pLFO		= m_pMapAddress + pDebugDir->m_PointerToRawData;
	m_pCVSig	= (const CVSignature *)m_pLFO;
	if (!CheckCVSig(*((unsigned int*)&m_pCVSig->m_Signature)))
	{
		return;
	}
  
	const CVDirHeader	*	pCVDirHeader;
	const CVSubSection	*	pCVSubSections;
  	//	Read in the directory header and subsections
	const unsigned char *	pDataPtr	= m_pLFO + m_pCVSig->m_FilePos;
	pCVDirHeader						= _GetCastDataPtr<CVDirHeader>(pDataPtr);
	pCVSubSections						= _GetCastDataPtr<CVSubSection>(pDataPtr);
  
	//	Run through and process each of the debug directories
	{
		const int				MaxSrcMods = 1024;
		const CVSubSection *	SrcModList[MaxSrcMods];
		const CVSubSection *	AlignSymList[MaxSrcMods];
  		//	Parse each subsection
		for (unsigned int i = 0; i < pCVDirHeader->m_NumDirEntries; i++)
		{
			const CVSubSection * pSubSection = &(pCVSubSections[i]);
			switch (ModuleType(pSubSection->m_SubSection))
			{
				case eMT_sstModule		:
				case eMT_sstTypes		:
				case eMT_sstPublic		:
				case eMT_sstPublicSym	:
				case eMT_sstSymbols		:
				case eMT_sstSrcLnSeg	:
				case eMT_sstLibraries	:
				case eMT_sstGlobalPub	:
				case eMT_sstGlobalTypes	:
				case eMT_sstMPC			:
				case eMT_sstSegMap		:
				case eMT_sstSegName		:
				case eMT_sstPreComp		:
				case eMT_unused			:
				case eMT_sstOffsetMap16	:
				case eMT_sstOffsetMap32	:
				case eMT_sstFileIndex	:
				case eMT_sstStaticSym	:	/* Boring */ break;
					
				case eMT_sstAlignSym	:
					{
						//	Alignment symbols table - this is somewhat glossed over in the documentation, but
						//	it seems that for each module, there's a matching one of these that contains all
						//	the symbol information for it.
						AlignSymList[m_NumCVAlignSyms++] = pSubSection;
						break;
					}; //case eMT_sstAlignSym
				case eMT_sstSrcModule	:
					{
						//	Module source table - there's one of these per module in PE file
						SrcModList[m_NumCVSrcMods++] = pSubSection;
						break;
					}; //case eMT_sstSrcModule
				case eMT_sstGlobalSym	: 
					{
						//	This is the global symbol lookup table - instead of searching each module for a
						//	proc address, this table contains an address sort list that allows you to look
						//	up the address directly in one of the alignment symbol tables. Note that there's
						//	only one of these per PE file, although there's also a static symbol table and a 
						//	public symbol table (eMT_sstStaticSym and eMT_sstPublic) that are supposed to
						//	do the same sort of thing.
						//assert(m_pCVGlobalSym);
						m_pCVGlobalSym = pSubSection;
						break;
					}; //case eMT_sstGlobalSym
			};
		}
		
		//	Transfer SrcModList and AlignSymList to m_pCVSrcMods and m_pCVAlignSym respectively
		{
			int i;
			m_pCVSrcMods	= new const CVSubSection *[m_NumCVSrcMods];
			m_pCVAlignSym	= new const CVSubSection *[m_NumCVAlignSyms];
			for (i = 0; i < m_NumCVSrcMods;		m_pCVSrcMods[i]	= SrcModList[i],i++);
			for (i = 0; i < m_NumCVAlignSyms;	m_pCVAlignSym[i]= AlignSymList[i],i++);
		}
	}
  	
	m_pCVDbgDir	= pDebugDir;
} //GPEMap::ProcessCVDbgDir
void GPEMap::ProcessCOFFDbgDir(const ImageDebugDirectory * pDebugDir)
{
	//	TODO...
	
} //GPEMap::ProcessCOFFDbgDir
  void GPEMap::OutputErrorF(const TCHAR * pError,...) const
{
	TCHAR	FullMsg[1024];
  	va_list	VArgs;
	va_start(VArgs,pError);
	_vsnprintf(FullMsg,1024,pError,VArgs);
	va_end(VArgs);
  	MessageBox(0,FullMsg,_T("GPEMap Error"),MB_OK);
} //GPEMap::OutputErrorF
  bool GPEMap::CheckCVSig(const unsigned int Sig)
{
  	if ((Sig == (unsigned int)'11BN') ||
		(Sig == (unsigned int)'90BN'))	//	I think NB09 is ok too...
	{
		return true;
	}
  	if (Sig == (unsigned int)'01BN')
	{
		//	The PDB format is based on a modified CodeView structure that is not publicly available. Please
		//	mail me at andrewp@redlemon.com if you know anything about it...
		OutputErrorF(_T("GPEMap::CheckCVSig - GPEMap does not support external program databases. If you are\n")
					 _T("using Visual Studio 6.0 then remove the check from 'Use Program Database' in\n")
					 _T("Project Settings->Link->Customize\n"));
		return false;
  	}
	else
	{
		//	(should be ok for unicode)
		OutputErrorF(_T("GPEMap::CheckCVSig - Unsupported codeview format ('%c%c%c%c')\n"),
					 ((char*)&Sig)[0],((char*)&Sig)[1],((char*)&Sig)[2],((char*)&Sig)[3]);
	}
  	return false;
} //GPEMap::CheckCVSig
bool GPEMap::GetCVCodeSource(const void * pAddr,GPEMap::CodeSourceData * pData) const
{
	//	Map pAddr
	const unsigned int	AddrUInt	= _ConvertAddrToMap(pAddr,m_SystemPageSize);
  	//	Find the source information for AddrUInt
	if (!CVLookupAddrSrc(AddrUInt,pData))
	{
		return false;
	}
  	//	Find the symbol information for AddrUInt
	return CVLookupAddrSym(AddrUInt,pData);
} //GPEMap::GetCVCodeSource
bool GPEMap::CVLookupAddrSym(const unsigned int AddrUInt,CodeSourceData * pData) const
{
	if (!m_pCVGlobalSym)
	{
		return false;
	}
  	const unsigned char *		pGlobalSymBase	= m_pLFO + m_pCVGlobalSym->m_LFO,
						*		pDataPtr		= pGlobalSymBase;
	const CVSymTableHeader *	pSymTableHdr	= _GetCastDataPtr<CVSymTableHeader>(pDataPtr);
	const unsigned char *		pSymTable		= pDataPtr;
  	//assert(SymHashFnc == 10);
	//assert(AddrHashFnc == 12);
	pDataPtr = pDataPtr + (pSymTableHdr->m_NumSymBytes + pSymTableHdr->m_NumSymHashBytes);
	const unsigned short	NumSegs			= *_GetCastDataPtr<unsigned short>(pDataPtr,2 /* (incl. padding) */);
	const unsigned int *	pSegTable		= _GetCastDataPtr<unsigned int>(pDataPtr,NumSegs);
	const unsigned int *	pOffsetCounts	= _GetCastDataPtr<unsigned int>(pDataPtr,NumSegs);
	const unsigned int *	pOffsetTable	= _GetCastDataPtr<unsigned int>(pDataPtr,NumSegs * 2);
  	pData->m_Fnc[0] = _T('\0');
  	//	TODO : Binary search
	for (unsigned int CurSeg = 0; CurSeg < NumSegs; CurSeg++)
	{
		const CVAddrHashOffset * pCurOff = (CVAddrHashOffset*)pOffsetTable;
  		for (unsigned int i = 0; i < pOffsetCounts[CurSeg]; i++)
		{
  			if ((AddrUInt >= pCurOff[0].m_CurMemOffset) && (AddrUInt <= pCurOff[1].m_CurMemOffset))
			{
				const unsigned char *	pSymData	= pSymTable + pCurOff[0].m_CurSymOffset;
				const unsigned short	SymLen		= *_GetCastDataPtr<unsigned short>(pSymData);
				const unsigned short	SymType		= *_GetCastDataPtr<unsigned short>(pSymData);
  				switch (SymType)
				{
					case S_PROCREF	:
						{
							const CVSymProcRef *	pProcRef	= (CVSymProcRef*)pSymData;
							const unsigned char *	pModAddr	= m_pLFO + m_pCVAlignSym[pProcRef->m_Module-1]->m_LFO;
							const CVSymProcStart *	pProcData	= (CVSymProcStart *)&(pModAddr[pProcRef->m_SymOffset+4]);
  							_sntprintf(pData->m_Fnc,eMaxFncName,_T("%.*s"),pProcData->m_NameLen,pProcData->m_Name);
  							return true;
						};
  					//	Can also do S_DATAREF for variables (see MSDN documentation and CVSymData for details)
				};
  				//	Unhandled symbol type...
				return false;
			}
  			pCurOff++;
		}
	}
  
	return false;
} //GPEMap::CVLookupAddrSym
bool GPEMap::CVLookupAddrSrc(const unsigned int AddrUInt,CodeSourceData * pData) const
{
	//	Run through the source module subsections
	for (int i = 0; i < m_NumCVSrcMods; i++)
	{
		//	Goto the i'th module source section
		const unsigned char *		pSrcModBase		= m_pLFO + m_pCVSrcMods[i]->m_LFO,
							*		pDataPtr		= pSrcModBase;
  		//	Get the module header information and the base source and segment data
		const CVSrcModuleHeader *	pSrcModHdr		= _GetCastDataPtr<CVSrcModuleHeader>(pDataPtr);
		const unsigned int *		pBaseSrcPtrs	= _GetCastDataPtr<unsigned int>(pDataPtr,pSrcModHdr->m_NumFiles);
		const unsigned int *		pBaseSegPairs	= _GetCastDataPtr<unsigned int>(pDataPtr,pSrcModHdr->m_NumSegs * 2);
		const unsigned short *		pBaseSegs		= _GetCastDataPtr<unsigned short>(pDataPtr,pSrcModHdr->m_NumSegs);
  
		//	TODO : Quick check of base seg pairs to reject this module fast
		bool bInModule = false;
		const unsigned int * pCurBasePair = pBaseSegPairs;
		for (unsigned int qs = 0; qs < pSrcModHdr->m_NumSegs; qs++)
		{
			if ((AddrUInt >= pCurBasePair[0]) && (AddrUInt <= pCurBasePair[1]))
			{
				bInModule = true;
				break;
			}
			pCurBasePair += 2;
		}
  		if (!bInModule)
		{
			//	Address is not in this module... goto the next one
			continue;
		}
  
		//	RUn through the source files
		for (unsigned int f = 0; f < pSrcModHdr->m_NumFiles; f++)
		{
			//	Goto the data for this source file
			pDataPtr = pSrcModBase + pBaseSrcPtrs[f];
  			//	Get the name, line and segment information for this file
			const unsigned short	NumSegs		= *_GetCastDataPtr<unsigned short>(pDataPtr);
			const unsigned short	Pad			= *_GetCastDataPtr<unsigned short>(pDataPtr);
			const unsigned int *	pSrcLines	= _GetCastDataPtr<unsigned int>(pDataPtr,NumSegs);
			const unsigned int *	pSegPairs	= _GetCastDataPtr<unsigned int>(pDataPtr,NumSegs * 2);
  			//	the documentation lies! FNameLen is only one byte...
			const unsigned char		FNameLen	= *_GetCastDataPtr<unsigned char>(pDataPtr);
			const char *			pFName		= _GetCastDataPtr<char>(pDataPtr,FNameLen);
  			//	Run through the file segments
			const unsigned int * pCurPair = pSegPairs;
			for (unsigned int s = 0; s < NumSegs; s++)
			{
				if ((AddrUInt >= pCurPair[0]) && (AddrUInt<= pCurPair[1]))
				{
					//	Found it! Find the line number it appears on
					const unsigned char *		pTmpDataPtr	= pSrcModBase + pSrcLines[s];
					const unsigned short		Seg			= *_GetCastDataPtr<unsigned short>(pTmpDataPtr);
					const unsigned short		Pair		= *_GetCastDataPtr<unsigned short>(pTmpDataPtr);
					const unsigned int *		pOffsets	=  _GetCastDataPtr<unsigned int>(pTmpDataPtr,Pair);
					const unsigned short *		pLines		=  _GetCastDataPtr<unsigned short>(pTmpDataPtr,Pair);
  					//	Find the matching offset
					for (int o = 0; o < Pair; o++)
					{
						if (pOffsets[o] >= AddrUInt)
						{
							_sntprintf(pData->m_File,eMaxSourceFileName,_T("%.*s"),FNameLen,pFName);
							pData->m_Line = pLines[o == 0 ? o : o-1];
  							return true;
						}
					}
  					return false;
				}
  				pCurPair += 2;
			}
		}
	}
  	return false;
} //GPEMap::CVLookupAddrSrc
bool GPEMap::GetCodeSource(const void * pAddr,CodeSourceData * pData) const
{
	if (!IsOK())
	{
		return false;
	}
  	//assert(pData);
	//	Initialize pData
	pData->m_File[0]	= _T('\0');
	pData->m_Line		= 0;
	pData->m_Fnc[0]		= _T('\0');
  	//	Process either using CodeView or COFF
	if (UsingCV())
	{
		return GetCVCodeSource(pAddr,pData);
	}
	else if (UsingCOFF())
	{
		//	TODO...
	}
  
	return false;
} //GPEMap::GetCodeSource
bool GPEMap::GetStackWalkCodeSource(const int			NumCalls,
									const void * *		pStackAddrs,
									CodeSourceData *	pData) const
{
	//	Run through the stack address array
	for (int i = 0; i < NumCalls; i++)
	{
		if (pStackAddrs[i])
		{
			//	Find the source file,line and procedure / data name
			if (!GetCodeSource(pStackAddrs[i],&(pData[i])))
			{
				return false;
			}
  			//	Helpful...
			TCHAR tmpStr[256];
			sprintf(tmpStr,_T("%s(%d) : Found in '%s'\n"),pData[i].m_File,pData[i].m_Line,pData[i].m_Fnc);
			OutputDebugString(tmpStr);
		}
	}
  	return true;
} //GPEMap::GetStackWalkCodeSource
  GPEMap::~GPEMap()
{
	if (IsOK())
	{
		ClosePEFile();
  		//	Delete the source module and alignment symbols arrays
		if (m_pCVSrcMods)
		{
			delete []m_pCVSrcMods;
		}
  		if (m_pCVAlignSym)
		{
			delete []m_pCVAlignSym;
		}
	}
  } //GPEMap::~GPEMap
bool GPEMap::_WalkTheStack(const int	NumCallsToCheck,
						   const int	NumCallsToSkip,
						   void * *		pStackAddrs)
{
	//	Check the parameters
	if (!NumCallsToCheck || !pStackAddrs)
	{
		return false;
	}
  	//	Initialize the stack walk variables and clear out the stack address array
	int CurStackIn	= 0;
	int CurSkip		= 0;
	int ParentEBP;
  	for (int i = 0; i < NumCallsToCheck; pStackAddrs[i++] = 0);
  	//	Store the parent EBP to begin with
	__asm MOV ParentEBP, EBP
  	do
	{
		//	Get the caller pointer and move on to the caller parent
		void * pCaller = *((void**)ParentEBP + 1);
		ParentEBP = *((int*)ParentEBP);
  		//	Either skip or store the caller in the stack address array
		if (CurSkip < NumCallsToSkip)
		{
			CurSkip++;
		}
		else
		{
			pStackAddrs[CurStackIn++] = pCaller;
		}
		
		//	Until ParentEBP is null or we've reached the end of the stack address array
	} while ((ParentEBP != 0) && (CurStackIn < NumCallsToCheck));
  
	return true;
} //GPEMap::_WalkTheStack
  
#ifdef _TESTING
  	//
	//	TESTING 
	//
	class TestClass
	{
	public :
		void FinalDoStuff(GPEMap * pMap)
		{
			const int				TestStackSize = 7;
			void *					TestStack[TestStackSize];
			GPEMap::CodeSourceData	TestStackSource[TestStackSize];
  			if (GPEMap::_WalkTheStack(TestStackSize,0,TestStack))
			{
				pMap->GetStackWalkCodeSource(TestStackSize,(const void**)TestStack,TestStackSource);
			}
		}
  		static void _PenultimateDoStuff(GPEMap * pMap)
		{
			TestClass t;
			t.FinalDoStuff(pMap);
		}
	};
  	void _DoMoreStuff(GPEMap * pMap)
	{
		TestClass::_PenultimateDoStuff(pMap);
	}
  	void _DoStuff(GPEMap * pMap)
	{
		_DoMoreStuff(pMap);
	}
  	void _TestGPEMap(const TCHAR * pPEFile)
	{
		if (pPEFile)
		{
			GPEMap	tmpMap(pPEFile);
  			_DoStuff(&tmpMap);
		}
		else
		{
			GPEMap tmpMap;
			_DoStuff(&tmpMap);
		}
	}
  #endif
   |