text: #include <windows.h>
  #include "mdmpRipper.h"
  #include <vgui/IVGui.h>
  #include "vgui_controls/MessageMap.h"
  #include "vgui_controls/MenuBar.h"
  #include "vgui_controls/Menu.h"
  #include "vgui_controls/MessageBox.h"
  #include "tier1/keyvalues.h"
  #include "vgui/ISurface.h"
  #include <vgui/ILocalize.h>
  #include "vgui_controls/Frame.h"
  #include "CMDModulePanel.h"
  #include "vgui_controls/ListPanel.h"
  #include <vgui_controls/RichText.h>
  #include "keyvalues.h"
  #include "vgui/ISystem.h"
  #include "vgui_controls/FileOpenDialog.h"
  #include "isqlwrapper.h"
  #include "CMDRipperMain.h"
  
  extern ISQLWrapper *g_pSqlWrapper;
  
  using namespace vgui;
  
  
  
  CMDModulePanel::CMDModulePanel( vgui::Panel *pParent, const char *pName ) :
  	BaseClass( pParent, pName, true )
  {
  	m_pTokenList = new ListPanel(this, "ModuleList");
  	m_pTokenList->AddColumnHeader(0, "name", "Module Name", 600, 0);
  	m_pTokenList->AddColumnHeader(1, "version", "Version", 100, 0);	
  	m_pTokenList->AddColumnHeader(2, "count", "Count", 86, 0);	
  	m_pTokenList->AddActionSignalTarget( this );
  
  	m_pAnalyzeText = new RichText(this, "AnalyzeText");
  	m_pAnalyzeText->SetVerticalScrollbar(true);
  	LoadControlSettings( "MDModulePanel.res" );	
  	m_pAnalyzeText->InsertString("Initializing...\n");
  
  	InitializeDebugEngine();
  	LoadKnownModules();
  
  	m_hThread = NULL;
  
  //	SetTitleBarVisible( false );
  //	SetSizeable( false );
  
  	
  	//SETUP_PANEL( this );	
  }
  
  CMDModulePanel::~CMDModulePanel( void )
  {
  	ReleaseDebugEngine( );
  }
  
  void CMDModulePanel::OnKeyCodeTyped( KeyCode code )
  {
  	switch ( code )
  	{
  	case KEY_G:
  		UpdateKnownDB( "GOOD" );
  		break;
  	case KEY_B:
  		UpdateKnownDB( "BAD" );
  		break;
  	case KEY_U:
  		UpdateKnownDB( "UNKNOWN" );
  		break;
  	case KEY_F:
  		ModuleLookUp();
  		break;
  	}
  }
  	
  void CMDModulePanel::OnCommand( const char *pCommand )
  {	
  	if ( !Q_strcmp( pCommand, "Close" ) )
  	{
  		//we want to close
  		Close();	
  	}	
  	if ( !Q_strcmp( pCommand, "ModuleLookUp" ) )
  	{
  		ModuleLookUp();
  	}	
  	if ( !Q_strcmp( pCommand, "SetGood" ) )
  	{
  		UpdateKnownDB( "GOOD" );
  	}
  	if ( !Q_strcmp( pCommand, "SetBad" ) )
  	{
  		UpdateKnownDB( "BAD" );
  	}
  	if ( !Q_strcmp( pCommand, "SetUnknown" ) )
  	{
  		UpdateKnownDB( "UNKNOWN" );
  	}
  }
  
  void CMDModulePanel::Close()
  {
  	if ( this )
  	{
  		m_pTokenList->DeleteAllItems();
  		m_MiniDumpList.RemoveAll();
  		m_knownModuleList.RemoveAll();
  		m_pAnalyzeText->SetText("");
  		SetVisible( false );
  		KeyValues *kv = new KeyValues( "Refresh" );
  		this->PostActionSignal( kv );
  	}
  }
  
  void CMDModulePanel::Create( CUtlVector<CMiniDumpObject *> *pMiniDump )
  {
  	LoadKnownModules();
  
  	for ( int i = 0; i < pMiniDump->Count(); i++ )
  	{
  		pMiniDump->Element(i)->PopulateListPanel( m_pTokenList, true );
  	}
  }
  
  void CMDModulePanel::Create( const char *filename )
  {
  	if ( g_pFullFileSystem->FileExists( filename ) )
  	{
  		LoadKnownModules();
  
  		CMiniDumpObject *newMDObj = new CMiniDumpObject( filename, &m_knownModuleList );
  		m_MiniDumpList.AddToTail( newMDObj );
  		newMDObj->PopulateListPanel( m_pTokenList, false );	
  
  		AnalyzeDumpFile( filename );
  	}
  }
  
  void CMDModulePanel::ModuleLookUp()
  {
  	int selectedIndex = m_pTokenList->GetSelectedItem( 0 );
      void *kv = m_pTokenList->GetItem( selectedIndex );
  	if ( kv )
  	{
  		const char *val = ((KeyValues *)kv)->GetString( "name", "" );
  		if ( val )
  		{
  			const char *moduleName = strrchr( val, '\\' ) + 1;
  			char google[1024] = "";
  			sprintf( google, "http://www.google.com/search?hl=en&q=%s", moduleName);	
  			KeyValues *kvPost = new KeyValues( "ModuleLookUp", "url", google );
  			this->PostActionSignal( kvPost );						
  		}
  	}
  }
  
  void SeparateVersion( const char *version, char *v1buf, char *v2buf, char *v3buf, char *v4buf )
  {
  	const char *endV1 = strchr( version, '.' )+1;
  	const char *endV2 = strchr( endV1+1, '.' )+1;
  	const char *endV3 = strchr( endV2+1, '.' )+1;
  	_mbsnbcpy( (unsigned char *)v1buf, (const unsigned char*)version, endV1 - version );
  	v1buf[endV1 - version - 1] = 0;
  	_mbsnbcpy( (unsigned char *)v2buf, (const unsigned char*)endV1, endV2 - endV1 );
  	v2buf[endV2 - endV1 - 1] = 0;
  	_mbsnbcpy( (unsigned char *)v3buf, (const unsigned char*)endV2, endV3 - endV2 );
  	v3buf[endV3 - endV2 - 1] = 0;
  	strcpy( v4buf, endV3 );
  }
  		
  void SetKeyValueColor( char *type, KeyValues *kv, bool knownVersion )
  {
  	int colorValue = 255;
  	if( !knownVersion )
  		colorValue = 155;
  
  	if ( !Q_strcmp( "GOOD", type ) )
  	{
  		((KeyValues *)kv)->SetColor( "cellcolor", Color(0,colorValue,0,255));
  	}
  	else if ( !Q_strcmp( "BAD", type ) )
  	{
  		((KeyValues *)kv)->SetColor( "cellcolor", Color(colorValue,0,0,255));
  	}
  	else
  	{
  		((KeyValues *)kv)->SetColor( "cellcolor", Color(255,255,0,255));
  	}
  }
  
  void CMDModulePanel::UpdateKnownDB( char *type )
  {
  	int selectedIndex = m_pTokenList->GetSelectedItem( 0 );
      void *kv = m_pTokenList->GetItem( selectedIndex );
  	char v1buf[10];
  	char v2buf[10];
  	char v3buf[10];
  	char v4buf[10];
  	char name[65];
  	char keybuf[10];
  	if ( kv )
  	{		
  		SetKeyValueColor( type, (KeyValues *)kv, true );
  		int key = ((KeyValues *)kv)->GetInt( "key" );
  		itoa( key, keybuf, 10 );
  		strcpy( name, strrchr(((KeyValues *)kv)->GetString( "name" ), '\\')+1);
  		SeparateVersion( ((KeyValues *)kv)->GetString("version"), v1buf, v2buf, v3buf, v4buf );		
  		if ( key == 0 )
  		{
  			//as far as we know, this is a non-existant module.
  			if ( !Q_strcmp( type, "UNKNOWN" ) )
  			{
  				return;
  			}
  			else 
  			{
  				char query[1024];
  				sprintf( query, "select * from knownmodules where name = \"%s\" and version1 = %s and version2 = %s and version3 = %s and version4 = %s;",
  					name, v1buf, v2buf, v3buf, v4buf );				
  				IResultSet *results = g_pSqlWrapper->PResultSetQuery( query );	// do the query
  				if ( !results )
  				{
  					return;
  				}
  
  				int numResults = results->GetCSQLRow();
  				if ( numResults > 0 )
  				{
  					//there is an entry... get our module list up to date with this entry
  
  					const ISQLRow *row = results->PSQLRowNextResult();		
  					Assert( row != NULL );
  					int realKey = row->NData(0);
  					const char *realType = row->PchData(6);
  					g_pSqlWrapper->FreeResult();
  
  					((KeyValues *)kv)->SetInt( "key", realKey );											
  					
  					if ( !Q_strcmp( realType, type ) )
  					{
  						//this user was out of sync with the database.  It doesn't actually need updating.
  						return;
  					}
  					else
  					{
  						char update[1024];
  						sprintf( update, "update knownmodules set type=\"%s\" where id = %i;", type, realKey);
  						g_pSqlWrapper->BInsert( update );						
  					}
  				}
  				else
  				{
  					g_pSqlWrapper->FreeResult();
  					//it isn't in there.  Let's add it.
  					char update[1024];
  					sprintf( update, "insert into knownmodules set name = \"%s\", version1 = %s, version2 = %s, version3 = %s, version4 = %s, type = \"%s\";",
  						name, v1buf, v2buf, v3buf, v4buf, type);
  					g_pSqlWrapper->BInsert( update );
  					results = g_pSqlWrapper->PResultSetQuery( query );	// do the query		
  					int numResults = results->GetCSQLRow();
  					if ( numResults > 0 )
  					{
  						const ISQLRow *row = results->PSQLRowNextResult();		
  						Assert( row != NULL );
  						int realKey = row->NData(0);
  						((KeyValues *)kv)->SetInt( "key", realKey );						
  					}                    
  					g_pSqlWrapper->FreeResult();
  				}
  			}
  		}
  		else
  		{
  			char query[1024];
  			sprintf( query, "select * from knownmodules where id = %i;",
  				key);				
  			IResultSet *results = g_pSqlWrapper->PResultSetQuery( query );	// do the query		
  
  			int numResults = results->GetCSQLRow();
  			if ( numResults > 0 )
  			{
  				//there is an entry... update it with the new info...
  
  				const ISQLRow *row = results->PSQLRowNextResult();		
  				Assert( row != NULL );
  				Assert( numResults == 1 );
  				Assert( !Q_stricmp( name, row->PchData(1) ) && atoi( v1buf ) == row->NData(2) && atoi( v2buf ) == row->NData(3) && 
  					atoi( v3buf ) == row->NData(4) && atoi( v4buf ) == row->NData(5) );
  				int realKey = row->NData(0);
  				const char *realType = row->PchData(6);
  				g_pSqlWrapper->FreeResult();
  
  				if ( !Q_strcmp( realType, type ) )
  				{
  					//we don't need to update... it is already updated already
  					return;
  				}								
                  
  				char update[1024];
  				sprintf( update, "update knownmodules set type=\"%s\" where id = %i;", type, realKey);
  				g_pSqlWrapper->BInsert( update );		
  				
  			}
  			else
  			{
  				//the module entry was mis-keyed.  First, check for an existing entry of this module.
  				char query[1024];
  				sprintf( query, "select * from knownmodules where name = \"%s\" and version1 = %s and version2 = %s and version3 = %s and version4 = %s;",
  					name, v1buf, v2buf, v3buf, v4buf );	
  				IResultSet *results = g_pSqlWrapper->PResultSetQuery( query );	// do the query		
  				
  				int numResults = results->GetCSQLRow();
  				if ( numResults > 0 )
  				{
                      //there is an existing entry.  Update its type and update the key for this keyvalue;
  					const ISQLRow *row = results->PSQLRowNextResult();
  					int realKey = row->NData(0);
  					((KeyValues *)kv)->SetInt( "key", realKey );
  					g_pSqlWrapper->FreeResult();
  					char update[1024];
  					sprintf( update, "update knownmodules set type=\"%s\" where id = %i;", type, realKey);
  					g_pSqlWrapper->BInsert( update );
  				}
  				else
  				{
  					g_pSqlWrapper->FreeResult();
  					//no exisiting entry.  Insert it.
  					char update[1024];
  					sprintf( update, "insert into knownmodules set name = \"%s\", version1 = %s, version2 = %s, version3 = %s, version4 = %s, type = \"%s\";",
  						name, v1buf, v2buf, v3buf, v4buf, type);
  					g_pSqlWrapper->BInsert( update );
  					results = g_pSqlWrapper->PResultSetQuery( query );	// do the query		
  					int numResults = results->GetCSQLRow();
  					if ( numResults > 0 )
  					{
  						const ISQLRow *row = results->PSQLRowNextResult();		
  						Assert( row != NULL );
  						int realKey = row->NData(0);
  						((KeyValues *)kv)->SetInt( "key", realKey );						
  					}                    
  					g_pSqlWrapper->FreeResult();
  				}
  			}
  		}
  	}
  }
  
  
  void CMDModulePanel::OnCompare( KeyValues *data )
  {
  	LoadKnownModules();
  
  	CUtlVector<HANDLE> *pMiniDumpHandles = (CUtlVector<HANDLE> *)(void *)data->GetInt( "handlePointer" );
  	
  
  	DWORD error;
  	int returnValue = 0;
  	for( int i = 0; i < pMiniDumpHandles->Count(); i++ )
  	{
  		m_MiniDumpList.AddToTail( new CMiniDumpObject( pMiniDumpHandles->Element( i ), &m_knownModuleList  ) );
  		returnValue = CloseHandle( pMiniDumpHandles->Element( i ) );
  		error = GetLastError();
  	}
  	Create( &m_MiniDumpList );
  	SetVisible( true );
  	MoveToFront();
  
  	pMiniDumpHandles->RemoveAll();
  	system("rmdir c:\\minidumptool /s/q");		
  }
  
  
  void CMDModulePanel::OnDbgOutput( int iMask, const char *pszDebugText)
  {
  	if ( m_pAnalyzeText && pszDebugText )
  	{
  		m_pAnalyzeText->InsertString( pszDebugText );
  	}
  }
  
  
  DWORD WINAPI CMDModulePanel::StaticAnalyzeThread( LPVOID lParam )
  {
  	CMDModulePanel *pClass = (CMDModulePanel *)lParam;
  	if ( pClass )
  	{
  		pClass->AnalyzeThread( );
  	}
  
  	return ( 0 );
  }
  
  void CMDModulePanel::LoadKnownModules()
  {
  	if ( m_knownModuleList.Count() > 0 )
  		return;
  
  	char rgchQueryBuf[ 1024 ] = "SELECT * from knownmodules;";
  	IResultSet *results = g_pSqlWrapper->PResultSetQuery( rgchQueryBuf );
  
  	if ( !results )
  	{
  		ivgui()->DPrintf( "LoadKnownModules() results are NULL" );
  		VGUIMessageBox( GetParent(), "Error", "Unable to retrieve known modules from database" );
  		return;
  	}
  
  	for ( int i = 0; i < results->GetCSQLRow(); i++ )
  	{
  		module newModule;
  		const ISQLRow *row = results->PSQLRowNextResult();		
  		Assert( row != NULL );
  		newModule.key = row->NData(0);
  		strcpy( newModule.name, row->PchData(1));
  		newModule.versionInfo.v1 = row->NData(2);
  		newModule.versionInfo.v2 = row->NData(3);
  		newModule.versionInfo.v3 = row->NData(4);
  		newModule.versionInfo.v4 = row->NData(5);		
  		if ( !Q_strcmp( row->PchData(6), "GOOD" ) )
  		{
  			newModule.myType = GOOD;
  		}
  		else if ( !Q_strcmp( row->PchData(6), "BAD" ) )
  		{
  			newModule.myType = BAD;
  		}
  		else
  		{
  			newModule.myType = UNKNOWN;
  		}
  		m_knownModuleList.AddToTail( newModule );
  	}
  
  	g_pSqlWrapper->FreeResult();
  }
  
  
  void CMDModulePanel::InitializeDebugEngine( void )
  {
      // Start things off by getting an initial interface from
      // the engine.  This can be any engine interface but is
      // generally IDebugClient as the client interface is
      // where sessions are started.
      if ( S_OK == DebugCreate( __uuidof ( IDebugClient ),
                                   (void**)&m_pDbgClient ) )
      {
          m_pDbgClient->QueryInterface( __uuidof ( IDebugControl ),
  									  ( void** )&m_pDbgControl );
  		m_pDbgClient->QueryInterface( __uuidof ( IDebugSymbols2 ),
  									  ( void** )&m_pDbgSymbols );
  
  		// Set out Panel to receive the debug outputs from the engine
  		m_cDbgOutput.SetOutputPanel( GetVPanel() );
  
  		// Install output callbacks so we get any output that the
  		// later calls produce.
  		m_pDbgClient->SetOutputCallbacks(&m_cDbgOutput);
  
  		if ( m_pDbgSymbols )
  		{
  			// Make sure we have a symbol path to use
  			char szSymbolSrv[ 512 ] = { 0 };
  			ExpandEnvironmentStrings( "%_NT_SYMBOL_PATH%", szSymbolSrv, sizeof (szSymbolSrv) );
  			if ( !Q_stricmp( "%_NT_SYMBOL_PATH%", szSymbolSrv ) )
  			{
  				ivgui()->DPrintf( "Setting symbol server" );
  				Q_strcpy( szSymbolSrv, "SRV*c:\\localsymbols*\\\\perforce\\symbols*http://msdl.microsoft.com/download/symbols" );
  				m_pDbgSymbols->SetSymbolPath( szSymbolSrv );
  			}
  			m_pDbgSymbols->AddSymbolOptions(SYMOPT_LOAD_LINES);
  		}
      }
  }
  
  void CMDModulePanel::ReleaseDebugEngine( void )
  {
  	// Clean up any resources.
      if ( m_pDbgSymbols != NULL )
      {
          m_pDbgSymbols->Release( );
      }
  
      if ( m_pDbgControl != NULL )
      {
          m_pDbgControl->Release( );
      }
  
      if ( m_pDbgClient != NULL )
      {
          // We don't want to see any output from the shutdown.
          m_pDbgClient->SetOutputCallbacks( NULL );
          
          m_pDbgClient->EndSession( DEBUG_END_PASSIVE );
          
          m_pDbgClient->Release( );
      }
  }
  
  
  void CMDModulePanel::AnalyzeDumpFile( const char *pszDumpFile )
  {
  	if (m_pDbgClient && m_pDbgControl)
  	{
  		char szBuf[ 256 ] = { 0 };
  		Q_snprintf( szBuf, sizeof ( szBuf ), "About to open [%s].\n", pszDumpFile );
  		m_pAnalyzeText->InsertString( szBuf );
  
  		// Everything's set up so open the dump file.
  		m_pDbgClient->OpenDumpFile(pszDumpFile);
  
  		// Finish initialization by waiting for the event that
  		// caused the dump.  This will return immediately as the
  		// dump file is considered to be at its event.
  		m_pDbgControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE);
  
  		DWORD dwThreadId = 0;
  		m_hThread = CreateThread( NULL, 0, StaticAnalyzeThread, (LPVOID)this, 0, &dwThreadId );
  	}
  }
  
  
  DWORD CMDModulePanel::AnalyzeThread( void )
  {
  	if ( m_pDbgControl )
  	{
  		// Tell the debug engine to analyze the current dump file
  		m_pDbgControl->Execute( DEBUG_OUTCTL_THIS_CLIENT,
  								"!analyze -v", 
  								DEBUG_EXECUTE_DEFAULT);
  	}
  
  	CloseHandle( m_hThread );
  	m_pAnalyzeText->InsertString( "Finished analyzing minidump file.\n" );
  
  	return ( 0 );
  }
  
  