diff options
Diffstat (limited to 'utils/xbox/vxconsole/vxconsole.cpp')
| -rw-r--r-- | utils/xbox/vxconsole/vxconsole.cpp | 1528 |
1 files changed, 1528 insertions, 0 deletions
diff --git a/utils/xbox/vxconsole/vxconsole.cpp b/utils/xbox/vxconsole/vxconsole.cpp new file mode 100644 index 0000000..976f3da --- /dev/null +++ b/utils/xbox/vxconsole/vxconsole.cpp @@ -0,0 +1,1528 @@ +//========= 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 ); +} + + |