You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
763 lines
20 KiB
763 lines
20 KiB
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Xbox console link
|
|
//
|
|
//=====================================================================================//
|
|
|
|
#include "xbox/xbox_console.h"
|
|
#include "xbox/xbox_vxconsole.h"
|
|
#include "tier0/threadtools.h"
|
|
#include "tier0/tslist.h"
|
|
#include "tier0/ICommandLine.h"
|
|
#include "tier0/memdbgon.h"
|
|
|
|
// all redirecting funneled here, stop redirecting in this module only
|
|
#undef OutputDebugStringA
|
|
|
|
#pragma comment( lib, "xbdm.lib" )
|
|
|
|
struct DebugString_t
|
|
{
|
|
unsigned int color;
|
|
char *pString;
|
|
};
|
|
|
|
#define XBX_DBGCOMMANDPREFIX "XCMD"
|
|
#define XBX_DBGRESPONSEPREFIX "XACK"
|
|
#define XBX_DBGPRINTPREFIX "XPRT"
|
|
#define XBX_DBGCOLORPREFIX "XCLR"
|
|
|
|
#define XBX_MAX_RCMDLENGTH 256
|
|
#define XBX_MAX_MESSAGE 2048
|
|
|
|
CThreadFastMutex g_xbx_dbgChannelMutex;
|
|
CThreadFastMutex g_xbx_dbgCommandHandlerMutex;
|
|
static char g_xbx_dbgRemoteBuf[XBX_MAX_RCMDLENGTH];
|
|
static HANDLE g_xbx_dbgValidEvent;
|
|
static HANDLE g_xbx_dbgCmdCompleteEvent;
|
|
bool g_xbx_bUseVXConsoleOutput = true;
|
|
bool g_xbx_bDoSyncOutput;
|
|
static ThreadHandle_t g_xbx_hDebugThread;
|
|
CTSQueue<DebugString_t> g_xbx_DebugStringQueue;
|
|
extern CInterlockedInt g_xbx_numProfileCounters;
|
|
extern unsigned int g_xbx_profileCounters[];
|
|
extern char g_xbx_profileName[];
|
|
int g_xbx_freeMemory;
|
|
|
|
_inline bool XBX_NoXBDM() { return false; }
|
|
|
|
static CXboxConsole XboxConsole;
|
|
|
|
DLL_EXPORT IXboxConsole *GetConsoleInterface()
|
|
{
|
|
return &XboxConsole;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Low level string output.
|
|
// Input string should be stack based, can get clobbered.
|
|
//-----------------------------------------------------------------------------
|
|
static void OutputStringToDevice( unsigned int color, char *pString, bool bRemoteValid )
|
|
{
|
|
if ( !bRemoteValid )
|
|
{
|
|
// local debug only
|
|
OutputDebugStringA( pString );
|
|
return;
|
|
}
|
|
|
|
// remote debug valid
|
|
// non pure colors don't translate well - find closest pure hue
|
|
unsigned int bestColor = color;
|
|
int r = ( bestColor & 0xFF );
|
|
int g = ( bestColor >> 8 ) & 0xFF;
|
|
int b = ( bestColor >> 16 ) & 0xFF;
|
|
if ( ( r && r != 255 ) || ( g && g != 255 ) || ( b && b != 255 ) )
|
|
{
|
|
int r0, g0, b0;
|
|
unsigned int minDist = 0xFFFFFFFF;
|
|
for ( int i=0; i<8; i++ )
|
|
{
|
|
r0 = g0 = b0 = 0;
|
|
if ( i&4 )
|
|
r0 = 255;
|
|
if ( i&2 )
|
|
g0 = 255;
|
|
if ( i&1 )
|
|
b0 = 255;
|
|
unsigned int d = ( r-r0 )*( r-r0 ) + ( g-g0 )*( g-g0 ) + ( b-b0 )*( b-b0 );
|
|
if ( minDist > d )
|
|
{
|
|
minDist = d;
|
|
bestColor = XMAKECOLOR( r0, g0, b0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
// create color string
|
|
char colorString[16];
|
|
sprintf( colorString, XBX_DBGCOLORPREFIX "[%8.8x]", bestColor );
|
|
|
|
// chunk line out, for each cr
|
|
char strBuffer[XBX_MAX_RCMDLENGTH];
|
|
char *pStart = pString;
|
|
char *pEnd = pStart + strlen( pStart );
|
|
char *pNext;
|
|
while ( pStart < pEnd )
|
|
{
|
|
pNext = strchr( pStart, '\n' );
|
|
if ( !pNext )
|
|
pNext = pEnd;
|
|
else
|
|
*pNext = '\0';
|
|
|
|
int length = _snprintf( strBuffer, XBX_MAX_RCMDLENGTH, "%s!%s%s", XBX_DBGPRINTPREFIX, colorString, pStart );
|
|
if ( length == -1 )
|
|
{
|
|
strBuffer[sizeof( strBuffer )-1] = '\0';
|
|
}
|
|
// Send the string
|
|
DmSendNotificationString( strBuffer );
|
|
|
|
// advance past cr
|
|
pStart = pNext+1;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// XBX_IsConsoleConnected
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool CXboxConsole::IsConsoleConnected()
|
|
{
|
|
bool bConnected;
|
|
|
|
if ( g_xbx_dbgValidEvent == NULL )
|
|
{
|
|
// init was never called
|
|
return false;
|
|
}
|
|
|
|
AUTO_LOCK_FM( g_xbx_dbgChannelMutex );
|
|
|
|
bConnected = ( WaitForSingleObject( g_xbx_dbgValidEvent, 0 ) == WAIT_OBJECT_0 );
|
|
|
|
return bConnected;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Output string to listening console. Queues output for slave thread.
|
|
// Needs to be lightweight.
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxConsole::DebugString( unsigned int color, const char* pFormat, ... )
|
|
{
|
|
if ( XBX_NoXBDM() )
|
|
return;
|
|
|
|
va_list args;
|
|
char szStringBuffer[XBX_MAX_MESSAGE];
|
|
int length;
|
|
|
|
// resolve string
|
|
va_start( args, pFormat );
|
|
length = _vsnprintf( szStringBuffer, sizeof( szStringBuffer ), pFormat, args );
|
|
if ( length == -1 )
|
|
{
|
|
szStringBuffer[sizeof( szStringBuffer ) - 1] = '\0';
|
|
}
|
|
va_end( args );
|
|
|
|
if ( !g_xbx_bDoSyncOutput )
|
|
{
|
|
// queue string for delayed output
|
|
DebugString_t debugString;
|
|
debugString.color = color;
|
|
debugString.pString = strdup( szStringBuffer );
|
|
g_xbx_DebugStringQueue.PushItem( debugString );
|
|
}
|
|
else
|
|
{
|
|
bool bRemoteValid = g_xbx_bUseVXConsoleOutput && XBX_IsConsoleConnected();
|
|
OutputStringToDevice( color, szStringBuffer, bRemoteValid );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Waits for debug queue to drain.
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxConsole::FlushDebugOutput()
|
|
{
|
|
while ( g_xbx_DebugStringQueue.Count() != 0 )
|
|
{
|
|
Sleep( 1 );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// _xdbg_strlen
|
|
//
|
|
// Critical section safe.
|
|
//-----------------------------------------------------------------------------
|
|
int _xdbg_strlen( const CHAR* str )
|
|
{
|
|
const CHAR* strEnd = str;
|
|
|
|
while( *strEnd )
|
|
strEnd++;
|
|
|
|
return strEnd - str;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// _xdbg_tolower
|
|
//
|
|
// Critical section safe.
|
|
//-----------------------------------------------------------------------------
|
|
inline CHAR _xdbg_tolower( CHAR ch )
|
|
{
|
|
if( ch >= 'A' && ch <= 'Z' )
|
|
return ch - ( 'A' - 'a' );
|
|
else
|
|
return ch;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// _xdbg_strnicmp
|
|
//
|
|
// Critical section safe.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL _xdbg_strnicmp( const CHAR* str1, const CHAR* str2, int n )
|
|
{
|
|
while ( ( _xdbg_tolower( *str1 ) == _xdbg_tolower( *str2 ) ) && *str1 && n > 0 )
|
|
{
|
|
--n;
|
|
++str1;
|
|
++str2;
|
|
}
|
|
return ( !n || _xdbg_tolower( *str1 ) == _xdbg_tolower( *str2 ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// _xdbg_strcpy
|
|
//
|
|
// Critical section safe.
|
|
//-----------------------------------------------------------------------------
|
|
VOID _xdbg_strcpy( CHAR* strDest, const CHAR* strSrc )
|
|
{
|
|
while ( ( *strDest++ = *strSrc++ ) != 0 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// _xdbg_strcpyn
|
|
//
|
|
// Critical section safe.
|
|
//-----------------------------------------------------------------------------
|
|
VOID _xdbg_strcpyn( CHAR* strDest, const CHAR* strSrc, int numChars )
|
|
{
|
|
while ( numChars>0 && ( *strDest++ = *strSrc++ ) != 0 )
|
|
numChars--;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// _xdbg_gettoken
|
|
//
|
|
// Critical section safe.
|
|
//-----------------------------------------------------------------------------
|
|
void _xdbg_gettoken( CHAR** tokenStream, CHAR* token, int tokenSize )
|
|
{
|
|
int c;
|
|
int len;
|
|
CHAR* data;
|
|
|
|
len = 0;
|
|
|
|
// skip prefix whitespace
|
|
data = *tokenStream;
|
|
while ( ( c = *data ) <= ' ' )
|
|
{
|
|
if ( !c )
|
|
goto cleanUp;
|
|
data++;
|
|
}
|
|
|
|
// parse a token
|
|
do
|
|
{
|
|
if ( len < tokenSize )
|
|
token[len++] = c;
|
|
|
|
data++;
|
|
c = *data;
|
|
} while ( c > ' ' );
|
|
|
|
if ( len >= tokenSize )
|
|
len = 0;
|
|
|
|
cleanUp:
|
|
token[len] = '\0';
|
|
*tokenStream = data;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// _xdbg_tokenize
|
|
//
|
|
// Critical section safe.
|
|
//-----------------------------------------------------------------------------
|
|
int _xdbg_tokenize( CHAR* tokenStream, CHAR** tokens, int maxTokens )
|
|
{
|
|
char token[64];
|
|
|
|
// tokenize stream into seperate tokens
|
|
int numTokens = 0;
|
|
while ( 1 )
|
|
{
|
|
tokens[numTokens++] = tokenStream;
|
|
if ( numTokens >= maxTokens )
|
|
break;
|
|
|
|
_xdbg_gettoken( &tokenStream, token, sizeof( token ) );
|
|
if ( !tokenStream[0] || !token[0] )
|
|
break;
|
|
|
|
*tokenStream = '\0';
|
|
tokenStream++;
|
|
}
|
|
return ( numTokens );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// _xdbg_findtoken
|
|
//
|
|
// Critical section safe. Returns -1 if not found
|
|
//-----------------------------------------------------------------------------
|
|
int _xdbg_findtoken( CHAR** tokens, int numTokens, CHAR* token )
|
|
{
|
|
int i;
|
|
int len;
|
|
|
|
len = _xdbg_strlen( token );
|
|
for ( i=0; i<numTokens; i++ )
|
|
{
|
|
if ( _xdbg_strnicmp( tokens[i], token, len ) )
|
|
return i;
|
|
}
|
|
|
|
// not found
|
|
return -1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// _DebugCommandHandler
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT __stdcall _DebugCommandHandler( const CHAR* strCommand, CHAR* strResponse, DWORD dwResponseLen, PDM_CMDCONT pdmcc )
|
|
{
|
|
CHAR buff[256];
|
|
CHAR* args[8];
|
|
int numArgs;
|
|
|
|
AUTO_LOCK_FM( g_xbx_dbgCommandHandlerMutex );
|
|
|
|
// skip over the command prefix and the exclamation mark
|
|
strCommand += _xdbg_strlen( XBX_DBGCOMMANDPREFIX ) + 1;
|
|
|
|
if ( strCommand[0] == '\0' )
|
|
{
|
|
// just a ping
|
|
goto cleanUp;
|
|
}
|
|
|
|
// get command and optional arguments
|
|
_xdbg_strcpyn( buff, strCommand, sizeof( buff ) );
|
|
numArgs = _xdbg_tokenize( buff, args, sizeof( args )/sizeof( CHAR* ) );
|
|
|
|
if ( _xdbg_strnicmp( args[0], "__connect__", 11 ) )
|
|
{
|
|
if ( numArgs > 1 && atoi( args[1] ) == VXCONSOLE_PROTOCOL_VERSION )
|
|
{
|
|
// initial connect - respond that we're connected
|
|
_xdbg_strcpyn( strResponse, XBX_DBGRESPONSEPREFIX "Connected To Application.", dwResponseLen );
|
|
SetEvent( g_xbx_dbgValidEvent );
|
|
|
|
// notify convar system to send its commands
|
|
// allows vxconsole to re-connect during game
|
|
_xdbg_strcpy( g_xbx_dbgRemoteBuf, "getcvars" );
|
|
XBX_QueueEvent( XEV_REMOTECMD, ( int )g_xbx_dbgRemoteBuf, 0, 0 );
|
|
}
|
|
else
|
|
{
|
|
_xdbg_strcpyn( strResponse, XBX_DBGRESPONSEPREFIX "Rejecting Connection: Wrong Protocol Version.", dwResponseLen );
|
|
}
|
|
goto cleanUp;
|
|
}
|
|
|
|
if ( _xdbg_strnicmp( args[0], "__disconnect__", 14 ) )
|
|
{
|
|
// respond that we're disconnected
|
|
_xdbg_strcpyn( strResponse, XBX_DBGRESPONSEPREFIX "Disconnected.", dwResponseLen );
|
|
ResetEvent( g_xbx_dbgValidEvent );
|
|
goto cleanUp;
|
|
}
|
|
|
|
if ( _xdbg_strnicmp( args[0], "__complete__", 12 ) )
|
|
{
|
|
// remote server has finished command - respond to acknowledge
|
|
_xdbg_strcpyn( strResponse, XBX_DBGRESPONSEPREFIX "OK", dwResponseLen );
|
|
|
|
// set the complete event - allows expected synchronous calling mechanism
|
|
SetEvent( g_xbx_dbgCmdCompleteEvent );
|
|
goto cleanUp;
|
|
}
|
|
|
|
if ( _xdbg_strnicmp( args[0], "__memory__", 10 ) )
|
|
{
|
|
// get a current stat of available memory
|
|
MEMORYSTATUS stat;
|
|
GlobalMemoryStatus( &stat );
|
|
g_xbx_freeMemory = stat.dwAvailPhys;
|
|
|
|
if ( _xdbg_findtoken( args, numArgs, "quiet" ) > 0 )
|
|
{
|
|
_xdbg_strcpyn( strResponse, XBX_DBGRESPONSEPREFIX "OK", dwResponseLen );
|
|
}
|
|
else
|
|
{
|
|
// 32 MB is reserved and fixed by OS, so not reporting
|
|
_snprintf( strResponse, dwResponseLen, XBX_DBGRESPONSEPREFIX "Available: %.2f MB, Used: %.2f MB, Free: %.2f MB",
|
|
stat.dwTotalPhys/( 1024.0f*1024.0f ) - 32.0f,
|
|
( stat.dwTotalPhys - stat.dwAvailPhys )/( 1024.0f*1024.0f ) - 32.0f,
|
|
stat.dwAvailPhys/( 1024.0f*1024.0f ) );
|
|
}
|
|
goto cleanUp;
|
|
}
|
|
|
|
if ( g_xbx_dbgRemoteBuf[0] )
|
|
{
|
|
// previous command still pending
|
|
_xdbg_strcpyn( strResponse, XBX_DBGRESPONSEPREFIX "Cannot execute: Previous command still pending", dwResponseLen );
|
|
}
|
|
else
|
|
{
|
|
// add the command to the event queue to be processed by main app
|
|
_xdbg_strcpy( g_xbx_dbgRemoteBuf, strCommand );
|
|
XBX_QueueEvent( XEV_REMOTECMD, ( int )g_xbx_dbgRemoteBuf, 0, 0 );
|
|
_xdbg_strcpyn( strResponse, XBX_DBGRESPONSEPREFIX "OK", dwResponseLen );
|
|
}
|
|
|
|
cleanUp:
|
|
return XBDM_NOERR;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// XBX_SendRemoteCommand
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxConsole::SendRemoteCommand( const char *pCommand, bool async )
|
|
{
|
|
char cmdString[XBX_MAX_RCMDLENGTH];
|
|
|
|
if ( XBX_NoXBDM() || !IsConsoleConnected() )
|
|
return;
|
|
|
|
AUTO_LOCK_FM( g_xbx_dbgChannelMutex );
|
|
|
|
_snprintf( cmdString, sizeof( cmdString ), "%s!%s", XBX_DBGCOMMANDPREFIX, pCommand );
|
|
HRESULT hr = DmSendNotificationString( cmdString );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
XBX_Error( "XBX_SendRemoteCommand: failed on %s", cmdString );
|
|
}
|
|
|
|
// wait for command completion
|
|
if ( !async )
|
|
{
|
|
DWORD timeout;
|
|
if ( !strnicmp( pCommand, "Assert()", 8 ) )
|
|
{
|
|
// the assert is waiting for user to make selection
|
|
timeout = INFINITE;
|
|
}
|
|
else
|
|
{
|
|
// no vxconsole operation should take this long
|
|
timeout = 15000;
|
|
}
|
|
|
|
if ( WaitForSingleObject( g_xbx_dbgCmdCompleteEvent, timeout ) == WAIT_TIMEOUT )
|
|
{
|
|
// we have no choice but to dump core
|
|
DmCrashDump( false );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Handle delayed VXConsole transactions
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
static unsigned _DebugThreadFunc( void *pParam )
|
|
{
|
|
while ( 1 )
|
|
{
|
|
Sleep( 10 );
|
|
|
|
if ( !g_xbx_DebugStringQueue.Count() && !g_xbx_numProfileCounters && !g_xbx_freeMemory )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( g_xbx_numProfileCounters )
|
|
{
|
|
// build and send asynchronously
|
|
char dbgCommand[XBX_MAX_RCMDLENGTH];
|
|
_snprintf( dbgCommand, sizeof( dbgCommand ), "SetProfileData() %s 0x%8.8x", g_xbx_profileName, g_xbx_profileCounters );
|
|
XBX_SendRemoteCommand( dbgCommand, true );
|
|
|
|
// mark as sent
|
|
g_xbx_numProfileCounters = 0;
|
|
}
|
|
|
|
if ( g_xbx_freeMemory )
|
|
{
|
|
// build and send asynchronously
|
|
char dbgCommand[XBX_MAX_RCMDLENGTH];
|
|
_snprintf( dbgCommand, sizeof( dbgCommand ), "FreeMemory() 0x%8.8x", g_xbx_freeMemory );
|
|
XBX_SendRemoteCommand( dbgCommand, true );
|
|
|
|
// mark as sent
|
|
g_xbx_freeMemory = 0;
|
|
}
|
|
|
|
bool bRemoteValid = g_xbx_bUseVXConsoleOutput && XBX_IsConsoleConnected();
|
|
while ( 1 )
|
|
{
|
|
DebugString_t debugString;
|
|
if ( !g_xbx_DebugStringQueue.PopItem( &debugString ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
OutputStringToDevice( debugString.color, debugString.pString, bRemoteValid );
|
|
free( debugString.pString );
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// XBX_InitConsoleMonitor
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxConsole::InitConsoleMonitor( bool bWaitForConnect )
|
|
{
|
|
if ( XBX_NoXBDM() )
|
|
return;
|
|
|
|
// create our events
|
|
g_xbx_dbgValidEvent = CreateEvent( XBOX_DONTCARE, TRUE, FALSE, NULL );
|
|
g_xbx_dbgCmdCompleteEvent = CreateEvent( XBOX_DONTCARE, FALSE, FALSE, NULL );
|
|
|
|
// register our command handler with the debug monitor
|
|
HRESULT hr = DmRegisterCommandProcessor( XBX_DBGCOMMANDPREFIX, _DebugCommandHandler );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
XBX_Error( "XBX_InitConsoleMonitor: failed to register command processor" );
|
|
}
|
|
|
|
// user can have output bypass slave thread
|
|
g_xbx_bDoSyncOutput = CommandLine()->FindParm( "-syncoutput" ) != 0;
|
|
|
|
// create a slave thread to do delayed VXConsole transactions
|
|
ThreadId_t threadID;
|
|
g_xbx_hDebugThread = CreateSimpleThread( _DebugThreadFunc, NULL, &threadID, 16*1024 );
|
|
ThreadSetDebugName( threadID, "DebugThread" );
|
|
ThreadSetAffinity( g_xbx_hDebugThread, XBOX_PROCESSOR_5 );
|
|
|
|
if ( bWaitForConnect )
|
|
{
|
|
XBX_DebugString( XBX_CLR_DEFAULT, "Waiting For VXConsole Connection...\n" );
|
|
WaitForSingleObject( g_xbx_dbgValidEvent, INFINITE );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sends a disconnect signal to possibly attached VXConsole.
|
|
//-----------------------------------------------------------------------------
|
|
void CXboxConsole::DisconnectConsoleMonitor()
|
|
{
|
|
if ( XBX_NoXBDM() )
|
|
return;
|
|
|
|
// caller is trying to safely stop vxconsole traffic, disconnect must be synchronous
|
|
XBX_SendRemoteCommand( "Disconnect()", false );
|
|
}
|
|
|
|
bool CXboxConsole::GetXboxName( char *pName, unsigned *pLength )
|
|
{
|
|
return ( DmGetXboxName( pName, (DWORD *)pLength ) == XBDM_NOERR );
|
|
}
|
|
|
|
void CXboxConsole::CrashDump( bool b )
|
|
{
|
|
DmCrashDump(b);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Walk to a specific module and dump size info
|
|
//-----------------------------------------------------------------------------
|
|
int CXboxConsole::DumpModuleSize( const char *pName )
|
|
{
|
|
HRESULT error;
|
|
PDM_WALK_MODULES pWalkMod = NULL;
|
|
DMN_MODLOAD modLoad;
|
|
int size = 0;
|
|
|
|
// iterate and find match
|
|
do
|
|
{
|
|
error = DmWalkLoadedModules( &pWalkMod, &modLoad );
|
|
if ( XBDM_NOERR == error && !stricmp( modLoad.Name, pName ) )
|
|
{
|
|
Msg( "0x%8.8x, %5.2f MB, %s\n", modLoad.BaseAddress, modLoad.Size/( 1024.0f*1024.0f ), modLoad.Name );
|
|
size = modLoad.Size;
|
|
error = XBDM_ENDOFLIST;
|
|
}
|
|
}
|
|
while ( XBDM_NOERR == error );
|
|
DmCloseLoadedModules( pWalkMod );
|
|
|
|
if ( error != XBDM_ENDOFLIST )
|
|
{
|
|
Warning( "DmWalkLoadedModules() failed.\n" );
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 360 spew sizes of dll modules
|
|
//-----------------------------------------------------------------------------
|
|
char const* HACK_stristr( char const* pStr, char const* pSearch ) // hack because moved code from above vstdlib
|
|
{
|
|
AssertValidStringPtr(pStr);
|
|
AssertValidStringPtr(pSearch);
|
|
|
|
if (!pStr || !pSearch)
|
|
return 0;
|
|
|
|
char const* pLetter = pStr;
|
|
|
|
// Check the entire string
|
|
while (*pLetter != 0)
|
|
{
|
|
// Skip over non-matches
|
|
if (tolower((unsigned char)*pLetter) == tolower((unsigned char)*pSearch))
|
|
{
|
|
// Check for match
|
|
char const* pMatch = pLetter + 1;
|
|
char const* pTest = pSearch + 1;
|
|
while (*pTest != 0)
|
|
{
|
|
// We've run off the end; don't bother.
|
|
if (*pMatch == 0)
|
|
return 0;
|
|
|
|
if (tolower((unsigned char)*pMatch) != tolower((unsigned char)*pTest))
|
|
break;
|
|
|
|
++pMatch;
|
|
++pTest;
|
|
}
|
|
|
|
// Found a match!
|
|
if (*pTest == 0)
|
|
return pLetter;
|
|
}
|
|
|
|
++pLetter;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CXboxConsole::DumpDllInfo( const char *pBasePath )
|
|
{
|
|
// Directories containing dlls
|
|
static char *dllDirs[] =
|
|
{
|
|
"bin",
|
|
"hl2\\bin",
|
|
"tf\\bin",
|
|
"portal\\bin",
|
|
"episodic\\bin"
|
|
};
|
|
|
|
char binPath[MAX_PATH];
|
|
char dllPath[MAX_PATH];
|
|
char searchPath[MAX_PATH];
|
|
|
|
HMODULE hModule;
|
|
WIN32_FIND_DATA wfd;
|
|
HANDLE hFind;
|
|
|
|
Msg( "Dumping Module Sizes...\n" );
|
|
|
|
for ( int i = 0; i < ARRAYSIZE( dllDirs ); ++i )
|
|
{
|
|
int totalSize = 0;
|
|
|
|
_snprintf( binPath, sizeof( binPath ), "%s\\%s", pBasePath, dllDirs[i] );
|
|
_snprintf( searchPath, sizeof( binPath ), "%s\\*.dll", binPath );
|
|
|
|
// show the directory we're searching
|
|
Msg( "\nDirectory: %s\n\n", binPath );
|
|
|
|
// Start the find and check for failure.
|
|
hFind = FindFirstFile( searchPath, &wfd );
|
|
if ( INVALID_HANDLE_VALUE == hFind )
|
|
{
|
|
Warning( "No Files Found.\n" );
|
|
}
|
|
else
|
|
{
|
|
// Load and unload each dll individually.
|
|
do
|
|
{
|
|
if ( !HACK_stristr( wfd.cFileName, "_360.dll" ) )
|
|
{
|
|
// exclude explicit pc dlls
|
|
// FindFirstFile does not support a spec mask of *_360.dll on the Xbox HDD
|
|
continue;
|
|
}
|
|
|
|
_snprintf( dllPath, sizeof( dllPath ), "%s\\%s", binPath, wfd.cFileName );
|
|
hModule = LoadLibrary( dllPath );
|
|
if ( hModule )
|
|
{
|
|
totalSize += DumpModuleSize( wfd.cFileName );
|
|
FreeLibrary( hModule );
|
|
}
|
|
else
|
|
{
|
|
Warning( "Failed to load: %s\n", dllPath );
|
|
}
|
|
}
|
|
while( FindNextFile( hFind, &wfd ) );
|
|
|
|
FindClose( hFind );
|
|
|
|
Msg( "Total Size: %.2f MB\n", totalSize/( 1024.0f*1024.0f ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CXboxConsole::OutputDebugString( const char *p )
|
|
{
|
|
::OutputDebugStringA( p );
|
|
}
|
|
|
|
bool CXboxConsole::IsDebuggerPresent()
|
|
{
|
|
return ( DmIsDebuggerPresent() != 0 );
|
|
}
|