summaryrefslogtreecommitdiff
path: root/utils/xbox/vxbdm/console.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/xbox/vxbdm/console.cpp')
-rw-r--r--utils/xbox/vxbdm/console.cpp763
1 files changed, 763 insertions, 0 deletions
diff --git a/utils/xbox/vxbdm/console.cpp b/utils/xbox/vxbdm/console.cpp
new file mode 100644
index 0000000..8ff746a
--- /dev/null
+++ b/utils/xbox/vxbdm/console.cpp
@@ -0,0 +1,763 @@
+//========= 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 );
+}