source-engine/utils/xbox/vxconsole/vxconsole.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

1529 lines
41 KiB
C++

//========= 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 );
}