Merge branch 'debugger'

This commit is contained in:
Daniel Gibson 2021-07-16 02:21:16 +02:00
commit 5e4e1d61be
42 changed files with 2048 additions and 658 deletions

2
.gitignore vendored
View file

@ -11,4 +11,6 @@
/neo/.vs* /neo/.vs*
/neo/out* /neo/out*
/neo/build* /neo/build*
/.vs*
/neo/CMakeSettings.json

View file

@ -801,6 +801,17 @@ set(src_d3xp
add_globbed_headers(src_d3xp "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 set(src_core
${src_renderer} ${src_renderer}
${src_framework} ${src_framework}
@ -862,6 +873,23 @@ if (TOOLS AND MFC_FOUND AND MSVC)
# Script editor # Script editor
file(GLOB src_script_editor "tools/script/*.cpp") file(GLOB src_script_editor "tools/script/*.cpp")
add_globbed_headers(src_script_editor "tools/script") 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? # sound editor?
file(GLOB src_sound_editor "tools/sound/*.cpp") file(GLOB src_sound_editor "tools/sound/*.cpp")
add_globbed_headers(src_sound_editor "tools/sound") add_globbed_headers(src_sound_editor "tools/sound")
@ -881,13 +909,17 @@ if (TOOLS AND MFC_FOUND AND MSVC)
${src_map_editor} ${src_map_editor}
${src_script_editor} ${src_script_editor}
${src_sound_editor} ${src_sound_editor}
${src_script_debugger}
"tools/edit_public.h" "tools/edit_public.h"
"tools/edit_gui_common.h" "tools/edit_gui_common.h"
) )
SET(CMAKE_MFC_FLAG 2) SET(CMAKE_MFC_FLAG 2)
set(TOOLS_DEFINES "ID_ALLOW_TOOLS;_AFXDLL") set(TOOLS_DEFINES "ID_ALLOW_TOOLS;_AFXDLL")
else() 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() endif()
@ -1052,9 +1084,10 @@ if(DEDICATED)
${src_stub_openal} ${src_stub_openal}
${src_stub_gl} ${src_stub_gl}
${src_sys_base} ${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 COMPILE_DEFINITIONS "ID_DEDICATED;__DOOM_DLL__")
set_target_properties(${DHEWM3BINARY}ded PROPERTIES LINK_FLAGS "${ldflags}") set_target_properties(${DHEWM3BINARY}ded PROPERTIES LINK_FLAGS "${ldflags}")

View file

@ -670,7 +670,7 @@ void idEditEntities::DisplayEntities( void ) {
=============================================================================== ===============================================================================
*/ */
idGameEdit gameEditLocal; idGameEditExt gameEditLocal;
idGameEdit * gameEdit = &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);
}

View file

@ -302,6 +302,16 @@ void idGameLocal::Clear( void ) {
#endif #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 idGameLocal::Init
@ -410,6 +420,10 @@ void idGameLocal::Init( void ) {
gamestate = GAMESTATE_NOMAP; gamestate = GAMESTATE_NOMAP;
Printf( "...%d aas types\n", aasList.Num() ); 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 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->isServer = isServer;
this->isClient = isClient; this->isClient = isClient;
@ -2436,14 +2450,14 @@ void idGameLocal::RunTimeGroup2() {
idGameLocal::RunFrame idGameLocal::RunFrame
================ ================
*/ */
gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) { gameReturn_t idGameLocal::RunFrame(const usercmd_t* clientCmds) {
idEntity * ent; idEntity* ent;
int num; int num;
float ms; float ms;
idTimer timer_think, timer_events, timer_singlethink; idTimer timer_think, timer_events, timer_singlethink;
gameReturn_t ret; gameReturn_t ret;
idPlayer *player; idPlayer* player;
const renderView_t *view; const renderView_t* view;
#ifdef _DEBUG #ifdef _DEBUG
if ( isMultiplayer ) { if ( isMultiplayer ) {

View file

@ -365,13 +365,13 @@ public:
virtual const idDict & GetPersistentPlayerInfo( int clientNum ); virtual const idDict & GetPersistentPlayerInfo( int clientNum );
virtual void SetPersistentPlayerInfo( int clientNum, const idDict &playerInfo ); 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 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 bool InitFromSaveGame(const char* mapName, idRenderWorld* renderWorld, idSoundWorld* soundWorld, idFile* saveGameFile );
virtual void SaveGame( idFile *saveGameFile ); virtual void SaveGame( idFile *saveGameFile );
virtual void MapShutdown( void ); virtual void MapShutdown( void );
virtual void CacheDictionaryMedia( const idDict *dict ); virtual void CacheDictionaryMedia( const idDict *dict );
virtual void SpawnPlayer( int clientNum ); 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 bool Draw( int clientNum );
virtual escReply_t HandleESC( idUserInterface **gui ); virtual escReply_t HandleESC( idUserInterface **gui );
virtual idUserInterface *StartMenu( void ); virtual idUserInterface *StartMenu( void );

View file

@ -33,6 +33,11 @@ If you have questions concerning this license or the applicable additional terms
#include "script/Script_Interpreter.h" #include "script/Script_Interpreter.h"
#include "framework/FileSystem.h"
// HvG: Debugger support
extern bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer );
/* /*
================ ================
idInterpreter::idInterpreter() idInterpreter::idInterpreter()
@ -183,7 +188,6 @@ idInterpreter::GetRegisterValue
Returns a string representation of the value of the register. This is Returns a string representation of the value of the register. This is
used primarily for the debugger and debugging 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 ) { 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; idVarDef *d;
char funcObject[ 1024 ]; char funcObject[ 1024 ];
char *funcName; char *funcName;
const idVarDef *scope; const idVarDef *scope = NULL;
const idVarDef *scopeObj;
const idTypeDef *field; const idTypeDef *field;
const idScriptObject *obj;
const function_t *func; const function_t *func;
out.Empty(); out.Empty();
if ( scopeDepth == -1 ) { if ( scopeDepth == -1 ) {
scopeDepth = callStackDepth; scopeDepth = callStackDepth;
} }
if ( scopeDepth == callStackDepth ) { if ( scopeDepth == callStackDepth ) {
func = currentFunction; func = currentFunction;
} else { } else {
@ -215,35 +219,44 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
funcName = strstr( funcObject, "::" ); funcName = strstr( funcObject, "::" );
if ( funcName ) { if ( funcName ) {
*funcName = '\0'; *funcName = '\0';
scope = gameLocal.program.GetDef( NULL, funcObject, &def_namespace ); scopeObj = gameLocal.program.GetDef( NULL, funcObject, &def_namespace );
funcName += 2; funcName += 2;
if ( scopeObj )
{
scope = gameLocal.program.GetDef( NULL, funcName, scopeObj );
}
} else { } else {
funcName = funcObject; funcName = funcObject;
scope = &def_namespace; scope = gameLocal.program.GetDef( NULL, func->Name(), &def_namespace );
scopeObj = NULL;
} }
// Get the function from the object if ( !scope )
d = gameLocal.program.GetDef( NULL, funcName, scope ); {
if ( !d ) {
return false; return false;
} }
// Get the variable itself and check various namespaces d = gameLocal.program.GetDef( NULL, name, scope );
d = gameLocal.program.GetDef( NULL, name, d );
if ( !d ) { // Check the objects for it if it wasnt local to the function
if ( scope == &def_namespace ) { if ( !d )
return false; {
} for ( ; scopeObj && scopeObj->TypeDef()->SuperClass(); scopeObj = scopeObj->TypeDef()->SuperClass()->def )
{
d = gameLocal.program.GetDef( NULL, name, scope ); d = gameLocal.program.GetDef( NULL, name, scopeObj );
if ( !d ) { if ( d )
d = gameLocal.program.GetDef( NULL, name, &def_namespace ); {
if ( !d ) { break;
return false;
} }
} }
} }
if ( !d )
{
out = "???";
return false;
}
reg = GetVariable( d ); reg = GetVariable( d );
switch( d->Type() ) { switch( d->Type() ) {
case ev_float: case ev_float:
@ -256,7 +269,7 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
break; break;
case ev_vector: case ev_vector:
if ( reg.vectorPtr ) { if ( reg.vectorPtr ) {
out = va( "%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z ); out = va( "%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z );
} else { } else {
out = "0,0,0"; out = "0,0,0";
@ -274,30 +287,55 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
break; break;
case ev_field: case ev_field:
{
idEntity* entity;
idScriptObject* obj;
if ( scope == &def_namespace ) { if ( scope == &def_namespace ) {
// should never happen, but handle it safely anyway // should never happen, but handle it safely anyway
return false; return false;
} }
field = scope->TypeDef()->GetParmType( reg.ptrOffset )->FieldType(); field = d->TypeDef()->FieldType();
obj = *reinterpret_cast<const idScriptObject **>( &localstack[ callStack[ callStackDepth ].stackbase ] ); entity = GetEntity ( *((int*)&localstack[ localstackBase ]) );
if ( !field || !obj ) { if ( !entity || !field )
{
return false; return false;
} }
obj = &entity->scriptObject;
if ( !obj ) {
return false;
}
switch ( field->Type() ) { switch ( field->Type() ) {
case ev_boolean: case ev_boolean:
out = va( "%d", *( reinterpret_cast<int *>( &obj->data[ reg.ptrOffset ] ) ) ); out = va( "%d", *( reinterpret_cast<int *>( &obj->data[ reg.ptrOffset ] ) ) );
return true; return true;
case ev_float: case ev_float:
out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) ); out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) );
return true; return true;
case ev_string: {
const char* str;
str = reinterpret_cast<const char*>( &obj->data[ reg.ptrOffset ] );
if ( !str ) {
out = "\"\"";
} else {
out = "\"";
out += str;
out += "\"";
}
return true;
}
default: default:
return false; return false;
} }
break; break;
}
case ev_string: case ev_string:
if ( reg.stringPtr ) { if ( reg.stringPtr ) {
@ -313,7 +351,6 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
return false; return false;
} }
} }
/* /*
================ ================
idInterpreter::GetCallstackDepth idInterpreter::GetCallstackDepth
@ -969,6 +1006,19 @@ bool idInterpreter::Execute( void ) {
// next statement // next statement
st = &gameLocal.program.GetStatement( instructionPointer ); 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 ) { switch( st->op ) {
case OP_RETURN: case OP_RETURN:
LeaveFunction( st->a ); LeaveFunction( st->a );
@ -1833,3 +1883,99 @@ bool idInterpreter::Execute( void ) {
return threadDying; 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<idProgram*>(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<idInterpreter*>(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("<UNKNOWN>");
msg->WriteString("<UNKNOWN>");
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<idProgram*>(program)->GetStatement(instructionPtr);
else // Use the calling statement as the filename and linenumber where the call was made from
st = &const_cast<idProgram*>(program)->GetStatement(stack->s);
if (st)
{
idStr qpath = const_cast<idProgram*>(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("<UNKNOWN>");
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;
}

View file

@ -1921,3 +1921,49 @@ void idThread::Event_InfluenceActive( void ) {
idThread::ReturnInt( false ); idThread::ReturnInt( false );
} }
} }
int idGameEditExt::ThreadGetNum(const idThread* thread) const
{
return const_cast<idThread*>(thread)->GetThreadNum();
}
const char* idGameEditExt::ThreadGetName(const idThread* thread) const
{
return const_cast<idThread*>(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<idThread*>(thread)->IsDoneProcessing();
}
bool idGameEditExt::ThreadIsWaiting(const idThread* thread) const
{
return const_cast<idThread*>(thread)->IsWaiting();
}
bool idGameEditExt::ThreadIsDying(const idThread* thread) const
{
return const_cast<idThread*>(thread)->IsDying();
}
void idGameEditExt::MSG_WriteThreadInfo(idBitMsg* msg, const idThread* thread, const idInterpreter* interpreter)
{
msg->WriteString(const_cast<idThread*>(thread)->GetThreadName());
msg->WriteInt(const_cast<idThread*>(thread)->GetThreadNum());
msg->WriteBits((int)(thread == interpreter->GetThread()), 1);
msg->WriteBits((int)const_cast<idThread*>(thread)->IsDoneProcessing(), 1);
msg->WriteBits((int)const_cast<idThread*>(thread)->IsWaiting(), 1);
msg->WriteBits((int)const_cast<idThread*>(thread)->IsDying(), 1);
}

View file

@ -244,7 +244,7 @@ void idInternalCVar::UpdateValue( void ) {
clamped = true; clamped = true;
} }
} }
if ( clamped || !idStr::IsNumeric( value ) || idStr::FindChar( value, '.' ) ) { if ( clamped || !idStr::IsNumeric( value ) || (idStr::FindChar( value, '.' )!=-1) ) {
valueString = idStr( integerValue ); valueString = idStr( integerValue );
value = valueString.c_str(); value = valueString.c_str();
} }

View file

@ -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_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_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." ); 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 // com_speeds times
@ -112,6 +116,8 @@ volatile int com_ticNumber; // 60 hz tics
int com_editors; // currently opened editor(s) int com_editors; // currently opened editor(s)
bool com_editorActive; // true if an editor has focus 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 #ifdef _WIN32
HWND com_hwndMsg = NULL; HWND com_hwndMsg = NULL;
bool com_outputMsg = false; bool com_outputMsg = false;
@ -245,6 +251,7 @@ idCommonLocal::idCommonLocal( void ) {
com_fullyInitialized = false; com_fullyInitialized = false;
com_refreshOnPrint = false; com_refreshOnPrint = false;
com_errorEntered = 0; com_errorEntered = 0;
com_debuggerSupported = false;
strcpy( errorMessage, "" ); strcpy( errorMessage, "" );
@ -382,12 +389,17 @@ void idCommonLocal::VPrintf( const char *fmt, va_list args ) {
// remove any color codes // remove any color codes
idStr::RemoveColors( msg ); idStr::RemoveColors( msg );
// echo to dedicated console and early console if ( com_enableDebuggerServer.GetBool( ) ) {
Sys_Printf( "%s", msg ); // print to script debugger server
if ( com_editors & EDITOR_DEBUGGER )
// print to script debugger server DebuggerServerPrint( msg );
// 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 0 // !@#
#if defined(_DEBUG) && defined(WIN32) #if defined(_DEBUG) && defined(WIN32)
if ( strlen( msg ) < 512 ) { if ( strlen( msg ) < 512 ) {
@ -1134,8 +1146,14 @@ Com_ScriptDebugger_f
static void Com_ScriptDebugger_f( const idCmdArgs &args ) { static void Com_ScriptDebugger_f( const idCmdArgs &args ) {
// Make sure it wasnt on the command line // Make sure it wasnt on the command line
if ( !( com_editors & EDITOR_DEBUGGER ) ) { 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); strCount += LocalizeMap(args.Argv(2), strTable, listHash, excludeList, write);
} else { } else {
idStrList files; idStrList files;
//wow, what now? a hardcoded path?
GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files); GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
for ( int i = 0; i < files.Num(); i++ ) { for ( int i = 0; i < files.Num(); i++ ) {
idStr file = fileSystem->OSPathToRelativePath(files[i]); idStr file = fileSystem->OSPathToRelativePath(files[i]);
@ -2398,6 +2417,14 @@ void idCommonLocal::Frame( void ) {
InitSIMD(); InitSIMD();
} }
if ( com_enableDebuggerServer.IsModified() ) {
if ( com_enableDebuggerServer.GetBool() ) {
DebuggerServerInit();
} else {
DebuggerServerShutdown();
}
}
eventLoop->RunEventLoop(); eventLoop->RunEventLoop();
com_frameTime = com_ticNumber * USERCMD_MSEC; com_frameTime = com_ticNumber * USERCMD_MSEC;
@ -2666,7 +2693,7 @@ void idCommonLocal::LoadGameDLL( void ) {
gameImport.AASFileManager = ::AASFileManager; gameImport.AASFileManager = ::AASFileManager;
gameImport.collisionModelManager = ::collisionModelManager; gameImport.collisionModelManager = ::collisionModelManager;
gameExport = *GetGameAPI( &gameImport ); gameExport = *GetGameAPI( &gameImport);
if ( gameExport.version != GAME_API_VERSION ) { if ( gameExport.version != GAME_API_VERSION ) {
Sys_DLL_Unload( gameDLL ); Sys_DLL_Unload( gameDLL );
@ -2709,6 +2736,7 @@ void idCommonLocal::UnloadGameDLL( void ) {
#endif #endif
com_debuggerSupported = false; // HvG: Reset debugger availability.
gameCallbacks.Reset(); // DG: these callbacks are invalid now because DLL has been unloaded 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 // initialize the user interfaces
uiManager->Init(); uiManager->Init();
// startup the script debugger
// DebuggerServerInit();
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04350" ) ); PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04350" ) );
// load the game dll // load the game dll
LoadGameDLL(); LoadGameDLL();
// startup the script debugger
if ( com_enableDebuggerServer.GetBool( ) )
DebuggerServerInit( );
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04351" ) ); PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04351" ) );
// init the session // init the session
@ -3214,7 +3243,8 @@ void idCommonLocal::ShutdownGame( bool reloading ) {
} }
// shutdown the script debugger // shutdown the script debugger
// DebuggerServerShutdown(); if ( com_enableDebuggerServer.GetBool() )
DebuggerServerShutdown();
idAsyncNetwork::client.Shutdown(); 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(); 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 // 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_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) // *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"); Warning("Called idCommon::GetAdditionalFunction() with out_fnptr == NULL!\n");
return false; return false;
} }
switch(ft) switch(ft)
{ {
case idCommon::FT_IsDemo: 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 // don't set *out_userArg, this function takes no arguments
return true; return true;
case idCommon::FT_UpdateDebugger:
*out_fnptr = (idCommon::FunctionPointer)updateDebugger;
com_debuggerSupported = true;
return true;
default: default:
*out_fnptr = NULL; *out_fnptr = NULL;
Warning("Called idCommon::SetCallback() with unknown FunctionType %d!\n", ft); 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 gameCallbacks;
idGameCallbacks::idGameCallbacks() idGameCallbacks::idGameCallbacks()

View file

@ -73,6 +73,9 @@ extern idCVar com_showAsyncStats;
extern idCVar com_showSoundDecoders; extern idCVar com_showSoundDecoders;
extern idCVar com_makingBuild; extern idCVar com_makingBuild;
extern idCVar com_updateLoadSize; 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_gameFrame; // game logic time
extern int time_gameDraw; // game present 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 int com_editors; // current active editor(s)
extern bool com_editorActive; // true if an editor has focus 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 #ifdef _WIN32
const char DMAP_MSGID[] = "DMAPOutput"; const char DMAP_MSGID[] = "DMAPOutput";
const char DMAP_DONE[] = "DMAPDone"; const char DMAP_DONE[] = "DMAPDone";
@ -111,6 +116,8 @@ struct MemInfo_t {
}; };
class idLangDict; class idLangDict;
class idInterpreter;
class idProgram;
class idCommon { class idCommon {
public: public:
@ -158,6 +165,7 @@ public:
// Writes cvars with the given flags to a file. // Writes cvars with the given flags to a file.
virtual void WriteFlaggedCVarsToFile( const char *filename, int flags, const char *setCmd ) = 0; virtual void WriteFlaggedCVarsToFile( const char *filename, int flags, const char *setCmd ) = 0;
// Begins redirection of console output to the given buffer. // Begins redirection of console output to the given buffer.
virtual void BeginRedirect( char *buffer, int buffersize, void (*flush)( const char * ) ) = 0; 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 // it returns true if we're currently running the doom3 demo
// not relevant for mods, only for game/ aka base.dll/base.so/... // not relevant for mods, only for game/ aka base.dll/base.so/...
FT_IsDemo = 1, 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 // returns true if that function is available in this version of dhewm3

View file

@ -886,10 +886,20 @@ const char *idFileSystemLocal::OSPathToRelativePath( const char *OSPath ) {
} }
if ( base ) { if ( base ) {
s = strstr( base, "/" ); // DG: on Windows base might look like "base\\pak008.pk4/script/doom_util.script"
if ( !s ) { // while on Linux it'll be more like "base/pak008.pk4/script/doom_util.script"
s = strstr( base, "\\" ); // 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 ) { if ( s ) {
strcpy( relativePath, s + 1 ); strcpy( relativePath, s + 1 );
if ( fs_debug.GetInteger() > 1 ) { if ( fs_debug.GetInteger() > 1 ) {

View file

@ -31,6 +31,7 @@ If you have questions concerning this license or the applicable additional terms
#include "idlib/BitMsg.h" #include "idlib/BitMsg.h"
#include "idlib/Dict.h" #include "idlib/Dict.h"
#include "idlib/containers/StrList.h"
#include "framework/UsercmdGen.h" #include "framework/UsercmdGen.h"
#include "renderer/RenderWorld.h" #include "renderer/RenderWorld.h"
#include "sound/sound.h" #include "sound/sound.h"
@ -232,6 +233,11 @@ enum {
class idEntity; class idEntity;
class idMD5Anim; 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 // FIXME: this interface needs to be reworked but it properly separates code for the time being
class idGameEdit { class idGameEdit {
@ -294,7 +300,7 @@ public:
virtual void EntitySetColor( idEntity *ent, const idVec3 color ); virtual void EntitySetColor( idEntity *ent, const idVec3 color );
// Player methods. // Player methods.
virtual bool PlayerIsValid() const; virtual bool PlayerIsValid( ) const;
virtual void PlayerGetOrigin( idVec3 &org ) const; virtual void PlayerGetOrigin( idVec3 &org ) const;
virtual void PlayerGetAxis( idMat3 &axis ) const; virtual void PlayerGetAxis( idMat3 &axis ) const;
virtual void PlayerGetViewAngles( idAngles &angles ) 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 int MapGetEntitiesMatchingClassWithString( const char *classname, const char *match, const char *list[], const int max ) const;
virtual void MapRemoveEntity( const char *name ) const; virtual void MapRemoveEntity( const char *name ) const;
virtual void MapEntityTranslate( const char *name, const idVec3 &v ) const; virtual void MapEntityTranslate( const char *name, const idVec3 &v ) const;
}; };
extern idGameEdit * gameEdit; 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 );
};
/* /*
=============================================================================== ===============================================================================

View file

@ -40,9 +40,8 @@ If you have questions concerning this license or the applicable additional terms
struct idGameCallbacks { struct idGameCallbacks {
typedef void (*ReloadImagesCallback)(void* userArg, const idCmdArgs &args); typedef void (*ReloadImagesCallback)(void* userArg, const idCmdArgs &args);
ReloadImagesCallback reloadImagesCB; ReloadImagesCallback reloadImagesCB;
void* reloadImagesUserArg; void* reloadImagesUserArg;
idGameCallbacks(); idGameCallbacks();

View file

@ -670,7 +670,7 @@ void idEditEntities::DisplayEntities( void ) {
=============================================================================== ===============================================================================
*/ */
idGameEdit gameEditLocal; idGameEditExt gameEditLocal;
idGameEdit * gameEdit = &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);
}

View file

@ -270,6 +270,14 @@ bool IsDoom3DemoVersion()
return ret; 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 // DG: hack to support the Demo version of Doom3
common->GetAdditionalFunction(idCommon::FT_IsDemo, (idCommon::FunctionPointer*)&isDemoFnPtr, NULL); 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 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->isServer = isServer;
this->isClient = isClient; this->isClient = isClient;
@ -2216,13 +2226,13 @@ idGameLocal::RunFrame
================ ================
*/ */
gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) { gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) {
idEntity * ent; idEntity * ent;
int num; int num;
float ms; float ms;
idTimer timer_think, timer_events, timer_singlethink; idTimer timer_think, timer_events, timer_singlethink;
gameReturn_t ret; gameReturn_t ret;
idPlayer *player; idPlayer *player;
const renderView_t *view; const renderView_t *view;
#ifdef _DEBUG #ifdef _DEBUG
if ( isMultiplayer ) { if ( isMultiplayer ) {

View file

@ -33,6 +33,11 @@ If you have questions concerning this license or the applicable additional terms
#include "script/Script_Interpreter.h" #include "script/Script_Interpreter.h"
#include "framework/FileSystem.h"
// HvG: Debugger support
extern bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer );
/* /*
================ ================
idInterpreter::idInterpreter() idInterpreter::idInterpreter()
@ -183,7 +188,6 @@ idInterpreter::GetRegisterValue
Returns a string representation of the value of the register. This is Returns a string representation of the value of the register. This is
used primarily for the debugger and debugging 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 ) { 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; idVarDef *d;
char funcObject[ 1024 ]; char funcObject[ 1024 ];
char *funcName; char *funcName;
const idVarDef *scope; const idVarDef *scope = NULL;
const idVarDef *scopeObj;
const idTypeDef *field; const idTypeDef *field;
const idScriptObject *obj;
const function_t *func; const function_t *func;
out.Empty(); out.Empty();
if ( scopeDepth == -1 ) { if ( scopeDepth == -1 ) {
scopeDepth = callStackDepth; scopeDepth = callStackDepth;
} }
if ( scopeDepth == callStackDepth ) { if ( scopeDepth == callStackDepth ) {
func = currentFunction; func = currentFunction;
} else { } else {
@ -215,35 +219,44 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
funcName = strstr( funcObject, "::" ); funcName = strstr( funcObject, "::" );
if ( funcName ) { if ( funcName ) {
*funcName = '\0'; *funcName = '\0';
scope = gameLocal.program.GetDef( NULL, funcObject, &def_namespace ); scopeObj = gameLocal.program.GetDef( NULL, funcObject, &def_namespace );
funcName += 2; funcName += 2;
if ( scopeObj )
{
scope = gameLocal.program.GetDef( NULL, funcName, scopeObj );
}
} else { } else {
funcName = funcObject; funcName = funcObject;
scope = &def_namespace; scope = gameLocal.program.GetDef( NULL, func->Name(), &def_namespace );
scopeObj = NULL;
} }
// Get the function from the object if ( !scope )
d = gameLocal.program.GetDef( NULL, funcName, scope ); {
if ( !d ) {
return false; return false;
} }
// Get the variable itself and check various namespaces d = gameLocal.program.GetDef( NULL, name, scope );
d = gameLocal.program.GetDef( NULL, name, d );
if ( !d ) { // Check the objects for it if it wasnt local to the function
if ( scope == &def_namespace ) { if ( !d )
return false; {
} for ( ; scopeObj && scopeObj->TypeDef()->SuperClass(); scopeObj = scopeObj->TypeDef()->SuperClass()->def )
{
d = gameLocal.program.GetDef( NULL, name, scope ); d = gameLocal.program.GetDef( NULL, name, scopeObj );
if ( !d ) { if ( d )
d = gameLocal.program.GetDef( NULL, name, &def_namespace ); {
if ( !d ) { break;
return false;
} }
} }
} }
if ( !d )
{
out = "???";
return false;
}
reg = GetVariable( d ); reg = GetVariable( d );
switch( d->Type() ) { switch( d->Type() ) {
case ev_float: case ev_float:
@ -256,7 +269,7 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
break; break;
case ev_vector: case ev_vector:
if ( reg.vectorPtr ) { if ( reg.vectorPtr ) {
out = va( "%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z ); out = va( "%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z );
} else { } else {
out = "0,0,0"; out = "0,0,0";
@ -274,30 +287,55 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
break; break;
case ev_field: case ev_field:
{
idEntity* entity;
idScriptObject* obj;
if ( scope == &def_namespace ) { if ( scope == &def_namespace ) {
// should never happen, but handle it safely anyway // should never happen, but handle it safely anyway
return false; return false;
} }
field = scope->TypeDef()->GetParmType( reg.ptrOffset )->FieldType(); field = d->TypeDef()->FieldType();
obj = *reinterpret_cast<const idScriptObject **>( &localstack[ callStack[ callStackDepth ].stackbase ] ); entity = GetEntity ( *((int*)&localstack[ localstackBase ]) );
if ( !field || !obj ) { if ( !entity || !field )
{
return false; return false;
} }
obj = &entity->scriptObject;
if ( !obj ) {
return false;
}
switch ( field->Type() ) { switch ( field->Type() ) {
case ev_boolean: case ev_boolean:
out = va( "%d", *( reinterpret_cast<int *>( &obj->data[ reg.ptrOffset ] ) ) ); out = va( "%d", *( reinterpret_cast<int *>( &obj->data[ reg.ptrOffset ] ) ) );
return true; return true;
case ev_float: case ev_float:
out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) ); out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) );
return true; return true;
case ev_string: {
const char* str;
str = reinterpret_cast<const char*>( &obj->data[ reg.ptrOffset ] );
if ( !str ) {
out = "\"\"";
} else {
out = "\"";
out += str;
out += "\"";
}
return true;
}
default: default:
return false; return false;
} }
break; break;
}
case ev_string: case ev_string:
if ( reg.stringPtr ) { if ( reg.stringPtr ) {
@ -969,6 +1007,19 @@ bool idInterpreter::Execute( void ) {
// next statement // next statement
st = &gameLocal.program.GetStatement( instructionPointer ); 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 ) { switch( st->op ) {
case OP_RETURN: case OP_RETURN:
LeaveFunction( st->a ); LeaveFunction( st->a );
@ -1833,3 +1884,98 @@ bool idInterpreter::Execute( void ) {
return threadDying; 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<idProgram*>(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<idInterpreter*>(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 ( "<UNKNOWN>" );
msg->WriteString ( "<UNKNOWN>" );
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<idProgram*>( program )->GetStatement( instructionPtr );
else // Use the calling statement as the filename and linenumber where the call was made from
st = &const_cast<idProgram*>( program )->GetStatement ( stack->s );
if ( st )
{
idStr qpath = const_cast<idProgram*>( 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 ( "<UNKNOWN>" );
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;
}

View file

@ -28,11 +28,11 @@ If you have questions concerning this license or the applicable additional terms
#include "sys/platform.h" #include "sys/platform.h"
#include "gamesys/SysCvar.h" #include "game/gamesys/SysCvar.h"
#include "Player.h" #include "game/Player.h"
#include "Camera.h" #include "game/Camera.h"
#include "script/Script_Thread.h" #include "Script_Thread.h"
const idEventDef EV_Thread_Execute( "<execute>", NULL ); const idEventDef EV_Thread_Execute( "<execute>", NULL );
const idEventDef EV_Thread_SetCallback( "<script_setcallback>", NULL ); const idEventDef EV_Thread_SetCallback( "<script_setcallback>", NULL );
@ -1841,3 +1841,49 @@ void idThread::Event_InfluenceActive( void ) {
idThread::ReturnInt( false ); idThread::ReturnInt( false );
} }
} }
int idGameEditExt::ThreadGetNum(const idThread* thread) const
{
return const_cast<idThread*>(thread)->GetThreadNum();
}
const char*idGameEditExt::ThreadGetName(const idThread* thread) const
{
return const_cast<idThread*>(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<idThread*>(thread)->IsDoneProcessing();
}
bool idGameEditExt::ThreadIsWaiting(const idThread* thread) const
{
return const_cast<idThread*>(thread)->IsWaiting();
}
bool idGameEditExt::ThreadIsDying(const idThread* thread) const
{
return const_cast<idThread*>(thread)->IsDying();
}
void idGameEditExt::MSG_WriteThreadInfo(idBitMsg* msg, const idThread* thread, const idInterpreter* interpreter)
{
msg->WriteString(const_cast<idThread*>(thread)->GetThreadName());
msg->WriteInt(const_cast<idThread*>(thread)->GetThreadNum());
msg->WriteBits((int)(thread == interpreter->GetThread()), 1);
msg->WriteBits((int)const_cast<idThread*>(thread)->IsDoneProcessing(), 1);
msg->WriteBits((int)const_cast<idThread*>(thread)->IsWaiting(), 1);
msg->WriteBits((int)const_cast<idThread*>(thread)->IsDying(), 1);
}

View file

@ -85,7 +85,7 @@ public:
void WriteShort( int c ); void WriteShort( int c );
void WriteUShort( int c ); void WriteUShort( int c );
void WriteInt( int c ); void WriteInt( int c );
void WriteFloat( float f ); void WriteFloat( float f );
void WriteFloat( float f, int exponentBits, int mantissaBits ); void WriteFloat( float f, int exponentBits, int mantissaBits );
void WriteAngle8( float f ); void WriteAngle8( float f );
void WriteAngle16( float f ); void WriteAngle16( float f );

View file

@ -2169,6 +2169,8 @@ idRenderSystemLocal::Shutdown
void idRenderSystemLocal::Shutdown( void ) { void idRenderSystemLocal::Shutdown( void ) {
common->Printf( "idRenderSystem::Shutdown()\n" ); common->Printf( "idRenderSystem::Shutdown()\n" );
common->SetRefreshOnPrint( false ); // without a renderer there's nothing to refresh
R_DoneFreeType( ); R_DoneFreeType( );
if ( glConfig.isInitialized ) { if ( glConfig.isInitialized ) {

View file

@ -126,21 +126,21 @@ IDI_DBG_EMPTY ICON "res\\dbg_empty.ico"
IDR_DBG_ACCELERATORS ACCELERATORS IDR_DBG_ACCELERATORS ACCELERATORS
BEGIN BEGIN
VK_F9, ID_DBG_DEBUG_QUICKWATCH, VIRTKEY, SHIFT, NOINVERT VK_F9, ID_DBG_DEBUG_QUICKWATCH, VIRTKEY, SHIFT, NOINVERT
VK_F5, ID_DBG_DEBUG_RUN, VIRTKEY, NOINVERT VK_F5, ID_DBG_DEBUG_RUN, VIRTKEY, NOINVERT
VK_F11, ID_DBG_DEBUG_STEPINTO, VIRTKEY, NOINVERT VK_F11, ID_DBG_DEBUG_STEPINTO, VIRTKEY, NOINVERT
VK_F10, ID_DBG_DEBUG_STEPOVER, VIRTKEY, NOINVERT VK_F10, ID_DBG_DEBUG_STEPOVER, VIRTKEY, NOINVERT
VK_F9, ID_DBG_DEBUG_TOGGLEBREAKPOINT, VIRTKEY, NOINVERT VK_F9, ID_DBG_DEBUG_TOGGLEBREAKPOINT, VIRTKEY, NOINVERT
VK_F3, ID_DBG_EDIT_FINDNEXT, VIRTKEY, NOINVERT VK_F3, ID_DBG_EDIT_FINDNEXT, VIRTKEY, NOINVERT
VK_F3, ID_DBG_EDIT_FINDPREV, VIRTKEY, SHIFT, 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_FINDSELECTED, VIRTKEY, CONTROL, NOINVERT VK_F3, ID_DBG_EDIT_FINDSELECTEDPREV, VIRTKEY, SHIFT, CONTROL,NOINVERT
VK_F3, ID_DBG_EDIT_FINDSELECTEDPREV, VIRTKEY, SHIFT, CONTROL, VK_F4, ID_DBG_FILE_CLOSE, VIRTKEY, CONTROL, NOINVERT
NOINVERT VK_TAB, ID_DBG_FILE_NEXT, VIRTKEY, CONTROL, NOINVERT
VK_F4, ID_DBG_FILE_CLOSE, VIRTKEY, CONTROL, NOINVERT VK_RETURN, ID_DBG_SEND_COMMAND, VIRTKEY, NOINVERT
VK_TAB, ID_DBG_FILE_NEXT, VIRTKEY, CONTROL, NOINVERT "O", ID_DBG_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
"O", ID_DBG_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT "F", ID_DBG_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT
"F", ID_DBG_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT
END END
@ -149,17 +149,17 @@ END
// Dialog // 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 | STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP |
WS_CAPTION | WS_SYSMENU WS_CAPTION | WS_SYSMENU
CAPTION "About Script Debugger" CAPTION "About Script Debugger"
FONT 8, "MS Shell Dlg", 400, 0, 0x1 FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN BEGIN
LTEXT "Script Debugger",IDC_STATIC,35,7,81,8 LTEXT "Script Debugger",IDC_STATIC,35,7,81,8
LTEXT "Version 0.01",IDC_STATIC,35,17,41,8 LTEXT "Version 1.1",IDC_STATIC,35,17,41,8
LTEXT "Original version by Raven",IDC_STATIC,35, LTEXT "Original version by Raven", IDC_STATIC, 35,28, 134, 8
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,165,50,50,14 DEFPUSHBUTTON "OK",IDOK,100,68,50,14
ICON 5098,IDC_STATIC,7,7,20,20 ICON 5098,IDC_STATIC,7,7,20,20
END END

View file

@ -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_FINDPREV 22022
#define ID_DBG_EDIT_FINDSELECTEDPREV 22023 #define ID_DBG_EDIT_FINDSELECTEDPREV 22023
#define ID_DBG_HELP_ABOUT 22024 #define ID_DBG_HELP_ABOUT 22024
#define ID_DBG_SEND_COMMAND 22025
// Next default values for new objects // Next default values for new objects
// //

View file

@ -269,8 +269,7 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN BEGIN
LTEXT "GUI Editor",IDC_STATIC,35,7,62,8 LTEXT "GUI Editor",IDC_STATIC,35,7,62,8
LTEXT "Version 0.15",IDC_STATIC,35,17,41,8 LTEXT "Version 0.15",IDC_STATIC,35,17,41,8
LTEXT "Original version by Raven",IDC_STATIC,35, LTEXT "Original version by Raven", IDC_STATIC, 35,28, 134, 8
28,134,8
DEFPUSHBUTTON "OK",IDOK,165,50,50,14 DEFPUSHBUTTON "OK",IDOK,165,50,50,14
ICON IDI_GUIED,IDC_STATIC,7,7,20,20 ICON IDI_GUIED,IDC_STATIC,7,7,20,20
END END

View file

@ -673,7 +673,30 @@ Sys_DLL_GetProcAddress
===================== =====================
*/ */
void *Sys_DLL_GetProcAddress( uintptr_t dllHandle, const char *procName ) { 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 // Launch the script debugger
if ( strstr( GetCommandLine(), "+debugger" ) ) { if ( strstr( GetCommandLine(), "+debugger" ) ) {
// DebuggerClientInit( lpCmdLine );
#ifdef ID_ALLOW_TOOLS
DebuggerClientInit(GetCommandLine());
#endif
return 0; return 0;
} }

View file

@ -392,7 +392,8 @@ void rvOpenFileDialog::SetFilter ( const char* s )
if ( semi != -1 ) if ( semi != -1 )
{ {
filter = filters.Left ( semi ); filter = filters.Left ( semi );
filters = filters.Right ( filters.Length ( ) - semi ); filters = filters.Right ( filters.Length ( ) - (semi + 1));
filters.Strip(' ');
} }
else else
{ {

View file

@ -37,8 +37,8 @@ If you have questions concerning this license or the applicable additional terms
rvDebuggerApp::rvDebuggerApp rvDebuggerApp::rvDebuggerApp
================ ================
*/ */
rvDebuggerApp::rvDebuggerApp ( ) : rvDebuggerApp::rvDebuggerApp ( ) //:
mOptions ( "Software\\id Software\\DOOM3\\Tools\\Debugger" ) //mOptions ( "Software\\id Software\\DOOM3\\Tools\\Debugger" )
{ {
mInstance = NULL; mInstance = NULL;
mDebuggerWindow = NULL; mDebuggerWindow = NULL;

View file

@ -29,7 +29,7 @@ If you have questions concerning this license or the applicable additional terms
#define DEBUGGERAPP_H_ #define DEBUGGERAPP_H_
#include "../../sys/win32/win_local.h" #include "../../sys/win32/win_local.h"
#include "../../framework/sync/Msg.h" //#include "../../framework/sync/Msg.h"
#ifndef REGISTRYOPTIONS_H_ #ifndef REGISTRYOPTIONS_H_
#include "../common/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 // These were changed to static by ID so to make it easy we just throw them
// in this header // 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 class rvDebuggerApp
{ {
public: public:
rvDebuggerApp ( ); rvDebuggerApp ( );
~rvDebuggerApp();
bool Initialize ( HINSTANCE hInstance ); bool Initialize ( HINSTANCE hInstance );
int Run ( void ); int Run ( void );

View file

@ -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 "tools/edit_gui_common.h"
#include "DebuggerApp.h" #include "DebuggerApp.h"
#else
#include "debugger_common.h"
#endif
#include "DebuggerBreakpoint.h" #include "DebuggerBreakpoint.h"
int rvDebuggerBreakpoint::mNextID = 1; 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; mFilename = filename;
mLineNumber = linenumber; mLineNumber = linenumber;
mEnabled = true; mEnabled = true;
mOnceOnly = onceOnly;
if ( id == -1 ) if ( id == -1 )
{ {

View file

@ -28,25 +28,28 @@ If you have questions concerning this license or the applicable additional terms
#ifndef DEBUGGERBREAKPOINT_H_ #ifndef DEBUGGERBREAKPOINT_H_
#define DEBUGGERBREAKPOINT_H_ #define DEBUGGERBREAKPOINT_H_
class idProgram;
class rvDebuggerBreakpoint class rvDebuggerBreakpoint
{ {
public: 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 ( rvDebuggerBreakpoint& bp );
~rvDebuggerBreakpoint ( void ); ~rvDebuggerBreakpoint ( void );
const char* GetFilename ( void ); const char* GetFilename ( void );
int GetLineNumber ( void ); int GetLineNumber ( void );
int GetID ( void ); int GetID ( void );
bool GetOnceOnly ( void );
protected: protected:
bool mEnabled; bool mEnabled;
bool mOnceOnly;
int mID; int mID;
int mLineNumber; int mLineNumber;
idStr mFilename; idStr mFilename;
private: private:
static int mNextID; static int mNextID;
@ -67,4 +70,9 @@ ID_INLINE int rvDebuggerBreakpoint::GetID ( void )
return mID; return mID;
} }
ID_INLINE bool rvDebuggerBreakpoint::GetOnceOnly( void )
{
return mOnceOnly;
}
#endif // DEBUGGERBREAKPOINT_H_ #endif // DEBUGGERBREAKPOINT_H_

View file

@ -73,7 +73,7 @@ bool rvDebuggerClient::Initialize ( void )
} }
// Server must be running on the local host on port 28980 // 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; mServerAdr.port = 27980;
// Attempt to let the server know we are here. The server may not be running so this // 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 ) bool rvDebuggerClient::ProcessMessages ( void )
{ {
netadr_t adrFrom; netadr_t adrFrom;
msg_t msg; idBitMsg msg;
byte buffer[MAX_MSGLEN]; 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 // 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 // Only accept packets from the debugger server for security reasons
if ( !Sys_CompareNetAdrBase ( adrFrom, mServerAdr ) ) if ( !Sys_CompareNetAdrBase ( adrFrom, mServerAdr ) )
{ {
continue; 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 ) if ( command == mWaitFor )
{ {
mWaitFor = DBMSG_UNKNOWN; mWaitFor = DBMSG_UNKNOWN;
@ -168,17 +172,39 @@ bool rvDebuggerClient::ProcessMessages ( void )
case DBMSG_INSPECTVARIABLE: case DBMSG_INSPECTVARIABLE:
HandleInspectVariable ( &msg ); HandleInspectVariable ( &msg );
break; break;
case DBMSG_REMOVEBREAKPOINT:
HandleRemoveBreakpoint( &msg );
break;
case DBMSG_INSPECTSCRIPTS:
HandleInspectScripts( &msg );
break;
} }
// Give the window a chance to process the message // Give the window a chance to process the message
msg.readcount = 0; msg.SetReadCount(0);
msg.bit = 0; msg.SetReadBit(0);
gDebuggerApp.GetWindow().ProcessNetMessage ( &msg ); gDebuggerApp.GetWindow().ProcessNetMessage ( &msg );
} }
return true; 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 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. 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]; char filename[MAX_PATH];
mBreak = true; mBreak = true;
// Line number // Line number
mBreakLineNumber = MSG_ReadInt ( msg ); mBreakLineNumber = msg->ReadInt ( );
// Filename // Filename
MSG_ReadString ( msg, filename, MAX_PATH ); msg->ReadString ( filename, MAX_PATH );
mBreakFilename = filename; mBreakFilename = filename;
//int64_t ptr64b = msg->ReadInt64();
//mBreakProgram = (idProgram*)ptr64b;
// Clear the variables // Clear the variables
mVariables.Clear ( ); mVariables.Clear ( );
@ -211,6 +240,26 @@ void rvDebuggerClient::HandleBreak ( msg_t* msg )
WaitFor ( DBMSG_INSPECTTHREADS, 2000 ); 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 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 ) void rvDebuggerClient::InspectVariable ( const char* name, int callstackDepth )
{ {
msg_t msg; idBitMsg msg;
byte buffer[MAX_MSGLEN]; byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) ); msg.Init( buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_INSPECTVARIABLE ); msg.BeginWriting();
MSG_WriteShort ( &msg, (short)(mCallstack.Num()-callstackDepth) ); msg.WriteShort ( (short)DBMSG_INSPECTVARIABLE );
MSG_WriteString ( &msg, name ); 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. 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; int depth;
ClearCallstack ( ); ClearCallstack ( );
// Read all of the callstack entries specfied in the message // 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; rvDebuggerCallstack* entry = new rvDebuggerCallstack;
char temp[1024]; char temp[1024];
// Function name // Function name
MSG_ReadString ( msg, temp, 1024 ); msg->ReadString ( temp, 1024 );
entry->mFunction = temp; entry->mFunction = idStr(temp);
// Filename // Filename
MSG_ReadString ( msg, temp, 1024 ); msg->ReadString ( temp, 1024 );
entry->mFilename = temp; entry->mFilename = idStr(temp);
// Line Number // Line Number
entry->mLineNumber = MSG_ReadInt ( msg ); entry->mLineNumber = msg->ReadInt ( );
// Add to list // Add to list
mCallstack.Append ( entry ); 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. 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; int count;
ClearThreads ( ); ClearThreads ( );
// Loop over the number of threads in the message // 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; rvDebuggerThread* entry = new rvDebuggerThread;
char temp[1024]; char temp[1024];
// Thread name // Thread name
MSG_ReadString ( msg, temp, 1024 ); msg->ReadString ( temp, 1024 );
entry->mName = temp; entry->mName = temp;
// Thread ID // Thread ID
entry->mID = MSG_ReadInt ( msg ); entry->mID = msg->ReadInt ( );
// Thread state // Thread state
entry->mCurrent = MSG_ReadBits ( msg, 1 ) ? true : false; entry->mCurrent = msg->ReadBits ( 1 ) ? true : false;
entry->mDoneProcessing = MSG_ReadBits ( msg, 1 ) ? true : false; entry->mDoneProcessing = msg->ReadBits ( 1 ) ? true : false;
entry->mWaiting = MSG_ReadBits ( msg, 1 ) ? true : false; entry->mWaiting = msg->ReadBits ( 1 ) ? true : false;
entry->mDying = MSG_ReadBits ( msg, 1 ) ? true : false; entry->mDying = msg->ReadBits ( 1 ) ? true : false;
// Add thread to list // Add thread to list
mThreads.Append ( entry ); 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 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 var[1024];
char value[1024]; char value[1024];
int callDepth; int callDepth;
callDepth = (short)MSG_ReadShort ( msg ); callDepth = (short)msg->ReadShort ( );
MSG_ReadString ( msg, var, 1024 ); msg->ReadString ( var, 1024 );
MSG_ReadString ( msg, value, 1024 ); msg->ReadString ( value, 1024 );
mVariables.Set ( va("%d:%s", mCallstack.Num()-callDepth, var), value ); 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 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] ); SendAddBreakpoint ( *mBreakpoints[index] );
@ -463,13 +538,14 @@ Send a message with no data to the debugger server
*/ */
void rvDebuggerClient::SendMessage ( EDebuggerMessage dbmsg ) void rvDebuggerClient::SendMessage ( EDebuggerMessage dbmsg )
{ {
msg_t msg; idBitMsg msg;
byte buffer[MAX_MSGLEN]; byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) ); msg.Init ( buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)dbmsg ); 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 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]; byte buffer[MAX_MSGLEN];
if ( !mConnected ) if ( !mConnected )
@ -512,14 +588,15 @@ void rvDebuggerClient::SendAddBreakpoint ( rvDebuggerBreakpoint& bp, bool onceOn
return; return;
} }
MSG_Init( &msg, buffer, sizeof( buffer ) ); msg.Init( buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_ADDBREAKPOINT ); msg.BeginWriting();
MSG_WriteBits ( &msg, onceOnly?1:0, 1 ); msg.WriteShort ( (short)DBMSG_ADDBREAKPOINT );
MSG_WriteInt ( &msg, (unsigned long) bp.GetLineNumber ( ) ); msg.WriteBits ( bp.GetOnceOnly() ? 1 : 0, 1 );
MSG_WriteInt ( &msg, bp.GetID ( ) ); msg.WriteInt ( (unsigned long) bp.GetLineNumber ( ) );
MSG_WriteString ( &msg, bp.GetFilename() ); 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 ) void rvDebuggerClient::SendRemoveBreakpoint ( rvDebuggerBreakpoint& bp )
{ {
msg_t msg; idBitMsg msg;
byte buffer[MAX_MSGLEN]; byte buffer[MAX_MSGLEN];
if ( !mConnected ) if ( !mConnected )
@ -539,11 +616,12 @@ void rvDebuggerClient::SendRemoveBreakpoint ( rvDebuggerBreakpoint& bp )
return; return;
} }
MSG_Init( &msg, buffer, sizeof( buffer ) ); msg.Init ( buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_REMOVEBREAKPOINT ); msg.BeginWriting( );
MSG_WriteInt ( &msg, bp.GetID() ); 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 ( ); 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( ) );
}

View file

@ -28,6 +28,9 @@ If you have questions concerning this license or the applicable additional terms
#ifndef DEBUGGERCLIENT_H_ #ifndef DEBUGGERCLIENT_H_
#define DEBUGGERCLIENT_H_ #define DEBUGGERCLIENT_H_
#include "DebuggerBreakpoint.h"
#include "idlib/containers/StrList.h"
class rvDebuggerCallstack class rvDebuggerCallstack
{ {
public: public:
@ -49,9 +52,6 @@ public:
bool mDoneProcessing; bool mDoneProcessing;
}; };
#ifndef DEBUGGERBREAKPOINT_H_
#include "DebuggerBreakpoint.h"
#endif
typedef idList<rvDebuggerCallstack*> rvDebuggerCallstackList; typedef idList<rvDebuggerCallstack*> rvDebuggerCallstackList;
typedef idList<rvDebuggerThread*> rvDebuggerThreadList; typedef idList<rvDebuggerThread*> rvDebuggerThreadList;
@ -75,19 +75,23 @@ public:
int GetActiveBreakpointID ( void ); int GetActiveBreakpointID ( void );
const char* GetBreakFilename ( void ); const char* GetBreakFilename ( void );
int GetBreakLineNumber ( void ); int GetBreakLineNumber ( void );
idProgram* GetBreakProgram ( void );
rvDebuggerCallstackList& GetCallstack ( void ); rvDebuggerCallstackList& GetCallstack ( void );
rvDebuggerThreadList& GetThreads ( void ); rvDebuggerThreadList& GetThreads ( void );
const char* GetVariableValue ( const char* name, int stackDepth ); const char* GetVariableValue ( const char* name, int stackDepth );
idStrList& GetServerScripts ( void );
void InspectVariable ( const char* name, int callstackDepth ); void InspectVariable ( const char* name, int callstackDepth );
void InspectScripts ( void );
void Break ( void ); void Break ( void );
void Resume ( void ); void Resume ( void );
void StepInto ( void ); void StepInto ( void );
void StepOver ( void ); void StepOver ( void );
void SendCommand ( const char* cmdStr );
// Breakpoints // 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 ); bool RemoveBreakpoint ( int bpID );
void ClearBreakpoints ( void ); void ClearBreakpoints ( void );
int GetBreakpointCount ( void ); int GetBreakpointCount ( void );
@ -98,7 +102,7 @@ protected:
void SendMessage ( EDebuggerMessage dbmsg ); void SendMessage ( EDebuggerMessage dbmsg );
void SendBreakpoints ( void ); void SendBreakpoints ( void );
void SendAddBreakpoint ( rvDebuggerBreakpoint& bp, bool onceOnly = false ); void SendAddBreakpoint ( rvDebuggerBreakpoint& bp );
void SendRemoveBreakpoint ( rvDebuggerBreakpoint& bp ); void SendRemoveBreakpoint ( rvDebuggerBreakpoint& bp );
void SendPacket ( void* data, int datasize ); void SendPacket ( void* data, int datasize );
@ -119,6 +123,8 @@ protected:
EDebuggerMessage mWaitFor; EDebuggerMessage mWaitFor;
idStrList mServerScripts;
private: private:
void ClearCallstack ( void ); void ClearCallstack ( void );
@ -127,10 +133,13 @@ private:
void UpdateWatches ( void ); void UpdateWatches ( void );
// Network message handlers // Network message handlers
void HandleBreak ( msg_t* msg ); void HandleBreak ( idBitMsg* msg );
void HandleInspectCallstack ( msg_t* msg ); void HandleInspectScripts ( idBitMsg* msg );
void HandleInspectThreads ( msg_t* msg ); void HandleInspectCallstack ( idBitMsg* msg );
void HandleInspectVariable ( msg_t* 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 ); mPort.SendPacket ( mServerAdr, data, size );
} }
/*
================
rvDebuggerClient::GetServerScripts
================
*/
ID_INLINE idStrList& rvDebuggerClient::GetServerScripts( void )
{
return mServerScripts;
}
#endif // DEBUGGERCLIENT_H_ #endif // DEBUGGERCLIENT_H_

View file

@ -53,7 +53,7 @@ Launch the dialog
*/ */
bool rvDebuggerFindDlg::DoModal ( rvDebuggerWindow* parent ) 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; 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 ) 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 ) switch ( msg )
{ {
@ -80,7 +80,8 @@ INT_PTR CALLBACK rvDebuggerFindDlg::DlgProc ( HWND wnd, UINT msg, WPARAM wparam,
case WM_INITDIALOG: case WM_INITDIALOG:
dlg = (rvDebuggerFindDlg*) lparam; dlg = (rvDebuggerFindDlg*) lparam;
SetWindowLong ( wnd, GWL_USERDATA, (LONG) dlg );
SetWindowLongPtr ( wnd, GWLP_USERDATA, (LONG_PTR) dlg );
dlg->mWnd = wnd; dlg->mWnd = wnd;
SetWindowText ( GetDlgItem ( dlg->mWnd, IDC_DBG_FIND ), dlg->mFindText ); SetWindowText ( GetDlgItem ( dlg->mWnd, IDC_DBG_FIND ), dlg->mFindText );
return TRUE; return TRUE;

View file

@ -46,6 +46,8 @@ enum EDebuggerMessage
DBMSG_INSPECTTHREADS, DBMSG_INSPECTTHREADS,
DBMSG_STEPOVER, DBMSG_STEPOVER,
DBMSG_STEPINTO, DBMSG_STEPINTO,
DBMSG_INSPECTSCRIPTS,
DBMSG_EXECCOMMAND
}; };
#endif // DEBUGGER_MESSAGES_H_ #endif // DEBUGGER_MESSAGES_H_

View file

@ -55,7 +55,7 @@ bool rvDebuggerQuickWatchDlg::DoModal ( rvDebuggerWindow* window, int callstackD
mDebuggerWindow = window; mDebuggerWindow = window;
mVariable = variable?variable:""; 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; 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 ) 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 ) 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 // Attach the dialog class pointer to the window
dlg = (rvDebuggerQuickWatchDlg*) lparam; dlg = (rvDebuggerQuickWatchDlg*) lparam;
SetWindowLong ( wnd, GWL_USERDATA, lparam ); SetWindowLongPtr ( wnd, GWLP_USERDATA, lparam );
dlg->mWnd = wnd; dlg->mWnd = wnd;
GetClientRect ( wnd, &client ); GetClientRect ( wnd, &client );

View file

@ -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 "tools/edit_gui_common.h"
#include "DebuggerApp.h" #include "DebuggerApp.h"
#else
#include "debugger_common.h"
#endif
#include "DebuggerScript.h" #include "DebuggerScript.h"
#include "../../game/script/Script_Program.h"
#include "../../ui/Window.h" #include "../../ui/Window.h"
#include "../../ui/UserInterfaceLocal.h" #include "../../ui/UserInterfaceLocal.h"
@ -57,6 +59,7 @@ rvDebuggerScript::~rvDebuggerScript ( void )
Unload ( ); Unload ( );
} }
/* /*
================ ================
rvDebuggerScript::Unload rvDebuggerScript::Unload
@ -72,10 +75,6 @@ void rvDebuggerScript::Unload ( void )
{ {
delete mInterface; delete mInterface;
} }
else
{
delete mProgram;
}
mContents = NULL; mContents = NULL;
mProgram = NULL; mProgram = NULL;
@ -116,60 +115,7 @@ bool rvDebuggerScript::Load ( const char* filename )
// Cleanup // Cleanup
fileSystem->FreeFile ( buffer ); 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; 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 ) bool rvDebuggerScript::IsLineCode ( int linenumber )
{ {
int i; //we let server decide.
return true;
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;
} }
/* /*

View file

@ -43,21 +43,22 @@ public:
const char* GetFilename ( void ); const char* GetFilename ( void );
const char* GetContents ( void ); const char* GetContents ( void );
idProgram* GetProgram ( void );
#if 0// Test code
idProgram& GetProgram ( void ); idProgram& GetProgram ( void );
#endif
bool IsLineCode ( int linenumber ); bool IsLineCode ( int linenumber );
bool IsFileModified ( bool updateTime = false ); bool IsFileModified ( bool updateTime = false );
protected: protected:
void Unload ( void ); void Unload ( void );
idProgram* mProgram; idProgram* mProgram;
idUserInterfaceLocal* mInterface; idUserInterfaceLocal* mInterface;
char* mContents; char* mContents;
idStr mFilename; idStr mFilename;
ID_TIME_T mModifiedTime; ID_TIME_T mModifiedTime;
}; };
ID_INLINE const char* rvDebuggerScript::GetFilename ( void ) ID_INLINE const char* rvDebuggerScript::GetFilename ( void )
@ -70,9 +71,10 @@ ID_INLINE const char* rvDebuggerScript::GetContents ( void )
return mContents?mContents:""; return mContents?mContents:"";
} }
ID_INLINE idProgram& rvDebuggerScript::GetProgram ( void ) ID_INLINE idProgram* rvDebuggerScript::GetProgram ( void )
{ {
return *mProgram; return mProgram;
} }
#endif // DEBUGGERSCRIPT_H_ #endif // DEBUGGERSCRIPT_H_

View file

@ -3,6 +3,8 @@
Doom 3 GPL Source Code Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 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"). 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 "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" #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" #include "DebuggerServer.h"
/* /*
@ -51,10 +52,17 @@ rvDebuggerServer::rvDebuggerServer ( )
mBreak = false; mBreak = false;
mBreakStepOver = false; mBreakStepOver = false;
mBreakStepInto = false; mBreakStepInto = false;
mGameThread = NULL; mGameThreadBreakCond = NULL;
mGameThreadBreakLock = NULL;
mLastStatementLine = -1; mLastStatementLine = -1;
mBreakStepOverFunc1 = NULL; mBreakStepOverFunc1 = NULL;
mBreakStepOverFunc2 = 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; return false;
} }
// Get a copy of the game thread handle so we can suspend the thread on a break // we're using a condition variable to pause the game thread in rbDebuggerServer::Break()
DuplicateHandle ( GetCurrentProcess(), GetCurrentThread ( ), GetCurrentProcess(), &mGameThread, 0, FALSE, DUPLICATE_SAME_ACCESS ); // until rvDebuggerServer::Resume() is called (from another thread)
mGameThreadBreakCond = SDL_CreateCond();
mGameThreadBreakLock = SDL_CreateMutex();
// Create a critical section to ensure that the shared thread // Create a critical section to ensure that the shared thread
// variables are protected // variables are protected
InitializeCriticalSection ( &mCriticalSection ); mCriticalSection = SDL_CreateMutex();
// Server must be running on the local host on port 28980 // 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; mClientAdr.port = 27981;
// Attempt to let the server know we are here. The server may not be running so this // 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 ) void rvDebuggerServer::OSPathToRelativePath( const char *osPath, idStr &qpath )
{ {
if ( strchr( osPath, ':' ) ) if ( strchr( osPath, ':' ) ) // XXX: what about linux?
{ {
qpath = fileSystem->OSPathToRelativePath( osPath ); qpath = fileSystem->OSPathToRelativePath( osPath );
} }
@ -130,8 +140,16 @@ void rvDebuggerServer::Shutdown ( void )
mPort.Close(); mPort.Close();
Resume(); // just in case we're still paused
// dont need the crit section anymore // 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 ) bool rvDebuggerServer::ProcessMessages ( void )
{ {
netadr_t adrFrom; netadr_t adrFrom;
msg_t msg; idBitMsg msg;
byte buffer[MAX_MSGLEN]; byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) );
// Check for pending udp packets on the debugger port // 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; short command;
msg.Init(buffer, sizeof(buffer));
// Only accept packets from the debugger server for security reasons msg.SetSize(msgSize);
if ( !Sys_CompareNetAdrBase ( adrFrom, mClientAdr ) ) msg.BeginReading();
{
continue; 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 ) switch ( command )
{ {
case DBMSG_CONNECT: case DBMSG_CONNECT:
mConnected = true; mConnected = true;
SendMessage ( DBMSG_CONNECTED ); SendMessage ( DBMSG_CONNECTED );
HandleInspectScripts ( nullptr );
com_editors |= EDITOR_DEBUGGER;
break; break;
case DBMSG_CONNECTED: case DBMSG_CONNECTED:
mConnected = true; mConnected = true;
HandleInspectScripts( nullptr );
com_editors |= EDITOR_DEBUGGER;
break; break;
case DBMSG_DISCONNECT: case DBMSG_DISCONNECT:
ClearBreakpoints ( ); ClearBreakpoints ( );
Resume ( ); Resume ( );
mConnected = false; mConnected = false;
com_editors &= ~EDITOR_DEBUGGER;
break; break;
case DBMSG_ADDBREAKPOINT: case DBMSG_ADDBREAKPOINT:
@ -188,7 +213,7 @@ bool rvDebuggerServer::ProcessMessages ( void )
break; break;
case DBMSG_RESUME: case DBMSG_RESUME:
Resume ( ); HandleResume ( &msg );
break; break;
case DBMSG_BREAK: case DBMSG_BREAK:
@ -197,11 +222,11 @@ bool rvDebuggerServer::ProcessMessages ( void )
case DBMSG_STEPOVER: case DBMSG_STEPOVER:
mBreakStepOver = true; mBreakStepOver = true;
mBreakStepOverDepth = mBreakInterpreter->GetCallstackDepth ( ); mBreakStepOverDepth = ((idGameEditExt*) gameEdit)->GetInterpreterCallStackDepth(mBreakInterpreter);
mBreakStepOverFunc1 = mBreakInterpreter->GetCallstack()[mBreakInterpreter->GetCallstackDepth()].f; mBreakStepOverFunc1 = ((idGameEditExt*) gameEdit)->GetInterpreterCallStackFunction(mBreakInterpreter);
if ( mBreakInterpreter->GetCallstackDepth() > 0 ) if (mBreakStepOverDepth)
{ {
mBreakStepOverFunc2 = mBreakInterpreter->GetCallstack()[mBreakInterpreter->GetCallstackDepth()-1].f; mBreakStepOverFunc2 = ((idGameEditExt*) gameEdit)->GetInterpreterCallStackFunction(mBreakInterpreter,mBreakStepOverDepth - 1);
} }
else else
{ {
@ -226,6 +251,14 @@ bool rvDebuggerServer::ProcessMessages ( void )
case DBMSG_INSPECTTHREADS: case DBMSG_INSPECTTHREADS:
HandleInspectThreads ( &msg ); HandleInspectThreads ( &msg );
break; 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 ) void rvDebuggerServer::SendMessage ( EDebuggerMessage dbmsg )
{ {
msg_t msg; idBitMsg msg;
byte buffer[MAX_MSGLEN]; byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) ); msg.Init( buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)dbmsg ); 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 rvDebuggerServer::HandleAddBreakpoint
Handle the DBMSG_ADDBREAKPOINT message being sent by the debugger client. This 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. data supplied in the message.
================ ================
*/ */
void rvDebuggerServer::HandleAddBreakpoint ( msg_t* msg ) void rvDebuggerServer::HandleAddBreakpoint ( idBitMsg* msg )
{ {
bool onceOnly = false; bool onceOnly = false;
long lineNumber; long lineNumber;
long id; long id;
char filename[MAX_PATH]; char filename[2048]; // DG: randomly chose this size
// Read the breakpoint info // Read the breakpoint info
onceOnly = MSG_ReadBits ( msg, 1 ) ? true : false; onceOnly = msg->ReadBits( 1 ) ? true : false;
lineNumber = MSG_ReadInt ( msg ); lineNumber = msg->ReadInt ( );
id = MSG_ReadInt ( msg ); id = msg->ReadInt ( );
MSG_ReadString ( msg, filename, MAX_PATH ); msg->ReadString ( filename, sizeof(filename) );
// Since breakpoints are used by both threads we need to //check for statement on requested breakpoint location
// protect them with a crit section if (!((idGameEditExt*) gameEdit)->IsLineCode(filename, lineNumber))
EnterCriticalSection ( &mCriticalSection ); {
mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber, id ) ); idBitMsg msgOut;
LeaveCriticalSection ( &mCriticalSection ); 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. list.
================ ================
*/ */
void rvDebuggerServer::HandleRemoveBreakpoint ( msg_t* msg ) void rvDebuggerServer::HandleRemoveBreakpoint ( idBitMsg* msg )
{ {
int i; int i;
int id; int id;
// ID that we are to remove // ID that we are to remove
id = MSG_ReadInt ( msg ); id = msg->ReadInt ( );
// Since breakpoints are used by both threads we need to // Since breakpoints are used by both threads we need to
// protect them with a crit section // 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 // Find the breakpoint that matches the given id and remove it from the list
for ( i = 0; i < mBreakpoints.Num(); i ++ ) 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; //Empty msg
const function_t* func; Resume();
func = stack->f;
// If the function is unknown then just fill in with default data.
if ( !func )
{
MSG_WriteString ( msg, "<UNKNOWN>" );
MSG_WriteString ( msg, "<UNKNOWN>" );
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, "<UNKNOWN>" );
MSG_WriteInt ( msg, 0 );
}
} }
/* /*
@ -368,31 +386,18 @@ Handle an incoming inspect callstack message by sending a message
back to the client with the callstack data. 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]; byte buffer[MAX_MSGLEN];
int i;
prstack_t temp;
MSG_Init( &msg, buffer, sizeof( buffer ) ); msgOut.Init(buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_INSPECTCALLSTACK ); 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 SendPacket (msgOut.GetData(), msgOut.GetSize() );
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 );
} }
/* /*
@ -402,35 +407,67 @@ rvDebuggerServer::HandleInspectThreads
Send the list of the current threads in the interpreter back to the debugger client 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; idBitMsg msgOut;
byte buffer[MAX_MSGLEN]; byte buffer[MAX_MSGLEN];
int i; int i;
// Initialize the message // Initialize the message
MSG_Init( &msg, buffer, sizeof( buffer ) ); msgOut.Init( buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_INSPECTTHREADS ); msgOut.SetAllowOverflow(true);
msgOut.BeginWriting();
msgOut.WriteShort ( (short)DBMSG_INSPECTTHREADS );
// Write the number of threads to the message // 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 // 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]; ((idGameEditExt*) gameEdit)->MSG_WriteThreadInfo(&msgOut,((idGameEditExt*) gameEdit)->GetThreadByIndex(i), mBreakInterpreter);
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 );
} }
// Send off the inspect threads packet to the debugger client // 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 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]; char varname[256];
int scopeDepth; int scopeDepth;
@ -450,28 +487,29 @@ void rvDebuggerServer::HandleInspectVariable ( msg_t* in_msg )
return; return;
} }
scopeDepth = (short)MSG_ReadShort ( in_msg ); scopeDepth = (short)msg->ReadShort ( );
MSG_ReadString ( in_msg, varname, 256 ); msg->ReadString ( varname, 256 );
idStr varvalue; idStr varvalue;
msg_t msg; idBitMsg msgOut;
byte buffer[MAX_MSGLEN]; byte buffer[MAX_MSGLEN];
// Initialize the message // Initialize the message
MSG_Init( &msg, buffer, sizeof( buffer ) ); msgOut.Init( buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_INSPECTVARIABLE ); msgOut.BeginWriting();
msgOut.WriteShort ( (short)DBMSG_INSPECTVARIABLE );
if ( !mBreakInterpreter->GetRegisterValue ( varname, varvalue, scopeDepth ) ) if (!((idGameEditExt*) gameEdit)->GetRegisterValue(mBreakInterpreter, varname, varvalue, scopeDepth ) )
{ {
varvalue = "???"; varvalue = "???";
} }
MSG_WriteShort ( &msg, (short)scopeDepth ); msgOut.WriteShort ( (short)scopeDepth );
MSG_WriteString ( &msg, varname ); msgOut.WriteString ( varname );
MSG_WriteString ( &msg, varvalue ); 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 ) void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram* program, int instructionPointer )
{ {
const statement_t* st;
const char* filename; const char* filename;
int i; int i;
@ -492,23 +529,24 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
return; return;
} }
// Grab the current statement and the filename that it came from // Grab the current statement and the filename that it came from
st = &program->GetStatement ( instructionPointer ); filename = ((idGameEditExt*) gameEdit)->GetFilenameForStatement(program, instructionPointer);
filename = program->GetFilename ( st->file ); int linenumber = ((idGameEditExt*) gameEdit)->GetLineNumberForStatement(program, instructionPointer);
// Operate on lines, not statements // Operate on lines, not statements
if ( mLastStatementLine == st->linenumber && mLastStatementFile == st->file ) if ( mLastStatementLine == linenumber && mLastStatementFile == filename)
{ {
return; return;
} }
// Save the last visited line and file so we can prevent // Save the last visited line and file so we can prevent
// double breaks on lines with more than one statement // double breaks on lines with more than one statement
mLastStatementFile = idStr( st->file ); mLastStatementFile = idStr(filename);
mLastStatementLine = st->linenumber; mLastStatementLine = linenumber;
// Reset stepping when the last function on the callstack is returned from // 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; mBreakStepOver = false;
mBreakStepInto = 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 // See if we are supposed to break on the next script line
if ( mBreakNext ) if ( mBreakNext )
{ {
HandleInspectScripts(nullptr);
Break ( interpreter, program, instructionPointer ); Break ( interpreter, program, instructionPointer );
return; return;
} }
@ -524,9 +563,8 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
// Only break on the same callstack depth and thread as the break over // Only break on the same callstack depth and thread as the break over
if ( mBreakStepOver ) if ( mBreakStepOver )
{ {
if ( ( interpreter->GetCurrentFunction ( ) == mBreakStepOverFunc1 || //virtual bool CheckForBreakpointHit(interpreter,function1,function2,depth)
interpreter->GetCurrentFunction ( ) == mBreakStepOverFunc2 )&& if (((idGameEditExt*) gameEdit)->CheckForBreakPointHit(interpreter, mBreakStepOverFunc1, mBreakStepOverFunc2, mBreakStepOverDepth))
( interpreter->GetCallstackDepth ( ) <= mBreakStepOverDepth ) )
{ {
Break ( interpreter, program, instructionPointer ); Break ( interpreter, program, instructionPointer );
return; return;
@ -536,6 +574,7 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
// See if we are supposed to break on the next line // See if we are supposed to break on the next line
if ( mBreakStepInto ) if ( mBreakStepInto )
{ {
HandleInspectScripts(nullptr);
// Break // Break
Break ( interpreter, program, instructionPointer ); Break ( interpreter, program, instructionPointer );
return; return;
@ -545,7 +584,7 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
OSPathToRelativePath(filename,qpath); OSPathToRelativePath(filename,qpath);
qpath.BackSlashesToSlashes ( ); qpath.BackSlashesToSlashes ( );
EnterCriticalSection ( &mCriticalSection ); SDL_LockMutex( mCriticalSection );
// Check all the breakpoints // Check all the breakpoints
for ( i = 0; i < mBreakpoints.Num ( ); i ++ ) for ( i = 0; i < mBreakpoints.Num ( ); i ++ )
@ -553,30 +592,50 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
rvDebuggerBreakpoint* bp = mBreakpoints[i]; rvDebuggerBreakpoint* bp = mBreakpoints[i];
// Skip if not match of the line number // Skip if not match of the line number
if ( st->linenumber != bp->GetLineNumber ( ) ) if ( linenumber != bp->GetLineNumber ( ) )
{ {
continue; continue;
} }
// Skip if no match of the filename // Skip if no match of the filename
if ( idStr::Icmp ( bp->GetFilename(), qpath ) ) if ( idStr::Icmp ( bp->GetFilename(), qpath.c_str() ) )
{ {
continue; continue;
} }
// Pop out of the critical section so we dont get stuck // DG: onceOnly support
LeaveCriticalSection ( &mCriticalSection ); 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 // We hit a breakpoint, so break
Break ( interpreter, program, instructionPointer ); Break ( interpreter, program, instructionPointer );
// Back into the critical section since we are going to have to leave it // Back into the critical section since we are going to have to leave it
EnterCriticalSection ( &mCriticalSection ); SDL_LockMutex( mCriticalSection );
break; break;
} }
LeaveCriticalSection ( &mCriticalSection ); SDL_UnlockMutex( mCriticalSection );
} }
/* /*
@ -589,9 +648,8 @@ the game has been halted
*/ */
void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, int instructionPointer ) void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, int instructionPointer )
{ {
msg_t msg; idBitMsg msg;
byte buffer[MAX_MSGLEN]; byte buffer[MAX_MSGLEN];
const statement_t* st;
const char* filename; const char* filename;
// Clear all the break types // Clear all the break types
@ -600,12 +658,10 @@ void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, i
mBreakNext = false; mBreakNext = false;
// Grab the current statement and the filename that it came from // Grab the current statement and the filename that it came from
st = &program->GetStatement ( instructionPointer ); filename = ((idGameEditExt*) gameEdit)->GetFilenameForStatement(program,instructionPointer);
filename = program->GetFilename ( st->file ); int linenumber = ((idGameEditExt*) gameEdit)->GetLineNumberForStatement(program, instructionPointer);
idStr fileStr = filename;
idStr qpath; fileStr.BackSlashesToSlashes();
OSPathToRelativePath(filename, qpath);
qpath.BackSlashesToSlashes ( );
// Give the mouse cursor back to the world // Give the mouse cursor back to the world
Sys_GrabMouseCursor( false ); Sys_GrabMouseCursor( false );
@ -617,19 +673,33 @@ void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, i
mBreakInstructionPointer = instructionPointer; mBreakInstructionPointer = instructionPointer;
// Inform the debugger of the breakpoint hit // Inform the debugger of the breakpoint hit
MSG_Init( &msg, buffer, sizeof( buffer ) ); msg.Init( buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_BREAK ); msg.BeginWriting();
MSG_WriteInt ( &msg, st->linenumber ); msg.WriteShort ( (short)DBMSG_BREAK );
MSG_WriteString ( &msg, qpath ); msg.WriteInt ( linenumber );
SendPacket ( msg.data, msg.cursize ); 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 // Suspend the game thread. Since this will be called from within the main game thread
// execution wont return until after the thread is resumed // 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 // Let the debugger client know that we have started back up again
SendMessage ( DBMSG_RESUMED ); 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 // This is to give some time between the keypress that
// told us to resume and the setforeground window. Otherwise the quake window // told us to resume and the setforeground window. Otherwise the quake window
// would just flash // would just flash
@ -640,8 +710,10 @@ void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, i
SetActiveWindow ( win32.hWnd ); SetActiveWindow ( win32.hWnd );
UpdateWindow ( win32.hWnd ); UpdateWindow ( win32.hWnd );
SetFocus ( win32.hWnd ); SetFocus ( win32.hWnd );
#endif
// Give the mouse cursor back to the game // Give the mouse cursor back to the game
// HVG_Note : there be dragons here. somewhere.
Sys_GrabMouseCursor( true ); Sys_GrabMouseCursor( true );
// Clear all commands that were generated before we went into suspended mode. This is // Clear all commands that were generated before we went into suspended mode. This is
@ -664,10 +736,11 @@ void rvDebuggerServer::Resume ( void )
return; return;
} }
mBreak = false;
// Start the game thread back up // 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; return;
} }
msg_t msg; idBitMsg msg;
byte buffer[MAX_MSGLEN]; byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) ); msg.Init( buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_PRINT ); msg.BeginWriting();
MSG_WriteString ( &msg, text ); msg.WriteShort ( (short)DBMSG_PRINT );
msg.WriteString ( text );
SendPacket ( msg.data, msg.cursize ); SendPacket ( msg.GetData(), msg.GetSize() );
} }

View file

@ -28,20 +28,16 @@ If you have questions concerning this license or the applicable additional terms
#ifndef DEBUGGERSERVER_H_ #ifndef DEBUGGERSERVER_H_
#define DEBUGGERSERVER_H_ #define DEBUGGERSERVER_H_
#ifndef DEBUGGERMESSAGES_H_
#include "DebuggerMessages.h" #include "DebuggerMessages.h"
#endif
#ifndef DEBUGGERBREAKPOINT_H_
#include "DebuggerBreakpoint.h" #include "DebuggerBreakpoint.h"
#endif #include "framework/Game.h"
#include <SDL.h>
#ifndef __GAME_LOCAL_H__
#include "../../game/Game.h"
#endif
class idInterpreter;
class idProgram; class function_t;
typedef struct prstack_s prstack_t;
class rvDebuggerServer class rvDebuggerServer
{ {
@ -50,31 +46,52 @@ public:
rvDebuggerServer ( ); rvDebuggerServer ( );
~rvDebuggerServer ( ); ~rvDebuggerServer ( );
bool Initialize ( void ); bool Initialize ( void );
void Shutdown ( 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; bool mConnected;
netadr_t mClientAdr; netadr_t mClientAdr;
idPort mPort; idPort mPort;
idList<rvDebuggerBreakpoint*> mBreakpoints; idList<rvDebuggerBreakpoint*> mBreakpoints;
CRITICAL_SECTION mCriticalSection; SDL_mutex* mCriticalSection;
HANDLE mGameThread;
SDL_cond* mGameThreadBreakCond;
SDL_mutex* mGameThreadBreakLock;
bool mBreak; bool mBreak;
bool mBreakNext; bool mBreakNext;
bool mBreakStepOver; bool mBreakStepOver;
bool mBreakStepInto; bool mBreakStepInto;
@ -87,27 +104,9 @@ protected:
idStr mLastStatementFile; idStr mLastStatementFile;
int mLastStatementLine; 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 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 ); mPort.SendPacket ( mClientAdr, data, size );
} }
/*
================
rvDebuggerServer::GameSuspended
================
*/
ID_INLINE bool rvDebuggerServer::GameSuspended( void )
{
return mBreak;
}
#endif // DEBUGGERSERVER_H_ #endif // DEBUGGERSERVER_H_

View file

@ -35,7 +35,7 @@ If you have questions concerning this license or the applicable additional terms
#include "DebuggerQuickWatchDlg.h" #include "DebuggerQuickWatchDlg.h"
#include "DebuggerFindDlg.h" #include "DebuggerFindDlg.h"
#define DEBUGGERWINDOWCLASS "QUAKE4_DEBUGGER_WINDOW" #define DEBUGGERWINDOWCLASS "DHEWM3_DEBUGGER_WINDOW"
#define ID_DBG_WINDOWMIN 18900 #define ID_DBG_WINDOWMIN 18900
#define ID_DBG_WINDOWMAX 19900 #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_WATCH 31007
#define IDC_DBG_THREADS 31008 #define IDC_DBG_THREADS 31008
#define IDC_DBG_TOOLBAR 31009 #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 #define ID_DBG_FILE_MRU1 10000
@ -167,7 +170,7 @@ bool rvDebuggerWindow::Create ( HINSTANCE instance )
UpdateTitle ( ); UpdateTitle ( );
Printf ( "Quake 4 Script Debugger v0.1\n\n" ); Printf ( "Dhewm3 Script Debugger v1.1\n\n" );
ShowWindow ( mWnd, SW_SHOW ); ShowWindow ( mWnd, SW_SHOW );
UpdateWindow ( mWnd ); UpdateWindow ( mWnd );
@ -248,7 +251,7 @@ LRESULT CALLBACK rvDebuggerWindow::ScriptWndProc ( HWND wnd, UINT msg, WPARAM wp
{ {
static int lastStart = -1; static int lastStart = -1;
static int lastEnd = -1; static int lastEnd = -1;
rvDebuggerWindow* window = (rvDebuggerWindow*)GetWindowLong ( wnd, GWL_USERDATA ); rvDebuggerWindow* window = (rvDebuggerWindow*)GetWindowLongPtr ( wnd, GWLP_USERDATA );
WNDPROC wndproc = window->mOldScriptProc; WNDPROC wndproc = window->mOldScriptProc;
switch ( msg ) switch ( msg )
@ -347,6 +350,23 @@ LRESULT CALLBACK rvDebuggerWindow::ScriptWndProc ( HWND wnd, UINT msg, WPARAM wp
break; 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 ); 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 ) 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 ) switch ( msg )
{ {
@ -384,15 +404,57 @@ LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wp
case WM_PAINT: case WM_PAINT:
{ {
HDC dc; 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; PAINTSTRUCT ps;
RECT rect; RECT rect;
GetClientRect ( wnd, &rect ); GetClientRect ( wnd, &rect );
dc = BeginPaint ( wnd, &ps ); dc = BeginPaint( wnd, &ps );
FillRect ( dc, &rect, GetSysColorBrush ( COLOR_3DFACE ) ); 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 ( ) ) if ( window->mScripts.Num ( ) )
{ {
for ( int i = 0; i < window->mClient->GetBreakpointCount(); i ++ ) 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 ); c = SendMessage ( window->mWndScript, EM_LINEINDEX, bp->GetLineNumber ( ) - 1, 0 );
SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c ); 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 ); c = SendMessage ( window->mWndScript, EM_LINEINDEX, window->mClient->GetBreakLineNumber() - 1, 0 );
SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c ); 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 ); c = SendMessage ( window->mWndScript, EM_LINEINDEX, window->mClient->GetCallstack()[window->mCurrentStackDepth]->mLineNumber - 1, 0 );
SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c ); 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.right -= s2;
rect.left = rect.right + 1; rect.left = rect.right + s2;
HPEN pen = CreatePen ( PS_SOLID, 1, GetSysColor ( COLOR_3DSHADOW ) ); HPEN pen = CreatePen ( PS_SOLID, s2, GetSysColor ( COLOR_BACKGROUND ) );
HPEN old = (HPEN)SelectObject ( dc, pen ); HPEN old = (HPEN)SelectObject ( dc, pen );
MoveToEx ( dc, rect.right, rect.top, NULL ); MoveToEx ( dc, rect.right, rect.top, NULL );
LineTo ( dc, rect.right, rect.bottom ); LineTo ( dc, rect.right, rect.bottom );
SelectObject ( dc, old ); SelectObject ( dc, old );
DeleteObject ( pen ); DeleteObject ( pen );
EndPaint ( wnd, &ps ); EndPaint ( wnd, &ps );
@ -466,7 +531,7 @@ void rvDebuggerWindow::UpdateTitle ( void )
{ {
idStr title; idStr title;
title = "Quake 4 Script Debugger - "; title = "Dhewm3 Script Debugger - ";
if ( mClient->IsConnected ( ) ) if ( mClient->IsConnected ( ) )
{ {
@ -487,7 +552,10 @@ void rvDebuggerWindow::UpdateTitle ( void )
if ( mScripts.Num ( ) ) if ( mScripts.Num ( ) )
{ {
title += " - ["; title += " - [";
title += idStr( mScripts[mActiveScript]->GetFilename() ).StripPath ( ); if (mActiveScript != -1)
title += idStr( mScripts[mActiveScript]->GetFilename() ).StripPath ( );
else
title += "Load Error";
title += "]"; 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 rvDebuggerWindow::UpdateWatch
@ -712,7 +830,7 @@ int rvDebuggerWindow::HandleInitMenu ( WPARAM wParam, LPARAM lParam )
case ID_DBG_DEBUG_STEPOVER: case ID_DBG_DEBUG_STEPOVER:
case ID_DBG_DEBUG_STEPINTO: case ID_DBG_DEBUG_STEPINTO:
case ID_DBG_DEBUG_SHOWNEXTSTATEMENT: case ID_DBG_DEBUG_SHOWNEXTSTATEMENT:
// case ID_DBG_DEBUG_QUICKWATCH: case ID_DBG_DEBUG_QUICKWATCH:
if ( !mClient->IsConnected() || !mClient->IsStopped() ) if ( !mClient->IsConnected() || !mClient->IsStopped() )
{ {
EnableMenuItem ( hmenu, nPos, MF_GRAYED|MF_BYPOSITION ); EnableMenuItem ( hmenu, nPos, MF_GRAYED|MF_BYPOSITION );
@ -737,6 +855,33 @@ int rvDebuggerWindow::HandleInitMenu ( WPARAM wParam, LPARAM lParam )
return 0; 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 rvDebuggerWindow::HandleCreate
@ -760,18 +905,18 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam )
// Create the script window // Create the script window
LoadLibrary ( "Riched20.dll" ); 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 ); 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 ); SendMessage ( mWndScript, EM_SETWORDBREAKPROC, 0, (LPARAM) ScriptWordBreakProc );
mOldScriptProc = (WNDPROC)GetWindowLong ( mWndScript, GWL_WNDPROC ); mOldScriptProc = (WNDPROC)GetWindowLongPtr ( mWndScript, GWLP_WNDPROC );
SetWindowLong ( mWndScript, GWL_USERDATA, (LONG)this ); SetWindowLongPtr ( mWndScript, GWLP_USERDATA, (LONG_PTR)this );
SetWindowLong ( mWndScript, GWL_WNDPROC, (LONG)ScriptWndProc ); SetWindowLongPtr ( mWndScript, GWLP_WNDPROC, (LONG_PTR)ScriptWndProc );
SendMessage ( mWndScript, EM_SETTABSTOPS, 1, (LPARAM)&tabsize ); SendMessage ( mWndScript, EM_SETTABSTOPS, 1, (LPARAM)&tabsize );
dc = GetDC ( mWndScript ); dc = GetDC ( mWndScript );
GetTextMetrics ( dc, &tm ); GetTextMetrics ( dc, &tm );
ZeroMemory ( &lf, sizeof(lf) ); ZeroMemory ( &lf, sizeof(lf) );
lf.lfHeight = tm.tmHeight; lf.lfHeight = tm.tmHeight * Win_GetWindowScalingFactor( mWndScript );
strcpy ( lf.lfFaceName, "Courier New" ); strcpy ( lf.lfFaceName, "Courier New" );
SendMessage ( mWndScript, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 ); 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, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );
SendMessage ( mWndOutput, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) ); SendMessage ( mWndOutput, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) );
SendMessage ( mWndOutput, EM_SETBKGNDCOLOR, 0, GetSysColor ( COLOR_3DFACE ) ); 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 ); 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, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );
SendMessage ( mWndConsole, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) ); SendMessage ( mWndConsole, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) );
SendMessage ( mWndConsole, EM_SETBKGNDCOLOR, 0, GetSysColor ( COLOR_3DFACE ) ); 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 ); mWndMargin = CreateWindow ( "STATIC", "", WS_VISIBLE|WS_CHILD, 0, 0, 0, 0, mWndScript, (HMENU)IDC_DBG_SPLITTER, mInstance, NULL );
SetWindowLong ( mWndMargin, GWL_USERDATA, (LONG)this ); SetWindowLongPtr ( mWndMargin, GWLP_USERDATA, (LONG_PTR)this );
SetWindowLong ( mWndMargin, GWL_WNDPROC, (LONG)MarginWndProc ); 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 ); 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 ); TabCtrl_InsertItem ( mWndTabs, 3, &item );
item.pszText = "Threads"; item.pszText = "Threads";
TabCtrl_InsertItem ( mWndTabs, 4, &item ); 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 ); 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 ); 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 ); 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; LVCOLUMN col;
col.mask = LVCF_WIDTH|LVCF_TEXT; 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.cx = 20;
col.pszText = ""; col.pszText = "";
ListView_InsertColumn ( mWndCallstack, 0, &col ); ListView_InsertColumn ( mWndCallstack, 0, &col );
col.cx = 150; col.cx = 150;
col.pszText = "Function"; col.pszText = "Function";
ListView_InsertColumn ( mWndCallstack, 1, &col ); ListView_InsertColumn ( mWndCallstack, 1, &col );
col.cx = 150; col.cx = 50;
col.pszText = "Line"; col.pszText = "Line";
ListView_InsertColumn ( mWndCallstack, 2, &col ); ListView_InsertColumn ( mWndCallstack, 2, &col );
col.cx = 150; col.cx = 350;
col.pszText = "Filename"; col.pszText = "Filename";
ListView_InsertColumn ( mWndCallstack, 3, &col ); 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_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_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) ); 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 ( mWndThreads, mImageList, LVSIL_SMALL );
ListView_SetImageList ( mWndCallstack, mImageList, LVSIL_SMALL ); ListView_SetImageList ( mWndCallstack, mImageList, LVSIL_SMALL );
ListView_SetImageList ( mWndBreakList, mImageList, LVSIL_SMALL );
EnableWindows ( FALSE ); EnableWindows ( FALSE );
EnableWindow ( mWndScriptList, true );
ListView_SetExtendedListViewStyle ( mWndCallstack, LVS_EX_FULLROWSELECT ); ListView_SetExtendedListViewStyle ( mWndCallstack, LVS_EX_FULLROWSELECT );
ListView_SetExtendedListViewStyle ( mWndThreads, 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_callstack", mWndCallstack );
gDebuggerApp.GetOptions().GetColumnWidths ( "cw_threads", mWndThreads ); gDebuggerApp.GetOptions().GetColumnWidths ( "cw_threads", mWndThreads );
@ -920,6 +1109,10 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam )
AddWatch ( s ); AddWatch ( s );
} }
RECT t;
GetClientRect(mWndScript, &t);
SendMessage(mWndScript, WM_SIZE, 0, MAKELPARAM(t.right - t.left, t.bottom - t.top));
return 0; return 0;
} }
@ -957,6 +1150,36 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
switch ( id ) 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: case ID_DBG_EDIT_FINDSELECTED:
{ {
idStr text; idStr text;
@ -1025,7 +1248,7 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
GetCurrentDirectory ( MAX_PATH, curDir ); GetCurrentDirectory ( MAX_PATH, curDir );
GetModuleFileName ( NULL, exeFile, MAX_PATH ); 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, CreateProcess ( NULL, (LPSTR)s,
NULL, NULL, FALSE, 0, NULL, curDir, &startup, &process ); NULL, NULL, FALSE, 0, NULL, curDir, &startup, &process );
@ -1049,13 +1272,13 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
LONG num; LONG num;
LONG dem; LONG dem;
SendMessage ( mWndScript, EM_GETZOOM, (LONG)&num, (LONG)&dem ); SendMessage ( mWndScript, EM_GETZOOM, (WPARAM)&num, (LPARAM)&dem );
if ( num != mZoomScaleNum || dem != mZoomScaleDem ) if ( num != mZoomScaleNum || dem != mZoomScaleDem )
{ {
mZoomScaleNum = num; mZoomScaleNum = num;
mZoomScaleDem = dem; mZoomScaleDem = dem;
GetClientRect ( mWndScript, &t ); 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 else
{ {
@ -1064,6 +1287,7 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
break; 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: case ID_DBG_DEBUG_TOGGLEBREAKPOINT:
ToggleBreakpoint ( ); ToggleBreakpoint ( );
break; break;
@ -1139,6 +1363,23 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
UpdateScript ( ); UpdateScript ( );
break; 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; return 0;
@ -1153,7 +1394,7 @@ Window procedure for the deubgger window
*/ */
LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ) 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 ) switch ( msg )
{ {
@ -1176,7 +1417,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
gDebuggerApp.GetOptions().SetString ( va("watch%d", i ), "" ); gDebuggerApp.GetOptions().SetString ( va("watch%d", i ), "" );
window->mWnd = NULL; window->mWnd = NULL;
SetWindowLong ( wnd, GWL_USERDATA, 0 ); SetWindowLongPtr ( wnd, GWLP_USERDATA, 0 );
break; break;
} }
@ -1202,8 +1443,13 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
case WM_SIZE: 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; 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.left = 0;
window->mSplitterRect.right = LOWORD(lparam); 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) ); 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 ); MoveWindow ( window->mWndTabs, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
SendMessage ( window->mWndTabs, TCM_ADJUSTRECT, FALSE, (LPARAM)&rect ); 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 ); MoveWindow ( window->mWndBorder, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
InflateRect ( &rect, -1, -1 ); InflateRect ( &rect, -1, -1 );
MoveWindow ( window->mWndOutput, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE ); 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->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->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->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; break;
} }
@ -1258,7 +1517,17 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
} }
break; 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: case WM_LBUTTONUP:
if ( window->mSplitterDrag ) if ( window->mSplitterDrag )
{ {
@ -1320,7 +1589,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
{ {
CREATESTRUCT* cs = (CREATESTRUCT*) lparam; CREATESTRUCT* cs = (CREATESTRUCT*) lparam;
window = (rvDebuggerWindow*) cs->lpCreateParams; window = (rvDebuggerWindow*) cs->lpCreateParams;
SetWindowLong ( wnd, GWL_USERDATA, (LONG)cs->lpCreateParams ); SetWindowLongPtr ( wnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams );
window->mWnd = wnd; window->mWnd = wnd;
window->HandleCreate ( wparam, lparam ); window->HandleCreate ( wparam, lparam );
@ -1439,7 +1708,6 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
} }
} }
break; break;
case IDC_DBG_CALLSTACK: case IDC_DBG_CALLSTACK:
if ( hdr->code == NM_DBLCLK ) if ( hdr->code == NM_DBLCLK )
{ {
@ -1454,14 +1722,74 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
} }
} }
break; 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: case IDC_DBG_TABS:
if ( hdr->code == TCN_SELCHANGE ) if ( hdr->code == TCN_SELCHANGE )
{ {
ShowWindow ( window->mWndOutput, SW_HIDE ); ShowWindow ( window->mWndOutput, SW_HIDE );
ShowWindow ( window->mWndConsole, SW_HIDE ); ShowWindow ( window->mWndConsole, SW_HIDE );
ShowWindow ( window->mWndConsoleInput, SW_HIDE );
ShowWindow ( window->mWndCallstack, SW_HIDE ); ShowWindow ( window->mWndCallstack, SW_HIDE );
ShowWindow ( window->mWndWatch, SW_HIDE ); ShowWindow ( window->mWndWatch, SW_HIDE );
ShowWindow ( window->mWndThreads, SW_HIDE ); ShowWindow ( window->mWndThreads, SW_HIDE );
ShowWindow ( window->mWndScriptList, SW_HIDE );
ShowWindow ( window->mWndBreakList, SW_HIDE );
switch ( TabCtrl_GetCurSel ( hdr->hwndFrom ) ) switch ( TabCtrl_GetCurSel ( hdr->hwndFrom ) )
{ {
case 0: case 0:
@ -1470,6 +1798,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
case 1: case 1:
ShowWindow ( window->mWndConsole, SW_SHOW ); ShowWindow ( window->mWndConsole, SW_SHOW );
ShowWindow( window->mWndConsoleInput, SW_SHOW );
break; break;
case 2: case 2:
@ -1483,6 +1812,14 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
case 4: case 4:
ShowWindow ( window->mWndThreads, SW_SHOW ); ShowWindow ( window->mWndThreads, SW_SHOW );
break; break;
case 5:
ShowWindow(window->mWndScriptList, SW_SHOW);
break;
case 6:
ShowWindow( window->mWndBreakList, SW_SHOW );
break;
} }
} }
break; break;
@ -1493,7 +1830,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
case WM_CLOSE: case WM_CLOSE:
if ( window->mClient->IsConnected ( ) ) 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; return 0;
} }
@ -1535,14 +1872,20 @@ rvDebuggerWindow::ProcessNetMessage
Process an incoming network message 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 ) switch ( command )
{ {
case DBMSG_REMOVEBREAKPOINT:
MessageBeep(MB_ICONEXCLAMATION);
InvalidateRect(mWndScript, NULL, FALSE);
UpdateBreakpointList();
break;
case DBMSG_RESUMED: case DBMSG_RESUMED:
UpdateTitle ( ); UpdateTitle ( );
UpdateToolbar ( ); UpdateToolbar ( );
@ -1554,9 +1897,9 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg )
char temp2[1024]; char temp2[1024];
int i; int i;
MSG_ReadShort ( msg ); msg->ReadShort ( );
MSG_ReadString ( msg, temp, 1024 ); msg->ReadString ( temp, 1024 );
MSG_ReadString ( msg, temp2, 1024 ); msg->ReadString ( temp2, 1024 );
if ( mTooltipVar.Icmp ( temp ) == 0 ) if ( mTooltipVar.Icmp ( temp ) == 0 )
{ {
mTooltipValue = temp2; mTooltipValue = temp2;
@ -1624,10 +1967,17 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg )
break; break;
case DBMSG_PRINT: case DBMSG_PRINT:
{
HWND prevFocus = GetFocus();
SetFocus ( mWndConsole );
SendMessage ( mWndConsole, EM_SETSEL, -1, -1 ); 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 ); SendMessage ( mWndConsole, EM_SCROLLCARET, 0, 0 );
UpdateWindow( mWndConsole );
SetFocus( prevFocus );
break; break;
}
case DBMSG_BREAK: case DBMSG_BREAK:
{ {
@ -1636,6 +1986,7 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg )
mCurrentStackDepth = 0; mCurrentStackDepth = 0;
mClient->InspectVariable ( mTooltipVar, mCurrentStackDepth ); mClient->InspectVariable ( mTooltipVar, mCurrentStackDepth );
UpdateWatch ( ); UpdateWatch ( );
UpdateBreakpointList();
EnableWindows ( TRUE ); EnableWindows ( TRUE );
OpenScript ( mClient->GetBreakFilename(), mClient->GetBreakLineNumber() - 1 ); OpenScript ( mClient->GetBreakFilename(), mClient->GetBreakLineNumber() - 1 );
UpdateTitle ( ); UpdateTitle ( );
@ -1643,7 +1994,11 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg )
SetForegroundWindow ( mWnd ); SetForegroundWindow ( mWnd );
break; break;
} }
case DBMSG_INSPECTSCRIPTS:
{
UpdateScriptList ( );
break;
}
case DBMSG_INSPECTCALLSTACK: case DBMSG_INSPECTCALLSTACK:
{ {
UpdateCallstack ( ); UpdateCallstack ( );
@ -1719,7 +2074,7 @@ Opens the script with the given filename and will scroll to the given line
number if one is specified 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; int i;
@ -1748,7 +2103,6 @@ bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber )
// Load the script // Load the script
if ( !script->Load ( filename ) ) if ( !script->Load ( filename ) )
{ {
delete script;
SetCursor ( LoadCursor ( NULL, IDC_ARROW ) ); SetCursor ( LoadCursor ( NULL, IDC_ARROW ) );
return false; return false;
} }
@ -1777,7 +2131,7 @@ bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber )
// Move to a specific line number? // Move to a specific line number?
if ( lineNumber != -1 ) if ( lineNumber != -1 )
{ {
int c; long c;
// Put the caret on the line number specified and scroll it into position. // 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 // 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). // and then scroll before going back to (c,c).
// NOTE: We scroll to the line before the one we want so its more visible // NOTE: We scroll to the line before the one we want so its more visible
SetFocus ( mWndScript ); 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_SETSEL, c, c + 1 );
SendMessage ( mWndScript, EM_SCROLLCARET, 0, 0 ); 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 ); SendMessage ( mWndScript, EM_SETSEL, c, c );
} }
else else
@ -1843,6 +2197,8 @@ void rvDebuggerWindow::ToggleBreakpoint ( void )
// Force a repaint of the script window // Force a repaint of the script window
InvalidateRect ( mWndScript, NULL, FALSE ); InvalidateRect ( mWndScript, NULL, FALSE );
UpdateBreakpointList();
} }
/* /*
@ -1895,6 +2251,8 @@ void rvDebuggerWindow::CreateToolbar ( void )
SendMessage( mWndToolbar, TB_ADDBITMAP, (WPARAM)4, (LPARAM) &tbab ); SendMessage( mWndToolbar, TB_ADDBITMAP, (WPARAM)4, (LPARAM) &tbab );
// Add the buttons to the toolbar // 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 }, TBBUTTON tbb[] = { { 0, 0, TBSTATE_ENABLED, BTNS_SEP, 0, 0, -1 },
{ 8, ID_DBG_FILE_OPEN, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, -1 }, { 8, ID_DBG_FILE_OPEN, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, -1 },
{ 0, 0, TBSTATE_ENABLED, BTNS_SEP, 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; return 1;
} }
@ -2193,7 +2552,7 @@ then the last text used will be searched for.
*/ */
bool rvDebuggerWindow::FindNext ( const char* text ) bool rvDebuggerWindow::FindNext ( const char* text )
{ {
int start; long start;
FINDTEXT ft; FINDTEXT ft;
if ( text ) 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 ); SendMessage ( mWndScript, EM_SCROLLCARET, 0, 0 );
return true; return true;
@ -2247,7 +2606,7 @@ then the last text used will be searched for.
*/ */
bool rvDebuggerWindow::FindPrev ( const char* text ) bool rvDebuggerWindow::FindPrev ( const char* text )
{ {
int start; long start;
FINDTEXT ft; FINDTEXT ft;
if ( text ) if ( text )

View file

@ -52,38 +52,57 @@ public:
rvDebuggerWindow ( ); rvDebuggerWindow ( );
~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 ); int HandleInitMenu ( WPARAM wParam, LPARAM lParam );
bool FindNext ( const char* text = NULL ); 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 ResizeImageList ( int& widthOut, int& heightOut);
void UpdateWindowMenu ( void ); static LRESULT CALLBACK WndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
void UpdateScript ( void ); static LRESULT CALLBACK MarginWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
void UpdateToolbar ( void ); static LRESULT CALLBACK ScriptWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
void UpdateTitle ( void ); static INT_PTR CALLBACK AboutDlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
void UpdateCallstack ( void ); static int CALLBACK ScriptWordBreakProc( LPTSTR text, int current, int max, int action );
void UpdateRecentFiles ( void );
bool OpenScript ( const char* filename, int lineNumber = -1 );
void EnableWindows ( bool state );
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 mWnd;
HWND mWndScript; HWND mWndScript;
@ -92,7 +111,10 @@ protected:
HWND mWndTabs; HWND mWndTabs;
HWND mWndBorder; HWND mWndBorder;
HWND mWndConsole; HWND mWndConsole;
HWND mWndConsoleInput;
HWND mWndCallstack; HWND mWndCallstack;
HWND mWndScriptList;
HWND mWndBreakList; // list of breakpoints
HWND mWndWatch; HWND mWndWatch;
HWND mWndThreads; HWND mWndThreads;
HWND mWndToolTips; HWND mWndToolTips;
@ -108,6 +130,7 @@ protected:
HINSTANCE mInstance; HINSTANCE mInstance;
HIMAGELIST mImageList; HIMAGELIST mImageList;
HIMAGELIST mTmpImageList;
RECT mSplitterRect; RECT mSplitterRect;
bool mSplitterDrag; bool mSplitterDrag;
@ -129,25 +152,6 @@ protected:
rvDebuggerClient* mClient; rvDebuggerClient* mClient;
rvDebuggerWatchList mWatches; 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 );
}; };
/* /*

View file

@ -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 "tools/edit_gui_common.h"
#include "../../sys/win32/rc/debugger_resource.h" #include "../../sys/win32/rc/debugger_resource.h"
#include "DebuggerApp.h" #include "DebuggerApp.h"
#else
#include "debugger_common.h"
#endif
#include "DebuggerServer.h" #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; static rvDebuggerServer* gDebuggerServer = NULL;
HWND gDebuggerWindow = NULL; static SDL_Thread* gDebuggerServerThread = NULL;
bool gDebuggerSuspend = false; static bool gDebuggerServerQuit = false;
bool gDebuggerConnnected = false;
HANDLE gDebuggerGameThread = NULL;
rvDebuggerServer* gDebuggerServer = NULL;
HANDLE gDebuggerServerThread = NULL;
DWORD gDebuggerServerThreadID = 0;
bool gDebuggerServerQuit = false;
#if defined( ID_ALLOW_TOOLS )
/* /*
================ ================
DebuggerMain DebuggerMain
@ -65,6 +65,9 @@ void DebuggerClientInit( const char *cmdline )
{ {
goto DebuggerClientInitDone; goto DebuggerClientInitDone;
} }
// hide the doom window by default
::ShowWindow( win32.hWnd, SW_HIDE );
gDebuggerApp.Run ( ); gDebuggerApp.Run ( );
@ -113,6 +116,7 @@ void DebuggerClientLaunch ( void )
CloseHandle ( process.hThread ); CloseHandle ( process.hThread );
CloseHandle ( process.hProcess ); CloseHandle ( process.hProcess );
} }
#endif // #if defined( ID_ALLOW_TOOLS )
/* /*
================ ================
@ -121,14 +125,14 @@ DebuggerServerThread
Thread proc for the debugger server Thread proc for the debugger server
================ ================
*/ */
DWORD CALLBACK DebuggerServerThread ( LPVOID param ) static int SDLCALL DebuggerServerThread ( void *param )
{ {
assert ( gDebuggerServer ); assert ( gDebuggerServer );
while ( !gDebuggerServerQuit ) while ( !gDebuggerServerQuit )
{ {
gDebuggerServer->ProcessMessages ( ); gDebuggerServer->ProcessMessages ( );
Sleep ( 1 ); SDL_Delay( 1 );
} }
return 0; return 0;
@ -143,8 +147,17 @@ Starts up the debugger server
*/ */
bool DebuggerServerInit ( void ) 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 // Dont do this if we are in the debugger already
if ( com_editors & EDITOR_DEBUGGER ) if ( gDebuggerServer != NULL
|| ( com_editors & EDITOR_DEBUGGER ) )
{ {
return false; return false;
} }
@ -163,9 +176,13 @@ bool DebuggerServerInit ( void )
gDebuggerServer = NULL; gDebuggerServer = NULL;
return false; return false;
} }
// Start the debugger server thread // 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; return true;
} }
@ -179,13 +196,14 @@ Shuts down the debugger server
*/ */
void DebuggerServerShutdown ( void ) void DebuggerServerShutdown ( void )
{ {
if ( gDebuggerServerThread ) if ( gDebuggerServerThread != NULL )
{ {
// Signal the debugger server to quit // Signal the debugger server to quit
gDebuggerServerQuit = true; gDebuggerServerQuit = true;
// Wait for the thread to finish // Wait for the thread to finish
WaitForSingleObject ( gDebuggerServerThread, INFINITE ); SDL_WaitThread( gDebuggerServerThread, NULL );
gDebuggerServerThread = NULL;
// Shutdown the server now // Shutdown the server now
gDebuggerServer->Shutdown(); gDebuggerServer->Shutdown();
@ -193,10 +211,10 @@ void DebuggerServerShutdown ( void )
delete gDebuggerServer; delete gDebuggerServer;
gDebuggerServer = NULL; gDebuggerServer = NULL;
// Cleanup the thread handle com_editors &= ~EDITOR_DEBUGGER;
CloseHandle ( gDebuggerServerThread );
gDebuggerServerThread = NULL;
} }
com_enableDebuggerServer.ClearModified( );
} }
/* /*

View file

@ -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

View file

@ -67,10 +67,6 @@ bool GUIEditorHandleMessage( void *msg ) { return false; }
void DebuggerClientLaunch( void ) {} void DebuggerClientLaunch( void ) {}
void DebuggerClientInit( const char *cmdline ) { common->Printf( "The Script Debugger Client only runs on Win32\n" ); } 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" ); } void PDAEditorInit( const idDict *spawnArgs ) { common->Printf( "The PDA editor only runs on Win32\n" ); }