|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// VXCONSOLE.CPP
//
// Valve XBox Console.
//=====================================================================================//
#include "vxconsole.h"
HWND g_hDlgMain; HWND g_hwndCommandCombo; HWND g_hwndOutputWindow; HWND g_hwndCommandHint; WNDPROC g_hwndCommandSubclassed; BOOL g_connectedToXBox; BOOL g_connectedToApp; PDMN_SESSION g_pdmnSession; PDM_CONNECTION g_pdmConnection; printQueue_t g_PrintQueue; UINT_PTR g_autoConnectTimer; BOOL g_autoConnect; BOOL g_debugCommands; BOOL g_captureDebugSpew; BOOL g_captureGameSpew = TRUE; CHAR g_xboxName[MAX_XBOXNAMELEN]; DWORD g_xboxAddress; HINSTANCE g_hInstance; HICON g_hIcons[MAX_ICONS]; HBRUSH g_hBackgroundBrush; HFONT g_hFixedFont; BOOL g_reboot; char* g_rebootArgv[MAX_ARGVELEMS]; int g_rebootArgc; COLORREF g_backgroundColor; TEXTMETRIC g_fixedFontMetrics; int g_connectCount; int g_currentIcon = -1; HACCEL g_hAccel; HMODULE g_hRichEdit; DWORD g_connectedTime; RECT g_mainWindowRect; HFONT g_hProportionalFont; HANDLE g_hCommandReadyEvent; int g_currentCommandSelection; int g_connectFailure; int g_configID; bool g_bSuppressBlink = false; BOOL g_bPlayTestMode = TRUE;
LRESULT CALLBACK Main_DlgProc( HWND, UINT, WPARAM, LPARAM ); LRESULT CALLBACK CommandWindow_SubclassedProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
bool ParseCommandLineArg( const char *pCmdLine, const char *pKey, char *pValueBuff, int valueBuffSize ) { const char* pArg = V_stristr( (char*)pCmdLine, pKey ); if ( !pArg ) { return false; }
if ( pValueBuff ) { // caller wants next token
pArg += strlen( pKey );
int i; for ( i=0; i<valueBuffSize; i++ ) { pValueBuff[i] = *pArg; if ( *pArg == '\0' || *pArg == ' ' ) { break; } pArg++; } pValueBuff[i] = '\0'; } return true; }
void MakeConfigString( const char *pString, int configID, char *pOutBuff, int outBuffSize ) { if ( configID <= 0 ) { // as-is, undecorated
V_snprintf( pOutBuff, outBuffSize, "%s", pString ); return; }
int len = strlen( pString ); bool bAddTerminalSlash = ( len > 1 && pString[len-1] == '\\' );
V_snprintf( pOutBuff, outBuffSize, "%s_%d", pString, configID );
if ( bAddTerminalSlash ) { V_strncat( pOutBuff, "\\", outBuffSize ); } }
//-----------------------------------------------------------------------------
// LoadConfig
//
//-----------------------------------------------------------------------------
void LoadConfig() { char buff[256]; int numArgs;
ConfigDlg_LoadConfig();
// initial menu state is from persisted config
g_captureDebugSpew = g_captureDebugSpew_StartupState;
Sys_GetRegistryString( "mainWindowRect", buff, "", sizeof( buff ) ); numArgs = sscanf( buff, "%d %d %d %d", &g_mainWindowRect.left, &g_mainWindowRect.top, &g_mainWindowRect.right, &g_mainWindowRect.bottom ); if ( numArgs != 4 || g_mainWindowRect.left < 0 || g_mainWindowRect.top < 0 || g_mainWindowRect.right < 0 || g_mainWindowRect.bottom < 0 ) memset( &g_mainWindowRect, 0, sizeof( g_mainWindowRect ) ); }
//-----------------------------------------------------------------------------
// SaveConfig
//
//-----------------------------------------------------------------------------
void SaveConfig() { char buff[256];
// get window placement
WINDOWPLACEMENT wp; memset( &wp, 0, sizeof( wp ) ); wp.length = sizeof( WINDOWPLACEMENT ); GetWindowPlacement( g_hDlgMain, &wp ); g_mainWindowRect = wp.rcNormalPosition;
sprintf( buff, "%d %d %d %d", g_mainWindowRect.left, g_mainWindowRect.top, g_mainWindowRect.right, g_mainWindowRect.bottom ); Sys_SetRegistryString( "mainWindowRect", buff ); }
//-----------------------------------------------------------------------------
// SetConnectionIcon
//
//-----------------------------------------------------------------------------
void SetConnectionIcon( int icon ) { if ( g_currentIcon == icon ) return; g_currentIcon = icon; SetClassLongPtr( g_hDlgMain, GCLP_HICON, ( LONG_PTR )g_hIcons[icon] ); }
//-----------------------------------------------------------------------------
// SetMainWindowTitle
//
//-----------------------------------------------------------------------------
void SetMainWindowTitle() { if ( !g_hDlgMain ) { return; }
char titleBuff[128]; if ( !g_xboxTargetName[0] ) { strcpy( titleBuff, VXCONSOLE_TITLE ); } else { sprintf( titleBuff, "%s: %s", VXCONSOLE_TITLE, g_xboxTargetName ); if ( g_configID ) { char configBuff[32]; sprintf( configBuff, " (%d)", g_configID ); V_strncat( titleBuff, configBuff, sizeof( titleBuff ) ); } } SetWindowText( g_hDlgMain, titleBuff ); }
//-----------------------------------------------------------------------------
// DmAPI_DisplayError
//
//-----------------------------------------------------------------------------
VOID DmAPI_DisplayError( const CHAR* message, HRESULT hr ) { CHAR strError[128];
ConsoleWindowPrintf( RGB( 255,0,0 ), "%s\n", message );
HRESULT hrError = DmTranslateError( hr, strError, sizeof( strError ) ); if ( !FAILED( hrError ) && strError[0] ) ConsoleWindowPrintf( RGB( 255,0,0 ), "Reason: '%s'\n", strError ); else ConsoleWindowPrintf( RGB( 255,0,0 ), "Reason: 0x%08lx\n", hr ); }
//-----------------------------------------------------------------------------
// DmAPI_SendCommand
//
// Send the specified string across the debugger channel to the application
//-----------------------------------------------------------------------------
HRESULT DmAPI_SendCommand( const char* strCommand, bool wait ) { DWORD dwResponseLen; CHAR strResponse[MAX_PATH]; int retval; bool bIgnorePingResponse; char* ptr;
retval = WaitForSingleObject( g_hCommandReadyEvent, wait ? INFINITE : 0 ); if ( retval != WAIT_OBJECT_0 ) { // cannot send command
// some other previous command has not responded and signaled the release
// testing has shown DmSendCommand() is not re-entrant and callers get
// their responses out of sync
return XBDM_UNDEFINED; }
// clear the event mutex until ready
ResetEvent( g_hCommandReadyEvent );
bIgnorePingResponse = false; dwResponseLen = sizeof( strResponse ); strResponse[0] = '\0';
if ( strCommand[0] == '*' ) { // skip past internal command identifier
strCommand++; } else if ( !stricmp( strCommand, VXCONSOLE_COMMAND_PREFIX "!" ) ) { // empty ping command
// must be done as a synchronous command with a response because there
// is no way to bind an asynch response to the owner
bIgnorePingResponse = true; } HRESULT hr = DmSendCommand( g_pdmConnection, strCommand, strResponse, &dwResponseLen ); if ( !FAILED( hr ) ) { // success
switch ( hr ) { case XBDM_NOERR: if ( !bIgnorePingResponse ) { // skip past possible ack prefix
ptr = strstr( strResponse, VXCONSOLE_COMMAND_ACK ); if ( ptr ) { ptr += strlen( VXCONSOLE_COMMAND_ACK );
// ignore remote acknowledge response
if ( !stricmp( ptr, "OK" ) ) break; } else { ptr = strResponse; } ConsoleWindowPrintf( XBX_CLR_DEFAULT, "%s\n", ptr ); } break;
case XBDM_MULTIRESPONSE: // multi-line response - loop, looking for end of response
while ( 1 ) { dwResponseLen = sizeof( strResponse );
hr = DmReceiveSocketLine( g_pdmConnection, strResponse, &dwResponseLen ); if ( FAILED( hr ) || strResponse[0] == '.' ) break; ConsoleWindowPrintf( XBX_CLR_DEFAULT, "%s\n", strResponse ); } break;
case XBDM_BINRESPONSE: ConsoleWindowPrintf( XBX_CLR_DEFAULT, "Binary response - not implemented\n" ); break;
case XBDM_READYFORBIN: ConsoleWindowPrintf( XBX_CLR_DEFAULT, "Ready for binary - not implemented\n" ); break;
default: ConsoleWindowPrintf( XBX_CLR_RED, "Unknown Response: ( %s ).\n", strResponse ); break; } }
SetEvent( g_hCommandReadyEvent ); return hr; }
//-----------------------------------------------------------------------------
// PrintToQueue
//
// Formats the string and adds it to the print queue
//-----------------------------------------------------------------------------
void PrintToQueue( COLORREF rgb, LPCTSTR strFormat, ... ) { char buffer[MAX_QUEUEDSTRINGLEN];
// enter critical section so we don't try to process the list
EnterCriticalSection( &g_PrintQueue.CriticalSection );
assert( g_PrintQueue.numMessages <= MAX_QUEUEDSTRINGS );
// when the queue is full, the main thread is probably blocked and not dequeing
if ( !g_captureGameSpew || g_PrintQueue.numMessages == MAX_QUEUEDSTRINGS ) { LeaveCriticalSection( &g_PrintQueue.CriticalSection ); return; }
va_list arglist; va_start( arglist, strFormat ); int len = _vsnprintf( buffer, MAX_QUEUEDSTRINGLEN, strFormat, arglist ); if ( len == -1 ) { buffer[sizeof(buffer)-1] = '\0'; } va_end( arglist ); // queue the message into the next slot
g_PrintQueue.pMessages[g_PrintQueue.numMessages] = Sys_CopyString( buffer ); g_PrintQueue.aColors[g_PrintQueue.numMessages++] = rgb;
// ensure we post a message to process the print queue
if ( g_PrintQueue.numMessages == 1 ) PostMessage( g_hDlgMain, WM_USER, 0, 0 );
// the main thread can now safely process the list
LeaveCriticalSection( &g_PrintQueue.CriticalSection ); }
//-----------------------------------------------------------------------------
// ProcessPrintQueue
//
//-----------------------------------------------------------------------------
void ProcessPrintQueue() { // enter critical section so we don't try to add anything while we're processing
EnterCriticalSection( &g_PrintQueue.CriticalSection );
// dequeue all entries
for ( int i = 0; i < g_PrintQueue.numMessages; i++ ) { ConsoleWindowPrintf( g_PrintQueue.aColors[i], "%s", g_PrintQueue.pMessages[i] ); Sys_Free( g_PrintQueue.pMessages[i] ); }
g_PrintQueue.numMessages = 0;
// now we can safely add to the list
LeaveCriticalSection( &g_PrintQueue.CriticalSection ); }
//-----------------------------------------------------------------------------
// ConsoleWindowPrintf
//
// Writes out a string directly to the console window
//-----------------------------------------------------------------------------
int ConsoleWindowPrintf( COLORREF rgb, LPCTSTR strFormat, ... ) { int dwStrLen; char strTemp[512]; va_list arglist; CHARRANGE cr = { -1, -2 };
if ( rgb != XBX_CLR_DEFAULT ) { // set whatever colors, etc. they want
CHARFORMAT cf = {0}; cf.cbSize = sizeof( cf ); cf.dwMask = CFM_COLOR; cf.dwEffects = 0; cf.crTextColor = rgb; SendDlgItemMessage( g_hDlgMain, IDC_OUTPUT, EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM )&cf ); }
// Get our string to print
va_start( arglist, strFormat ); dwStrLen = _vsnprintf( strTemp, sizeof( strTemp ), strFormat, arglist ); va_end( arglist );
// Move the selection to the end
SendDlgItemMessage( g_hDlgMain, IDC_OUTPUT, EM_EXSETSEL, 0, ( LPARAM )&cr );
// Add the text and scroll it into view
SendDlgItemMessage( g_hDlgMain, IDC_OUTPUT, EM_REPLACESEL, 0, ( LONG )( LPSTR )strTemp ); SendDlgItemMessage( g_hDlgMain, IDC_OUTPUT, EM_SCROLLCARET, 0, 0L );
if ( g_bPlayTestMode ) { char szLogPath[MAX_PATH]; char szLogName[MAX_PATH]; V_snprintf( szLogName, sizeof( szLogName ), "vxconsole_%s.log", g_xboxTargetName ); V_ComposeFileName( g_localPath, szLogName, szLogPath, sizeof( szLogPath ) ); FILE *fp = fopen( szLogPath, "at+" ); if ( fp ) { fprintf( fp, strTemp ); fclose( fp ); } }
return dwStrLen; }
//-----------------------------------------------------------------------------
// ProcessCommand
//
//-----------------------------------------------------------------------------
bool ProcessCommand( const char* strCmdIn ) { char strRemoteCmd[MAX_PATH + 10]; TCHAR strCmdBak[MAX_PATH]; char strCmd[MAX_PATH]; char* argv[MAX_ARGVELEMS]; BOOL isXCommand = FALSE; BOOL isLocal = FALSE; BOOL isRemote = FALSE; int iIndex;
// local copy for destructive purposes
strcpy( strCmd, strCmdIn );
// copy of the original command string
lstrcpyA( strCmdBak, strCmdIn );
ConsoleWindowPrintf( XBX_CLR_DEFAULT, "] %s\n", strCmd );
// parse argstring into components
int argc = CmdToArgv( strCmd, argv, MAX_ARGVELEMS ); if ( !argc ) { // empty command
return true; }
if ( ( iIndex = ComboBox_GetCount( g_hwndCommandCombo ) ) >= MAX_COMMANDHISTORY ) { // Limit the history of items, purge oldest
ComboBox_DeleteString( g_hwndCommandCombo, 0 ); }
// add to end of list
iIndex = ComboBox_InsertItemData( g_hwndCommandCombo, -1, strCmdBak ); ComboBox_SetCurSel( g_hwndCommandCombo, -1 );
// find command in local list
for ( int i=0; i<g_numLocalCommands; i++ ) { if ( lstrcmpiA( g_localCommands[i].strCommand, argv[0] ) ) { // no match
continue; }
isLocal = TRUE; if ( !g_localCommands[i].pfnHandler ) { // no handler, remote xcommand
isXCommand = TRUE; }
if ( ( g_localCommands[i].flags & FN_XBOX ) && !g_connectedToXBox ) { // not allowed yet
ConsoleWindowPrintf( RGB( 255,0,0 ), "'%s' is not available until connected to XBox.\n", argv[0] ); return true; } else if ( ( g_localCommands[i].flags & FN_APP ) && !g_connectedToApp ) { // not allowed yet
ConsoleWindowPrintf( RGB( 255,0,0 ), "'%s' is not available until connected to Application.\n", argv[0] ); return true; } if ( isXCommand ) break;
// do local command
g_localCommands[i].pfnHandler( argc, argv ); return true; }
// find command in remote list
if ( !isLocal && !isXCommand && g_connectedToApp ) { for ( int i=0; i<g_numRemoteCommands; i++ ) { if ( lstrcmpiA( g_remoteCommands[i]->strCommand, argv[0] ) ) { // no match
continue; }
isRemote = TRUE;
if ( !g_connectedToApp ) { // not allowed yet
ConsoleWindowPrintf( RGB( 255,0,0 ), "'%s' is not available until connected to Application.\n", argv[0] ); return true; } break; } }
if ( !isLocal && !isXCommand && !isRemote ) { if ( !g_connectedToApp || g_numRemoteCommands != 0 ) { // unrecognized
ConsoleWindowPrintf( RGB( 255,0,0 ), "'%s' is not a recognized command.\n", argv[0] ); return true; } }
if ( isXCommand ) { // send the xcommand directly
lstrcpyA( strRemoteCmd, strCmdBak ); } else { // add remote command prefix
lstrcpyA( strRemoteCmd, VXCONSOLE_COMMAND_PREFIX "!" ); lstrcatA( strRemoteCmd, strCmdBak ); }
// send the command to the Xbox
HRESULT hr = DmAPI_SendCommand( strRemoteCmd, true ); if ( FAILED( hr ) ) { DmAPI_DisplayError( "DmSendCommand", hr ); return false; }
return TRUE; }
//-----------------------------------------------------------------------------
// CommandWindow_HandleKey
//
// Handle a WM_KEYDOWN in our RTF cmd window
//-----------------------------------------------------------------------------
BOOL CommandWindow_HandleKey( WPARAM wParam ) { BOOL bHandled = FALSE; int curSel; int numItems;
if ( wParam >= VK_F1 && wParam <= VK_F12 ) { if ( Bindings_TranslateKey( wParam ) ) { // handled
return true; } }
switch ( wParam ) { case VK_TAB: case VK_UP: case VK_DOWN: if ( IsWindowVisible( g_hwndCommandHint ) ) { // hint window open
char hintCmd[MAX_PATH]; char userCmd[MAX_PATH];
// scroll through the hint selections
curSel = SendMessage( g_hwndCommandHint, (UINT)LB_GETCURSEL, NULL, NULL ); SendMessage( g_hwndCommandHint, (UINT)LB_GETTEXT, (WPARAM)curSel, (LPARAM)hintCmd );
numItems = SendMessage( g_hwndCommandHint, (UINT)LB_GETCOUNT, NULL, NULL ); if ( numItems < 0 ) numItems = 0;
if ( wParam == VK_TAB ) { // get command typed so far
ComboBox_GetText( g_hwndCommandCombo, userCmd, MAX_PATH );
// strip the auto-space off the end
int len = Q_strlen(userCmd); if ( userCmd[len-1] == ' ' ) { userCmd[len-1] = '\0'; }
if ( !stricmp( userCmd, hintCmd ) ) { // cycle to next or prev command with tab or shift-tab
if ( GetKeyState(VK_SHIFT) < 0 ) { wParam = VK_UP; } else { wParam = VK_DOWN; } } }
// move the selection
if ( wParam == VK_UP ) curSel--; else if ( wParam == VK_DOWN ) curSel++; if ( curSel < 0 ) curSel = numItems - 1; else if ( curSel > numItems-1 ) curSel = 0; if ( curSel < 0 ) curSel = 0;
// set the selection and get highlighted command
SendMessage( g_hwndCommandHint, (UINT)LB_SETCURSEL, (WPARAM)curSel, NULL ); SendMessage( g_hwndCommandHint, (UINT)LB_GETTEXT, (WPARAM)curSel, (LPARAM)hintCmd );
// add a space to the end for easier parameter setting
Q_strncat( hintCmd, " ", sizeof(hintCmd), 1 );
// replace command string
ComboBox_SetText( g_hwndCommandCombo, hintCmd );
// set cursor to end of command
SendMessage( g_hwndCommandCombo, (UINT)CB_SETEDITSEL, (WPARAM)0, MAKELONG( MAX_PATH,MAX_PATH ) );
bHandled = TRUE; } else { curSel = SendMessage( g_hwndCommandCombo, (UINT)CB_GETCURSEL, NULL, NULL ); if ( curSel < 0 ) { // combo box has no selection
// override combo box behavior and set selection
numItems = SendMessage( g_hwndCommandCombo, (UINT)CB_GETCOUNT, NULL, NULL ); if ( numItems > 0 ) { if ( wParam == VK_UP ) { // set to bottom of list
curSel = numItems-1; } else if ( wParam == VK_DOWN ) { // set to top of list
curSel = 0; }
SendMessage( g_hwndCommandCombo, (UINT)CB_SETCURSEL, (WPARAM)curSel, NULL ); bHandled = TRUE; } } } break;
case VK_RETURN: // user hit return in the combo box
if ( ComboBox_GetDroppedState( g_hwndCommandCombo ) ) { ComboBox_ShowDropdown( g_hwndCommandCombo, FALSE ); } else { PostMessage( g_hDlgMain, WM_APP, 0, 0 ); bHandled = TRUE; } break; }
return bHandled; }
//-----------------------------------------------------------------------------
// CommandWindow_SubclassedProc
//
//-----------------------------------------------------------------------------
LRESULT CALLBACK CommandWindow_SubclassedProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { switch ( msg ) { case WM_SYSKEYDOWN: case WM_KEYDOWN: if ( CommandWindow_HandleKey( wParam ) ) return 0; break;
case WM_CHAR: if ( wParam == VK_RETURN ) return 0; break; }
return CallWindowProc( g_hwndCommandSubclassed, hDlg, msg, wParam, lParam ); }
//-----------------------------------------------------------------------------
// Main_SizeWindow
//
// Handles a WM_SIZE message by resizing all our child windows to match the main window
//-----------------------------------------------------------------------------
void Main_SizeWindow( HWND hDlg, UINT wParam, int cx, int cy ) { if ( cx==0 || cy==0 ) { RECT rcClient; GetClientRect( hDlg, &rcClient ); cx = rcClient.right; cy = rcClient.bottom; }
// if we're big enough, position our child windows
if ( g_hwndCommandCombo && cx > 64 && cy > 64 ) { RECT rcCmd; RECT rcHint; RECT rcOut;
// fit the "command" combo box into our window
GetWindowRect( g_hwndCommandCombo, &rcCmd ); ScreenToClient( hDlg, ( LPPOINT )&rcCmd ); ScreenToClient( hDlg, ( LPPOINT )&rcCmd + 1 ); int x = rcCmd.left; int dx = cx - 8 - x; int dy = rcCmd.bottom - rcCmd.top; int y = cy - 8 - dy; SetWindowPos( g_hwndCommandCombo, NULL, x, y, dx, dy, SWP_NOZORDER );
// position the "hint popup" window above the "command" window
GetWindowRect( g_hwndCommandHint, &rcHint ); ScreenToClient( g_hDlgMain, ( LPPOINT )&rcHint ); ScreenToClient( g_hDlgMain, ( LPPOINT )&rcHint + 1 ); SetWindowPos( g_hwndCommandHint, NULL, rcCmd.left, ( rcCmd.top - 4 ) - ( rcHint.bottom - rcHint.top + 1 ), 0, 0, SWP_NOSIZE | SWP_NOZORDER );
// position the "Cmd" label
RECT rcStaticCmd; HWND hStaticCmd = GetDlgItem( g_hDlgMain, IDC_LABEL ); GetWindowRect( hStaticCmd, &rcStaticCmd ); ScreenToClient( hDlg, ( LPPOINT )&rcStaticCmd ); ScreenToClient( hDlg, ( LPPOINT )&rcStaticCmd + 1 ); SetWindowPos( hStaticCmd, NULL, 8, y + ( dy - ( rcStaticCmd.bottom - rcStaticCmd.top ) ) / 2 - 1, 0, 0, SWP_NOSIZE | SWP_NOZORDER );
// position the "output" window
GetWindowRect( g_hwndOutputWindow, &rcOut ); ScreenToClient( hDlg, ( LPPOINT )&rcOut ); int dwWidth = cx - rcOut.left - 8; int dwHeight = y - rcOut.top - 8; SetWindowPos( g_hwndOutputWindow, NULL, 0, 0, dwWidth, dwHeight, SWP_NOMOVE | SWP_NOZORDER ); } }
//-----------------------------------------------------------------------------
// _SortCommands
//
//-----------------------------------------------------------------------------
int _SortCommands( const void* a, const void* b ) { const char* strA = *( const char** )a; const char* strB = *( const char** )b;
return ( stricmp( strA, strB ) ); }
//-----------------------------------------------------------------------------
// EnableCommandHint
//
// Open/Close the command hint popup
//-----------------------------------------------------------------------------
void EnableCommandHint( bool enable ) { int w = 0; int h = 0; int itemHeight; int i; int maxLen; int len; const char* cmds[256]; int numLocalCommands; int numRemoteCommands; int curSel;
if ( !enable ) goto cleanUp;
// get the current command
char strCmd[MAX_PATH]; ComboBox_GetText( g_hwndCommandCombo, strCmd, MAX_PATH ); if ( !strCmd[0] ) { // user has typed nothing
enable = false; goto cleanUp; }
SendMessage( g_hwndCommandHint, ( UINT )LB_RESETCONTENT, NULL, NULL );
// get a list of possible matches
maxLen = 0; numLocalCommands = MatchLocalCommands( strCmd, cmds, 256 ); numRemoteCommands = MatchRemoteCommands( strCmd, cmds+numLocalCommands, 256-numLocalCommands ); for ( i=0; i<numLocalCommands+numRemoteCommands; i++ ) { len = strlen( cmds[i] ); if ( maxLen < len ) maxLen = len; } if ( !maxLen ) { // no matches
enable = false; goto cleanUp; }
// sort the list ( eschew listbox's autosorting )
qsort( cmds, numLocalCommands+numRemoteCommands, sizeof( const char* ), _SortCommands );
curSel = -1; len = strlen( strCmd ); for ( i=0; i<numLocalCommands+numRemoteCommands; i++ ) { // populate the listbox
SendMessage( g_hwndCommandHint, ( UINT )LB_ADDSTRING, 0, ( LPARAM )cmds[i] ); // determine first best match
if ( curSel == -1 && !strnicmp( strCmd, cmds[i], len ) ) curSel = i; }
if ( curSel != -1 ) { // set the selection to the first best string
// ensure the top string is shown ( avoids odd auto-vscroll choices )
SendMessage( g_hwndCommandHint, ( UINT )LB_SETCURSEL, ( WPARAM )curSel, NULL ); if ( !curSel ) SendMessage( g_hwndCommandHint, ( UINT )LB_SETTOPINDEX, 0, NULL ); }
RECT rcCmd; GetWindowRect( g_hwndCommandCombo, &rcCmd ); ScreenToClient( g_hDlgMain, ( LPPOINT )&rcCmd ); ScreenToClient( g_hDlgMain, ( LPPOINT )&rcCmd + 1 );
// clamp listbox height to client space
itemHeight = SendMessage( g_hwndCommandHint, ( UINT )LB_GETITEMHEIGHT, 0, NULL ); if ( itemHeight <= 0 ) { // oops, shouldn't happen
enable = false; goto cleanUp; }
h = ( numLocalCommands + numRemoteCommands )*itemHeight + 2; if ( h > rcCmd.top - 8) { h = rcCmd.top - 8; } // clamp listbox width
w = ( maxLen + 5 ) * g_fixedFontMetrics.tmMaxCharWidth;
// position the "hint popup" window above the "command" window
SetWindowPos( g_hwndCommandHint, NULL, rcCmd.left, ( rcCmd.top - 4 ) - h, w, h, SWP_NOZORDER );
cleanUp: BOOL isVisible = IsWindowVisible( g_hwndCommandHint ); if ( !enable && isVisible ) ShowWindow( g_hwndCommandHint, SW_HIDE ); else if ( enable && !isVisible ) ShowWindow( g_hwndCommandHint, SW_SHOWNA ); }
//-----------------------------------------------------------------------------
// Main_DlgProc
//
//-----------------------------------------------------------------------------
LRESULT CALLBACK Main_DlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { WORD wID = LOWORD( wParam ); BOOL isConnect;
switch ( message ) { case WM_APP: // user has pressed enter
// take the string from the command window and process it
// extra room for \r\n
char strCmd[MAX_PATH + 3]; ComboBox_GetText( g_hwndCommandCombo, strCmd, MAX_PATH ); ProcessCommand( strCmd ); ComboBox_SetText( g_hwndCommandCombo, "" ); EnableCommandHint( false ); break;
case WM_USER: ProcessPrintQueue(); break;
case WM_TIMER: // Don't do auto-connect stuff while the Assert dialog is up
// (it uses a synchronous command, so calling 'Dm' funcs here can cause a lockup)
if ( !g_AssertDialogActive ) { if ( wID == TIMERID_AUTOCONNECT ) { AutoConnectTimerProc( hDlg, TIMERID_AUTOCONNECT ); return 0L; } } break;
case WM_CTLCOLORLISTBOX: case WM_CTLCOLOREDIT: SetBkColor( ( HDC )wParam,g_backgroundColor ); return ( BOOL )g_hBackgroundBrush; case WM_SIZE: Main_SizeWindow( hDlg, wParam, LOWORD( lParam ), HIWORD( lParam ) ); break;
case WM_SYSCOMMAND: if ( wID == SC_CLOSE ) { PostMessage( hDlg, WM_CLOSE, 0, 0 ); return 0L; } break;
case WM_CLOSE: // disconnect before closing
lc_disconnect( 0, NULL );
SaveConfig(); DestroyWindow( hDlg ); break;
case WM_DESTROY: SubclassWindow( g_hwndCommandCombo, g_hwndCommandSubclassed ); PostQuitMessage( 0 ); return 0L;
case WM_INITMENU: isConnect = g_connectedToXBox || g_connectedToApp; CheckMenuItem( ( HMENU )wParam, IDM_AUTOCONNECT, MF_BYCOMMAND | ( g_autoConnect ? MF_CHECKED : MF_UNCHECKED ) ); CheckMenuItem( ( HMENU )wParam, IDM_CAPTUREGAMESPEW, MF_BYCOMMAND | ( g_captureGameSpew ? MF_CHECKED : MF_UNCHECKED ) ); CheckMenuItem( ( HMENU )wParam, IDM_CAPTUREDEBUGSPEW, MF_BYCOMMAND | ( g_captureDebugSpew ? MF_CHECKED : MF_UNCHECKED ) ); CheckMenuItem( ( HMENU )wParam, IDM_DEBUGCOMMANDS, MF_BYCOMMAND | ( g_debugCommands ? MF_CHECKED : MF_UNCHECKED ) ); CheckMenuItem( ( HMENU )wParam, IDM_PLAYTESTMODE, MF_BYCOMMAND | ( g_bPlayTestMode ? MF_CHECKED : MF_UNCHECKED ) ); EnableMenuItem( ( HMENU )wParam, IDM_SYNCFILES, MF_BYCOMMAND | ( g_connectedToXBox ? MF_ENABLED : MF_GRAYED ) ); EnableMenuItem( ( HMENU )wParam, IDM_SYNCINSTALL, MF_BYCOMMAND | ( g_connectedToXBox ? MF_ENABLED : MF_GRAYED ) ); return 0L;
case WM_COMMAND: switch ( wID ) { case IDC_COMMAND: switch ( HIWORD( wParam ) ) { case CBN_EDITCHANGE: case CBN_SETFOCUS: EnableCommandHint( true ); break;
case CBN_KILLFOCUS: EnableCommandHint( false ); break; } break;
case IDM_CONFIG: ConfigDlg_Open(); return 0L; case IDM_CAPTUREGAMESPEW: g_captureGameSpew ^= 1; return 0L;
case IDM_CAPTUREDEBUGSPEW: g_captureDebugSpew ^= 1; return 0L;
case IDM_DEBUGCOMMANDS: g_debugCommands ^= 1; return 0L;
case IDM_BUG: BugDlg_Open(); return 0L;
case IDM_MEMORYDUMP: ShowMemDump_Open(); return 0L;
case IDM_TIMESTAMPLOG: TimeStampLog_Open(); return 0L;
case IDM_SYNCFILES: SyncFilesDlg_Open(); return 0L;
case IDM_SYNCINSTALL: InstallDlg_Open(); return 0L;
case IDM_EXCLUDEPATHS: ExcludePathsDlg_Open(); return 0L;
case IDM_CPU_SAMPLES: CpuProfileSamples_Open(); return 0L;
case IDM_CPU_HISTORY: CpuProfileHistory_Open(); return 0L;
case IDM_D3D_SAMPLES: TexProfileSamples_Open(); return 0L;
case IDM_D3D_HISTORY: TexProfileHistory_Open(); return 0L;
case IDM_SHOWMEMORYUSAGE: MemProfile_Open(); return 0L;
case IDM_PLAYTESTMODE: g_bPlayTestMode ^= 1; return 0L;
case IDM_SHOWRESOURCES_TEXTURES: ShowTextures_Open(); return 0L;
case IDM_SHOWRESOURCES_MATERIALS: ShowMaterials_Open(); return 0L;
case IDM_SHOWRESOURCES_SOUNDS: ShowSounds_Open(); return 0L;
case IDM_SHOWRESOURCES_MODELS: NotImplementedYet(); return 0L;
case IDM_AUTOCONNECT: if ( g_connectedToXBox || g_connectedToApp || g_autoConnect ) lc_disconnect( 0, NULL ); else lc_autoConnect( 0, NULL ); return 0L;
case IDM_BINDINGS_EDIT: Bindings_Open(); return 0L;
case IDM_BINDINGS_BIND1: case IDM_BINDINGS_BIND2: case IDM_BINDINGS_BIND3: case IDM_BINDINGS_BIND4: case IDM_BINDINGS_BIND5: case IDM_BINDINGS_BIND6: case IDM_BINDINGS_BIND7: case IDM_BINDINGS_BIND8: case IDM_BINDINGS_BIND9: case IDM_BINDINGS_BIND10: case IDM_BINDINGS_BIND11: case IDM_BINDINGS_BIND12: Bindings_MenuSelection( wID ); return 0L;
case IDM_EXIT: PostMessage( hDlg, WM_CLOSE, 0, 0 ); return 0L; } break; }
return ( DefDlgProc( hDlg, message, wParam, lParam ) ); }
//-----------------------------------------------------------------------------
// CmdToArgv
//
// Parses a string into argv and return # of args.
//-----------------------------------------------------------------------------
int CmdToArgv( char* str, char* argv[], int maxargs ) { int argc = 0; int argcT = 0; char* strNil = str + lstrlenA( str );
while ( argcT < maxargs ) { // Eat whitespace
while ( *str && ( *str==' ' ) ) str++;
if ( !*str ) { argv[argcT++] = strNil; } else { // Find the end of this arg
char chEnd = ( *str == '"' || *str == '\'' ) ? *str++ : ' '; char* strArgEnd = str; while ( *strArgEnd && ( *strArgEnd != chEnd ) ) strArgEnd++;
// Record this argument
argv[argcT++] = str; argc = argcT;
// Move strArg to the next argument ( or not, if we hit the end )
str = *strArgEnd ? strArgEnd + 1 : strArgEnd; *strArgEnd = 0; } }
return argc; }
//-----------------------------------------------------------------------------
// CreateCommandHint
//
//-----------------------------------------------------------------------------
void CreateCommandHint() { // create the "hint" popup
g_hwndCommandHint = CreateWindowEx( WS_EX_NOPARENTNOTIFY, "LISTBOX", "", WS_BORDER|WS_CHILD|LBS_HASSTRINGS|WS_VSCROLL, 0, 0, 100, 0, g_hDlgMain, ( HMENU )IDC_COMMANDHINT, g_hInstance, NULL );
// force the popup to head of z-order
// to draw over all other children
BringWindowToTop( g_hwndCommandHint );
// set font
SendDlgItemMessage( g_hDlgMain, IDC_COMMANDHINT, WM_SETFONT, ( WPARAM )g_hFixedFont, TRUE ); }
//-----------------------------------------------------------------------------
// CreateResources
//
//-----------------------------------------------------------------------------
bool CreateResources() { LOGFONT lf; HFONT hFontOld; HDC hDC; int i;
// pull in common controls
INITCOMMONCONTROLSEX initCommon; initCommon.dwSize = sizeof( INITCOMMONCONTROLSEX ); initCommon.dwICC = ICC_LISTVIEW_CLASSES|ICC_USEREX_CLASSES; if ( !InitCommonControlsEx( &initCommon ) ) return false;
// pull in rich edit controls
g_hRichEdit = LoadLibrary( "Riched32.dll" ); if ( !g_hRichEdit ) return false; g_backgroundColor = XBX_CLR_LTGREY; g_hBackgroundBrush = CreateSolidBrush( g_backgroundColor );
// get icons
g_hIcons[ICON_APPLICATION] = LoadIcon( g_hInstance, MAKEINTRESOURCE( IDI_VXCONSOLE ) ); g_hIcons[ICON_DISCONNECTED] = LoadIcon( g_hInstance, MAKEINTRESOURCE( IDI_DISCONNECTED ) ); g_hIcons[ICON_CONNECTED_XBOX] = LoadIcon( g_hInstance, MAKEINTRESOURCE( IDI_CONNECT1_ON ) ); g_hIcons[ICON_CONNECTED_APP0] = LoadIcon( g_hInstance, MAKEINTRESOURCE( IDI_CONNECT2_OFF ) ); g_hIcons[ICON_CONNECTED_APP1] = LoadIcon( g_hInstance, MAKEINTRESOURCE( IDI_CONNECT2_ON ) ); for ( i=0; i<MAX_ICONS; i++ ) { if ( !g_hIcons[i] ) return false; }
// get the font feight
hDC = GetWindowDC( NULL ); int nHeight = -MulDiv( VXCONSOLE_FONTSIZE, GetDeviceCaps( hDC, LOGPIXELSY ), 72 ); ReleaseDC( NULL, hDC );
// create fixed font
memset( &lf, 0, sizeof( LOGFONT ) ); lf.lfHeight = nHeight; lf.lfWeight = 400; strcpy( lf.lfFaceName, VXCONSOLE_FONT ); g_hFixedFont = CreateFontIndirect( &lf ); if ( !g_hFixedFont ) return false;
// create proportional font
memset( &lf, 0, sizeof( LOGFONT ) ); lf.lfHeight = -11; lf.lfWeight = 400; strcpy( lf.lfFaceName, "Tahoma" ); g_hProportionalFont = CreateFontIndirect( &lf ); if ( !g_hProportionalFont ) return false;
// get the font metrics
hDC = GetWindowDC( NULL ); hFontOld = ( HFONT )SelectObject( hDC, g_hFixedFont ); GetTextMetrics( hDC, &g_fixedFontMetrics ); SelectObject( hDC, hFontOld ); ReleaseDC( NULL, hDC );
return true; }
//-----------------------------------------------------------------------------
// Shutdown
//
// Free all resources
//-----------------------------------------------------------------------------
void Shutdown() { BugReporter_FreeInterfaces();
if ( g_PrintQueue.bInit ) { DeleteCriticalSection( &g_PrintQueue.CriticalSection ); g_PrintQueue.bInit = false; }
if ( g_hCommandReadyEvent ) { CloseHandle( g_hCommandReadyEvent ); g_hCommandReadyEvent = 0; }
if ( g_hRichEdit ) { FreeLibrary( g_hRichEdit ); g_hRichEdit = 0; }
if ( g_hBackgroundBrush ) { DeleteObject( g_hBackgroundBrush ); g_hBackgroundBrush = 0; }
if ( g_hFixedFont ) { DeleteObject( g_hFixedFont ); g_hFixedFont = 0; }
if ( g_hProportionalFont ) { DeleteObject( g_hProportionalFont ); g_hProportionalFont = 0; } }
//-----------------------------------------------------------------------------
// Startup
//
//-----------------------------------------------------------------------------
bool Startup() { // Load the xenon debugging dll (xbdm.dll) manually due to its absence from system path
const char *pPath = getenv( "path" ); const char *pXEDKDir = getenv( "xedk" ); if ( !pXEDKDir ) pXEDKDir = "";
int len = strlen( pPath ) + strlen( pXEDKDir ) + 256; char *pNewPath = (char*)_alloca( len ); sprintf( pNewPath, "path=%s;%s\\bin\\win32", pPath, pXEDKDir ); _putenv( pNewPath );
HMODULE hXBDM = LoadLibrary( TEXT( "xbdm.dll" ) ); if ( !hXBDM ) { if ( pXEDKDir[0] ) Sys_Error( "Couldn't load xbdm.dll" ); else Sys_Error( "Couldn't load xbdm.dll\nXEDK environment variable not set." ); }
LoadConfig();
if ( !CreateResources() ) return false;
// set up our print queue
InitializeCriticalSection( &g_PrintQueue.CriticalSection ); g_PrintQueue.bInit = true;
// manual reset, initially signaled
g_hCommandReadyEvent = CreateEvent( NULL, TRUE, TRUE, NULL );
// set up our window class
WNDCLASS wndclass; memset( &wndclass, 0, sizeof( wndclass ) ); wndclass.style = 0; wndclass.lpfnWndProc = Main_DlgProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = VXCONSOLE_WINDOWBYTES; wndclass.hInstance = g_hInstance; wndclass.hIcon = g_hIcons[ICON_DISCONNECTED]; wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ); wndclass.hbrBackground = g_hBackgroundBrush; wndclass.lpszMenuName = MAKEINTRESOURCE( MENU_VXCONSOLE ); wndclass.lpszClassName = VXCONSOLE_CLASSNAME; if ( !RegisterClass( &wndclass ) ) return false;
g_hAccel = LoadAccelerators( g_hInstance, MAKEINTRESOURCE( IDR_MAIN_ACCEL ) ); if ( !g_hAccel ) return false;
// Create our main dialog
g_hDlgMain = CreateDialog( g_hInstance, MAKEINTRESOURCE( IDD_VXCONSOLE ), 0, NULL ); if ( !g_hDlgMain ) return false; SetWindowLong( g_hDlgMain, VXCONSOLE_CONFIGID, g_configID );
if ( !BugDlg_Init() ) return false;
if ( !CpuProfile_Init() ) return false;
if ( !TexProfile_Init() ) return false;
if ( !MemProfile_Init() ) return false;
if ( !Bindings_Init() ) return false;
if ( !SyncFilesDlg_Init() ) return false;
if ( !ShowMaterials_Init() ) return false;
if ( !ShowTextures_Init() ) return false;
if ( !ShowSounds_Init() ) return false;
if ( !ShowMemDump_Init() ) return false;
if ( !TimeStampLog_Init() ) return false;
if ( !ExcludePathsDlg_Init() ) return false;
g_hwndOutputWindow = GetDlgItem( g_hDlgMain, IDC_OUTPUT ); g_hwndCommandCombo = GetDlgItem( g_hDlgMain, IDC_COMMAND );
CreateCommandHint();
// subclass our dropdown command listbox to handle return key
g_hwndCommandSubclassed = SubclassWindow( GetWindow( g_hwndCommandCombo, GW_CHILD ), CommandWindow_SubclassedProc );
// Change the font type of the output window to courier
CHARFORMAT cf; cf.cbSize = sizeof( CHARFORMAT ); SendMessage( g_hwndOutputWindow, EM_GETCHARFORMAT, 0, ( LPARAM )&cf ); cf.dwMask &= ~CFM_COLOR; cf.yHeight = VXCONSOLE_FONTSIZE*20; lstrcpyA( cf.szFaceName, VXCONSOLE_FONT ); SendMessage( g_hwndOutputWindow, EM_SETCHARFORMAT, SCF_ALL, ( LPARAM )&cf ); SendMessage( g_hwndOutputWindow, EM_SETBKGNDCOLOR, 0, g_backgroundColor );
// ensure the output window adheres to its z ordering
LONG style = GetWindowLong( g_hwndOutputWindow, GWL_STYLE ); style |= WS_CLIPSIBLINGS; SetWindowLong( g_hwndOutputWindow, GWL_STYLE, style );
// change the font of the command and its hint window to courier
SendDlgItemMessage( g_hDlgMain, IDC_COMMAND, WM_SETFONT, ( WPARAM )g_hFixedFont, TRUE );
// set the window title
SetMainWindowTitle();
ConsoleWindowPrintf( XBX_CLR_DEFAULT, "VXConsole %s [%s Build: %s %s] [Protocol: %d]\n", VXCONSOLE_VERSION, VXCONSOLE_BUILDTYPE, __DATE__, __TIME__, VXCONSOLE_PROTOCOL_VERSION ); ConsoleWindowPrintf( XBX_CLR_DEFAULT, "type '*help' for list of commands...\n\n" );
g_currentIcon = -1; SetConnectionIcon( ICON_DISCONNECTED );
if ( g_alwaysAutoConnect) { // user wants to auto-connect at startup
lc_autoConnect( 0, NULL ); }
if ( g_mainWindowRect.right && g_mainWindowRect.bottom ) MoveWindow( g_hDlgMain, g_mainWindowRect.left, g_mainWindowRect.top, g_mainWindowRect.right-g_mainWindowRect.left, g_mainWindowRect.bottom-g_mainWindowRect.top, FALSE );
// ready for display
int cmdShow = SW_SHOWNORMAL; if ( g_startMinimized ) cmdShow = SW_SHOWMINIMIZED; ShowWindow( g_hDlgMain, cmdShow );
// success
return true; }
//-----------------------------------------------------------------------------
// WinMain
//
// Entry point for program
//-----------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow ) { bool error = true; MSG msg = {0};
g_hInstance = hInstance;
g_bSuppressBlink = ParseCommandLineArg( pCmdLine, "-noblink", NULL, 0 );
// optional -config <ID> can specify a specific configuration
char buff[128]; buff[0] = '\0'; ParseCommandLineArg( pCmdLine, "-config ", buff, sizeof( buff ) ); g_configID = atoi( buff );
MakeConfigString( VXCONSOLE_REGISTRY, g_configID, buff, sizeof( buff ) ); Sys_SetRegistryPrefix( buff );
HWND hwnd = FindWindow( VXCONSOLE_CLASSNAME, NULL ); if ( hwnd && GetWindowLong( hwnd, VXCONSOLE_CONFIGID ) == g_configID ) { // single instance only
// bring other version to foreground
if ( IsIconic( hwnd ) ) ShowWindow( hwnd, SW_RESTORE ); SetForegroundWindow( hwnd ); return ( FALSE ); }
if ( !Startup() ) goto cleanUp;
// message pump
while ( GetMessage( &msg, NULL, 0, 0 ) ) { if ( !TranslateAccelerator( g_hDlgMain, g_hAccel, &msg ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } }
// no-error, end of app
error = false;
cleanUp: if ( error ) { char str[255]; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, str, 255, NULL ); MessageBox( NULL, str, NULL, MB_OK ); }
Shutdown();
return ( msg.wParam ); }
|