diff --git a/.gitignore b/.gitignore index 258b4d5a..f174fc58 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,6 @@ /neo/.vs* /neo/out* /neo/build* +/.vs* +/neo/CMakeSettings.json diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index d8c32b78..2736e6d7 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -801,6 +801,17 @@ set(src_d3xp add_globbed_headers(src_d3xp "d3xp") +set(src_debuggerServer + tools/debugger/DebuggerBreakpoint.h + tools/debugger/DebuggerBreakpoint.cpp + tools/debugger/DebuggerServer.h + tools/debugger/DebuggerServer.cpp + tools/debugger/DebuggerScript.h + tools/debugger/DebuggerScript.cpp + tools/debugger/DebuggerMessages.h + tools/debugger/debugger.cpp +) + set(src_core ${src_renderer} ${src_framework} @@ -862,6 +873,23 @@ if (TOOLS AND MFC_FOUND AND MSVC) # Script editor file(GLOB src_script_editor "tools/script/*.cpp") add_globbed_headers(src_script_editor "tools/script") + # Script Debugger + set(src_debuggerClient + tools/debugger/DebuggerClient.h + tools/debugger/DebuggerClient.cpp + tools/debugger/DebuggerApp.h + tools/debugger/DebuggerApp.cpp + tools/debugger/DebuggerQuickWatchDlg.h + tools/debugger/DebuggerQuickWatchDlg.cpp + tools/debugger/DebuggerWindow.h + tools/debugger/DebuggerWindow.cpp + tools/debugger/DebuggerFindDlg.h + tools/debugger/DebuggerFindDlg.cpp + ) + set(src_script_debugger + ${src_debuggerServer} + ${src_debuggerClient} + ) # sound editor? file(GLOB src_sound_editor "tools/sound/*.cpp") add_globbed_headers(src_sound_editor "tools/sound") @@ -881,13 +909,17 @@ if (TOOLS AND MFC_FOUND AND MSVC) ${src_map_editor} ${src_script_editor} ${src_sound_editor} + ${src_script_debugger} "tools/edit_public.h" "tools/edit_gui_common.h" ) SET(CMAKE_MFC_FLAG 2) set(TOOLS_DEFINES "ID_ALLOW_TOOLS;_AFXDLL") else() - set(src_editor_tools "tools/edit_stub.cpp" "tools/edit_public.h") + set(src_editor_tools "tools/edit_stub.cpp" "tools/edit_public.h" "tools/debugger/debugger_common.h") + list(APPEND src_editor_tools + ${src_debuggerServer} + ) endif() @@ -1052,9 +1084,10 @@ if(DEDICATED) ${src_stub_openal} ${src_stub_gl} ${src_sys_base} + ${src_debuggerServer} ) - source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX neo FILES ${src_core} ${src_sys_base} ${src_stub_openal} ${src_stub_gl}) + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX neo FILES ${src_core} ${src_sys_base} ${src_stub_openal} ${src_stub_gl} ${src_debuggerServer}) set_target_properties(${DHEWM3BINARY}ded PROPERTIES COMPILE_DEFINITIONS "ID_DEDICATED;__DOOM_DLL__") set_target_properties(${DHEWM3BINARY}ded PROPERTIES LINK_FLAGS "${ldflags}") diff --git a/neo/d3xp/GameEdit.cpp b/neo/d3xp/GameEdit.cpp index 0c637bdb..5d68b80f 100644 --- a/neo/d3xp/GameEdit.cpp +++ b/neo/d3xp/GameEdit.cpp @@ -670,7 +670,7 @@ void idEditEntities::DisplayEntities( void ) { =============================================================================== */ -idGameEdit gameEditLocal; +idGameEditExt gameEditLocal; idGameEdit * gameEdit = &gameEditLocal; @@ -1146,3 +1146,62 @@ void idGameEdit::MapEntityTranslate( const char *name, const idVec3 &v ) const { } } } + + +/*********************************************************************** + + Debugger + +***********************************************************************/ + +bool idGameEditExt::IsLineCode( const char *filename, int linenumber ) const { + idStr fileStr; + idProgram *program = &gameLocal.program; + for ( int i = 0; i < program->NumStatements( ); i++ ) { + fileStr = program->GetFilename( program->GetStatement( i ).file ); + fileStr.BackSlashesToSlashes( ); + + if ( strcmp( filename, fileStr.c_str( ) ) == 0 + && program->GetStatement( i ).linenumber == linenumber + ) { + return true; + } + } + return false; +} + +void idGameEditExt::GetLoadedScripts(idStrList** result) +{ + (*result)->Clear(); + idProgram* program = &gameLocal.program; + + for (int i = 0; i < program->NumFilenames(); i++) + { + (*result)->AddUnique(idStr(program->GetFilename(i))); + } +} + +void idGameEditExt::MSG_WriteScriptList(idBitMsg* msg) +{ + idProgram* program = &gameLocal.program; + + msg->WriteInt(program->NumFilenames()); + for (int i = 0; i < program->NumFilenames(); i++) + { + idStr file = program->GetFilename(i); + //fix this. it seams that scripts triggered by the runtime are stored with a wrong path + //the use // instead of '\' + file.BackSlashesToSlashes(); + msg->WriteString(file); + } +} + +const char* idGameEditExt::GetFilenameForStatement(idProgram* program, int index) const +{ + return program->GetFilenameForStatement(index); +} + +int idGameEditExt::GetLineNumberForStatement(idProgram* program, int index) const +{ + return program->GetLineNumberForStatement(index); +} diff --git a/neo/d3xp/Game_local.cpp b/neo/d3xp/Game_local.cpp index 08aaf9bf..f7a8ca8b 100644 --- a/neo/d3xp/Game_local.cpp +++ b/neo/d3xp/Game_local.cpp @@ -302,6 +302,16 @@ void idGameLocal::Clear( void ) { #endif } +static bool ( *updateDebuggerFnPtr )( idInterpreter *interpreter, idProgram *program, int instructionPointer ) = NULL; +bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ) { + bool ret = false; + if ( interpreter != nullptr && program != nullptr ) { + ret = updateDebuggerFnPtr ? updateDebuggerFnPtr( interpreter , program, instructionPointer ) : false; + } + return ret; +} + + /* =========== idGameLocal::Init @@ -410,6 +420,10 @@ void idGameLocal::Init( void ) { gamestate = GAMESTATE_NOMAP; Printf( "...%d aas types\n", aasList.Num() ); + + //debugger support + common->GetAdditionalFunction( idCommon::FT_UpdateDebugger,( idCommon::FunctionPointer * ) &updateDebuggerFnPtr,NULL); + } /* @@ -1312,7 +1326,7 @@ void idGameLocal::MapPopulate( void ) { idGameLocal::InitFromNewMap =================== */ -void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randseed ) { +void idGameLocal::InitFromNewMap(const char* mapName, idRenderWorld* renderWorld, idSoundWorld* soundWorld, bool isServer, bool isClient, int randseed) { this->isServer = isServer; this->isClient = isClient; @@ -2436,14 +2450,14 @@ void idGameLocal::RunTimeGroup2() { idGameLocal::RunFrame ================ */ -gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) { - idEntity * ent; - int num; - float ms; - idTimer timer_think, timer_events, timer_singlethink; - gameReturn_t ret; - idPlayer *player; - const renderView_t *view; +gameReturn_t idGameLocal::RunFrame(const usercmd_t* clientCmds) { + idEntity* ent; + int num; + float ms; + idTimer timer_think, timer_events, timer_singlethink; + gameReturn_t ret; + idPlayer* player; + const renderView_t* view; #ifdef _DEBUG if ( isMultiplayer ) { diff --git a/neo/d3xp/Game_local.h b/neo/d3xp/Game_local.h index acd4fff0..f9a96935 100644 --- a/neo/d3xp/Game_local.h +++ b/neo/d3xp/Game_local.h @@ -365,13 +365,13 @@ public: virtual const idDict & GetPersistentPlayerInfo( int clientNum ); virtual void SetPersistentPlayerInfo( int clientNum, const idDict &playerInfo ); - virtual void InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randSeed ); - virtual bool InitFromSaveGame( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, idFile *saveGameFile ); + virtual void InitFromNewMap(const char* mapName, idRenderWorld* renderWorld, idSoundWorld* soundWorld, bool isServer, bool isClient, int randSeed ); + virtual bool InitFromSaveGame(const char* mapName, idRenderWorld* renderWorld, idSoundWorld* soundWorld, idFile* saveGameFile ); virtual void SaveGame( idFile *saveGameFile ); virtual void MapShutdown( void ); virtual void CacheDictionaryMedia( const idDict *dict ); virtual void SpawnPlayer( int clientNum ); - virtual gameReturn_t RunFrame( const usercmd_t *clientCmds ); + virtual gameReturn_t RunFrame(const usercmd_t* clientCmds ); virtual bool Draw( int clientNum ); virtual escReply_t HandleESC( idUserInterface **gui ); virtual idUserInterface *StartMenu( void ); diff --git a/neo/d3xp/script/Script_Interpreter.cpp b/neo/d3xp/script/Script_Interpreter.cpp index c8e0cc45..8efd298d 100644 --- a/neo/d3xp/script/Script_Interpreter.cpp +++ b/neo/d3xp/script/Script_Interpreter.cpp @@ -33,6 +33,11 @@ If you have questions concerning this license or the applicable additional terms #include "script/Script_Interpreter.h" +#include "framework/FileSystem.h" + +// HvG: Debugger support +extern bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ); + /* ================ idInterpreter::idInterpreter() @@ -183,7 +188,6 @@ idInterpreter::GetRegisterValue Returns a string representation of the value of the register. This is used primarily for the debugger and debugging -//FIXME: This is pretty much wrong. won't access data in most situations. ================ */ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDepth ) { @@ -191,17 +195,17 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep idVarDef *d; char funcObject[ 1024 ]; char *funcName; - const idVarDef *scope; + const idVarDef *scope = NULL; + const idVarDef *scopeObj; const idTypeDef *field; - const idScriptObject *obj; const function_t *func; out.Empty(); - + if ( scopeDepth == -1 ) { scopeDepth = callStackDepth; - } - + } + if ( scopeDepth == callStackDepth ) { func = currentFunction; } else { @@ -215,35 +219,44 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep funcName = strstr( funcObject, "::" ); if ( funcName ) { *funcName = '\0'; - scope = gameLocal.program.GetDef( NULL, funcObject, &def_namespace ); - funcName += 2; + scopeObj = gameLocal.program.GetDef( NULL, funcObject, &def_namespace ); + funcName += 2; + if ( scopeObj ) + { + scope = gameLocal.program.GetDef( NULL, funcName, scopeObj ); + } } else { funcName = funcObject; - scope = &def_namespace; + scope = gameLocal.program.GetDef( NULL, func->Name(), &def_namespace ); + scopeObj = NULL; } - // Get the function from the object - d = gameLocal.program.GetDef( NULL, funcName, scope ); - if ( !d ) { + if ( !scope ) + { return false; } - // Get the variable itself and check various namespaces - d = gameLocal.program.GetDef( NULL, name, d ); - if ( !d ) { - if ( scope == &def_namespace ) { - return false; - } - - d = gameLocal.program.GetDef( NULL, name, scope ); - if ( !d ) { - d = gameLocal.program.GetDef( NULL, name, &def_namespace ); - if ( !d ) { - return false; + d = gameLocal.program.GetDef( NULL, name, scope ); + + // Check the objects for it if it wasnt local to the function + if ( !d ) + { + for ( ; scopeObj && scopeObj->TypeDef()->SuperClass(); scopeObj = scopeObj->TypeDef()->SuperClass()->def ) + { + d = gameLocal.program.GetDef( NULL, name, scopeObj ); + if ( d ) + { + break; } } - } + } + if ( !d ) + { + out = "???"; + return false; + } + reg = GetVariable( d ); switch( d->Type() ) { case ev_float: @@ -256,7 +269,7 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep break; case ev_vector: - if ( reg.vectorPtr ) { + if ( reg.vectorPtr ) { out = va( "%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z ); } else { out = "0,0,0"; @@ -274,30 +287,55 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep break; case ev_field: + { + idEntity* entity; + idScriptObject* obj; + if ( scope == &def_namespace ) { // should never happen, but handle it safely anyway return false; } - field = scope->TypeDef()->GetParmType( reg.ptrOffset )->FieldType(); - obj = *reinterpret_cast( &localstack[ callStack[ callStackDepth ].stackbase ] ); - if ( !field || !obj ) { + field = d->TypeDef()->FieldType(); + entity = GetEntity ( *((int*)&localstack[ localstackBase ]) ); + if ( !entity || !field ) + { return false; } + obj = &entity->scriptObject; + if ( !obj ) { + return false; + } + switch ( field->Type() ) { - case ev_boolean: - out = va( "%d", *( reinterpret_cast( &obj->data[ reg.ptrOffset ] ) ) ); - return true; + case ev_boolean: + out = va( "%d", *( reinterpret_cast( &obj->data[ reg.ptrOffset ] ) ) ); + return true; - case ev_float: - out = va( "%g", *( reinterpret_cast( &obj->data[ reg.ptrOffset ] ) ) ); - return true; + case ev_float: + out = va( "%g", *( reinterpret_cast( &obj->data[ reg.ptrOffset ] ) ) ); + return true; + + case ev_string: { + const char* str; + str = reinterpret_cast( &obj->data[ reg.ptrOffset ] ); + if ( !str ) { + out = "\"\""; + } else { + out = "\""; + out += str; + out += "\""; + } + return true; + } - default: - return false; + default: + return false; } + break; + } case ev_string: if ( reg.stringPtr ) { @@ -313,7 +351,6 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep return false; } } - /* ================ idInterpreter::GetCallstackDepth @@ -969,6 +1006,19 @@ bool idInterpreter::Execute( void ) { // next statement st = &gameLocal.program.GetStatement( instructionPointer ); + if ( !updateGameDebugger( this, &gameLocal.program, instructionPointer ) + && g_debugScript.GetBool( ) ) + { + static int lastLineNumber = -1; + if (lastLineNumber != gameLocal.program.GetStatement(instructionPointer).linenumber) { + gameLocal.Printf("%s (%d)\n", + gameLocal.program.GetFilename(gameLocal.program.GetStatement(instructionPointer).file), + gameLocal.program.GetStatement(instructionPointer).linenumber + ); + lastLineNumber = gameLocal.program.GetStatement(instructionPointer).linenumber; + } + } + switch( st->op ) { case OP_RETURN: LeaveFunction( st->a ); @@ -1833,3 +1883,99 @@ bool idInterpreter::Execute( void ) { return threadDying; } + + +bool idGameEditExt::CheckForBreakPointHit(const idInterpreter* interpreter, const function_t* function1, const function_t* function2, int depth) const +{ + return ((interpreter->GetCurrentFunction() == function1 || + interpreter->GetCurrentFunction() == function2) && + (interpreter->GetCallstackDepth() <= depth)); +} + +bool idGameEditExt::ReturnedFromFunction(const idProgram* program, const idInterpreter* interpreter, int index) const +{ + + return (const_cast(program)->GetStatement(index).op == OP_RETURN && interpreter->GetCallstackDepth() <= 1); +} + +bool idGameEditExt::GetRegisterValue(const idInterpreter* interpreter, const char* name, idStr& out, int scopeDepth) const +{ + return const_cast(interpreter)->GetRegisterValue(name, out, scopeDepth); +} + +const idThread* idGameEditExt::GetThread(const idInterpreter* interpreter) const +{ + return interpreter->GetThread(); +} + +void idGameEditExt::MSG_WriteCallstackFunc(idBitMsg* msg, const prstack_t* stack, const idProgram* program, int instructionPtr) +{ + const statement_t* st; + const function_t* func; + + func = stack->f; + + // If the function is unknown then just fill in with default data. + if (!func) + { + msg->WriteString(""); + msg->WriteString(""); + msg->WriteInt(0); + return; + } + else + { + msg->WriteString(va("%s( )", func->Name())); + } + + if (stack->s == -1) //this is a fake stack created by debugger, use intruction pointer for retrieval. + st = &const_cast(program)->GetStatement(instructionPtr); + else // Use the calling statement as the filename and linenumber where the call was made from + st = &const_cast(program)->GetStatement(stack->s); + + if (st) + { + idStr qpath = const_cast(program)->GetFilename(st->file); + if (idStr::FindChar(qpath, ':') != -1) + qpath = fileSystem->OSPathToRelativePath(qpath.c_str()); + qpath.BackSlashesToSlashes(); + msg->WriteString(qpath); + msg->WriteInt(st->linenumber); + } + else + { + msg->WriteString(""); + msg->WriteInt(0); + } +} + +void idGameEditExt::MSG_WriteInterpreterInfo(idBitMsg* msg, const idInterpreter* interpreter, const idProgram* program, int instructionPtr) +{ + int i; + prstack_s temp; + + msg->WriteShort((int)interpreter->GetCallstackDepth()); + + // write out the current function + temp.f = interpreter->GetCurrentFunction(); + temp.s = -1; + temp.stackbase = 0; + MSG_WriteCallstackFunc(msg, &temp, program, instructionPtr); + + // Run through all of the callstack and write each to the msg + for (i = interpreter->GetCallstackDepth() - 1; i > 0; i--) + { + MSG_WriteCallstackFunc(msg, interpreter->GetCallstack() + i, program, instructionPtr); + } +} + + +int idGameEditExt::GetInterpreterCallStackDepth(const idInterpreter* interpreter) +{ + return interpreter->GetCallstackDepth(); +} + +const function_t* idGameEditExt::GetInterpreterCallStackFunction(const idInterpreter* interpreter, int stackDepth/* = -1*/) +{ + return interpreter->GetCallstack()[stackDepth > -1 ? stackDepth : interpreter->GetCallstackDepth()].f; +} \ No newline at end of file diff --git a/neo/d3xp/script/Script_Thread.cpp b/neo/d3xp/script/Script_Thread.cpp index cb826331..ef0b8ed9 100644 --- a/neo/d3xp/script/Script_Thread.cpp +++ b/neo/d3xp/script/Script_Thread.cpp @@ -1921,3 +1921,49 @@ void idThread::Event_InfluenceActive( void ) { idThread::ReturnInt( false ); } } + +int idGameEditExt::ThreadGetNum(const idThread* thread) const +{ + return const_cast(thread)->GetThreadNum(); +} + +const char* idGameEditExt::ThreadGetName(const idThread* thread) const +{ + return const_cast(thread)->GetThreadName(); +} + +int idGameEditExt::GetTotalScriptThreads() const +{ + return idThread::GetThreads().Num(); +} + +const idThread* idGameEditExt::GetThreadByIndex(int index) const +{ + return idThread::GetThreads()[index]; +} + +bool idGameEditExt::ThreadIsDoneProcessing(const idThread* thread) const +{ + return const_cast(thread)->IsDoneProcessing(); +} + +bool idGameEditExt::ThreadIsWaiting(const idThread* thread) const +{ + return const_cast(thread)->IsWaiting(); +} + +bool idGameEditExt::ThreadIsDying(const idThread* thread) const +{ + return const_cast(thread)->IsDying(); +} + +void idGameEditExt::MSG_WriteThreadInfo(idBitMsg* msg, const idThread* thread, const idInterpreter* interpreter) +{ + msg->WriteString(const_cast(thread)->GetThreadName()); + msg->WriteInt(const_cast(thread)->GetThreadNum()); + + msg->WriteBits((int)(thread == interpreter->GetThread()), 1); + msg->WriteBits((int)const_cast(thread)->IsDoneProcessing(), 1); + msg->WriteBits((int)const_cast(thread)->IsWaiting(), 1); + msg->WriteBits((int)const_cast(thread)->IsDying(), 1); +} \ No newline at end of file diff --git a/neo/framework/CVarSystem.cpp b/neo/framework/CVarSystem.cpp index 413e3d5d..a10860ee 100644 --- a/neo/framework/CVarSystem.cpp +++ b/neo/framework/CVarSystem.cpp @@ -244,7 +244,7 @@ void idInternalCVar::UpdateValue( void ) { clamped = true; } } - if ( clamped || !idStr::IsNumeric( value ) || idStr::FindChar( value, '.' ) ) { + if ( clamped || !idStr::IsNumeric( value ) || (idStr::FindChar( value, '.' )!=-1) ) { valueString = idStr( integerValue ); value = valueString.c_str(); } diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index 959e7543..7d98d780 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -98,6 +98,10 @@ idCVar com_timescale( "timescale", "1", CVAR_SYSTEM | CVAR_FLOAT, "scales the ti idCVar com_makingBuild( "com_makingBuild", "0", CVAR_BOOL | CVAR_SYSTEM, "1 when making a build" ); idCVar com_updateLoadSize( "com_updateLoadSize", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "update the load size after loading a map" ); +idCVar com_enableDebuggerServer( "com_enableDebuggerServer", "0", CVAR_BOOL | CVAR_SYSTEM, "toggle debugger server and try to connect to com_dbgClientAdr" ); +idCVar com_dbgClientAdr( "com_dbgClientAdr", "localhost", CVAR_SYSTEM | CVAR_ARCHIVE, "debuggerApp client address" ); +idCVar com_dbgServerAdr( "com_dbgServerAdr", "localhost", CVAR_SYSTEM | CVAR_ARCHIVE, "debugger server address" ); + idCVar com_product_lang_ext( "com_product_lang_ext", "1", CVAR_INTEGER | CVAR_SYSTEM | CVAR_ARCHIVE, "Extension to use when creating language files." ); // com_speeds times @@ -112,6 +116,8 @@ volatile int com_ticNumber; // 60 hz tics int com_editors; // currently opened editor(s) bool com_editorActive; // true if an editor has focus +bool com_debuggerSupported; // only set to true when the updateDebugger function is set. see GetAdditionalFunction() + #ifdef _WIN32 HWND com_hwndMsg = NULL; bool com_outputMsg = false; @@ -245,6 +251,7 @@ idCommonLocal::idCommonLocal( void ) { com_fullyInitialized = false; com_refreshOnPrint = false; com_errorEntered = 0; + com_debuggerSupported = false; strcpy( errorMessage, "" ); @@ -382,12 +389,17 @@ void idCommonLocal::VPrintf( const char *fmt, va_list args ) { // remove any color codes idStr::RemoveColors( msg ); - // echo to dedicated console and early console - Sys_Printf( "%s", msg ); - - // print to script debugger server - // DebuggerServerPrint( msg ); - + if ( com_enableDebuggerServer.GetBool( ) ) { + // print to script debugger server + if ( com_editors & EDITOR_DEBUGGER ) + DebuggerServerPrint( msg ); + else + // only echo to dedicated console and early console when debugger is not running so no + // deadlocks occur if engine functions called from the debuggerthread trace stuff.. + Sys_Printf( "%s", msg ); + } else { + Sys_Printf( "%s", msg ); + } #if 0 // !@# #if defined(_DEBUG) && defined(WIN32) if ( strlen( msg ) < 512 ) { @@ -1134,8 +1146,14 @@ Com_ScriptDebugger_f static void Com_ScriptDebugger_f( const idCmdArgs &args ) { // Make sure it wasnt on the command line if ( !( com_editors & EDITOR_DEBUGGER ) ) { - common->Printf( "Script debugger is currently disabled\n" ); - // DebuggerClientLaunch(); + + //start debugger server if needed + if ( !com_enableDebuggerServer.GetBool() ) + com_enableDebuggerServer.SetBool( true ); + + //start debugger client. + DebuggerClientLaunch(); + } } @@ -2020,6 +2038,7 @@ void Com_LocalizeMaps_f( const idCmdArgs &args ) { strCount += LocalizeMap(args.Argv(2), strTable, listHash, excludeList, write); } else { idStrList files; + //wow, what now? a hardcoded path? GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files); for ( int i = 0; i < files.Num(); i++ ) { idStr file = fileSystem->OSPathToRelativePath(files[i]); @@ -2398,6 +2417,14 @@ void idCommonLocal::Frame( void ) { InitSIMD(); } + if ( com_enableDebuggerServer.IsModified() ) { + if ( com_enableDebuggerServer.GetBool() ) { + DebuggerServerInit(); + } else { + DebuggerServerShutdown(); + } + } + eventLoop->RunEventLoop(); com_frameTime = com_ticNumber * USERCMD_MSEC; @@ -2666,7 +2693,7 @@ void idCommonLocal::LoadGameDLL( void ) { gameImport.AASFileManager = ::AASFileManager; gameImport.collisionModelManager = ::collisionModelManager; - gameExport = *GetGameAPI( &gameImport ); + gameExport = *GetGameAPI( &gameImport); if ( gameExport.version != GAME_API_VERSION ) { Sys_DLL_Unload( gameDLL ); @@ -2709,6 +2736,7 @@ void idCommonLocal::UnloadGameDLL( void ) { #endif + com_debuggerSupported = false; // HvG: Reset debugger availability. gameCallbacks.Reset(); // DG: these callbacks are invalid now because DLL has been unloaded } @@ -3175,14 +3203,15 @@ void idCommonLocal::InitGame( void ) { // initialize the user interfaces uiManager->Init(); - // startup the script debugger - // DebuggerServerInit(); - PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04350" ) ); // load the game dll LoadGameDLL(); + // startup the script debugger + if ( com_enableDebuggerServer.GetBool( ) ) + DebuggerServerInit( ); + PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04351" ) ); // init the session @@ -3214,7 +3243,8 @@ void idCommonLocal::ShutdownGame( bool reloading ) { } // shutdown the script debugger - // DebuggerServerShutdown(); + if ( com_enableDebuggerServer.GetBool() ) + DebuggerServerShutdown(); idAsyncNetwork::client.Shutdown(); @@ -3277,11 +3307,21 @@ bool idCommonLocal::SetCallback(idCommon::CallbackType cbt, idCommon::FunctionPo } } -static bool isDemo(void) +static bool isDemo( void ) { return sessLocal.IsDemoVersion(); } +static bool updateDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ) +{ + if (com_editors & EDITOR_DEBUGGER) + { + DebuggerServerCheckBreakpoint( interpreter, program, instructionPointer ); + return true; + } + return false; +} + // returns true if that function is available in this version of dhewm3 // *out_fnptr will be the function (you'll have to cast it probably) // *out_userArg will be an argument you have to pass to the function, if appropriate (else NULL) @@ -3295,6 +3335,7 @@ bool idCommonLocal::GetAdditionalFunction(idCommon::FunctionType ft, idCommon::F Warning("Called idCommon::GetAdditionalFunction() with out_fnptr == NULL!\n"); return false; } + switch(ft) { case idCommon::FT_IsDemo: @@ -3302,6 +3343,11 @@ bool idCommonLocal::GetAdditionalFunction(idCommon::FunctionType ft, idCommon::F // don't set *out_userArg, this function takes no arguments return true; + case idCommon::FT_UpdateDebugger: + *out_fnptr = (idCommon::FunctionPointer)updateDebugger; + com_debuggerSupported = true; + return true; + default: *out_fnptr = NULL; Warning("Called idCommon::SetCallback() with unknown FunctionType %d!\n", ft); @@ -3309,7 +3355,6 @@ bool idCommonLocal::GetAdditionalFunction(idCommon::FunctionType ft, idCommon::F } } - idGameCallbacks gameCallbacks; idGameCallbacks::idGameCallbacks() diff --git a/neo/framework/Common.h b/neo/framework/Common.h index c0400bb4..3262e6cc 100644 --- a/neo/framework/Common.h +++ b/neo/framework/Common.h @@ -73,6 +73,9 @@ extern idCVar com_showAsyncStats; extern idCVar com_showSoundDecoders; extern idCVar com_makingBuild; extern idCVar com_updateLoadSize; +extern idCVar com_enableDebuggerServer; +extern idCVar com_dbgClientAdr; +extern idCVar com_dbgServerAdr; extern int time_gameFrame; // game logic time extern int time_gameDraw; // game present time @@ -84,6 +87,8 @@ extern volatile int com_ticNumber; // 60 hz tics, incremented by async functio extern int com_editors; // current active editor(s) extern bool com_editorActive; // true if an editor has focus +extern bool com_debuggerSupported; // only set to true when the updateDebugger function is set. see GetAdditionalFunction() + #ifdef _WIN32 const char DMAP_MSGID[] = "DMAPOutput"; const char DMAP_DONE[] = "DMAPDone"; @@ -111,6 +116,8 @@ struct MemInfo_t { }; class idLangDict; +class idInterpreter; +class idProgram; class idCommon { public: @@ -158,6 +165,7 @@ public: // Writes cvars with the given flags to a file. virtual void WriteFlaggedCVarsToFile( const char *filename, int flags, const char *setCmd ) = 0; + // Begins redirection of console output to the given buffer. virtual void BeginRedirect( char *buffer, int buffersize, void (*flush)( const char * ) ) = 0; @@ -265,6 +273,11 @@ public: // it returns true if we're currently running the doom3 demo // not relevant for mods, only for game/ aka base.dll/base.so/... FT_IsDemo = 1, + // the function's signature is bool fn(idInterpreter,idProgram,int) with arguments: + // idInterpreter *interpreter, idProgram *program, int instructionPointer + // it returns true if the game debugger is active. + // relevant for mods. + FT_UpdateDebugger, }; // returns true if that function is available in this version of dhewm3 diff --git a/neo/framework/FileSystem.cpp b/neo/framework/FileSystem.cpp index bbe77f02..31b952a4 100644 --- a/neo/framework/FileSystem.cpp +++ b/neo/framework/FileSystem.cpp @@ -886,10 +886,20 @@ const char *idFileSystemLocal::OSPathToRelativePath( const char *OSPath ) { } if ( base ) { - s = strstr( base, "/" ); - if ( !s ) { - s = strstr( base, "\\" ); + // DG: on Windows base might look like "base\\pak008.pk4/script/doom_util.script" + // while on Linux it'll be more like "base/pak008.pk4/script/doom_util.script" + // I /think/ we want to get rid of the bla.pk4 part, at least that's what happens implicitly on Windows + // (I hope these problems don't exist if the file is not from a .pk4, so that case is handled like before) + s = strstr( base, ".pk4/" ); + if ( s != NULL ) { + s += 4; // skip ".pk4", but *not* the following '/', that'll be skipped below + } else { + s = strchr( base, '/' ); + if ( s == NULL ) { + s = strchr( base, '\\' ); + } } + if ( s ) { strcpy( relativePath, s + 1 ); if ( fs_debug.GetInteger() > 1 ) { diff --git a/neo/framework/Game.h b/neo/framework/Game.h index 8a4c4e1c..af64d2c8 100644 --- a/neo/framework/Game.h +++ b/neo/framework/Game.h @@ -31,6 +31,7 @@ If you have questions concerning this license or the applicable additional terms #include "idlib/BitMsg.h" #include "idlib/Dict.h" +#include "idlib/containers/StrList.h" #include "framework/UsercmdGen.h" #include "renderer/RenderWorld.h" #include "sound/sound.h" @@ -232,6 +233,11 @@ enum { class idEntity; class idMD5Anim; +class idThread; +class function_t; +class idProgram; +class idInterpreter; +typedef struct prstack_s prstack_t; // FIXME: this interface needs to be reworked but it properly separates code for the time being class idGameEdit { @@ -294,7 +300,7 @@ public: virtual void EntitySetColor( idEntity *ent, const idVec3 color ); // Player methods. - virtual bool PlayerIsValid() const; + virtual bool PlayerIsValid( ) const; virtual void PlayerGetOrigin( idVec3 &org ) const; virtual void PlayerGetAxis( idMat3 &axis ) const; virtual void PlayerGetViewAngles( idAngles &angles ) const; @@ -310,11 +316,44 @@ public: virtual int MapGetEntitiesMatchingClassWithString( const char *classname, const char *match, const char *list[], const int max ) const; virtual void MapRemoveEntity( const char *name ) const; virtual void MapEntityTranslate( const char *name, const idVec3 &v ) const; - }; extern idGameEdit * gameEdit; +// In game script Debugging Support +class idGameEditExt : public idGameEdit { +public: + virtual ~idGameEditExt( void ) { } + // IdProgram + virtual void GetLoadedScripts( idStrList ** result ); + virtual bool IsLineCode( const char* filename, int linenumber) const; + virtual const char * GetFilenameForStatement( idProgram* program, int index ) const; + virtual int GetLineNumberForStatement( idProgram* program, int index ) const; + + // idInterpreter + virtual bool CheckForBreakPointHit( const idInterpreter* interpreter, const function_t* function1, const function_t* function2, int depth ) const; + virtual bool ReturnedFromFunction( const idProgram* program, const idInterpreter* interpreter, int index ) const; + virtual bool GetRegisterValue( const idInterpreter* interpreter, const char* name, idStr& out, int scopeDepth ) const; + virtual const idThread* GetThread( const idInterpreter* interpreter ) const; + virtual int GetInterpreterCallStackDepth( const idInterpreter* interpreter ); + virtual const function_t* GetInterpreterCallStackFunction( const idInterpreter* interpreter, int stackDepth = -1 ); + + // IdThread + virtual const char * ThreadGetName( const idThread* thread ) const; + virtual int ThreadGetNum( const idThread* thread ) const; + virtual bool ThreadIsDoneProcessing( const idThread* thread ) const; + virtual bool ThreadIsWaiting( const idThread* thread ) const; + virtual bool ThreadIsDying( const idThread* thread ) const; + virtual int GetTotalScriptThreads( ) const; + virtual const idThread* GetThreadByIndex( int index ) const; + + // MSG helpers + virtual void MSG_WriteThreadInfo( idBitMsg* msg, const idThread* thread, const idInterpreter* interpreter ); + virtual void MSG_WriteCallstackFunc( idBitMsg* msg, const prstack_t* stack, const idProgram* program, int instructionPtr ); + virtual void MSG_WriteInterpreterInfo( idBitMsg* msg, const idInterpreter* interpreter, const idProgram* program, int instructionPtr ); + virtual void MSG_WriteScriptList( idBitMsg* msg ); +}; + /* =============================================================================== diff --git a/neo/framework/GameCallbacks_local.h b/neo/framework/GameCallbacks_local.h index 51d9763a..58bad7fc 100644 --- a/neo/framework/GameCallbacks_local.h +++ b/neo/framework/GameCallbacks_local.h @@ -40,9 +40,8 @@ If you have questions concerning this license or the applicable additional terms struct idGameCallbacks { typedef void (*ReloadImagesCallback)(void* userArg, const idCmdArgs &args); - ReloadImagesCallback reloadImagesCB; - void* reloadImagesUserArg; - + ReloadImagesCallback reloadImagesCB; + void* reloadImagesUserArg; idGameCallbacks(); diff --git a/neo/game/GameEdit.cpp b/neo/game/GameEdit.cpp index 0c637bdb..57e7eb28 100644 --- a/neo/game/GameEdit.cpp +++ b/neo/game/GameEdit.cpp @@ -670,7 +670,7 @@ void idEditEntities::DisplayEntities( void ) { =============================================================================== */ -idGameEdit gameEditLocal; +idGameEditExt gameEditLocal; idGameEdit * gameEdit = &gameEditLocal; @@ -1146,3 +1146,64 @@ void idGameEdit::MapEntityTranslate( const char *name, const idVec3 &v ) const { } } } + +/*********************************************************************** + + Debugger + +***********************************************************************/ + +bool idGameEditExt::IsLineCode(const char* filename, int linenumber) const +{ + idStr fileStr; + idProgram* program = &gameLocal.program; + for (int i = 0; i < program->NumStatements(); i++) + { + fileStr = program->GetFilename(program->GetStatement(i).file); + fileStr.BackSlashesToSlashes(); + + if (strcmp(filename, fileStr.c_str()) == 0 + && program->GetStatement(i).linenumber == linenumber + ) + { + return true; + } + } + return false; +} + +void idGameEditExt::GetLoadedScripts( idStrList** result ) +{ + (*result)->Clear(); + idProgram* program = &gameLocal.program; + + for (int i = 0; i < program->NumFilenames(); i++) + { + (*result)->AddUnique( idStr(program->GetFilename( i )) ); + } +} + +void idGameEditExt::MSG_WriteScriptList( idBitMsg* msg) +{ + idProgram* program = &gameLocal.program; + + msg->WriteInt( program->NumFilenames() ); + for (int i = 0; i < program->NumFilenames(); i++) + { + idStr file = program->GetFilename(i); + //fix this. it seams that scripts triggered by the runtime are stored with a wrong path + //the use // instead of '\' + file.BackSlashesToSlashes(); + msg->WriteString(file); + } +} + +const char*idGameEditExt::GetFilenameForStatement(idProgram* program, int index) const +{ + return program->GetFilenameForStatement(index); +} + +int idGameEditExt::GetLineNumberForStatement(idProgram* program, int index) const +{ + return program->GetLineNumberForStatement(index); +} \ No newline at end of file diff --git a/neo/game/Game_local.cpp b/neo/game/Game_local.cpp index ef820d66..f7bc67e6 100644 --- a/neo/game/Game_local.cpp +++ b/neo/game/Game_local.cpp @@ -270,6 +270,14 @@ bool IsDoom3DemoVersion() return ret; } +static bool ( *updateDebuggerFnPtr )( idInterpreter *interpreter, idProgram *program, int instructionPointer ) = NULL; +bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ) { + bool ret = false; + if ( interpreter != nullptr && program != nullptr ) { + ret = updateDebuggerFnPtr ? updateDebuggerFnPtr( interpreter, program, instructionPointer ) : false; + } + return ret; +} /* @@ -352,6 +360,8 @@ void idGameLocal::Init( void ) { // DG: hack to support the Demo version of Doom3 common->GetAdditionalFunction(idCommon::FT_IsDemo, (idCommon::FunctionPointer*)&isDemoFnPtr, NULL); + //debugger support + common->GetAdditionalFunction(idCommon::FT_UpdateDebugger,(idCommon::FunctionPointer*) &updateDebuggerFnPtr,NULL); } /* @@ -1192,7 +1202,7 @@ void idGameLocal::MapPopulate( void ) { idGameLocal::InitFromNewMap =================== */ -void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randseed ) { +void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randseed) { this->isServer = isServer; this->isClient = isClient; @@ -2216,13 +2226,13 @@ idGameLocal::RunFrame ================ */ gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) { - idEntity * ent; - int num; - float ms; - idTimer timer_think, timer_events, timer_singlethink; - gameReturn_t ret; - idPlayer *player; - const renderView_t *view; + idEntity * ent; + int num; + float ms; + idTimer timer_think, timer_events, timer_singlethink; + gameReturn_t ret; + idPlayer *player; + const renderView_t *view; #ifdef _DEBUG if ( isMultiplayer ) { diff --git a/neo/game/script/Script_Interpreter.cpp b/neo/game/script/Script_Interpreter.cpp index b6d044cf..11fd06ab 100644 --- a/neo/game/script/Script_Interpreter.cpp +++ b/neo/game/script/Script_Interpreter.cpp @@ -33,6 +33,11 @@ If you have questions concerning this license or the applicable additional terms #include "script/Script_Interpreter.h" +#include "framework/FileSystem.h" + +// HvG: Debugger support +extern bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ); + /* ================ idInterpreter::idInterpreter() @@ -183,7 +188,6 @@ idInterpreter::GetRegisterValue Returns a string representation of the value of the register. This is used primarily for the debugger and debugging -//FIXME: This is pretty much wrong. won't access data in most situations. ================ */ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDepth ) { @@ -191,17 +195,17 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep idVarDef *d; char funcObject[ 1024 ]; char *funcName; - const idVarDef *scope; + const idVarDef *scope = NULL; + const idVarDef *scopeObj; const idTypeDef *field; - const idScriptObject *obj; const function_t *func; out.Empty(); - + if ( scopeDepth == -1 ) { scopeDepth = callStackDepth; - } - + } + if ( scopeDepth == callStackDepth ) { func = currentFunction; } else { @@ -215,35 +219,44 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep funcName = strstr( funcObject, "::" ); if ( funcName ) { *funcName = '\0'; - scope = gameLocal.program.GetDef( NULL, funcObject, &def_namespace ); - funcName += 2; + scopeObj = gameLocal.program.GetDef( NULL, funcObject, &def_namespace ); + funcName += 2; + if ( scopeObj ) + { + scope = gameLocal.program.GetDef( NULL, funcName, scopeObj ); + } } else { funcName = funcObject; - scope = &def_namespace; + scope = gameLocal.program.GetDef( NULL, func->Name(), &def_namespace ); + scopeObj = NULL; } - // Get the function from the object - d = gameLocal.program.GetDef( NULL, funcName, scope ); - if ( !d ) { + if ( !scope ) + { return false; } - // Get the variable itself and check various namespaces - d = gameLocal.program.GetDef( NULL, name, d ); - if ( !d ) { - if ( scope == &def_namespace ) { - return false; - } - - d = gameLocal.program.GetDef( NULL, name, scope ); - if ( !d ) { - d = gameLocal.program.GetDef( NULL, name, &def_namespace ); - if ( !d ) { - return false; + d = gameLocal.program.GetDef( NULL, name, scope ); + + // Check the objects for it if it wasnt local to the function + if ( !d ) + { + for ( ; scopeObj && scopeObj->TypeDef()->SuperClass(); scopeObj = scopeObj->TypeDef()->SuperClass()->def ) + { + d = gameLocal.program.GetDef( NULL, name, scopeObj ); + if ( d ) + { + break; } } - } + } + if ( !d ) + { + out = "???"; + return false; + } + reg = GetVariable( d ); switch( d->Type() ) { case ev_float: @@ -256,7 +269,7 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep break; case ev_vector: - if ( reg.vectorPtr ) { + if ( reg.vectorPtr ) { out = va( "%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z ); } else { out = "0,0,0"; @@ -274,30 +287,55 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep break; case ev_field: + { + idEntity* entity; + idScriptObject* obj; + if ( scope == &def_namespace ) { // should never happen, but handle it safely anyway return false; } - field = scope->TypeDef()->GetParmType( reg.ptrOffset )->FieldType(); - obj = *reinterpret_cast( &localstack[ callStack[ callStackDepth ].stackbase ] ); - if ( !field || !obj ) { + field = d->TypeDef()->FieldType(); + entity = GetEntity ( *((int*)&localstack[ localstackBase ]) ); + if ( !entity || !field ) + { return false; } + obj = &entity->scriptObject; + if ( !obj ) { + return false; + } + switch ( field->Type() ) { - case ev_boolean: - out = va( "%d", *( reinterpret_cast( &obj->data[ reg.ptrOffset ] ) ) ); - return true; + case ev_boolean: + out = va( "%d", *( reinterpret_cast( &obj->data[ reg.ptrOffset ] ) ) ); + return true; - case ev_float: - out = va( "%g", *( reinterpret_cast( &obj->data[ reg.ptrOffset ] ) ) ); - return true; + case ev_float: + out = va( "%g", *( reinterpret_cast( &obj->data[ reg.ptrOffset ] ) ) ); + return true; + + case ev_string: { + const char* str; + str = reinterpret_cast( &obj->data[ reg.ptrOffset ] ); + if ( !str ) { + out = "\"\""; + } else { + out = "\""; + out += str; + out += "\""; + } + return true; + } - default: - return false; + default: + return false; } + break; + } case ev_string: if ( reg.stringPtr ) { @@ -969,6 +1007,19 @@ bool idInterpreter::Execute( void ) { // next statement st = &gameLocal.program.GetStatement( instructionPointer ); + if ( !updateGameDebugger( this, &gameLocal.program, instructionPointer ) + && g_debugScript.GetBool( ) ) + { + static int lastLineNumber = -1; + if ( lastLineNumber != gameLocal.program.GetStatement ( instructionPointer ).linenumber ) { + gameLocal.Printf ( "%s (%d)\n", + gameLocal.program.GetFilename ( gameLocal.program.GetStatement ( instructionPointer ).file ), + gameLocal.program.GetStatement ( instructionPointer ).linenumber + ); + lastLineNumber = gameLocal.program.GetStatement ( instructionPointer ).linenumber; + } + } + switch( st->op ) { case OP_RETURN: LeaveFunction( st->a ); @@ -1833,3 +1884,98 @@ bool idInterpreter::Execute( void ) { return threadDying; } + +bool idGameEditExt::CheckForBreakPointHit(const idInterpreter* interpreter, const function_t* function1, const function_t* function2, int depth) const +{ + return ( ( interpreter->GetCurrentFunction ( ) == function1 || + interpreter->GetCurrentFunction ( ) == function2)&& + ( interpreter->GetCallstackDepth ( ) <= depth) ); +} + +bool idGameEditExt::ReturnedFromFunction(const idProgram* program, const idInterpreter* interpreter, int index) const +{ + + return ( const_cast(program)->GetStatement(index).op == OP_RETURN && interpreter->GetCallstackDepth ( ) <= 1 ); +} + +bool idGameEditExt::GetRegisterValue(const idInterpreter* interpreter, const char* name, idStr& out, int scopeDepth) const +{ + return const_cast(interpreter)->GetRegisterValue(name, out, scopeDepth); +} + +const idThread*idGameEditExt::GetThread(const idInterpreter* interpreter) const +{ + return interpreter->GetThread(); +} + +void idGameEditExt::MSG_WriteCallstackFunc(idBitMsg* msg, const prstack_t* stack, const idProgram * program, int instructionPtr) +{ + const statement_t* st; + const function_t* func; + + func = stack->f; + + // If the function is unknown then just fill in with default data. + if ( !func ) + { + msg->WriteString ( "" ); + msg->WriteString ( "" ); + msg->WriteInt ( 0 ); + return; + } + else + { + msg->WriteString ( va("%s( )", func->Name() ) ); + } + + if (stack->s == -1) //this is a fake stack created by debugger, use intruction pointer for retrieval. + st = &const_cast( program )->GetStatement( instructionPtr ); + else // Use the calling statement as the filename and linenumber where the call was made from + st = &const_cast( program )->GetStatement ( stack->s ); + + if ( st ) + { + idStr qpath = const_cast( program )->GetFilename( st->file ); + if (idStr::FindChar( qpath, ':' ) != -1) + qpath = fileSystem->OSPathToRelativePath( qpath.c_str() ); + qpath.BackSlashesToSlashes ( ); + msg->WriteString( qpath ); + msg->WriteInt( st->linenumber ); + } + else + { + msg->WriteString ( "" ); + msg->WriteInt ( 0 ); + } +} + +void idGameEditExt::MSG_WriteInterpreterInfo(idBitMsg* msg, const idInterpreter* interpreter, const idProgram* program, int instructionPtr) +{ + int i; + prstack_s temp; + + msg->WriteShort((int)interpreter->GetCallstackDepth( ) ); + + // write out the current function + temp.f = interpreter->GetCurrentFunction( ); + temp.s = -1; + temp.stackbase = 0; + MSG_WriteCallstackFunc( msg, &temp, program, instructionPtr); + + // Run through all of the callstack and write each to the msg + for (i = interpreter->GetCallstackDepth() - 1; i > 0; i--) + { + MSG_WriteCallstackFunc( msg, interpreter->GetCallstack( ) + i, program, instructionPtr); + } +} + + +int idGameEditExt::GetInterpreterCallStackDepth(const idInterpreter* interpreter) +{ + return interpreter->GetCallstackDepth(); +} + +const function_t*idGameEditExt::GetInterpreterCallStackFunction( const idInterpreter* interpreter, int stackDepth/* = -1*/) +{ + return interpreter->GetCallstack( )[ stackDepth > -1 ? stackDepth :interpreter->GetCallstackDepth( ) ].f; +} \ No newline at end of file diff --git a/neo/game/script/Script_Thread.cpp b/neo/game/script/Script_Thread.cpp index 58daa5d9..546ede9e 100644 --- a/neo/game/script/Script_Thread.cpp +++ b/neo/game/script/Script_Thread.cpp @@ -28,11 +28,11 @@ If you have questions concerning this license or the applicable additional terms #include "sys/platform.h" -#include "gamesys/SysCvar.h" -#include "Player.h" -#include "Camera.h" +#include "game/gamesys/SysCvar.h" +#include "game/Player.h" +#include "game/Camera.h" -#include "script/Script_Thread.h" +#include "Script_Thread.h" const idEventDef EV_Thread_Execute( "", NULL ); const idEventDef EV_Thread_SetCallback( "", NULL ); @@ -1841,3 +1841,49 @@ void idThread::Event_InfluenceActive( void ) { idThread::ReturnInt( false ); } } + +int idGameEditExt::ThreadGetNum(const idThread* thread) const +{ + return const_cast(thread)->GetThreadNum(); +} + +const char*idGameEditExt::ThreadGetName(const idThread* thread) const +{ + return const_cast(thread)->GetThreadName(); +} + +int idGameEditExt::GetTotalScriptThreads() const +{ + return idThread::GetThreads().Num(); +} + +const idThread*idGameEditExt::GetThreadByIndex(int index) const +{ + return idThread::GetThreads()[index]; +} + +bool idGameEditExt::ThreadIsDoneProcessing(const idThread* thread) const +{ + return const_cast(thread)->IsDoneProcessing(); +} + +bool idGameEditExt::ThreadIsWaiting(const idThread* thread) const +{ + return const_cast(thread)->IsWaiting(); +} + +bool idGameEditExt::ThreadIsDying(const idThread* thread) const +{ + return const_cast(thread)->IsDying(); +} + +void idGameEditExt::MSG_WriteThreadInfo(idBitMsg* msg, const idThread* thread, const idInterpreter* interpreter) +{ + msg->WriteString(const_cast(thread)->GetThreadName()); + msg->WriteInt(const_cast(thread)->GetThreadNum()); + + msg->WriteBits((int)(thread == interpreter->GetThread()), 1); + msg->WriteBits((int)const_cast(thread)->IsDoneProcessing(), 1); + msg->WriteBits((int)const_cast(thread)->IsWaiting(), 1); + msg->WriteBits((int)const_cast(thread)->IsDying(), 1); +} \ No newline at end of file diff --git a/neo/idlib/BitMsg.h b/neo/idlib/BitMsg.h index e853bf7c..f27b2dab 100644 --- a/neo/idlib/BitMsg.h +++ b/neo/idlib/BitMsg.h @@ -85,7 +85,7 @@ public: void WriteShort( int c ); void WriteUShort( int c ); void WriteInt( int c ); - void WriteFloat( float f ); + void WriteFloat( float f ); void WriteFloat( float f, int exponentBits, int mantissaBits ); void WriteAngle8( float f ); void WriteAngle16( float f ); diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 55fa1c4f..221a7d2e 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -2169,6 +2169,8 @@ idRenderSystemLocal::Shutdown void idRenderSystemLocal::Shutdown( void ) { common->Printf( "idRenderSystem::Shutdown()\n" ); + common->SetRefreshOnPrint( false ); // without a renderer there's nothing to refresh + R_DoneFreeType( ); if ( glConfig.isInitialized ) { diff --git a/neo/sys/win32/rc/Debugger.rc b/neo/sys/win32/rc/Debugger.rc index bee22e2f..e9a1c032 100644 --- a/neo/sys/win32/rc/Debugger.rc +++ b/neo/sys/win32/rc/Debugger.rc @@ -126,21 +126,21 @@ IDI_DBG_EMPTY ICON "res\\dbg_empty.ico" IDR_DBG_ACCELERATORS ACCELERATORS BEGIN - VK_F9, ID_DBG_DEBUG_QUICKWATCH, VIRTKEY, SHIFT, NOINVERT - VK_F5, ID_DBG_DEBUG_RUN, VIRTKEY, NOINVERT - VK_F11, ID_DBG_DEBUG_STEPINTO, VIRTKEY, NOINVERT - VK_F10, ID_DBG_DEBUG_STEPOVER, VIRTKEY, NOINVERT - VK_F9, ID_DBG_DEBUG_TOGGLEBREAKPOINT, VIRTKEY, NOINVERT - VK_F3, ID_DBG_EDIT_FINDNEXT, VIRTKEY, NOINVERT - VK_F3, ID_DBG_EDIT_FINDPREV, VIRTKEY, SHIFT, NOINVERT - VK_F3, ID_DBG_EDIT_FINDSELECTED, VIRTKEY, CONTROL, NOINVERT - VK_F3, ID_DBG_EDIT_FINDSELECTED, VIRTKEY, CONTROL, NOINVERT - VK_F3, ID_DBG_EDIT_FINDSELECTEDPREV, VIRTKEY, SHIFT, CONTROL, - NOINVERT - VK_F4, ID_DBG_FILE_CLOSE, VIRTKEY, CONTROL, NOINVERT - VK_TAB, ID_DBG_FILE_NEXT, VIRTKEY, CONTROL, NOINVERT - "O", ID_DBG_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT - "F", ID_DBG_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT + VK_F9, ID_DBG_DEBUG_QUICKWATCH, VIRTKEY, SHIFT, NOINVERT + VK_F5, ID_DBG_DEBUG_RUN, VIRTKEY, NOINVERT + VK_F11, ID_DBG_DEBUG_STEPINTO, VIRTKEY, NOINVERT + VK_F10, ID_DBG_DEBUG_STEPOVER, VIRTKEY, NOINVERT + VK_F9, ID_DBG_DEBUG_TOGGLEBREAKPOINT, VIRTKEY, NOINVERT + VK_F3, ID_DBG_EDIT_FINDNEXT, VIRTKEY, NOINVERT + VK_F3, ID_DBG_EDIT_FINDPREV, VIRTKEY, SHIFT, NOINVERT + VK_F3, ID_DBG_EDIT_FINDSELECTED, VIRTKEY, CONTROL, NOINVERT + VK_F3, ID_DBG_EDIT_FINDSELECTEDPREV, VIRTKEY, SHIFT, CONTROL,NOINVERT + VK_F4, ID_DBG_FILE_CLOSE, VIRTKEY, CONTROL, NOINVERT + VK_TAB, ID_DBG_FILE_NEXT, VIRTKEY, CONTROL, NOINVERT + VK_RETURN, ID_DBG_SEND_COMMAND, VIRTKEY, NOINVERT + "O", ID_DBG_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "F", ID_DBG_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT + END @@ -149,17 +149,17 @@ END // Dialog // -IDD_DBG_ABOUT DIALOGEX 0, 0, 222, 71 +IDD_DBG_ABOUT DIALOGEX 0, 0, 250, 90 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Script Debugger" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "Script Debugger",IDC_STATIC,35,7,81,8 - LTEXT "Version 0.01",IDC_STATIC,35,17,41,8 - LTEXT "Original version by Raven",IDC_STATIC,35, - 28,134,8 - DEFPUSHBUTTON "OK",IDOK,165,50,50,14 + LTEXT "Version 1.1",IDC_STATIC,35,17,41,8 + LTEXT "Original version by Raven", IDC_STATIC, 35,28, 134, 8 + LTEXT "Dhewm3 version by Harrie van Ginneken \n\t\t\t\tand\n\t\t\t Daniel Gibson", IDC_STATIC, 35, 38, 134, 32 + DEFPUSHBUTTON "OK",IDOK,100,68,50,14 ICON 5098,IDC_STATIC,7,7,20,20 END diff --git a/neo/sys/win32/rc/Debugger_resource.h b/neo/sys/win32/rc/Debugger_resource.h index 55a6613e..2374c00e 100644 --- a/neo/sys/win32/rc/Debugger_resource.h +++ b/neo/sys/win32/rc/Debugger_resource.h @@ -76,6 +76,7 @@ If you have questions concerning this license or the applicable additional terms #define ID_DBG_EDIT_FINDPREV 22022 #define ID_DBG_EDIT_FINDSELECTEDPREV 22023 #define ID_DBG_HELP_ABOUT 22024 +#define ID_DBG_SEND_COMMAND 22025 // Next default values for new objects // diff --git a/neo/sys/win32/rc/GuiEd.rc b/neo/sys/win32/rc/GuiEd.rc index 6de83dc7..cd33f829 100644 --- a/neo/sys/win32/rc/GuiEd.rc +++ b/neo/sys/win32/rc/GuiEd.rc @@ -269,8 +269,7 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "GUI Editor",IDC_STATIC,35,7,62,8 LTEXT "Version 0.15",IDC_STATIC,35,17,41,8 - LTEXT "Original version by Raven",IDC_STATIC,35, - 28,134,8 + LTEXT "Original version by Raven", IDC_STATIC, 35,28, 134, 8 DEFPUSHBUTTON "OK",IDOK,165,50,50,14 ICON IDI_GUIED,IDC_STATIC,7,7,20,20 END diff --git a/neo/sys/win32/win_main.cpp b/neo/sys/win32/win_main.cpp index 8562da0b..d263a0a9 100644 --- a/neo/sys/win32/win_main.cpp +++ b/neo/sys/win32/win_main.cpp @@ -673,7 +673,30 @@ Sys_DLL_GetProcAddress ===================== */ void *Sys_DLL_GetProcAddress( uintptr_t dllHandle, const char *procName ) { - return (void *)GetProcAddress( (HINSTANCE)dllHandle, procName ); + void * adr = (void*)GetProcAddress((HINSTANCE)dllHandle, procName); + if (!adr) + { + DWORD e = GetLastError(); + LPVOID msgBuf = nullptr; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + e, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&msgBuf, + 0, NULL); + + idStr errorStr = va("[%i (0x%X)]\t%s", e, e, msgBuf); + + if (errorStr.Length()) + common->Warning("GetProcAddress( %i %s) Failed ! %s", dllHandle, procName, errorStr.c_str()); + + ::LocalFree(msgBuf); + } + return adr; } /* @@ -1031,7 +1054,10 @@ int main(int argc, char *argv[]) { // Launch the script debugger if ( strstr( GetCommandLine(), "+debugger" ) ) { - // DebuggerClientInit( lpCmdLine ); + +#ifdef ID_ALLOW_TOOLS + DebuggerClientInit(GetCommandLine()); +#endif return 0; } diff --git a/neo/tools/common/OpenFileDialog.cpp b/neo/tools/common/OpenFileDialog.cpp index 6da65c8f..405d48f9 100644 --- a/neo/tools/common/OpenFileDialog.cpp +++ b/neo/tools/common/OpenFileDialog.cpp @@ -392,7 +392,8 @@ void rvOpenFileDialog::SetFilter ( const char* s ) if ( semi != -1 ) { filter = filters.Left ( semi ); - filters = filters.Right ( filters.Length ( ) - semi ); + filters = filters.Right ( filters.Length ( ) - (semi + 1)); + filters.Strip(' '); } else { diff --git a/neo/tools/debugger/DebuggerApp.cpp b/neo/tools/debugger/DebuggerApp.cpp index 82aacaa1..1176d84a 100644 --- a/neo/tools/debugger/DebuggerApp.cpp +++ b/neo/tools/debugger/DebuggerApp.cpp @@ -37,8 +37,8 @@ If you have questions concerning this license or the applicable additional terms rvDebuggerApp::rvDebuggerApp ================ */ -rvDebuggerApp::rvDebuggerApp ( ) : - mOptions ( "Software\\id Software\\DOOM3\\Tools\\Debugger" ) +rvDebuggerApp::rvDebuggerApp ( ) //: + //mOptions ( "Software\\id Software\\DOOM3\\Tools\\Debugger" ) { mInstance = NULL; mDebuggerWindow = NULL; diff --git a/neo/tools/debugger/DebuggerApp.h b/neo/tools/debugger/DebuggerApp.h index d659b1d4..4438e97d 100644 --- a/neo/tools/debugger/DebuggerApp.h +++ b/neo/tools/debugger/DebuggerApp.h @@ -29,7 +29,7 @@ If you have questions concerning this license or the applicable additional terms #define DEBUGGERAPP_H_ #include "../../sys/win32/win_local.h" -#include "../../framework/sync/Msg.h" +//#include "../../framework/sync/Msg.h" #ifndef REGISTRYOPTIONS_H_ #include "../common/RegistryOptions.h" @@ -49,13 +49,15 @@ If you have questions concerning this license or the applicable additional terms // These were changed to static by ID so to make it easy we just throw them // in this header -const int MAX_MSGLEN = 1400; +// we need a lot to be able to list all threads in mars_city1 +const int MAX_MSGLEN = 8600; class rvDebuggerApp { public: rvDebuggerApp ( ); + ~rvDebuggerApp(); bool Initialize ( HINSTANCE hInstance ); int Run ( void ); diff --git a/neo/tools/debugger/DebuggerBreakpoint.cpp b/neo/tools/debugger/DebuggerBreakpoint.cpp index 975f2eb2..89df7f57 100644 --- a/neo/tools/debugger/DebuggerBreakpoint.cpp +++ b/neo/tools/debugger/DebuggerBreakpoint.cpp @@ -25,20 +25,23 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ - +#if defined( ID_ALLOW_TOOLS ) #include "tools/edit_gui_common.h" - - #include "DebuggerApp.h" +#else +#include "debugger_common.h" +#endif + #include "DebuggerBreakpoint.h" int rvDebuggerBreakpoint::mNextID = 1; -rvDebuggerBreakpoint::rvDebuggerBreakpoint ( const char* filename, int linenumber, int id ) +rvDebuggerBreakpoint::rvDebuggerBreakpoint ( const char* filename, int linenumber, int id, bool onceOnly ) { mFilename = filename; mLineNumber = linenumber; mEnabled = true; + mOnceOnly = onceOnly; if ( id == -1 ) { diff --git a/neo/tools/debugger/DebuggerBreakpoint.h b/neo/tools/debugger/DebuggerBreakpoint.h index a1f07a68..84e85de1 100644 --- a/neo/tools/debugger/DebuggerBreakpoint.h +++ b/neo/tools/debugger/DebuggerBreakpoint.h @@ -28,25 +28,28 @@ If you have questions concerning this license or the applicable additional terms #ifndef DEBUGGERBREAKPOINT_H_ #define DEBUGGERBREAKPOINT_H_ +class idProgram; + class rvDebuggerBreakpoint { public: - rvDebuggerBreakpoint ( const char* filename, int linenumber, int id = -1 ); + rvDebuggerBreakpoint ( const char* filename, int linenumber, int id = -1, bool onceOnly = false ); rvDebuggerBreakpoint ( rvDebuggerBreakpoint& bp ); ~rvDebuggerBreakpoint ( void ); const char* GetFilename ( void ); int GetLineNumber ( void ); int GetID ( void ); + bool GetOnceOnly ( void ); protected: bool mEnabled; + bool mOnceOnly; int mID; int mLineNumber; idStr mFilename; - private: static int mNextID; @@ -67,4 +70,9 @@ ID_INLINE int rvDebuggerBreakpoint::GetID ( void ) return mID; } +ID_INLINE bool rvDebuggerBreakpoint::GetOnceOnly( void ) +{ + return mOnceOnly; +} + #endif // DEBUGGERBREAKPOINT_H_ diff --git a/neo/tools/debugger/DebuggerClient.cpp b/neo/tools/debugger/DebuggerClient.cpp index 6ce67255..3ffb3862 100644 --- a/neo/tools/debugger/DebuggerClient.cpp +++ b/neo/tools/debugger/DebuggerClient.cpp @@ -73,7 +73,7 @@ bool rvDebuggerClient::Initialize ( void ) } // Server must be running on the local host on port 28980 - Sys_StringToNetAdr ( "localhost", &mServerAdrt, true ); + Sys_StringToNetAdr ( com_dbgServerAdr.GetString( ), &mServerAdr, true ); mServerAdr.port = 27980; // Attempt to let the server know we are here. The server may not be running so this @@ -110,25 +110,29 @@ Process all incomding messages from the debugger server bool rvDebuggerClient::ProcessMessages ( void ) { netadr_t adrFrom; - msg_t msg; + idBitMsg msg; byte buffer[MAX_MSGLEN]; - MSG_Init( &msg, buffer, sizeof( buffer ) ); + msg.SetSize(MAX_MSGLEN); + msg.BeginReading(); + int msgSize; // Check for pending udp packets on the debugger port - while ( mPort.GetPacket ( adrFrom, msg.data, msg.cursize, msg.maxsize ) ) + while ( mPort.GetPacket ( adrFrom, buffer,msgSize, MAX_MSGLEN) ) { - unsigned short command; + short command; + msg.Init(buffer, sizeof(buffer)); + msg.SetSize(msgSize); + msg.BeginReading(); // Only accept packets from the debugger server for security reasons if ( !Sys_CompareNetAdrBase ( adrFrom, mServerAdr ) ) { continue; } + command = msg.ReadShort ( ); - command = (unsigned short) MSG_ReadShort ( &msg ); - - // Is this what we are waiting for? + // Is this what we are waiting for? if ( command == mWaitFor ) { mWaitFor = DBMSG_UNKNOWN; @@ -168,17 +172,39 @@ bool rvDebuggerClient::ProcessMessages ( void ) case DBMSG_INSPECTVARIABLE: HandleInspectVariable ( &msg ); break; + + case DBMSG_REMOVEBREAKPOINT: + HandleRemoveBreakpoint( &msg ); + break; + case DBMSG_INSPECTSCRIPTS: + HandleInspectScripts( &msg ); + break; } // Give the window a chance to process the message - msg.readcount = 0; - msg.bit = 0; + msg.SetReadCount(0); + msg.SetReadBit(0); gDebuggerApp.GetWindow().ProcessNetMessage ( &msg ); } return true; } +void rvDebuggerClient::HandleRemoveBreakpoint(idBitMsg* msg) +{ + long lineNumber; + char filename[MAX_PATH]; + + // Read the breakpoint info + + lineNumber = msg->ReadInt(); + msg->ReadString(filename, MAX_PATH); + + rvDebuggerBreakpoint* bp = FindBreakpoint(filename, lineNumber); + if(bp) + RemoveBreakpoint(bp->GetID()); +} + /* ================ rvDebuggerClient::HandleBreak @@ -187,19 +213,22 @@ Handle the DBMSG_BREAK message send from the server. This message is handled by caching the file and linenumber where the break occured. ================ */ -void rvDebuggerClient::HandleBreak ( msg_t* msg ) +void rvDebuggerClient::HandleBreak ( idBitMsg* msg ) { char filename[MAX_PATH]; mBreak = true; // Line number - mBreakLineNumber = MSG_ReadInt ( msg ); + mBreakLineNumber = msg->ReadInt ( ); // Filename - MSG_ReadString ( msg, filename, MAX_PATH ); + msg->ReadString ( filename, MAX_PATH ); mBreakFilename = filename; + //int64_t ptr64b = msg->ReadInt64(); + //mBreakProgram = (idProgram*)ptr64b; + // Clear the variables mVariables.Clear ( ); @@ -211,6 +240,26 @@ void rvDebuggerClient::HandleBreak ( msg_t* msg ) WaitFor ( DBMSG_INSPECTTHREADS, 2000 ); } + +/* +================ +rvDebuggerClient::InspectScripts + +Instructs the client to inspect the loaded scripts +================ +*/ +void rvDebuggerClient::InspectScripts ( void ) +{ + idBitMsg msg; + byte buffer[MAX_MSGLEN]; + + msg.Init(buffer, sizeof(buffer)); + msg.BeginWriting(); + msg.WriteShort((short)DBMSG_INSPECTSCRIPTS); + SendPacket(msg.GetData(), msg.GetSize()); +} + + /* ================ rvDebuggerClient::InspectVariable @@ -222,15 +271,41 @@ will in turn respond back to the client with the variable value */ void rvDebuggerClient::InspectVariable ( const char* name, int callstackDepth ) { - msg_t msg; - byte buffer[MAX_MSGLEN]; + idBitMsg msg; + byte buffer[MAX_MSGLEN]; - MSG_Init( &msg, buffer, sizeof( buffer ) ); - MSG_WriteShort ( &msg, (int)DBMSG_INSPECTVARIABLE ); - MSG_WriteShort ( &msg, (short)(mCallstack.Num()-callstackDepth) ); - MSG_WriteString ( &msg, name ); + msg.Init( buffer, sizeof( buffer ) ); + msg.BeginWriting(); + msg.WriteShort ( (short)DBMSG_INSPECTVARIABLE ); + msg.WriteShort ( (short)(mCallstack.Num()-callstackDepth) ); + msg.WriteString ( name ); - SendPacket ( msg.data, msg.cursize ); + SendPacket ( msg.GetData(), msg.GetSize()); +} + +/* +================ +rvDebuggerClient::HandleInspectScripts + +Handle the message DBMSG_INSPECTSCRIPTS being sent from the server. This message +is handled by adding the script entries to a list for later lookup. +================ +*/ +void rvDebuggerClient::HandleInspectScripts( idBitMsg* msg ) +{ + int totalScripts; + + mServerScripts.Clear(); + + // Read all of the callstack entries specfied in the message + for (totalScripts = msg->ReadInt(); totalScripts > 0; totalScripts--) + { + char temp[1024]; + + // Script Name + msg->ReadString(temp, 1024); + mServerScripts.Append(temp); + } } /* @@ -241,29 +316,29 @@ Handle the message DBMSG_INSPECTCALLSTACK being sent from the server. This mess is handled by adding the callstack entries to a list for later lookup. ================ */ -void rvDebuggerClient::HandleInspectCallstack ( msg_t* msg ) +void rvDebuggerClient::HandleInspectCallstack ( idBitMsg* msg ) { int depth; ClearCallstack ( ); // Read all of the callstack entries specfied in the message - for ( depth = (short)MSG_ReadShort ( msg ) ; depth > 0; depth -- ) + for ( depth = (short)msg->ReadShort ( ) ; depth > 0; depth -- ) { rvDebuggerCallstack* entry = new rvDebuggerCallstack; char temp[1024]; // Function name - MSG_ReadString ( msg, temp, 1024 ); - entry->mFunction = temp; + msg->ReadString ( temp, 1024 ); + entry->mFunction = idStr(temp); // Filename - MSG_ReadString ( msg, temp, 1024 ); - entry->mFilename = temp; + msg->ReadString ( temp, 1024 ); + entry->mFilename = idStr(temp); // Line Number - entry->mLineNumber = MSG_ReadInt ( msg ); + entry->mLineNumber = msg->ReadInt ( ); // Add to list mCallstack.Append ( entry ); @@ -278,31 +353,31 @@ Handle the message DBMSG_INSPECTTHREADS being sent from the server. This messag is handled by adding the list of threads to a list for later lookup. ================ */ -void rvDebuggerClient::HandleInspectThreads ( msg_t* msg ) +void rvDebuggerClient::HandleInspectThreads ( idBitMsg* msg ) { int count; ClearThreads ( ); // Loop over the number of threads in the message - for ( count = (short)MSG_ReadShort ( msg ) ; count > 0; count -- ) + for ( count = (short)msg->ReadShort ( ) ; count > 0; count -- ) { rvDebuggerThread* entry = new rvDebuggerThread; char temp[1024]; // Thread name - MSG_ReadString ( msg, temp, 1024 ); + msg->ReadString ( temp, 1024 ); entry->mName = temp; // Thread ID - entry->mID = MSG_ReadInt ( msg ); + entry->mID = msg->ReadInt ( ); // Thread state - entry->mCurrent = MSG_ReadBits ( msg, 1 ) ? true : false; - entry->mDoneProcessing = MSG_ReadBits ( msg, 1 ) ? true : false; - entry->mWaiting = MSG_ReadBits ( msg, 1 ) ? true : false; - entry->mDying = MSG_ReadBits ( msg, 1 ) ? true : false; + entry->mCurrent = msg->ReadBits ( 1 ) ? true : false; + entry->mDoneProcessing = msg->ReadBits ( 1 ) ? true : false; + entry->mWaiting = msg->ReadBits ( 1 ) ? true : false; + entry->mDying = msg->ReadBits ( 1 ) ? true : false; // Add thread to list mThreads.Append ( entry ); @@ -317,15 +392,15 @@ Handle the message DBMSG_INSPECTVARIABLE being sent from the server. This messa is handled by adding the inspected variable to a dictionary for later lookup ================ */ -void rvDebuggerClient::HandleInspectVariable ( msg_t* msg ) +void rvDebuggerClient::HandleInspectVariable ( idBitMsg* msg ) { char var[1024]; char value[1024]; int callDepth; - callDepth = (short)MSG_ReadShort ( msg ); - MSG_ReadString ( msg, var, 1024 ); - MSG_ReadString ( msg, value, 1024 ); + callDepth = (short)msg->ReadShort ( ); + msg->ReadString ( var, 1024 ); + msg->ReadString ( value, 1024 ); mVariables.Set ( va("%d:%s", mCallstack.Num()-callDepth, var), value ); } @@ -422,7 +497,7 @@ Adds a breakpoint to the client and server with the give nfilename and linenumbe */ int rvDebuggerClient::AddBreakpoint ( const char* filename, int lineNumber, bool onceOnly ) { - int index = mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber ) ); + int index = mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber, -1, onceOnly ) ); SendAddBreakpoint ( *mBreakpoints[index] ); @@ -463,13 +538,14 @@ Send a message with no data to the debugger server */ void rvDebuggerClient::SendMessage ( EDebuggerMessage dbmsg ) { - msg_t msg; + idBitMsg msg; byte buffer[MAX_MSGLEN]; - MSG_Init( &msg, buffer, sizeof( buffer ) ); - MSG_WriteShort ( &msg, (int)dbmsg ); + msg.Init ( buffer, sizeof( buffer ) ); + msg.BeginWriting ( ); + msg.WriteShort ( (short)dbmsg ); - SendPacket ( msg.data, msg.cursize ); + SendPacket ( msg.GetData(), msg.GetSize() ); } /* @@ -502,9 +578,9 @@ rvDebuggerClient::SendAddBreakpoint Send an individual breakpoint over to the debugger server ================ */ -void rvDebuggerClient::SendAddBreakpoint ( rvDebuggerBreakpoint& bp, bool onceOnly ) +void rvDebuggerClient::SendAddBreakpoint ( rvDebuggerBreakpoint& bp ) { - msg_t msg; + idBitMsg msg; byte buffer[MAX_MSGLEN]; if ( !mConnected ) @@ -512,14 +588,15 @@ void rvDebuggerClient::SendAddBreakpoint ( rvDebuggerBreakpoint& bp, bool onceOn return; } - MSG_Init( &msg, buffer, sizeof( buffer ) ); - MSG_WriteShort ( &msg, (int)DBMSG_ADDBREAKPOINT ); - MSG_WriteBits ( &msg, onceOnly?1:0, 1 ); - MSG_WriteInt ( &msg, (unsigned long) bp.GetLineNumber ( ) ); - MSG_WriteInt ( &msg, bp.GetID ( ) ); - MSG_WriteString ( &msg, bp.GetFilename() ); + msg.Init( buffer, sizeof( buffer ) ); + msg.BeginWriting(); + msg.WriteShort ( (short)DBMSG_ADDBREAKPOINT ); + msg.WriteBits ( bp.GetOnceOnly() ? 1 : 0, 1 ); + msg.WriteInt ( (unsigned long) bp.GetLineNumber ( ) ); + msg.WriteInt ( bp.GetID ( ) ); + msg.WriteString ( bp.GetFilename() ); // FIXME: this implies make7bit ?! - SendPacket ( msg.data, msg.cursize ); + SendPacket ( msg.GetData(), msg.GetSize() ); } /* @@ -531,7 +608,7 @@ Sends a remove breakpoint message to the debugger server */ void rvDebuggerClient::SendRemoveBreakpoint ( rvDebuggerBreakpoint& bp ) { - msg_t msg; + idBitMsg msg; byte buffer[MAX_MSGLEN]; if ( !mConnected ) @@ -539,11 +616,12 @@ void rvDebuggerClient::SendRemoveBreakpoint ( rvDebuggerBreakpoint& bp ) return; } - MSG_Init( &msg, buffer, sizeof( buffer ) ); - MSG_WriteShort ( &msg, (int)DBMSG_REMOVEBREAKPOINT ); - MSG_WriteInt ( &msg, bp.GetID() ); + msg.Init ( buffer, sizeof( buffer ) ); + msg.BeginWriting( ); + msg.WriteShort ( (short)DBMSG_REMOVEBREAKPOINT ); + msg.WriteInt ( bp.GetID() ); - SendPacket ( msg.data, msg.cursize ); + SendPacket ( msg.GetData(), msg.GetSize() ); } /* @@ -583,3 +661,25 @@ void rvDebuggerClient::ClearThreads ( void ) mThreads.Clear ( ); } +/* +================ +rvDebuggerClient::SendCommand +================ +*/ +void rvDebuggerClient::SendCommand( const char *cmdStr ) +{ + idBitMsg msg; + byte buffer[MAX_MSGLEN]; + + if ( !mConnected ) { + return; + } + + msg.Init( buffer, sizeof( buffer ) ); + msg.BeginWriting( ); + msg.WriteShort( ( short ) DBMSG_EXECCOMMAND ); + msg.WriteString( cmdStr ); // FIXME: this implies make7bit ?! + + SendPacket( msg.GetData( ), msg.GetSize( ) ); +} + diff --git a/neo/tools/debugger/DebuggerClient.h b/neo/tools/debugger/DebuggerClient.h index dfc67717..0116bf9b 100644 --- a/neo/tools/debugger/DebuggerClient.h +++ b/neo/tools/debugger/DebuggerClient.h @@ -28,6 +28,9 @@ If you have questions concerning this license or the applicable additional terms #ifndef DEBUGGERCLIENT_H_ #define DEBUGGERCLIENT_H_ +#include "DebuggerBreakpoint.h" +#include "idlib/containers/StrList.h" + class rvDebuggerCallstack { public: @@ -49,9 +52,6 @@ public: bool mDoneProcessing; }; -#ifndef DEBUGGERBREAKPOINT_H_ -#include "DebuggerBreakpoint.h" -#endif typedef idList rvDebuggerCallstackList; typedef idList rvDebuggerThreadList; @@ -75,19 +75,23 @@ public: int GetActiveBreakpointID ( void ); const char* GetBreakFilename ( void ); int GetBreakLineNumber ( void ); + idProgram* GetBreakProgram ( void ); rvDebuggerCallstackList& GetCallstack ( void ); rvDebuggerThreadList& GetThreads ( void ); const char* GetVariableValue ( const char* name, int stackDepth ); + idStrList& GetServerScripts ( void ); void InspectVariable ( const char* name, int callstackDepth ); - + void InspectScripts ( void ); void Break ( void ); void Resume ( void ); void StepInto ( void ); void StepOver ( void ); + void SendCommand ( const char* cmdStr ); + // Breakpoints - int AddBreakpoint ( const char* filename, int lineNumber, bool onceOnly = false ); + int AddBreakpoint ( const char* filename, int lineNumber, bool onceOnly = false); bool RemoveBreakpoint ( int bpID ); void ClearBreakpoints ( void ); int GetBreakpointCount ( void ); @@ -98,7 +102,7 @@ protected: void SendMessage ( EDebuggerMessage dbmsg ); void SendBreakpoints ( void ); - void SendAddBreakpoint ( rvDebuggerBreakpoint& bp, bool onceOnly = false ); + void SendAddBreakpoint ( rvDebuggerBreakpoint& bp ); void SendRemoveBreakpoint ( rvDebuggerBreakpoint& bp ); void SendPacket ( void* data, int datasize ); @@ -119,6 +123,8 @@ protected: EDebuggerMessage mWaitFor; + idStrList mServerScripts; + private: void ClearCallstack ( void ); @@ -127,10 +133,13 @@ private: void UpdateWatches ( void ); // Network message handlers - void HandleBreak ( msg_t* msg ); - void HandleInspectCallstack ( msg_t* msg ); - void HandleInspectThreads ( msg_t* msg ); - void HandleInspectVariable ( msg_t* msg ); + void HandleBreak ( idBitMsg* msg ); + void HandleInspectScripts ( idBitMsg* msg ); + void HandleInspectCallstack ( idBitMsg* msg ); + void HandleInspectThreads ( idBitMsg* msg ); + void HandleInspectVariable ( idBitMsg* msg ); + void HandleGameDLLHandle ( idBitMsg* msg ); + void HandleRemoveBreakpoint ( idBitMsg* msg ); }; /* @@ -286,4 +295,14 @@ ID_INLINE void rvDebuggerClient::SendPacket ( void* data, int size ) mPort.SendPacket ( mServerAdr, data, size ); } + +/* +================ +rvDebuggerClient::GetServerScripts +================ +*/ +ID_INLINE idStrList& rvDebuggerClient::GetServerScripts( void ) +{ + return mServerScripts; +} #endif // DEBUGGERCLIENT_H_ diff --git a/neo/tools/debugger/DebuggerFindDlg.cpp b/neo/tools/debugger/DebuggerFindDlg.cpp index 59ce96c6..cf5dd28f 100644 --- a/neo/tools/debugger/DebuggerFindDlg.cpp +++ b/neo/tools/debugger/DebuggerFindDlg.cpp @@ -53,7 +53,7 @@ Launch the dialog */ bool rvDebuggerFindDlg::DoModal ( rvDebuggerWindow* parent ) { - if ( DialogBoxParam ( parent->GetInstance(), MAKEINTRESOURCE(IDD_DBG_FIND), parent->GetWindow(), DlgProc, (LONG)this ) ) + if ( DialogBoxParam ( parent->GetInstance(), MAKEINTRESOURCE(IDD_DBG_FIND), parent->GetWindow(), DlgProc, (LPARAM)this ) ) { return true; } @@ -70,7 +70,7 @@ Dialog Procedure for the find dialog */ INT_PTR CALLBACK rvDebuggerFindDlg::DlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ) { - rvDebuggerFindDlg* dlg = (rvDebuggerFindDlg*) GetWindowLong ( wnd, GWL_USERDATA ); + rvDebuggerFindDlg* dlg = (rvDebuggerFindDlg*) GetWindowLongPtr ( wnd, GWLP_USERDATA); switch ( msg ) { @@ -80,7 +80,8 @@ INT_PTR CALLBACK rvDebuggerFindDlg::DlgProc ( HWND wnd, UINT msg, WPARAM wparam, case WM_INITDIALOG: dlg = (rvDebuggerFindDlg*) lparam; - SetWindowLong ( wnd, GWL_USERDATA, (LONG) dlg ); + + SetWindowLongPtr ( wnd, GWLP_USERDATA, (LONG_PTR) dlg ); dlg->mWnd = wnd; SetWindowText ( GetDlgItem ( dlg->mWnd, IDC_DBG_FIND ), dlg->mFindText ); return TRUE; diff --git a/neo/tools/debugger/DebuggerMessages.h b/neo/tools/debugger/DebuggerMessages.h index 6e0cba43..91d8b43f 100644 --- a/neo/tools/debugger/DebuggerMessages.h +++ b/neo/tools/debugger/DebuggerMessages.h @@ -46,6 +46,8 @@ enum EDebuggerMessage DBMSG_INSPECTTHREADS, DBMSG_STEPOVER, DBMSG_STEPINTO, + DBMSG_INSPECTSCRIPTS, + DBMSG_EXECCOMMAND }; #endif // DEBUGGER_MESSAGES_H_ \ No newline at end of file diff --git a/neo/tools/debugger/DebuggerQuickWatchDlg.cpp b/neo/tools/debugger/DebuggerQuickWatchDlg.cpp index 7cf18fe3..cee73a8f 100644 --- a/neo/tools/debugger/DebuggerQuickWatchDlg.cpp +++ b/neo/tools/debugger/DebuggerQuickWatchDlg.cpp @@ -55,7 +55,7 @@ bool rvDebuggerQuickWatchDlg::DoModal ( rvDebuggerWindow* window, int callstackD mDebuggerWindow = window; mVariable = variable?variable:""; - DialogBoxParam ( window->GetInstance(), MAKEINTRESOURCE(IDD_DBG_QUICKWATCH), window->GetWindow(), DlgProc, (LONG)this ); + DialogBoxParam ( window->GetInstance(), MAKEINTRESOURCE(IDD_DBG_QUICKWATCH), window->GetWindow(), DlgProc, (LPARAM)this ); return true; } @@ -69,7 +69,7 @@ Dialog Procedure for the quick watch dialog */ INT_PTR CALLBACK rvDebuggerQuickWatchDlg::DlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ) { - rvDebuggerQuickWatchDlg* dlg = (rvDebuggerQuickWatchDlg*) GetWindowLong ( wnd, GWL_USERDATA ); + rvDebuggerQuickWatchDlg* dlg = (rvDebuggerQuickWatchDlg*) GetWindowLongPtr ( wnd, GWLP_USERDATA); switch ( msg ) { @@ -128,7 +128,7 @@ INT_PTR CALLBACK rvDebuggerQuickWatchDlg::DlgProc ( HWND wnd, UINT msg, WPARAM w // Attach the dialog class pointer to the window dlg = (rvDebuggerQuickWatchDlg*) lparam; - SetWindowLong ( wnd, GWL_USERDATA, lparam ); + SetWindowLongPtr ( wnd, GWLP_USERDATA, lparam ); dlg->mWnd = wnd; GetClientRect ( wnd, &client ); diff --git a/neo/tools/debugger/DebuggerScript.cpp b/neo/tools/debugger/DebuggerScript.cpp index fc15dfac..f1d6944e 100644 --- a/neo/tools/debugger/DebuggerScript.cpp +++ b/neo/tools/debugger/DebuggerScript.cpp @@ -26,12 +26,14 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ +#if defined( ID_ALLOW_TOOLS ) #include "tools/edit_gui_common.h" - - #include "DebuggerApp.h" +#else +#include "debugger_common.h" +#endif + #include "DebuggerScript.h" -#include "../../game/script/Script_Program.h" #include "../../ui/Window.h" #include "../../ui/UserInterfaceLocal.h" @@ -57,6 +59,7 @@ rvDebuggerScript::~rvDebuggerScript ( void ) Unload ( ); } + /* ================ rvDebuggerScript::Unload @@ -72,10 +75,6 @@ void rvDebuggerScript::Unload ( void ) { delete mInterface; } - else - { - delete mProgram; - } mContents = NULL; mProgram = NULL; @@ -116,60 +115,7 @@ bool rvDebuggerScript::Load ( const char* filename ) // Cleanup fileSystem->FreeFile ( buffer ); - - // Now compile the script so we can tell what a valid line is, etc.. If its - // a gui file then we need to parse it using the userinterface system rather - // than the normal script compiler. - try - { - // Parse the script using the script compiler - mProgram = new idProgram; - mProgram->BeginCompilation ( ); - mProgram->CompileFile ( SCRIPT_DEFAULT ); - - //BSM Nerve: Loads a game specific main script file - idStr gamedir = cvarSystem->GetCVarString( "fs_game" ); - if(gamedir.Length() > 0) { - - idStr scriptFile = va("script/%s_main.script", gamedir.c_str()); - if(fileSystem->ReadFile(scriptFile.c_str(), NULL) > 0) { - mProgram.CompileFile(scriptFile.c_str()); - } - - } - - // Make sure the file isnt already compiled before trying to compile it again - for ( int f = mProgram->NumFilenames() - 1; f >= 0; f -- ) - { - idStr qpath; - qpath = fileSystem->OSPathToRelativePath ( mProgram->GetFilename ( f ) ); - qpath.BackSlashesToSlashes ( ); - if ( !qpath.Cmp ( filename ) ) - { - break; - } - } - - if ( f < 0 ) - { - mProgram->CompileText ( filename, mContents, false ); - } - - mProgram->FinishCompilation ( ); - } - catch ( idException& ) - { - // Failed to parse the script so fail to load the file - delete mProgram; - mProgram = NULL; - delete[] mContents; - mContents = NULL; - - // TODO: Should cache the error for the dialog box - - return false; - } - + return true; } @@ -194,21 +140,8 @@ Determines whether or not the given line number within the script is a valid lin */ bool rvDebuggerScript::IsLineCode ( int linenumber ) { - int i; - - assert ( mProgram ); - - // Run through all the statements in the program and see if any match the - // linenumber that we are checking. - for ( i = 0; i < mProgram->NumStatements ( ); i ++ ) - { - if ( mProgram->GetStatement ( i ).linenumber == linenumber ) - { - return true; - } - } - - return false; + //we let server decide. + return true; } /* diff --git a/neo/tools/debugger/DebuggerScript.h b/neo/tools/debugger/DebuggerScript.h index d60086a3..ae090f8d 100644 --- a/neo/tools/debugger/DebuggerScript.h +++ b/neo/tools/debugger/DebuggerScript.h @@ -43,21 +43,22 @@ public: const char* GetFilename ( void ); const char* GetContents ( void ); - + idProgram* GetProgram ( void ); +#if 0// Test code idProgram& GetProgram ( void ); +#endif bool IsLineCode ( int linenumber ); bool IsFileModified ( bool updateTime = false ); protected: - void Unload ( void ); idProgram* mProgram; idUserInterfaceLocal* mInterface; char* mContents; idStr mFilename; - ID_TIME_T mModifiedTime; + ID_TIME_T mModifiedTime; }; ID_INLINE const char* rvDebuggerScript::GetFilename ( void ) @@ -70,9 +71,10 @@ ID_INLINE const char* rvDebuggerScript::GetContents ( void ) return mContents?mContents:""; } -ID_INLINE idProgram& rvDebuggerScript::GetProgram ( void ) +ID_INLINE idProgram* rvDebuggerScript::GetProgram ( void ) { - return *mProgram; + return mProgram; } + #endif // DEBUGGERSCRIPT_H_ \ No newline at end of file diff --git a/neo/tools/debugger/DebuggerServer.cpp b/neo/tools/debugger/DebuggerServer.cpp index 9606b19a..612c22a4 100644 --- a/neo/tools/debugger/DebuggerServer.cpp +++ b/neo/tools/debugger/DebuggerServer.cpp @@ -3,6 +3,8 @@ Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 1999-2011 Raven Software +Copyright (C) 2021 Harrie van Ginneken This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). @@ -26,17 +28,16 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ +#if defined( ID_ALLOW_TOOLS ) #include "tools/edit_gui_common.h" - - -#include "../../game/gamesys/Event.h" -#include "../../game/gamesys/Class.h" -#include "../../game/script/Script_Program.h" -#include "../../game/script/Script_Interpreter.h" -#include "../../game/script/Script_Thread.h" -#include "../../game/script/Script_Compiler.h" -#include "../../framework/sync/Msg.h" #include "DebuggerApp.h" +#else +#include "debugger_common.h" +// we need a lot to be able to list all threads in mars_city1 +const int MAX_MSGLEN = 8600; +#endif + + #include "DebuggerServer.h" /* @@ -51,10 +52,17 @@ rvDebuggerServer::rvDebuggerServer ( ) mBreak = false; mBreakStepOver = false; mBreakStepInto = false; - mGameThread = NULL; + mGameThreadBreakCond = NULL; + mGameThreadBreakLock = NULL; mLastStatementLine = -1; mBreakStepOverFunc1 = NULL; mBreakStepOverFunc2 = NULL; + mBreakInstructionPointer = 0; + mBreakInterpreter = NULL; + mBreakProgram = NULL; + mGameDLLHandle = 0; + mBreakStepOverDepth = 0; + mCriticalSection = NULL; } /* @@ -82,15 +90,17 @@ bool rvDebuggerServer::Initialize ( void ) return false; } - // Get a copy of the game thread handle so we can suspend the thread on a break - DuplicateHandle ( GetCurrentProcess(), GetCurrentThread ( ), GetCurrentProcess(), &mGameThread, 0, FALSE, DUPLICATE_SAME_ACCESS ); + // we're using a condition variable to pause the game thread in rbDebuggerServer::Break() + // until rvDebuggerServer::Resume() is called (from another thread) + mGameThreadBreakCond = SDL_CreateCond(); + mGameThreadBreakLock = SDL_CreateMutex(); // Create a critical section to ensure that the shared thread // variables are protected - InitializeCriticalSection ( &mCriticalSection ); + mCriticalSection = SDL_CreateMutex(); // Server must be running on the local host on port 28980 - Sys_StringToNetAdr ( "localhost", &mClientAdr, true ); + Sys_StringToNetAdr ( com_dbgClientAdr.GetString( ), &mClientAdr, true ); mClientAdr.port = 27981; // Attempt to let the server know we are here. The server may not be running so this @@ -102,7 +112,7 @@ bool rvDebuggerServer::Initialize ( void ) void rvDebuggerServer::OSPathToRelativePath( const char *osPath, idStr &qpath ) { - if ( strchr( osPath, ':' ) ) + if ( strchr( osPath, ':' ) ) // XXX: what about linux? { qpath = fileSystem->OSPathToRelativePath( osPath ); } @@ -130,8 +140,16 @@ void rvDebuggerServer::Shutdown ( void ) mPort.Close(); + Resume(); // just in case we're still paused + // dont need the crit section anymore - DeleteCriticalSection ( &mCriticalSection ); + SDL_DestroyMutex( mCriticalSection ); + mCriticalSection = NULL; + + SDL_DestroyCond( mGameThreadBreakCond ); + mGameThreadBreakCond = NULL; + SDL_DestroyMutex( mGameThreadBreakLock ); + mGameThreadBreakLock = NULL; } /* @@ -144,39 +162,46 @@ Process all incoming network messages from the debugger client bool rvDebuggerServer::ProcessMessages ( void ) { netadr_t adrFrom; - msg_t msg; + idBitMsg msg; byte buffer[MAX_MSGLEN]; - MSG_Init( &msg, buffer, sizeof( buffer ) ); - // Check for pending udp packets on the debugger port - while ( mPort.GetPacket ( adrFrom, msg.data, msg.cursize, msg.maxsize ) ) + int msgSize; + while ( mPort.GetPacket ( adrFrom, buffer, msgSize, MAX_MSGLEN) ) { - unsigned short command; - - // Only accept packets from the debugger server for security reasons - if ( !Sys_CompareNetAdrBase ( adrFrom, mClientAdr ) ) - { - continue; + short command; + msg.Init(buffer, sizeof(buffer)); + msg.SetSize(msgSize); + msg.BeginReading(); + + if ( adrFrom.type != NA_LOOPBACK ) { + // Only accept packets from the debugger server for security reasons + if ( !Sys_CompareNetAdrBase( adrFrom, mClientAdr ) ) + continue; } - command = (unsigned short) MSG_ReadShort ( &msg ); + command = msg.ReadShort( ); switch ( command ) { case DBMSG_CONNECT: mConnected = true; SendMessage ( DBMSG_CONNECTED ); + HandleInspectScripts ( nullptr ); + com_editors |= EDITOR_DEBUGGER; break; case DBMSG_CONNECTED: mConnected = true; + HandleInspectScripts( nullptr ); + com_editors |= EDITOR_DEBUGGER; break; case DBMSG_DISCONNECT: ClearBreakpoints ( ); Resume ( ); mConnected = false; + com_editors &= ~EDITOR_DEBUGGER; break; case DBMSG_ADDBREAKPOINT: @@ -188,7 +213,7 @@ bool rvDebuggerServer::ProcessMessages ( void ) break; case DBMSG_RESUME: - Resume ( ); + HandleResume ( &msg ); break; case DBMSG_BREAK: @@ -197,11 +222,11 @@ bool rvDebuggerServer::ProcessMessages ( void ) case DBMSG_STEPOVER: mBreakStepOver = true; - mBreakStepOverDepth = mBreakInterpreter->GetCallstackDepth ( ); - mBreakStepOverFunc1 = mBreakInterpreter->GetCallstack()[mBreakInterpreter->GetCallstackDepth()].f; - if ( mBreakInterpreter->GetCallstackDepth() > 0 ) + mBreakStepOverDepth = ((idGameEditExt*) gameEdit)->GetInterpreterCallStackDepth(mBreakInterpreter); + mBreakStepOverFunc1 = ((idGameEditExt*) gameEdit)->GetInterpreterCallStackFunction(mBreakInterpreter); + if (mBreakStepOverDepth) { - mBreakStepOverFunc2 = mBreakInterpreter->GetCallstack()[mBreakInterpreter->GetCallstackDepth()-1].f; + mBreakStepOverFunc2 = ((idGameEditExt*) gameEdit)->GetInterpreterCallStackFunction(mBreakInterpreter,mBreakStepOverDepth - 1); } else { @@ -226,6 +251,14 @@ bool rvDebuggerServer::ProcessMessages ( void ) case DBMSG_INSPECTTHREADS: HandleInspectThreads ( &msg ); break; + + case DBMSG_INSPECTSCRIPTS: + HandleInspectScripts( &msg ); + break; + + case DBMSG_EXECCOMMAND: + HandleExecCommand( &msg ); + break; } } @@ -241,13 +274,14 @@ Send a message with no data to the debugger server. */ void rvDebuggerServer::SendMessage ( EDebuggerMessage dbmsg ) { - msg_t msg; + idBitMsg msg; byte buffer[MAX_MSGLEN]; - MSG_Init( &msg, buffer, sizeof( buffer ) ); - MSG_WriteShort ( &msg, (int)dbmsg ); + msg.Init( buffer, sizeof( buffer ) ); + msg.BeginWriting(); + msg.WriteShort ( (short)dbmsg ); - SendPacket ( msg.data, msg.cursize ); + SendPacket ( msg.GetData(), msg.GetSize() ); } /* @@ -255,29 +289,44 @@ void rvDebuggerServer::SendMessage ( EDebuggerMessage dbmsg ) rvDebuggerServer::HandleAddBreakpoint Handle the DBMSG_ADDBREAKPOINT message being sent by the debugger client. This -message is handled by adding a new breakpoint to the breakpoint list with the +message is handled by first checking if it is valid +and is added as a new breakpoint to the breakpoint list with the data supplied in the message. ================ */ -void rvDebuggerServer::HandleAddBreakpoint ( msg_t* msg ) +void rvDebuggerServer::HandleAddBreakpoint ( idBitMsg* msg ) { bool onceOnly = false; long lineNumber; long id; - char filename[MAX_PATH]; + char filename[2048]; // DG: randomly chose this size // Read the breakpoint info - onceOnly = MSG_ReadBits ( msg, 1 ) ? true : false; - lineNumber = MSG_ReadInt ( msg ); - id = MSG_ReadInt ( msg ); + onceOnly = msg->ReadBits( 1 ) ? true : false; + lineNumber = msg->ReadInt ( ); + id = msg->ReadInt ( ); - MSG_ReadString ( msg, filename, MAX_PATH ); + msg->ReadString ( filename, sizeof(filename) ); - // Since breakpoints are used by both threads we need to - // protect them with a crit section - EnterCriticalSection ( &mCriticalSection ); - mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber, id ) ); - LeaveCriticalSection ( &mCriticalSection ); + //check for statement on requested breakpoint location + if (!((idGameEditExt*) gameEdit)->IsLineCode(filename, lineNumber)) + { + idBitMsg msgOut; + byte buffer[MAX_MSGLEN]; + + msgOut.Init(buffer, sizeof(buffer)); + msgOut.BeginWriting(); + msgOut.WriteShort((short)DBMSG_REMOVEBREAKPOINT); + msgOut.WriteInt(lineNumber); + msgOut.WriteString(filename); + SendPacket(msgOut.GetData(), msgOut.GetSize()); + return; + } + + + SDL_LockMutex( mCriticalSection ); + mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber, id, onceOnly ) ); + SDL_UnlockMutex( mCriticalSection ); } /* @@ -289,17 +338,17 @@ message is handled by removing the breakpoint that matches the given id from the list. ================ */ -void rvDebuggerServer::HandleRemoveBreakpoint ( msg_t* msg ) +void rvDebuggerServer::HandleRemoveBreakpoint ( idBitMsg* msg ) { int i; int id; // ID that we are to remove - id = MSG_ReadInt ( msg ); + id = msg->ReadInt ( ); // Since breakpoints are used by both threads we need to // protect them with a crit section - EnterCriticalSection ( &mCriticalSection ); + SDL_LockMutex( mCriticalSection ); // Find the breakpoint that matches the given id and remove it from the list for ( i = 0; i < mBreakpoints.Num(); i ++ ) @@ -312,52 +361,21 @@ void rvDebuggerServer::HandleRemoveBreakpoint ( msg_t* msg ) } } - LeaveCriticalSection ( &mCriticalSection ); + SDL_UnlockMutex( mCriticalSection ); } /* ================ -rvDebuggerServer::MSG_WriteCallstackFunc +rvDebuggerServer::HandleResume -Writes a single callstack entry to the given message +Resume the game thread. ================ + */ -void rvDebuggerServer::MSG_WriteCallstackFunc ( msg_t* msg, const prstack_t* stack ) +void rvDebuggerServer::HandleResume(idBitMsg* msg) { - const statement_t* st; - const function_t* func; - - func = stack->f; - - // If the function is unknown then just fill in with default data. - if ( !func ) - { - MSG_WriteString ( msg, "" ); - MSG_WriteString ( msg, "" ); - MSG_WriteInt ( msg, 0 ); - return; - } - else - { - MSG_WriteString ( msg, va("%s( ??? )", func->Name() ) ); - } - - // Use the calling statement as the filename and linenumber where - // the call was made from - st = &mBreakProgram->GetStatement ( stack->s ); - if ( st ) - { - idStr qpath; - OSPathToRelativePath(mBreakProgram->GetFilename( st->file ), qpath); - qpath.BackSlashesToSlashes ( ); - MSG_WriteString ( msg, qpath ); - MSG_WriteInt ( msg, st->linenumber ); - } - else - { - MSG_WriteString ( msg, "" ); - MSG_WriteInt ( msg, 0 ); - } + //Empty msg + Resume(); } /* @@ -368,31 +386,18 @@ Handle an incoming inspect callstack message by sending a message back to the client with the callstack data. ================ */ -void rvDebuggerServer::HandleInspectCallstack ( msg_t* in_msg ) +void rvDebuggerServer::HandleInspectCallstack ( idBitMsg* msg ) { - msg_t msg; + idBitMsg msgOut; byte buffer[MAX_MSGLEN]; - int i; - prstack_t temp; - MSG_Init( &msg, buffer, sizeof( buffer ) ); - MSG_WriteShort ( &msg, (int)DBMSG_INSPECTCALLSTACK ); + msgOut.Init(buffer, sizeof( buffer ) ); + msgOut.BeginWriting(); + msgOut.WriteShort ( (short)DBMSG_INSPECTCALLSTACK ); - MSG_WriteShort ( &msg, (int)mBreakInterpreter->GetCallstackDepth ( ) ); + ((idGameEditExt*) gameEdit)->MSG_WriteInterpreterInfo(&msgOut, mBreakInterpreter, mBreakProgram, mBreakInstructionPointer); - // write out the current function - temp.f = mBreakInterpreter->GetCurrentFunction ( ); - temp.s = 0; - temp.stackbase = 0; - MSG_WriteCallstackFunc ( &msg, &temp ); - - // Run through all of the callstack and write each to the msg - for ( i = mBreakInterpreter->GetCallstackDepth ( ) - 1; i > 0; i -- ) - { - MSG_WriteCallstackFunc ( &msg, mBreakInterpreter->GetCallstack ( ) + i ); - } - - SendPacket ( msg.data, msg.cursize ); + SendPacket (msgOut.GetData(), msgOut.GetSize() ); } /* @@ -402,35 +407,67 @@ rvDebuggerServer::HandleInspectThreads Send the list of the current threads in the interpreter back to the debugger client ================ */ -void rvDebuggerServer::HandleInspectThreads ( msg_t* in_msg ) +void rvDebuggerServer::HandleInspectThreads ( idBitMsg* msg ) { - msg_t msg; - byte buffer[MAX_MSGLEN]; - int i; + idBitMsg msgOut; + byte buffer[MAX_MSGLEN]; + int i; // Initialize the message - MSG_Init( &msg, buffer, sizeof( buffer ) ); - MSG_WriteShort ( &msg, (int)DBMSG_INSPECTTHREADS ); + msgOut.Init( buffer, sizeof( buffer ) ); + msgOut.SetAllowOverflow(true); + msgOut.BeginWriting(); + msgOut.WriteShort ( (short)DBMSG_INSPECTTHREADS ); // Write the number of threads to the message - MSG_WriteShort ( &msg, (int)idThread::GetThreads().Num() ); + msgOut.WriteShort ((short)((idGameEditExt*) gameEdit)->GetTotalScriptThreads() ); // Loop through all of the threads and write their name and number to the message - for ( i = 0; i < idThread::GetThreads().Num(); i ++ ) + for ( i = 0; i < ((idGameEditExt*) gameEdit)->GetTotalScriptThreads(); i ++ ) { - idThread* thread = idThread::GetThreads()[i]; - - MSG_WriteString ( &msg, thread->GetThreadName ( ) ); - MSG_WriteInt ( &msg, thread->GetThreadNum ( ) ); - - MSG_WriteBits ( &msg, (int)(thread == mBreakInterpreter->GetThread ( )), 1 ); - MSG_WriteBits ( &msg, (int)thread->IsDoneProcessing(), 1 ); - MSG_WriteBits ( &msg, (int)thread->IsWaiting(), 1 ); - MSG_WriteBits ( &msg, (int)thread->IsDying(), 1 ); + ((idGameEditExt*) gameEdit)->MSG_WriteThreadInfo(&msgOut,((idGameEditExt*) gameEdit)->GetThreadByIndex(i), mBreakInterpreter); } // Send off the inspect threads packet to the debugger client - SendPacket ( msg.data, msg.cursize ); + SendPacket (msgOut.GetData(), msgOut.GetSize() ); +} + +/* +================ +rvDebuggerServer::HandleExecCommand + +Send the list of the current loaded scripts in the interpreter back to the debugger client +================ +*/ +void rvDebuggerServer::HandleExecCommand( idBitMsg *msg ) { + char cmdStr[2048]; // HvG: randomly chose this size + + msg->ReadString( cmdStr, sizeof( cmdStr ) ); + cmdSystem->BufferCommandText( CMD_EXEC_APPEND, cmdStr ); // valid command + cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "\n" ); +} + + +/* +================ +rvDebuggerServer::HandleInspectScripts + +Send the list of the current loaded scripts in the interpreter back to the debugger client +================ +*/ +void rvDebuggerServer::HandleInspectScripts( idBitMsg* msg ) +{ + idBitMsg msgOut; + byte buffer[MAX_MSGLEN]; + + // Initialize the message + msgOut.Init(buffer, sizeof(buffer)); + msgOut.BeginWriting(); + msgOut.WriteShort((short)DBMSG_INSPECTSCRIPTS); + + ((idGameEditExt*) gameEdit)->MSG_WriteScriptList( &msgOut ); + + SendPacket(msgOut.GetData(), msgOut.GetSize()); } /* @@ -440,7 +477,7 @@ rvDebuggerServer::HandleInspectVariable Respondes to a request from the debugger client to inspect the value of a given variable ================ */ -void rvDebuggerServer::HandleInspectVariable ( msg_t* in_msg ) +void rvDebuggerServer::HandleInspectVariable ( idBitMsg* msg ) { char varname[256]; int scopeDepth; @@ -450,28 +487,29 @@ void rvDebuggerServer::HandleInspectVariable ( msg_t* in_msg ) return; } - scopeDepth = (short)MSG_ReadShort ( in_msg ); - MSG_ReadString ( in_msg, varname, 256 ); + scopeDepth = (short)msg->ReadShort ( ); + msg->ReadString ( varname, 256 ); idStr varvalue; - msg_t msg; + idBitMsg msgOut; byte buffer[MAX_MSGLEN]; // Initialize the message - MSG_Init( &msg, buffer, sizeof( buffer ) ); - MSG_WriteShort ( &msg, (int)DBMSG_INSPECTVARIABLE ); + msgOut.Init( buffer, sizeof( buffer ) ); + msgOut.BeginWriting(); + msgOut.WriteShort ( (short)DBMSG_INSPECTVARIABLE ); - if ( !mBreakInterpreter->GetRegisterValue ( varname, varvalue, scopeDepth ) ) + if (!((idGameEditExt*) gameEdit)->GetRegisterValue(mBreakInterpreter, varname, varvalue, scopeDepth ) ) { varvalue = "???"; } - MSG_WriteShort ( &msg, (short)scopeDepth ); - MSG_WriteString ( &msg, varname ); - MSG_WriteString ( &msg, varvalue ); + msgOut.WriteShort ( (short)scopeDepth ); + msgOut.WriteString ( varname ); + msgOut.WriteString ( varvalue ); - SendPacket ( msg.data, msg.cursize ); + SendPacket (msgOut.GetData(), msgOut.GetSize() ); } /* @@ -484,7 +522,6 @@ Check to see if any breakpoints have been hit. This includes "break next", */ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram* program, int instructionPointer ) { - const statement_t* st; const char* filename; int i; @@ -492,23 +529,24 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram* return; } + // Grab the current statement and the filename that it came from - st = &program->GetStatement ( instructionPointer ); - filename = program->GetFilename ( st->file ); + filename = ((idGameEditExt*) gameEdit)->GetFilenameForStatement(program, instructionPointer); + int linenumber = ((idGameEditExt*) gameEdit)->GetLineNumberForStatement(program, instructionPointer); // Operate on lines, not statements - if ( mLastStatementLine == st->linenumber && mLastStatementFile == st->file ) + if ( mLastStatementLine == linenumber && mLastStatementFile == filename) { return; } - + // Save the last visited line and file so we can prevent // double breaks on lines with more than one statement - mLastStatementFile = idStr( st->file ); - mLastStatementLine = st->linenumber; + mLastStatementFile = idStr(filename); + mLastStatementLine = linenumber; // Reset stepping when the last function on the callstack is returned from - if ( st->op == OP_RETURN && interpreter->GetCallstackDepth ( ) <= 1 ) + if ( ((idGameEditExt*) gameEdit)->ReturnedFromFunction(program, interpreter,instructionPointer)) { mBreakStepOver = false; mBreakStepInto = false; @@ -517,6 +555,7 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram* // See if we are supposed to break on the next script line if ( mBreakNext ) { + HandleInspectScripts(nullptr); Break ( interpreter, program, instructionPointer ); return; } @@ -524,9 +563,8 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram* // Only break on the same callstack depth and thread as the break over if ( mBreakStepOver ) { - if ( ( interpreter->GetCurrentFunction ( ) == mBreakStepOverFunc1 || - interpreter->GetCurrentFunction ( ) == mBreakStepOverFunc2 )&& - ( interpreter->GetCallstackDepth ( ) <= mBreakStepOverDepth ) ) + //virtual bool CheckForBreakpointHit(interpreter,function1,function2,depth) + if (((idGameEditExt*) gameEdit)->CheckForBreakPointHit(interpreter, mBreakStepOverFunc1, mBreakStepOverFunc2, mBreakStepOverDepth)) { Break ( interpreter, program, instructionPointer ); return; @@ -536,6 +574,7 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram* // See if we are supposed to break on the next line if ( mBreakStepInto ) { + HandleInspectScripts(nullptr); // Break Break ( interpreter, program, instructionPointer ); return; @@ -545,7 +584,7 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram* OSPathToRelativePath(filename,qpath); qpath.BackSlashesToSlashes ( ); - EnterCriticalSection ( &mCriticalSection ); + SDL_LockMutex( mCriticalSection ); // Check all the breakpoints for ( i = 0; i < mBreakpoints.Num ( ); i ++ ) @@ -553,30 +592,50 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram* rvDebuggerBreakpoint* bp = mBreakpoints[i]; // Skip if not match of the line number - if ( st->linenumber != bp->GetLineNumber ( ) ) + if ( linenumber != bp->GetLineNumber ( ) ) { continue; } // Skip if no match of the filename - if ( idStr::Icmp ( bp->GetFilename(), qpath ) ) + if ( idStr::Icmp ( bp->GetFilename(), qpath.c_str() ) ) { continue; } - // Pop out of the critical section so we dont get stuck - LeaveCriticalSection ( &mCriticalSection ); + // DG: onceOnly support + if ( bp->GetOnceOnly() ) { + // we'll do the one Break() a few lines below; remove it here while mBreakpoints is unmodified + // (it can be modifed from the client while in Break() below) + mBreakpoints.RemoveIndex( i ); + delete bp; + // also tell client to remove the breakpoint + idBitMsg msgOut; + byte buffer[MAX_MSGLEN]; + msgOut.Init( buffer, sizeof( buffer ) ); + msgOut.BeginWriting(); + msgOut.WriteShort( (short)DBMSG_REMOVEBREAKPOINT ); + msgOut.WriteInt( linenumber ); + msgOut.WriteString( qpath.c_str() ); + SendPacket( msgOut.GetData(), msgOut.GetSize() ); + } + // DG end + + // Pop out of the critical section so we dont get stuck + SDL_UnlockMutex( mCriticalSection ); + + HandleInspectScripts(nullptr); // We hit a breakpoint, so break Break ( interpreter, program, instructionPointer ); // Back into the critical section since we are going to have to leave it - EnterCriticalSection ( &mCriticalSection ); + SDL_LockMutex( mCriticalSection ); break; } - LeaveCriticalSection ( &mCriticalSection ); + SDL_UnlockMutex( mCriticalSection ); } /* @@ -589,9 +648,8 @@ the game has been halted */ void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, int instructionPointer ) { - msg_t msg; + idBitMsg msg; byte buffer[MAX_MSGLEN]; - const statement_t* st; const char* filename; // Clear all the break types @@ -600,12 +658,10 @@ void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, i mBreakNext = false; // Grab the current statement and the filename that it came from - st = &program->GetStatement ( instructionPointer ); - filename = program->GetFilename ( st->file ); - - idStr qpath; - OSPathToRelativePath(filename, qpath); - qpath.BackSlashesToSlashes ( ); + filename = ((idGameEditExt*) gameEdit)->GetFilenameForStatement(program,instructionPointer); + int linenumber = ((idGameEditExt*) gameEdit)->GetLineNumberForStatement(program, instructionPointer); + idStr fileStr = filename; + fileStr.BackSlashesToSlashes(); // Give the mouse cursor back to the world Sys_GrabMouseCursor( false ); @@ -617,19 +673,33 @@ void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, i mBreakInstructionPointer = instructionPointer; // Inform the debugger of the breakpoint hit - MSG_Init( &msg, buffer, sizeof( buffer ) ); - MSG_WriteShort ( &msg, (int)DBMSG_BREAK ); - MSG_WriteInt ( &msg, st->linenumber ); - MSG_WriteString ( &msg, qpath ); - SendPacket ( msg.data, msg.cursize ); + msg.Init( buffer, sizeof( buffer ) ); + msg.BeginWriting(); + msg.WriteShort ( (short)DBMSG_BREAK ); + msg.WriteInt ( linenumber ); + msg.WriteString ( fileStr.c_str() ); + + //msg.WriteInt64( (int64_t)mBreakProgram ); + + SendPacket ( msg.GetData(), msg.GetSize() ); // Suspend the game thread. Since this will be called from within the main game thread // execution wont return until after the thread is resumed - SuspendThread ( mGameThread ); + // DG: the original code used Win32 SuspendThread() here, but as there is no equivalent + // function in SDL and as this is only called within the main game thread anyway, + // just use a condition variable to put this thread to sleep until Resume() has set mBreak + SDL_LockMutex( mGameThreadBreakLock ); + while ( mBreak ) { + SDL_CondWait( mGameThreadBreakCond, mGameThreadBreakLock ); + } + SDL_UnlockMutex( mGameThreadBreakLock ); // Let the debugger client know that we have started back up again SendMessage ( DBMSG_RESUMED ); + // this should be platform specific + // TODO: maybe replace with SDL code? or does it not matter if debugger client runs on another machine? +#if defined( ID_ALLOW_TOOLS ) // This is to give some time between the keypress that // told us to resume and the setforeground window. Otherwise the quake window // would just flash @@ -640,8 +710,10 @@ void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, i SetActiveWindow ( win32.hWnd ); UpdateWindow ( win32.hWnd ); SetFocus ( win32.hWnd ); +#endif // Give the mouse cursor back to the game + // HVG_Note : there be dragons here. somewhere. Sys_GrabMouseCursor( true ); // Clear all commands that were generated before we went into suspended mode. This is @@ -664,10 +736,11 @@ void rvDebuggerServer::Resume ( void ) return; } - mBreak = false; - // Start the game thread back up - ResumeThread ( mGameThread ); + SDL_LockMutex( mGameThreadBreakLock ); + mBreak = false; + SDL_CondSignal( mGameThreadBreakCond); + SDL_UnlockMutex( mGameThreadBreakLock ); } /* @@ -703,12 +776,13 @@ void rvDebuggerServer::Print ( const char* text ) return; } - msg_t msg; + idBitMsg msg; byte buffer[MAX_MSGLEN]; - MSG_Init( &msg, buffer, sizeof( buffer ) ); - MSG_WriteShort ( &msg, (int)DBMSG_PRINT ); - MSG_WriteString ( &msg, text ); + msg.Init( buffer, sizeof( buffer ) ); + msg.BeginWriting(); + msg.WriteShort ( (short)DBMSG_PRINT ); + msg.WriteString ( text ); - SendPacket ( msg.data, msg.cursize ); + SendPacket ( msg.GetData(), msg.GetSize() ); } diff --git a/neo/tools/debugger/DebuggerServer.h b/neo/tools/debugger/DebuggerServer.h index 04b9782d..efa100ad 100644 --- a/neo/tools/debugger/DebuggerServer.h +++ b/neo/tools/debugger/DebuggerServer.h @@ -28,20 +28,16 @@ If you have questions concerning this license or the applicable additional terms #ifndef DEBUGGERSERVER_H_ #define DEBUGGERSERVER_H_ -#ifndef DEBUGGERMESSAGES_H_ + #include "DebuggerMessages.h" -#endif - -#ifndef DEBUGGERBREAKPOINT_H_ #include "DebuggerBreakpoint.h" -#endif +#include "framework/Game.h" +#include -#ifndef __GAME_LOCAL_H__ -#include "../../game/Game.h" -#endif -class idInterpreter; -class idProgram; + +class function_t; +typedef struct prstack_s prstack_t; class rvDebuggerServer { @@ -50,31 +46,52 @@ public: rvDebuggerServer ( ); ~rvDebuggerServer ( ); - bool Initialize ( void ); - void Shutdown ( void ); + bool Initialize ( void ); + void Shutdown ( void ); - bool ProcessMessages ( void ); + bool ProcessMessages ( void ); - bool IsConnected ( void ); + bool IsConnected ( void ); - void CheckBreakpoints ( idInterpreter* interpreter, idProgram* program, int instructionPointer ); + void CheckBreakpoints ( idInterpreter *interpreter, idProgram *program, int instructionPointer ); - void Print ( const char* text ); + void Print ( const char *text ); - void OSPathToRelativePath( const char *osPath, idStr &qpath ); + void OSPathToRelativePath ( const char *osPath, idStr &qpath ); -protected: + bool GameSuspended ( void ); +private: + + void ClearBreakpoints ( void ); + + void Break ( idInterpreter *interpreter, idProgram *program, int instructionPointer ); + void Resume ( void ); + + void SendMessage ( EDebuggerMessage dbmsg ); + void SendPacket ( void* data, int datasize ); + + // Message handlers + void HandleAddBreakpoint ( idBitMsg *msg ); + void HandleRemoveBreakpoint ( idBitMsg *msg ); + void HandleResume ( idBitMsg *msg ); + void HandleInspectVariable ( idBitMsg *msg ); + void HandleInspectCallstack ( idBitMsg *msg ); + void HandleInspectThreads ( idBitMsg *msg ); + void HandleInspectScripts ( idBitMsg *msg ); + void HandleExecCommand ( idBitMsg *msg ); + //// - // protected member variables bool mConnected; netadr_t mClientAdr; idPort mPort; idList mBreakpoints; - CRITICAL_SECTION mCriticalSection; + SDL_mutex* mCriticalSection; - HANDLE mGameThread; + SDL_cond* mGameThreadBreakCond; + SDL_mutex* mGameThreadBreakLock; bool mBreak; + bool mBreakNext; bool mBreakStepOver; bool mBreakStepInto; @@ -87,27 +104,9 @@ protected: idStr mLastStatementFile; int mLastStatementLine; + uintptr_t mGameDLLHandle; + idStrList mScriptFileList; -private: - - void ClearBreakpoints ( void ); - - void Break ( idInterpreter* interpreter, idProgram* program, int instructionPointer ); - void Resume ( void ); - - void SendMessage ( EDebuggerMessage dbmsg ); - void SendPacket ( void* data, int datasize ); - - // Message handlers - void HandleAddBreakpoint ( msg_t* msg ); - void HandleRemoveBreakpoint ( msg_t* msg ); - void HandleResume ( msg_t* msg ); - void HandleInspectVariable ( msg_t* msg ); - void HandleInspectCallstack ( msg_t* msg ); - void HandleInspectThreads ( msg_t* msg ); - - // MSG helper routines - void MSG_WriteCallstackFunc ( msg_t* msg, const prstack_t* stack ); }; /* @@ -125,9 +124,19 @@ ID_INLINE bool rvDebuggerServer::IsConnected ( void ) rvDebuggerServer::SendPacket ================ */ -ID_INLINE void rvDebuggerServer::SendPacket ( void* data, int size ) +ID_INLINE void rvDebuggerServer::SendPacket ( void *data, int size ) { mPort.SendPacket ( mClientAdr, data, size ); } +/* +================ +rvDebuggerServer::GameSuspended +================ +*/ +ID_INLINE bool rvDebuggerServer::GameSuspended( void ) +{ + return mBreak; +} + #endif // DEBUGGERSERVER_H_ diff --git a/neo/tools/debugger/DebuggerWindow.cpp b/neo/tools/debugger/DebuggerWindow.cpp index fbf33e79..f13ea29d 100644 --- a/neo/tools/debugger/DebuggerWindow.cpp +++ b/neo/tools/debugger/DebuggerWindow.cpp @@ -35,7 +35,7 @@ If you have questions concerning this license or the applicable additional terms #include "DebuggerQuickWatchDlg.h" #include "DebuggerFindDlg.h" -#define DEBUGGERWINDOWCLASS "QUAKE4_DEBUGGER_WINDOW" +#define DEBUGGERWINDOWCLASS "DHEWM3_DEBUGGER_WINDOW" #define ID_DBG_WINDOWMIN 18900 #define ID_DBG_WINDOWMAX 19900 @@ -49,6 +49,9 @@ If you have questions concerning this license or the applicable additional terms #define IDC_DBG_WATCH 31007 #define IDC_DBG_THREADS 31008 #define IDC_DBG_TOOLBAR 31009 +#define IDC_DBG_SCRIPTLIST 31010 +#define IDC_DBG_CONSOLEINPUT 31011 +#define IDC_DBG_BREAKLIST 31012 #define ID_DBG_FILE_MRU1 10000 @@ -167,7 +170,7 @@ bool rvDebuggerWindow::Create ( HINSTANCE instance ) UpdateTitle ( ); - Printf ( "Quake 4 Script Debugger v0.1\n\n" ); + Printf ( "Dhewm3 Script Debugger v1.1\n\n" ); ShowWindow ( mWnd, SW_SHOW ); UpdateWindow ( mWnd ); @@ -248,7 +251,7 @@ LRESULT CALLBACK rvDebuggerWindow::ScriptWndProc ( HWND wnd, UINT msg, WPARAM wp { static int lastStart = -1; static int lastEnd = -1; - rvDebuggerWindow* window = (rvDebuggerWindow*)GetWindowLong ( wnd, GWL_USERDATA ); + rvDebuggerWindow* window = (rvDebuggerWindow*)GetWindowLongPtr ( wnd, GWLP_USERDATA ); WNDPROC wndproc = window->mOldScriptProc; switch ( msg ) @@ -347,6 +350,23 @@ LRESULT CALLBACK rvDebuggerWindow::ScriptWndProc ( HWND wnd, UINT msg, WPARAM wp break; } + case WM_SIZE: + { + float scaling_factor = Win_GetWindowScalingFactor(wnd); + int s18 = int(18 * scaling_factor); + int s10 = int(10 * scaling_factor); + + RECT rect; + window->mMarginSize = window->mZoomScaleDem ? ((long)(s18 * (float)window->mZoomScaleNum / (float)window->mZoomScaleDem)) : s18; + + GetWindowRect(window->mWndToolbar, &rect); + MoveWindow(window->mWndMargin, 0, 0, window->mMarginSize, window->mSplitterRect.top - (rect.bottom - rect.top), TRUE); + // FIXME: was *2.25, increased for line numbers up to 9999; but neither works particularly well + // if DPI scaling is involved, because script code text and linenumbers aren't DPI scaled + int lmargin = s18 * 3.5; + SendMessage(window->mWndScript, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(lmargin, s10)); + + } } return CallWindowProc ( wndproc, wnd, msg, wparam, lparam ); @@ -354,7 +374,7 @@ LRESULT CALLBACK rvDebuggerWindow::ScriptWndProc ( HWND wnd, UINT msg, WPARAM wp LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ) { - rvDebuggerWindow* window = (rvDebuggerWindow*) GetWindowLong ( wnd, GWL_USERDATA ); + rvDebuggerWindow* window = (rvDebuggerWindow*) GetWindowLongPtr ( wnd, GWLP_USERDATA ); switch ( msg ) { @@ -384,15 +404,57 @@ LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wp case WM_PAINT: { HDC dc; + float scaling_factor = Win_GetWindowScalingFactor(wnd); + int s2 = int(2 * scaling_factor); + int s4 = int(4 * scaling_factor); + int width,height; - int size = window->mMarginSize - 2; + window->ResizeImageList(width,height); PAINTSTRUCT ps; RECT rect; GetClientRect ( wnd, &rect ); - dc = BeginPaint ( wnd, &ps ); - FillRect ( dc, &rect, GetSysColorBrush ( COLOR_3DFACE ) ); + dc = BeginPaint( wnd, &ps ); + FillRect( dc, &rect, GetSysColorBrush( COLOR_3DSHADOW ) ); + //draw line nrs + int iMaxNumberOfLines = ( (rect.bottom - rect.top ) / height ) + height; + int iFirstVisibleLine = SendMessage( window->mWndScript, EM_GETFIRSTVISIBLELINE, 0, 0 ); + HFONT hf = CreateFont( height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Courier New" ); + HFONT hfOld = ( HFONT ) SelectObject( dc, hf ); + SetBkMode( dc, OPAQUE ); + // I think it looks nicer when the line number background is white + SetBkColor( dc, RGB( 255, 255, 255 ) ); + SetTextColor( dc, RGB( 0, 0, 255 ) ); + + int lnrWidth = 8; + GetCharWidth32( dc, '9', '9', &lnrWidth ); + lnrWidth *= 4; // we want enough width for 4 chars ("9999"), not just one + lnrWidth += 2 * s4; // we want some space around the line number + + RECT lnrRect = rect; + lnrRect.left = rect.right; + lnrRect.right = lnrRect.left + lnrWidth; + FillRect( dc, &lnrRect, WHITE_BRUSH ); + + for (int i = 0; i < iMaxNumberOfLines; ++i ) + { + int c; + POINTL pos; + c = SendMessage( window->mWndScript, EM_LINEINDEX, iFirstVisibleLine + i , 0 ); + SendMessage( window->mWndScript, EM_POSFROMCHAR, ( WPARAM ) &pos, c ); + + RECT t = lnrRect; + t.top = pos.y; + t.bottom = t.top + height; + t.right -= s4; // a little space between text and "border" to code part of window + + idStr lntxt( iFirstVisibleLine + i + 1); + DrawText( dc, lntxt, lntxt.Length(), &t, DT_RIGHT ); + } + DeleteObject( hf ); + + //draw breakpoints if ( window->mScripts.Num ( ) ) { for ( int i = 0; i < window->mClient->GetBreakpointCount(); i ++ ) @@ -407,7 +469,8 @@ LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wp c = SendMessage ( window->mWndScript, EM_LINEINDEX, bp->GetLineNumber ( ) - 1, 0 ); SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c ); - ImageList_DrawEx ( window->mImageList, 2, dc, rect.left, pos.y, size, size, CLR_NONE, CLR_NONE, ILD_NORMAL ); + ImageList_DrawEx ( window->mTmpImageList, 2, dc, rect.left, pos.y, width, height, CLR_NONE, CLR_NONE, ILD_NORMAL ); + } } @@ -421,7 +484,7 @@ LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wp c = SendMessage ( window->mWndScript, EM_LINEINDEX, window->mClient->GetBreakLineNumber() - 1, 0 ); SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c ); - ImageList_DrawEx ( window->mImageList, 3, dc, rect.left, pos.y, size, size, CLR_NONE, CLR_NONE, ILD_NORMAL ); + ImageList_DrawEx ( window->mTmpImageList, 3, dc, rect.left, pos.y, width, height, CLR_NONE, CLR_NONE, ILD_NORMAL ); } } @@ -434,17 +497,19 @@ LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wp c = SendMessage ( window->mWndScript, EM_LINEINDEX, window->mClient->GetCallstack()[window->mCurrentStackDepth]->mLineNumber - 1, 0 ); SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c ); - ImageList_DrawEx ( window->mImageList, 1, dc, rect.left, pos.y, size, size, CLR_NONE, CLR_NONE, ILD_NORMAL ); + ImageList_DrawEx ( window->mTmpImageList, 1, dc, rect.left, pos.y, width, height, CLR_NONE, CLR_NONE, ILD_NORMAL ); } } } + RECT tmp = rect; - rect.right-=2; - rect.left = rect.right + 1; - HPEN pen = CreatePen ( PS_SOLID, 1, GetSysColor ( COLOR_3DSHADOW ) ); + rect.right -= s2; + rect.left = rect.right + s2; + HPEN pen = CreatePen ( PS_SOLID, s2, GetSysColor ( COLOR_BACKGROUND ) ); HPEN old = (HPEN)SelectObject ( dc, pen ); MoveToEx ( dc, rect.right, rect.top, NULL ); LineTo ( dc, rect.right, rect.bottom ); + SelectObject ( dc, old ); DeleteObject ( pen ); EndPaint ( wnd, &ps ); @@ -466,7 +531,7 @@ void rvDebuggerWindow::UpdateTitle ( void ) { idStr title; - title = "Quake 4 Script Debugger - "; + title = "Dhewm3 Script Debugger - "; if ( mClient->IsConnected ( ) ) { @@ -487,7 +552,10 @@ void rvDebuggerWindow::UpdateTitle ( void ) if ( mScripts.Num ( ) ) { title += " - ["; - title += idStr( mScripts[mActiveScript]->GetFilename() ).StripPath ( ); + if (mActiveScript != -1) + title += idStr( mScripts[mActiveScript]->GetFilename() ).StripPath ( ); + else + title += "Load Error"; title += "]"; } @@ -590,6 +658,56 @@ void rvDebuggerWindow::UpdateCallstack ( void ) } } +void rvDebuggerWindow::UpdateScriptList(void) +{ + LVITEM item; + ListView_DeleteAllItems(mWndScriptList); + ZeroMemory(&item, sizeof(item)); + item.mask = LVIF_TEXT | LVIF_IMAGE; + + idStrList& scripts = mClient->GetServerScripts(); + for (int i = 0; i < scripts.Num(); i++) + { + item.iItem = ListView_GetItemCount(mWndScriptList); + item.pszText = ""; + //find in activeScripts + item.iImage = 0; + for (int j = 0; j < mScripts.Num(); j++) + { + if (!idStr::Icmp(mScripts[j]->GetFilename(), scripts[i])) + { + item.iImage = 1; + break; + } + } + ListView_InsertItem(mWndScriptList, &item); + ListView_SetItemText(mWndScriptList, item.iItem, 1, (LPSTR)scripts[i].c_str()); + } +} + + +void rvDebuggerWindow::UpdateBreakpointList( void ) +{ + LVITEM item; + ListView_DeleteAllItems( mWndBreakList ); + ZeroMemory( &item, sizeof( item ) ); + item.mask = LVIF_TEXT | LVIF_IMAGE; + + int numBreakPoints = mClient->GetBreakpointCount(); + for ( int i = 0; i < numBreakPoints; i++ ) + { + rvDebuggerBreakpoint* bp = mClient->GetBreakpoint( i ); + item.iItem = ListView_GetItemCount( mWndBreakList ); + item.pszText = ""; + item.iImage = 2; // breakpoint + ListView_InsertItem( mWndBreakList, &item ); + + idStr lineStr( bp->GetLineNumber() ); + ListView_SetItemText( mWndBreakList, item.iItem, 1, (LPSTR)bp->GetFilename() ); + ListView_SetItemText( mWndBreakList, item.iItem, 2, (LPSTR)lineStr.c_str() ); + } +} + /* ================ rvDebuggerWindow::UpdateWatch @@ -712,7 +830,7 @@ int rvDebuggerWindow::HandleInitMenu ( WPARAM wParam, LPARAM lParam ) case ID_DBG_DEBUG_STEPOVER: case ID_DBG_DEBUG_STEPINTO: case ID_DBG_DEBUG_SHOWNEXTSTATEMENT: -// case ID_DBG_DEBUG_QUICKWATCH: + case ID_DBG_DEBUG_QUICKWATCH: if ( !mClient->IsConnected() || !mClient->IsStopped() ) { EnableMenuItem ( hmenu, nPos, MF_GRAYED|MF_BYPOSITION ); @@ -737,6 +855,33 @@ int rvDebuggerWindow::HandleInitMenu ( WPARAM wParam, LPARAM lParam ) return 0; } + +void rvDebuggerWindow::ResizeImageList(int& widthOut, int& heightOut) +{ + //mTmpImageList + float scaling_factor = Win_GetWindowScalingFactor(mWnd); + int s16 = int(16 * scaling_factor); + + TEXTMETRIC tm; + HDC dc; + dc = GetDC(mWndScript); + + GetTextMetrics(dc, &tm); + int height = mZoomScaleDem ? (tm.tmHeight * (float)mZoomScaleNum / (float)mZoomScaleDem) : 16; + height *= scaling_factor; + int width = mZoomScaleDem ? (s16 * (float)mZoomScaleNum / (float)mZoomScaleDem) : s16; + + ImageList_Destroy(mTmpImageList); + mTmpImageList = ImageList_Create(width, height, ILC_COLOR | ILC_MASK , 0, 2); + ImageList_AddIcon(mTmpImageList, (HICON)LoadImage(mInstance, MAKEINTRESOURCE(IDI_DBG_EMPTY), IMAGE_ICON, width, height, LR_DEFAULTSIZE | LR_DEFAULTCOLOR)); + ImageList_AddIcon(mTmpImageList, (HICON)LoadImage(mInstance, MAKEINTRESOURCE(IDI_DBG_CURRENT), IMAGE_ICON, width, height, LR_DEFAULTSIZE | LR_DEFAULTCOLOR)); + ImageList_AddIcon(mTmpImageList, (HICON)LoadImage(mInstance, MAKEINTRESOURCE(IDI_DBG_BREAKPOINT), IMAGE_ICON, width, height, LR_DEFAULTSIZE | LR_DEFAULTCOLOR)); + ImageList_AddIcon(mTmpImageList, (HICON)LoadImage(mInstance, MAKEINTRESOURCE(IDI_DBG_CURRENTLINE), IMAGE_ICON, width, height, LR_DEFAULTSIZE | LR_DEFAULTCOLOR)); + + widthOut = width; + heightOut = height; +} + /* ================ rvDebuggerWindow::HandleCreate @@ -760,18 +905,18 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam ) // Create the script window LoadLibrary ( "Riched20.dll" ); mWndScript = CreateWindow ( "RichEdit20A", "", WS_CHILD|WS_BORDER|ES_NOHIDESEL|ES_READONLY|ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|ES_AUTOHSCROLL|WS_VSCROLL|WS_HSCROLL, 0, 0, 100, 100, mWnd, (HMENU) IDC_DBG_SCRIPT, mInstance, 0 ); - SendMessage ( mWndScript, EM_SETEVENTMASK, 0, ENM_SCROLL|ENM_CHANGE ); + SendMessage ( mWndScript, EM_SETEVENTMASK, 0, ENM_SCROLL | ENM_CHANGE | ENM_UPDATE | ENM_SCROLLEVENTS | ENM_REQUESTRESIZE) ; SendMessage ( mWndScript, EM_SETWORDBREAKPROC, 0, (LPARAM) ScriptWordBreakProc ); - mOldScriptProc = (WNDPROC)GetWindowLong ( mWndScript, GWL_WNDPROC ); - SetWindowLong ( mWndScript, GWL_USERDATA, (LONG)this ); - SetWindowLong ( mWndScript, GWL_WNDPROC, (LONG)ScriptWndProc ); + mOldScriptProc = (WNDPROC)GetWindowLongPtr ( mWndScript, GWLP_WNDPROC ); + SetWindowLongPtr ( mWndScript, GWLP_USERDATA, (LONG_PTR)this ); + SetWindowLongPtr ( mWndScript, GWLP_WNDPROC, (LONG_PTR)ScriptWndProc ); SendMessage ( mWndScript, EM_SETTABSTOPS, 1, (LPARAM)&tabsize ); dc = GetDC ( mWndScript ); GetTextMetrics ( dc, &tm ); ZeroMemory ( &lf, sizeof(lf) ); - lf.lfHeight = tm.tmHeight; + lf.lfHeight = tm.tmHeight * Win_GetWindowScalingFactor( mWndScript ); strcpy ( lf.lfFaceName, "Courier New" ); SendMessage ( mWndScript, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 ); @@ -782,15 +927,22 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam ) SendMessage ( mWndOutput, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 ); SendMessage ( mWndOutput, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) ); SendMessage ( mWndOutput, EM_SETBKGNDCOLOR, 0, GetSysColor ( COLOR_3DFACE ) ); + SendMessage ( mWndOutput, EM_SETEVENTMASK, 0, ENM_SCROLL | ENM_CHANGE | ENM_UPDATE | ENM_SCROLLEVENTS); mWndConsole = CreateWindow ( "RichEdit20A", "", WS_CHILD|ES_READONLY|ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|ES_AUTOHSCROLL|WS_VSCROLL|WS_HSCROLL, 0, 0, 100, 100, mWnd, (HMENU) IDC_DBG_CONSOLE, mInstance, 0 ); SendMessage ( mWndConsole, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 ); SendMessage ( mWndConsole, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) ); SendMessage ( mWndConsole, EM_SETBKGNDCOLOR, 0, GetSysColor ( COLOR_3DFACE ) ); + mWndConsoleInput = CreateWindow( "RichEdit20A", "", WS_CHILD | ES_WANTRETURN | ES_AUTOVSCROLL | WS_VSCROLL | WS_BORDER, 0, 0, 100, 18, mWnd, ( HMENU ) IDC_DBG_CONSOLEINPUT, mInstance, 0 ); + lf.lfHeight = -MulDiv( 8, GetDeviceCaps( dc, LOGPIXELSY ), 72 ); + strcpy( lf.lfFaceName, "Arial" ); + SendMessage( mWndConsoleInput, WM_SETFONT, ( WPARAM ) CreateFontIndirect( &lf ), 0 ); + SendMessage( mWndConsoleInput, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG( 18, 10 ) ); + mWndMargin = CreateWindow ( "STATIC", "", WS_VISIBLE|WS_CHILD, 0, 0, 0, 0, mWndScript, (HMENU)IDC_DBG_SPLITTER, mInstance, NULL ); - SetWindowLong ( mWndMargin, GWL_USERDATA, (LONG)this ); - SetWindowLong ( mWndMargin, GWL_WNDPROC, (LONG)MarginWndProc ); + SetWindowLongPtr ( mWndMargin, GWLP_USERDATA, (LONG_PTR)this ); + SetWindowLongPtr ( mWndMargin, GWLP_WNDPROC, (LONG_PTR)MarginWndProc ); mWndBorder = CreateWindow ( "STATIC", "", WS_VISIBLE|WS_CHILD|SS_GRAYFRAME, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_BORDER, mInstance, NULL ); @@ -816,23 +968,52 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam ) TabCtrl_InsertItem ( mWndTabs, 3, &item ); item.pszText = "Threads"; TabCtrl_InsertItem ( mWndTabs, 4, &item ); + item.pszText = "Scripts"; + TabCtrl_InsertItem ( mWndTabs, 5, &item ); + item.pszText = "Breakpoints"; + TabCtrl_InsertItem ( mWndTabs, 6, &item ); mWndCallstack = CreateWindow ( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_SHAREIMAGELISTS, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_CALLSTACK, mInstance, NULL ); mWndWatch = CreateWindow ( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_EDITLABELS|LVS_OWNERDRAWFIXED, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_WATCH, mInstance, NULL ); mWndThreads = CreateWindow ( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_SHAREIMAGELISTS, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_THREADS, mInstance, NULL ); + mWndScriptList = CreateWindow( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_SHAREIMAGELISTS, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_SCRIPTLIST, mInstance, NULL ); + mWndBreakList = CreateWindow( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_SHAREIMAGELISTS, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_BREAKLIST, mInstance, NULL ); LVCOLUMN col; col.mask = LVCF_WIDTH|LVCF_TEXT; + + col.cx = 20; + col.pszText = ""; + ListView_InsertColumn( mWndBreakList, 0, &col ); +#if 0 // TODO: figure out how to get the function name in UpdateBreakpointList() + col.cx = 150; + col.pszText = "Function"; + ListView_InsertColumn( mWndBreakList, 1, &col ); +#endif + col.cx = 350; + col.pszText = "Filename"; + ListView_InsertColumn( mWndBreakList, 1, &col ); + col.cx = 50; + col.pszText = "Line"; + ListView_InsertColumn( mWndBreakList, 2, &col ); + + col.cx = 20; + col.pszText = ""; + ListView_InsertColumn ( mWndScriptList, 0, &col); + col.cx = 350; + col.pszText = "Filename"; + ListView_InsertColumn ( mWndScriptList, 1, &col ); + col.cx = 20; col.pszText = ""; ListView_InsertColumn ( mWndCallstack, 0, &col ); col.cx = 150; col.pszText = "Function"; ListView_InsertColumn ( mWndCallstack, 1, &col ); - col.cx = 150; + col.cx = 50; col.pszText = "Line"; ListView_InsertColumn ( mWndCallstack, 2, &col ); - col.cx = 150; + col.cx = 350; col.pszText = "Filename"; ListView_InsertColumn ( mWndCallstack, 3, &col ); @@ -863,13 +1044,21 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam ) ImageList_AddIcon ( mImageList, (HICON)LoadImage ( mInstance, MAKEINTRESOURCE(IDI_DBG_CURRENT), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_DEFAULTCOLOR) ); ImageList_AddIcon ( mImageList, (HICON)LoadImage ( mInstance, MAKEINTRESOURCE(IDI_DBG_BREAKPOINT), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_DEFAULTCOLOR) ); ImageList_AddIcon ( mImageList, (HICON)LoadImage ( mInstance, MAKEINTRESOURCE(IDI_DBG_CURRENTLINE), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_DEFAULTCOLOR) ); + + int w, h; + ResizeImageList(w, h); + ListView_SetImageList ( mWndScriptList, mTmpImageList, LVSIL_SMALL ); ListView_SetImageList ( mWndThreads, mImageList, LVSIL_SMALL ); ListView_SetImageList ( mWndCallstack, mImageList, LVSIL_SMALL ); + ListView_SetImageList ( mWndBreakList, mImageList, LVSIL_SMALL ); EnableWindows ( FALSE ); - + EnableWindow ( mWndScriptList, true ); + ListView_SetExtendedListViewStyle ( mWndCallstack, LVS_EX_FULLROWSELECT ); ListView_SetExtendedListViewStyle ( mWndThreads, LVS_EX_FULLROWSELECT ); + ListView_SetExtendedListViewStyle ( mWndScriptList, LVS_EX_FULLROWSELECT ); + ListView_SetExtendedListViewStyle ( mWndBreakList, LVS_EX_FULLROWSELECT ); gDebuggerApp.GetOptions().GetColumnWidths ( "cw_callstack", mWndCallstack ); gDebuggerApp.GetOptions().GetColumnWidths ( "cw_threads", mWndThreads ); @@ -920,6 +1109,10 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam ) AddWatch ( s ); } + RECT t; + GetClientRect(mWndScript, &t); + SendMessage(mWndScript, WM_SIZE, 0, MAKELPARAM(t.right - t.left, t.bottom - t.top)); + return 0; } @@ -957,6 +1150,36 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam ) switch ( id ) { + case ID_DBG_SEND_COMMAND: + { + if ( mClient->IsConnected( ) && GetFocus( ) == mWndConsoleInput ) { + GETTEXTLENGTHEX textLen; + int chars; + textLen.flags = GTL_DEFAULT | GTL_USECRLF; + textLen.codepage = CP_ACP; + chars = SendMessage( mWndConsoleInput, EM_GETTEXTLENGTHEX, ( WPARAM ) &textLen, 0 ); + + char *text = new char[chars + 1]; + + GETTEXTEX getText; + getText.cb = chars + 1; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT | GT_USECRLF; + getText.lpDefaultChar = NULL; + getText.lpUsedDefChar = NULL; + SendMessage( mWndConsoleInput, EM_GETTEXTEX, ( WPARAM ) &getText, ( LPARAM ) text ); + idStr parse = text; + delete[] text; + + mClient->SendCommand( parse.c_str() ); + + SendMessage( mWndConsoleInput, EM_SETSEL, 0, -1 ); + SendMessage( mWndConsoleInput, EM_REPLACESEL, FALSE, ( LPARAM ) "" ); + UpdateWindow( mWndConsoleInput ); + } + break; + } + case ID_DBG_EDIT_FINDSELECTED: { idStr text; @@ -1025,7 +1248,7 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam ) GetCurrentDirectory ( MAX_PATH, curDir ); GetModuleFileName ( NULL, exeFile, MAX_PATH ); - const char* s = va("%s +set fs_game %s +set fs_cdpath %s", exeFile, cvarSystem->GetCVarString( "fs_game" ), cvarSystem->GetCVarString( "fs_cdpath" ) ); + const char* s = va("%s +set fs_game %s +set fs_cdpath %s +set com_enableDebuggerServer 1", exeFile, cvarSystem->GetCVarString( "fs_game" ), cvarSystem->GetCVarString( "fs_cdpath" ) ); CreateProcess ( NULL, (LPSTR)s, NULL, NULL, FALSE, 0, NULL, curDir, &startup, &process ); @@ -1049,13 +1272,13 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam ) LONG num; LONG dem; - SendMessage ( mWndScript, EM_GETZOOM, (LONG)&num, (LONG)&dem ); + SendMessage ( mWndScript, EM_GETZOOM, (WPARAM)&num, (LPARAM)&dem ); if ( num != mZoomScaleNum || dem != mZoomScaleDem ) { mZoomScaleNum = num; mZoomScaleDem = dem; GetClientRect ( mWndScript, &t ); - SendMessage ( mWnd, WM_SIZE, 0, MAKELPARAM(t.right-t.left,t.bottom-t.top) ); + SendMessage ( mWndScript, WM_SIZE, 0, MAKELPARAM(t.right-t.left ,t.bottom-t.top) ); } else { @@ -1064,6 +1287,7 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam ) break; } + case 111: // DG: Debugger.rc has 'MENUITEM "Toggle &Breakpoint\tF9", 111' for the context menu no idea why 111 but this works case ID_DBG_DEBUG_TOGGLEBREAKPOINT: ToggleBreakpoint ( ); break; @@ -1139,6 +1363,23 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam ) UpdateScript ( ); break; } + + // DG: support "Run To Cursor" from context menu + case ID_DBG_DEBUG_RUNTOCURSOR: + { + // Find the currently selected line + DWORD sel; + SendMessage( mWndScript, EM_GETSEL, (WPARAM)&sel, 0 ); + int lineNumber = SendMessage( mWndScript, EM_LINEFROMCHAR, sel, 0 ) + 1; + + const char* filename = mScripts[mActiveScript]->GetFilename(); + mClient->AddBreakpoint( filename, lineNumber, true ); + mClient->Resume(); + break; + } + + // TODO: case ID_DBG_DEBUG_SHOWNEXTSTATEMENT: + // whatever this is supposed to do (also from context menu) } return 0; @@ -1153,7 +1394,7 @@ Window procedure for the deubgger window */ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ) { - rvDebuggerWindow* window = (rvDebuggerWindow*) GetWindowLong ( wnd, GWL_USERDATA ); + rvDebuggerWindow* window = (rvDebuggerWindow*) GetWindowLongPtr ( wnd, GWLP_USERDATA ); switch ( msg ) { @@ -1176,7 +1417,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, gDebuggerApp.GetOptions().SetString ( va("watch%d", i ), "" ); window->mWnd = NULL; - SetWindowLong ( wnd, GWL_USERDATA, 0 ); + SetWindowLongPtr ( wnd, GWLP_USERDATA, 0 ); break; } @@ -1202,8 +1443,13 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, case WM_SIZE: { + float scaling_factor = Win_GetWindowScalingFactor(wnd); + int s18 = int(18 * scaling_factor); + int s4 = int(4 * scaling_factor); + int s10 = int(10 * scaling_factor); + RECT rect; - window->mMarginSize = window->mZoomScaleDem ? ((long)(18.0f * (float)window->mZoomScaleNum / (float)window->mZoomScaleDem)):18; + window->mMarginSize = window->mZoomScaleDem ? ((long)(s18 * (float)window->mZoomScaleNum / (float)window->mZoomScaleDem)): s18; window->mSplitterRect.left = 0; window->mSplitterRect.right = LOWORD(lparam); @@ -1215,14 +1461,27 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, SetRect ( &rect, 0, window->mSplitterRect.bottom, LOWORD(lparam), HIWORD(lparam) ); MoveWindow ( window->mWndTabs, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE ); SendMessage ( window->mWndTabs, TCM_ADJUSTRECT, FALSE, (LPARAM)&rect ); - rect.bottom -= 4 ; + rect.bottom -= s4; MoveWindow ( window->mWndBorder, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE ); InflateRect ( &rect, -1, -1 ); MoveWindow ( window->mWndOutput, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE ); - MoveWindow ( window->mWndConsole, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE ); + MoveWindow ( window->mWndConsole, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top - s18, TRUE ); + MoveWindow ( window->mWndConsoleInput, rect.left, rect.bottom-s18, rect.right - rect.left, s18, TRUE ); MoveWindow ( window->mWndCallstack, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE ); MoveWindow ( window->mWndWatch, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE ); MoveWindow ( window->mWndThreads, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE ); + MoveWindow ( window->mWndScriptList, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE ); + MoveWindow ( window->mWndBreakList, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE ); + + // FIXME: was *2.25, increased for line numbers up to 9999; but neither works particularly well + // if DPI scaling is involved, because script code text and linenumbers aren't DPI scaled + int lmargin = s18 * 3.5; + SendMessage(window->mWndScript, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(lmargin, s10)); + SendMessage(window->mWndCallstack, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(s18, s10)); + SendMessage(window->mWndOutput, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(s18, s10)); + SendMessage(window->mWndConsole, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(s18, s10)); + SendMessage( window->mWndConsoleInput, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG( s18, s10 ) ); + break; } @@ -1258,7 +1517,17 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, } break; } + case WM_MOUSEWHEEL: + { + HDC dc = GetDC(wnd); + DrawFocusRect(dc, &window->mSplitterRect); + ReleaseDC(wnd, dc); + RECT client; + GetClientRect(wnd, &client); + SendMessage(wnd, WM_SIZE, 0, MAKELPARAM(client.right - client.left, client.bottom - client.top)); + + } case WM_LBUTTONUP: if ( window->mSplitterDrag ) { @@ -1320,7 +1589,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, { CREATESTRUCT* cs = (CREATESTRUCT*) lparam; window = (rvDebuggerWindow*) cs->lpCreateParams; - SetWindowLong ( wnd, GWL_USERDATA, (LONG)cs->lpCreateParams ); + SetWindowLongPtr ( wnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams ); window->mWnd = wnd; window->HandleCreate ( wparam, lparam ); @@ -1439,7 +1708,6 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, } } break; - case IDC_DBG_CALLSTACK: if ( hdr->code == NM_DBLCLK ) { @@ -1454,14 +1722,74 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, } } break; + case IDC_DBG_SCRIPTLIST: + if ( hdr->code == NM_DBLCLK ) + { + int sel = ListView_GetNextItem(hdr->hwndFrom, -1, LVNI_SELECTED); + + if (sel != -1) + { + LVITEM item = { 0 }; + char temp[1024] = { 0 }; + item.mask = LVIF_TEXT; + item.pszText = temp; + item.cchTextMax = sizeof(temp) - 1; + item.iSubItem = 1; + item.iItem = sel; + + ListView_GetItem(hdr->hwndFrom, &item); + + if (strlen(item.pszText) > 0) + { + window->OpenScript(item.pszText); + window->UpdateScriptList(); + } + } + } + break; + + case IDC_DBG_BREAKLIST: + if ( hdr->code == NM_DBLCLK || hdr->code == NM_CLICK ) { + LPNMITEMACTIVATE ia = (LPNMITEMACTIVATE)lparam; + int sel = ia->iItem; + if ( sel != -1 ) { + rvDebuggerBreakpoint* bp = window->mClient->GetBreakpoint( sel ); + if ( bp != NULL ) { + if ( hdr->code == NM_DBLCLK ) { + // double clicked breakpoint => show it in its file + window->OpenScript( bp->GetFilename(), bp->GetLineNumber() - 1 ); + } else if( ia->iSubItem == 0 ) { + // clicked breakpoint symbol => delete breakpoint + window->mClient->RemoveBreakpoint( bp->GetID() ); + window->UpdateBreakpointList(); + } + } + } + } else if ( hdr->code == LVN_KEYDOWN ) { + // when user selects a breakpoints and presses the Del key, remove the breakpoint + int sel = ListView_GetNextItem( hdr->hwndFrom, -1, LVNI_SELECTED ); + if ( sel != -1 ) { + LPNMLVKEYDOWN kd = (LPNMLVKEYDOWN)lparam; + rvDebuggerBreakpoint* bp = window->mClient->GetBreakpoint( sel ); + if ( kd->wVKey == VK_DELETE && bp != NULL ) { + window->mClient->RemoveBreakpoint( bp->GetID() ); + window->UpdateBreakpointList(); + } + } + } + break; + case IDC_DBG_TABS: if ( hdr->code == TCN_SELCHANGE ) { ShowWindow ( window->mWndOutput, SW_HIDE ); ShowWindow ( window->mWndConsole, SW_HIDE ); + ShowWindow ( window->mWndConsoleInput, SW_HIDE ); ShowWindow ( window->mWndCallstack, SW_HIDE ); ShowWindow ( window->mWndWatch, SW_HIDE ); ShowWindow ( window->mWndThreads, SW_HIDE ); + ShowWindow ( window->mWndScriptList, SW_HIDE ); + ShowWindow ( window->mWndBreakList, SW_HIDE ); switch ( TabCtrl_GetCurSel ( hdr->hwndFrom ) ) { case 0: @@ -1470,6 +1798,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, case 1: ShowWindow ( window->mWndConsole, SW_SHOW ); + ShowWindow( window->mWndConsoleInput, SW_SHOW ); break; case 2: @@ -1483,6 +1812,14 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, case 4: ShowWindow ( window->mWndThreads, SW_SHOW ); break; + + case 5: + ShowWindow(window->mWndScriptList, SW_SHOW); + break; + + case 6: + ShowWindow( window->mWndBreakList, SW_SHOW ); + break; } } break; @@ -1493,7 +1830,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, case WM_CLOSE: if ( window->mClient->IsConnected ( ) ) { - if ( IDNO == MessageBox ( wnd, "The debugger is currently connected to a running version of the game. Are you sure you want to close now?", "Quake 4 Script Debugger", MB_YESNO|MB_ICONQUESTION ) ) + if ( IDNO == MessageBox ( wnd, "The debugger is currently connected to a running version of the game. Are you sure you want to close now?", "Dhewm3 Script Debugger", MB_YESNO|MB_ICONQUESTION ) ) { return 0; } @@ -1535,14 +1872,20 @@ rvDebuggerWindow::ProcessNetMessage Process an incoming network message ================ */ -void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg ) +void rvDebuggerWindow::ProcessNetMessage ( idBitMsg* msg ) { - unsigned short command; + short command; - command = (unsigned short)MSG_ReadShort ( msg ); + command = msg->ReadShort( ); switch ( command ) { + case DBMSG_REMOVEBREAKPOINT: + MessageBeep(MB_ICONEXCLAMATION); + InvalidateRect(mWndScript, NULL, FALSE); + UpdateBreakpointList(); + break; + case DBMSG_RESUMED: UpdateTitle ( ); UpdateToolbar ( ); @@ -1554,9 +1897,9 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg ) char temp2[1024]; int i; - MSG_ReadShort ( msg ); - MSG_ReadString ( msg, temp, 1024 ); - MSG_ReadString ( msg, temp2, 1024 ); + msg->ReadShort ( ); + msg->ReadString ( temp, 1024 ); + msg->ReadString ( temp2, 1024 ); if ( mTooltipVar.Icmp ( temp ) == 0 ) { mTooltipValue = temp2; @@ -1624,10 +1967,17 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg ) break; case DBMSG_PRINT: + { + HWND prevFocus = GetFocus(); + SetFocus ( mWndConsole ); SendMessage ( mWndConsole, EM_SETSEL, -1, -1 ); - SendMessage ( mWndConsole, EM_REPLACESEL, 0, (LPARAM)(const char*)(msg->data) + msg->readcount ); + SendMessage ( mWndConsole, EM_REPLACESEL, 0, (LPARAM)(const char*)(msg->GetData()) + msg->GetReadCount() ); + SendMessage( mWndConsole, EM_SETSEL, -1, -1 ); SendMessage ( mWndConsole, EM_SCROLLCARET, 0, 0 ); + UpdateWindow( mWndConsole ); + SetFocus( prevFocus ); break; + } case DBMSG_BREAK: { @@ -1636,6 +1986,7 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg ) mCurrentStackDepth = 0; mClient->InspectVariable ( mTooltipVar, mCurrentStackDepth ); UpdateWatch ( ); + UpdateBreakpointList(); EnableWindows ( TRUE ); OpenScript ( mClient->GetBreakFilename(), mClient->GetBreakLineNumber() - 1 ); UpdateTitle ( ); @@ -1643,7 +1994,11 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg ) SetForegroundWindow ( mWnd ); break; } - + case DBMSG_INSPECTSCRIPTS: + { + UpdateScriptList ( ); + break; + } case DBMSG_INSPECTCALLSTACK: { UpdateCallstack ( ); @@ -1719,7 +2074,7 @@ Opens the script with the given filename and will scroll to the given line number if one is specified ================ */ -bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber ) +bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber, idProgram* program ) { int i; @@ -1748,7 +2103,6 @@ bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber ) // Load the script if ( !script->Load ( filename ) ) { - delete script; SetCursor ( LoadCursor ( NULL, IDC_ARROW ) ); return false; } @@ -1777,7 +2131,7 @@ bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber ) // Move to a specific line number? if ( lineNumber != -1 ) { - int c; + long c; // Put the caret on the line number specified and scroll it into position. // This is a bit of a hack since we set the selection twice, but setting the @@ -1785,10 +2139,10 @@ bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber ) // and then scroll before going back to (c,c). // NOTE: We scroll to the line before the one we want so its more visible SetFocus ( mWndScript ); - c = SendMessage ( mWndScript, EM_LINEINDEX, lineNumber - 1, 0 ); + c = SendMessage ( mWndScript, EM_LINEINDEX, (long)lineNumber - 1, 0 ); SendMessage ( mWndScript, EM_SETSEL, c, c + 1 ); SendMessage ( mWndScript, EM_SCROLLCARET, 0, 0 ); - c = SendMessage ( mWndScript, EM_LINEINDEX, lineNumber, 0 ); + c = SendMessage ( mWndScript, EM_LINEINDEX, (long)lineNumber, 0 ); SendMessage ( mWndScript, EM_SETSEL, c, c ); } else @@ -1843,6 +2197,8 @@ void rvDebuggerWindow::ToggleBreakpoint ( void ) // Force a repaint of the script window InvalidateRect ( mWndScript, NULL, FALSE ); + + UpdateBreakpointList(); } /* @@ -1895,6 +2251,8 @@ void rvDebuggerWindow::CreateToolbar ( void ) SendMessage( mWndToolbar, TB_ADDBITMAP, (WPARAM)4, (LPARAM) &tbab ); // Add the buttons to the toolbar + // FIXME: warning C4838: conversion from 'int' to 'BYTE' requires a narrowing conversion + // most probably because TBBUTTON has 4 more bytes in bReserved for alignment on _WIN64 TBBUTTON tbb[] = { { 0, 0, TBSTATE_ENABLED, BTNS_SEP, 0, 0, -1 }, { 8, ID_DBG_FILE_OPEN, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, -1 }, { 0, 0, TBSTATE_ENABLED, BTNS_SEP, 0, 0, -1 }, @@ -2013,6 +2371,7 @@ int rvDebuggerWindow::HandleActivate ( WPARAM wparam, LPARAM lparam ) } } } + UpdateBreakpointList(); return 1; } @@ -2193,7 +2552,7 @@ then the last text used will be searched for. */ bool rvDebuggerWindow::FindNext ( const char* text ) { - int start; + long start; FINDTEXT ft; if ( text ) @@ -2230,7 +2589,7 @@ bool rvDebuggerWindow::FindNext ( const char* text ) } } - SendMessage ( mWndScript, EM_SETSEL, start, start + mFind.Length() ); + SendMessage ( mWndScript, EM_SETSEL, start, start + (long)mFind.Length() ); SendMessage ( mWndScript, EM_SCROLLCARET, 0, 0 ); return true; @@ -2247,7 +2606,7 @@ then the last text used will be searched for. */ bool rvDebuggerWindow::FindPrev ( const char* text ) { - int start; + long start; FINDTEXT ft; if ( text ) diff --git a/neo/tools/debugger/DebuggerWindow.h b/neo/tools/debugger/DebuggerWindow.h index d1e57476..5b9cec0f 100644 --- a/neo/tools/debugger/DebuggerWindow.h +++ b/neo/tools/debugger/DebuggerWindow.h @@ -52,38 +52,57 @@ public: rvDebuggerWindow ( ); ~rvDebuggerWindow ( ); - bool Create ( HINSTANCE hInstance ); + bool Create ( HINSTANCE hInstance ); - static bool Activate ( void ); + static bool Activate ( void ); - void ProcessNetMessage ( msg_t* msg ); + void ProcessNetMessage ( idBitMsg * msg ); - void Printf ( const char* format, ... ); + void Printf ( const char* format, ... ); - HWND GetWindow ( void ); + HWND GetWindow ( void ); - void AddWatch ( const char* name, bool update = true ); + void AddWatch ( const char* name, bool update = true ); - HINSTANCE GetInstance ( void ); + HINSTANCE GetInstance ( void ); -protected: +private: + bool RegisterClass ( void ); + void CreateToolbar ( void ); + bool InitRecentFiles ( void ); - bool FindPrev ( const char* text = NULL ); - bool FindNext ( const char* text = NULL ); + int HandleInitMenu ( WPARAM wParam, LPARAM lParam ); + int HandleCommand ( WPARAM wParam, LPARAM lParam ); + int HandleCreate ( WPARAM wparam, LPARAM lparam ); + int HandleActivate ( WPARAM wparam, LPARAM lparam ); + int HandleDrawItem ( WPARAM wparam, LPARAM lparam ); + void HandleTooltipGetDispInfo( WPARAM wparam, LPARAM lparam ); - void UpdateWatch ( void ); - void UpdateWindowMenu ( void ); - void UpdateScript ( void ); - void UpdateToolbar ( void ); - void UpdateTitle ( void ); - void UpdateCallstack ( void ); - void UpdateRecentFiles ( void ); - bool OpenScript ( const char* filename, int lineNumber = -1 ); - void EnableWindows ( bool state ); + void ResizeImageList ( int& widthOut, int& heightOut); + static LRESULT CALLBACK WndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ); + static LRESULT CALLBACK MarginWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ); + static LRESULT CALLBACK ScriptWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ); + static INT_PTR CALLBACK AboutDlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ); + static int CALLBACK ScriptWordBreakProc( LPTSTR text, int current, int max, int action ); - int GetSelectedText ( idStr& text ); + bool FindPrev ( const char* text = NULL ); + bool FindNext ( const char* text = NULL ); - void ToggleBreakpoint ( void ); + void UpdateBreakpointList( void ); + void UpdateScriptList ( void ); + void UpdateWatch ( void ); + void UpdateWindowMenu ( void ); + void UpdateScript ( void ); + void UpdateToolbar ( void ); + void UpdateTitle ( void ); + void UpdateCallstack ( void ); + void UpdateRecentFiles ( void ); + bool OpenScript ( const char* filename, int lineNumber = -1, idProgram* program = nullptr ); + void EnableWindows ( bool state ); + + int GetSelectedText ( idStr& text ); + + void ToggleBreakpoint ( void ); HWND mWnd; HWND mWndScript; @@ -92,7 +111,10 @@ protected: HWND mWndTabs; HWND mWndBorder; HWND mWndConsole; + HWND mWndConsoleInput; HWND mWndCallstack; + HWND mWndScriptList; + HWND mWndBreakList; // list of breakpoints HWND mWndWatch; HWND mWndThreads; HWND mWndToolTips; @@ -108,6 +130,7 @@ protected: HINSTANCE mInstance; HIMAGELIST mImageList; + HIMAGELIST mTmpImageList; RECT mSplitterRect; bool mSplitterDrag; @@ -129,25 +152,6 @@ protected: rvDebuggerClient* mClient; rvDebuggerWatchList mWatches; - -private: - - bool RegisterClass ( void ); - void CreateToolbar ( void ); - bool InitRecentFiles ( void ); - - int HandleInitMenu ( WPARAM wParam, LPARAM lParam ); - int HandleCommand ( WPARAM wParam, LPARAM lParam ); - int HandleCreate ( WPARAM wparam, LPARAM lparam ); - int HandleActivate ( WPARAM wparam, LPARAM lparam ); - int HandleDrawItem ( WPARAM wparam, LPARAM lparam ); - void HandleTooltipGetDispInfo ( WPARAM wparam, LPARAM lparam ); - - static LRESULT CALLBACK WndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ); - static LRESULT CALLBACK MarginWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ); - static LRESULT CALLBACK ScriptWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ); - static INT_PTR CALLBACK AboutDlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ); - static int CALLBACK ScriptWordBreakProc ( LPTSTR text, int current, int max, int action ); }; /* diff --git a/neo/tools/debugger/debugger.cpp b/neo/tools/debugger/debugger.cpp index 4e5d5f6e..3bb9043c 100644 --- a/neo/tools/debugger/debugger.cpp +++ b/neo/tools/debugger/debugger.cpp @@ -26,26 +26,26 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ +#if defined( ID_ALLOW_TOOLS ) #include "tools/edit_gui_common.h" - - #include "../../sys/win32/rc/debugger_resource.h" #include "DebuggerApp.h" +#else +#include "debugger_common.h" +#endif + #include "DebuggerServer.h" -DWORD CALLBACK DebuggerThread ( LPVOID param ); +#if defined( ID_ALLOW_TOOLS ) +rvDebuggerApp gDebuggerApp; // this is also used in other source files +static HWND gDebuggerWindow = NULL; +#endif -rvDebuggerApp gDebuggerApp; -HWND gDebuggerWindow = NULL; -bool gDebuggerSuspend = false; -bool gDebuggerConnnected = false; -HANDLE gDebuggerGameThread = NULL; - -rvDebuggerServer* gDebuggerServer = NULL; -HANDLE gDebuggerServerThread = NULL; -DWORD gDebuggerServerThreadID = 0; -bool gDebuggerServerQuit = false; +static rvDebuggerServer* gDebuggerServer = NULL; +static SDL_Thread* gDebuggerServerThread = NULL; +static bool gDebuggerServerQuit = false; +#if defined( ID_ALLOW_TOOLS ) /* ================ DebuggerMain @@ -65,6 +65,9 @@ void DebuggerClientInit( const char *cmdline ) { goto DebuggerClientInitDone; } + + // hide the doom window by default + ::ShowWindow( win32.hWnd, SW_HIDE ); gDebuggerApp.Run ( ); @@ -113,6 +116,7 @@ void DebuggerClientLaunch ( void ) CloseHandle ( process.hThread ); CloseHandle ( process.hProcess ); } +#endif // #if defined( ID_ALLOW_TOOLS ) /* ================ @@ -121,14 +125,14 @@ DebuggerServerThread Thread proc for the debugger server ================ */ -DWORD CALLBACK DebuggerServerThread ( LPVOID param ) +static int SDLCALL DebuggerServerThread ( void *param ) { assert ( gDebuggerServer ); while ( !gDebuggerServerQuit ) { gDebuggerServer->ProcessMessages ( ); - Sleep ( 1 ); + SDL_Delay( 1 ); } return 0; @@ -143,8 +147,17 @@ Starts up the debugger server */ bool DebuggerServerInit ( void ) { + com_enableDebuggerServer.ClearModified( ); + + if ( !com_debuggerSupported ) + { + common->Warning( "Called DebuggerServerInit() without the gameDLL supporting it!\n" ); + return false; + } + // Dont do this if we are in the debugger already - if ( com_editors & EDITOR_DEBUGGER ) + if ( gDebuggerServer != NULL + || ( com_editors & EDITOR_DEBUGGER ) ) { return false; } @@ -163,9 +176,13 @@ bool DebuggerServerInit ( void ) gDebuggerServer = NULL; return false; } - + // Start the debugger server thread - gDebuggerServerThread = CreateThread ( NULL, 0, DebuggerServerThread, 0, 0, &gDebuggerServerThreadID ); +#if SDL_VERSION_ATLEAST(2, 0, 0) + gDebuggerServerThread = SDL_CreateThread( DebuggerServerThread, "DebuggerServer", NULL ); +#else // SDL 1.2 + gDebuggerServerThread = SDL_CreateThread( DebuggerServerThread, NULL ); +#endif return true; } @@ -179,13 +196,14 @@ Shuts down the debugger server */ void DebuggerServerShutdown ( void ) { - if ( gDebuggerServerThread ) + if ( gDebuggerServerThread != NULL ) { // Signal the debugger server to quit gDebuggerServerQuit = true; // Wait for the thread to finish - WaitForSingleObject ( gDebuggerServerThread, INFINITE ); + SDL_WaitThread( gDebuggerServerThread, NULL ); + gDebuggerServerThread = NULL; // Shutdown the server now gDebuggerServer->Shutdown(); @@ -193,10 +211,10 @@ void DebuggerServerShutdown ( void ) delete gDebuggerServer; gDebuggerServer = NULL; - // Cleanup the thread handle - CloseHandle ( gDebuggerServerThread ); - gDebuggerServerThread = NULL; + com_editors &= ~EDITOR_DEBUGGER; } + + com_enableDebuggerServer.ClearModified( ); } /* diff --git a/neo/tools/debugger/debugger_common.h b/neo/tools/debugger/debugger_common.h new file mode 100644 index 00000000..e98962c6 --- /dev/null +++ b/neo/tools/debugger/debugger_common.h @@ -0,0 +1,162 @@ +// header that includes all the other needed headers, replacement for precompiled.h (only used by debugger) +// this could be cleaned up more. + +#ifndef DEBUGGER_COMMON_H +#define DEBUGGER_COMMON_H + +#include "framework/Game.h" + +// non-portable system services +#include "sys/platform.h" +#include "sys/sys_public.h" + +// id lib +#include "idlib/Lib.h" + +// memory management and arrays +#include "idlib/Heap.h" +#include "idlib/containers/List.h" + +// math +#include "idlib/math/Simd.h" +#include "idlib/math/Math.h" +#include "idlib/math/Random.h" +#include "idlib/math/Complex.h" +#include "idlib/math/Vector.h" +#include "idlib/math/Matrix.h" +#include "idlib/math/Angles.h" +#include "idlib/math/Quat.h" +#include "idlib/math/Rotation.h" +#include "idlib/math/Plane.h" +#include "idlib/math/Pluecker.h" +#include "idlib/math/Polynomial.h" +#include "idlib/math/Extrapolate.h" +#include "idlib/math/Interpolate.h" +#include "idlib/math/Curve.h" +#include "idlib/math/Ode.h" +#include "idlib/math/Lcp.h" + +// bounding volumes +#include "idlib/bv/Sphere.h" +#include "idlib/bv/Bounds.h" +#include "idlib/bv/Box.h" +#include "idlib/bv/Frustum.h" + +// geometry +#include "idlib/geometry/DrawVert.h" +#include "idlib/geometry/JointTransform.h" +#include "idlib/geometry/Winding.h" +#include "idlib/geometry/Winding2D.h" +#include "idlib/geometry/Surface.h" +#include "idlib/geometry/Surface_Patch.h" +#include "idlib/geometry/Surface_Polytope.h" +#include "idlib/geometry/Surface_SweptSpline.h" +#include "idlib/geometry/TraceModel.h" + +// text manipulation +#include "idlib/Str.h" +#include "idlib/Token.h" +#include "idlib/Lexer.h" +#include "idlib/Parser.h" +#include "idlib/Base64.h" +#include "idlib/CmdArgs.h" + +// containers +#include "idlib/containers/BTree.h" +#include "idlib/containers/BinSearch.h" +#include "idlib/containers/HashIndex.h" +#include "idlib/containers/HashTable.h" +#include "idlib/containers/StaticList.h" +#include "idlib/containers/LinkList.h" +#include "idlib/containers/Hierarchy.h" +#include "idlib/containers/Queue.h" +#include "idlib/containers/Stack.h" +#include "idlib/containers/StrList.h" +#include "idlib/containers/StrPool.h" +#include "idlib/containers/VectorSet.h" +#include "idlib/containers/PlaneSet.h" + +// hashing +#include "idlib/hashing/CRC32.h" +#include "idlib/hashing/MD4.h" +#include "idlib/hashing/MD5.h" + +// misc +#include "idlib/Dict.h" +#include "idlib/LangDict.h" +#include "idlib/BitMsg.h" +#include "idlib/MapFile.h" +#include "idlib/Timer.h" + +// framework +#include "framework/BuildVersion.h" +#include "framework/Licensee.h" +#include "framework/CmdSystem.h" +#include "framework/CVarSystem.h" +#include "framework/Common.h" +#include "framework/File.h" +#include "framework/FileSystem.h" +#include "framework/UsercmdGen.h" + +// decls +#include "framework/DeclManager.h" +#include "framework/DeclTable.h" +#include "framework/DeclSkin.h" +#include "framework/DeclEntityDef.h" +#include "framework/DeclFX.h" +#include "framework/DeclParticle.h" +#include "framework/DeclAF.h" +#include "framework/DeclPDA.h" + +// We have expression parsing and evaluation code in multiple places: +// materials, sound shaders, and guis. We should unify them. + +// renderer +#include "renderer/qgl.h" +#include "renderer/Cinematic.h" +#include "renderer/Material.h" +#include "renderer/Model.h" +#include "renderer/ModelManager.h" +#include "renderer/RenderSystem.h" +#include "renderer/RenderWorld.h" + +// sound engine +#include "sound/sound.h" + +// asynchronous networking +#include "framework/async/NetworkSystem.h" + +// user interfaces +#include "ui/ListGUI.h" +#include "ui/UserInterface.h" + +// collision detection system +#include "cm/CollisionModel.h" + +// AAS files and manager +#include "tools/compilers/aas/AASFile.h" +#include "tools/compilers/aas/AASFileManager.h" + +// game interface +#include "framework/Game.h" + +//----------------------------------------------------- + +#include "framework/DemoChecksum.h" + +// framework +#include "framework/Compressor.h" +#include "framework/EventLoop.h" +#include "framework/KeyInput.h" +#include "framework/EditField.h" +#include "framework/Console.h" +#include "framework/DemoFile.h" +#include "framework/Session.h" + +// asynchronous networking +#include "framework/async/AsyncNetwork.h" + +// Compilers for map, model, video etc. processing. +#include "tools/compilers/compiler_public.h" + +#endif // DEBUGGER_COMMON_H diff --git a/neo/tools/edit_stub.cpp b/neo/tools/edit_stub.cpp index 79c2c887..34d99e14 100644 --- a/neo/tools/edit_stub.cpp +++ b/neo/tools/edit_stub.cpp @@ -67,10 +67,6 @@ bool GUIEditorHandleMessage( void *msg ) { return false; } void DebuggerClientLaunch( void ) {} void DebuggerClientInit( const char *cmdline ) { common->Printf( "The Script Debugger Client only runs on Win32\n" ); } -bool DebuggerServerInit( void ) { return false; } -void DebuggerServerShutdown( void ) {} -void DebuggerServerPrint( const char *text ) {} -void DebuggerServerCheckBreakpoint( idInterpreter *interpreter, idProgram *program, int instructionPointer ) {} void PDAEditorInit( const idDict *spawnArgs ) { common->Printf( "The PDA editor only runs on Win32\n" ); }