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/out*
/neo/build*
/.vs*
/neo/CMakeSettings.json

View file

@ -801,6 +801,17 @@ set(src_d3xp
add_globbed_headers(src_d3xp "d3xp")
set(src_debuggerServer
tools/debugger/DebuggerBreakpoint.h
tools/debugger/DebuggerBreakpoint.cpp
tools/debugger/DebuggerServer.h
tools/debugger/DebuggerServer.cpp
tools/debugger/DebuggerScript.h
tools/debugger/DebuggerScript.cpp
tools/debugger/DebuggerMessages.h
tools/debugger/debugger.cpp
)
set(src_core
${src_renderer}
${src_framework}
@ -862,6 +873,23 @@ if (TOOLS AND MFC_FOUND AND MSVC)
# Script editor
file(GLOB src_script_editor "tools/script/*.cpp")
add_globbed_headers(src_script_editor "tools/script")
# Script Debugger
set(src_debuggerClient
tools/debugger/DebuggerClient.h
tools/debugger/DebuggerClient.cpp
tools/debugger/DebuggerApp.h
tools/debugger/DebuggerApp.cpp
tools/debugger/DebuggerQuickWatchDlg.h
tools/debugger/DebuggerQuickWatchDlg.cpp
tools/debugger/DebuggerWindow.h
tools/debugger/DebuggerWindow.cpp
tools/debugger/DebuggerFindDlg.h
tools/debugger/DebuggerFindDlg.cpp
)
set(src_script_debugger
${src_debuggerServer}
${src_debuggerClient}
)
# sound editor?
file(GLOB src_sound_editor "tools/sound/*.cpp")
add_globbed_headers(src_sound_editor "tools/sound")
@ -881,13 +909,17 @@ if (TOOLS AND MFC_FOUND AND MSVC)
${src_map_editor}
${src_script_editor}
${src_sound_editor}
${src_script_debugger}
"tools/edit_public.h"
"tools/edit_gui_common.h"
)
SET(CMAKE_MFC_FLAG 2)
set(TOOLS_DEFINES "ID_ALLOW_TOOLS;_AFXDLL")
else()
set(src_editor_tools "tools/edit_stub.cpp" "tools/edit_public.h")
set(src_editor_tools "tools/edit_stub.cpp" "tools/edit_public.h" "tools/debugger/debugger_common.h")
list(APPEND src_editor_tools
${src_debuggerServer}
)
endif()
@ -1052,9 +1084,10 @@ if(DEDICATED)
${src_stub_openal}
${src_stub_gl}
${src_sys_base}
${src_debuggerServer}
)
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX neo FILES ${src_core} ${src_sys_base} ${src_stub_openal} ${src_stub_gl})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX neo FILES ${src_core} ${src_sys_base} ${src_stub_openal} ${src_stub_gl} ${src_debuggerServer})
set_target_properties(${DHEWM3BINARY}ded PROPERTIES COMPILE_DEFINITIONS "ID_DEDICATED;__DOOM_DLL__")
set_target_properties(${DHEWM3BINARY}ded PROPERTIES LINK_FLAGS "${ldflags}")

View file

@ -670,7 +670,7 @@ void idEditEntities::DisplayEntities( void ) {
===============================================================================
*/
idGameEdit gameEditLocal;
idGameEditExt gameEditLocal;
idGameEdit * gameEdit = &gameEditLocal;
@ -1146,3 +1146,62 @@ void idGameEdit::MapEntityTranslate( const char *name, const idVec3 &v ) const {
}
}
}
/***********************************************************************
Debugger
***********************************************************************/
bool idGameEditExt::IsLineCode( const char *filename, int linenumber ) const {
idStr fileStr;
idProgram *program = &gameLocal.program;
for ( int i = 0; i < program->NumStatements( ); i++ ) {
fileStr = program->GetFilename( program->GetStatement( i ).file );
fileStr.BackSlashesToSlashes( );
if ( strcmp( filename, fileStr.c_str( ) ) == 0
&& program->GetStatement( i ).linenumber == linenumber
) {
return true;
}
}
return false;
}
void idGameEditExt::GetLoadedScripts(idStrList** result)
{
(*result)->Clear();
idProgram* program = &gameLocal.program;
for (int i = 0; i < program->NumFilenames(); i++)
{
(*result)->AddUnique(idStr(program->GetFilename(i)));
}
}
void idGameEditExt::MSG_WriteScriptList(idBitMsg* msg)
{
idProgram* program = &gameLocal.program;
msg->WriteInt(program->NumFilenames());
for (int i = 0; i < program->NumFilenames(); i++)
{
idStr file = program->GetFilename(i);
//fix this. it seams that scripts triggered by the runtime are stored with a wrong path
//the use // instead of '\'
file.BackSlashesToSlashes();
msg->WriteString(file);
}
}
const char* idGameEditExt::GetFilenameForStatement(idProgram* program, int index) const
{
return program->GetFilenameForStatement(index);
}
int idGameEditExt::GetLineNumberForStatement(idProgram* program, int index) const
{
return program->GetLineNumberForStatement(index);
}

View file

@ -302,6 +302,16 @@ void idGameLocal::Clear( void ) {
#endif
}
static bool ( *updateDebuggerFnPtr )( idInterpreter *interpreter, idProgram *program, int instructionPointer ) = NULL;
bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ) {
bool ret = false;
if ( interpreter != nullptr && program != nullptr ) {
ret = updateDebuggerFnPtr ? updateDebuggerFnPtr( interpreter , program, instructionPointer ) : false;
}
return ret;
}
/*
===========
idGameLocal::Init
@ -410,6 +420,10 @@ void idGameLocal::Init( void ) {
gamestate = GAMESTATE_NOMAP;
Printf( "...%d aas types\n", aasList.Num() );
//debugger support
common->GetAdditionalFunction( idCommon::FT_UpdateDebugger,( idCommon::FunctionPointer * ) &updateDebuggerFnPtr,NULL);
}
/*
@ -1312,7 +1326,7 @@ void idGameLocal::MapPopulate( void ) {
idGameLocal::InitFromNewMap
===================
*/
void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randseed ) {
void idGameLocal::InitFromNewMap(const char* mapName, idRenderWorld* renderWorld, idSoundWorld* soundWorld, bool isServer, bool isClient, int randseed) {
this->isServer = isServer;
this->isClient = isClient;
@ -2436,14 +2450,14 @@ void idGameLocal::RunTimeGroup2() {
idGameLocal::RunFrame
================
*/
gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) {
idEntity * ent;
int num;
float ms;
idTimer timer_think, timer_events, timer_singlethink;
gameReturn_t ret;
idPlayer *player;
const renderView_t *view;
gameReturn_t idGameLocal::RunFrame(const usercmd_t* clientCmds) {
idEntity* ent;
int num;
float ms;
idTimer timer_think, timer_events, timer_singlethink;
gameReturn_t ret;
idPlayer* player;
const renderView_t* view;
#ifdef _DEBUG
if ( isMultiplayer ) {

View file

@ -365,13 +365,13 @@ public:
virtual const idDict & GetPersistentPlayerInfo( int clientNum );
virtual void SetPersistentPlayerInfo( int clientNum, const idDict &playerInfo );
virtual void InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randSeed );
virtual bool InitFromSaveGame( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, idFile *saveGameFile );
virtual void InitFromNewMap(const char* mapName, idRenderWorld* renderWorld, idSoundWorld* soundWorld, bool isServer, bool isClient, int randSeed );
virtual bool InitFromSaveGame(const char* mapName, idRenderWorld* renderWorld, idSoundWorld* soundWorld, idFile* saveGameFile );
virtual void SaveGame( idFile *saveGameFile );
virtual void MapShutdown( void );
virtual void CacheDictionaryMedia( const idDict *dict );
virtual void SpawnPlayer( int clientNum );
virtual gameReturn_t RunFrame( const usercmd_t *clientCmds );
virtual gameReturn_t RunFrame(const usercmd_t* clientCmds );
virtual bool Draw( int clientNum );
virtual escReply_t HandleESC( idUserInterface **gui );
virtual idUserInterface *StartMenu( void );

View file

@ -33,6 +33,11 @@ If you have questions concerning this license or the applicable additional terms
#include "script/Script_Interpreter.h"
#include "framework/FileSystem.h"
// HvG: Debugger support
extern bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer );
/*
================
idInterpreter::idInterpreter()
@ -183,7 +188,6 @@ idInterpreter::GetRegisterValue
Returns a string representation of the value of the register. This is
used primarily for the debugger and debugging
//FIXME: This is pretty much wrong. won't access data in most situations.
================
*/
bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDepth ) {
@ -191,17 +195,17 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
idVarDef *d;
char funcObject[ 1024 ];
char *funcName;
const idVarDef *scope;
const idVarDef *scope = NULL;
const idVarDef *scopeObj;
const idTypeDef *field;
const idScriptObject *obj;
const function_t *func;
out.Empty();
if ( scopeDepth == -1 ) {
scopeDepth = callStackDepth;
}
}
if ( scopeDepth == callStackDepth ) {
func = currentFunction;
} else {
@ -215,35 +219,44 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
funcName = strstr( funcObject, "::" );
if ( funcName ) {
*funcName = '\0';
scope = gameLocal.program.GetDef( NULL, funcObject, &def_namespace );
funcName += 2;
scopeObj = gameLocal.program.GetDef( NULL, funcObject, &def_namespace );
funcName += 2;
if ( scopeObj )
{
scope = gameLocal.program.GetDef( NULL, funcName, scopeObj );
}
} else {
funcName = funcObject;
scope = &def_namespace;
scope = gameLocal.program.GetDef( NULL, func->Name(), &def_namespace );
scopeObj = NULL;
}
// Get the function from the object
d = gameLocal.program.GetDef( NULL, funcName, scope );
if ( !d ) {
if ( !scope )
{
return false;
}
// Get the variable itself and check various namespaces
d = gameLocal.program.GetDef( NULL, name, d );
if ( !d ) {
if ( scope == &def_namespace ) {
return false;
}
d = gameLocal.program.GetDef( NULL, name, scope );
if ( !d ) {
d = gameLocal.program.GetDef( NULL, name, &def_namespace );
if ( !d ) {
return false;
d = gameLocal.program.GetDef( NULL, name, scope );
// Check the objects for it if it wasnt local to the function
if ( !d )
{
for ( ; scopeObj && scopeObj->TypeDef()->SuperClass(); scopeObj = scopeObj->TypeDef()->SuperClass()->def )
{
d = gameLocal.program.GetDef( NULL, name, scopeObj );
if ( d )
{
break;
}
}
}
}
if ( !d )
{
out = "???";
return false;
}
reg = GetVariable( d );
switch( d->Type() ) {
case ev_float:
@ -256,7 +269,7 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
break;
case ev_vector:
if ( reg.vectorPtr ) {
if ( reg.vectorPtr ) {
out = va( "%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z );
} else {
out = "0,0,0";
@ -274,30 +287,55 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
break;
case ev_field:
{
idEntity* entity;
idScriptObject* obj;
if ( scope == &def_namespace ) {
// should never happen, but handle it safely anyway
return false;
}
field = scope->TypeDef()->GetParmType( reg.ptrOffset )->FieldType();
obj = *reinterpret_cast<const idScriptObject **>( &localstack[ callStack[ callStackDepth ].stackbase ] );
if ( !field || !obj ) {
field = d->TypeDef()->FieldType();
entity = GetEntity ( *((int*)&localstack[ localstackBase ]) );
if ( !entity || !field )
{
return false;
}
obj = &entity->scriptObject;
if ( !obj ) {
return false;
}
switch ( field->Type() ) {
case ev_boolean:
out = va( "%d", *( reinterpret_cast<int *>( &obj->data[ reg.ptrOffset ] ) ) );
return true;
case ev_boolean:
out = va( "%d", *( reinterpret_cast<int *>( &obj->data[ reg.ptrOffset ] ) ) );
return true;
case ev_float:
out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) );
return true;
case ev_float:
out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) );
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:
return false;
default:
return false;
}
break;
}
case ev_string:
if ( reg.stringPtr ) {
@ -313,7 +351,6 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
return false;
}
}
/*
================
idInterpreter::GetCallstackDepth
@ -969,6 +1006,19 @@ bool idInterpreter::Execute( void ) {
// next statement
st = &gameLocal.program.GetStatement( instructionPointer );
if ( !updateGameDebugger( this, &gameLocal.program, instructionPointer )
&& g_debugScript.GetBool( ) )
{
static int lastLineNumber = -1;
if (lastLineNumber != gameLocal.program.GetStatement(instructionPointer).linenumber) {
gameLocal.Printf("%s (%d)\n",
gameLocal.program.GetFilename(gameLocal.program.GetStatement(instructionPointer).file),
gameLocal.program.GetStatement(instructionPointer).linenumber
);
lastLineNumber = gameLocal.program.GetStatement(instructionPointer).linenumber;
}
}
switch( st->op ) {
case OP_RETURN:
LeaveFunction( st->a );
@ -1833,3 +1883,99 @@ bool idInterpreter::Execute( void ) {
return threadDying;
}
bool idGameEditExt::CheckForBreakPointHit(const idInterpreter* interpreter, const function_t* function1, const function_t* function2, int depth) const
{
return ((interpreter->GetCurrentFunction() == function1 ||
interpreter->GetCurrentFunction() == function2) &&
(interpreter->GetCallstackDepth() <= depth));
}
bool idGameEditExt::ReturnedFromFunction(const idProgram* program, const idInterpreter* interpreter, int index) const
{
return (const_cast<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 );
}
}
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;
}
}
if ( clamped || !idStr::IsNumeric( value ) || idStr::FindChar( value, '.' ) ) {
if ( clamped || !idStr::IsNumeric( value ) || (idStr::FindChar( value, '.' )!=-1) ) {
valueString = idStr( integerValue );
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_updateLoadSize( "com_updateLoadSize", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "update the load size after loading a map" );
idCVar com_enableDebuggerServer( "com_enableDebuggerServer", "0", CVAR_BOOL | CVAR_SYSTEM, "toggle debugger server and try to connect to com_dbgClientAdr" );
idCVar com_dbgClientAdr( "com_dbgClientAdr", "localhost", CVAR_SYSTEM | CVAR_ARCHIVE, "debuggerApp client address" );
idCVar com_dbgServerAdr( "com_dbgServerAdr", "localhost", CVAR_SYSTEM | CVAR_ARCHIVE, "debugger server address" );
idCVar com_product_lang_ext( "com_product_lang_ext", "1", CVAR_INTEGER | CVAR_SYSTEM | CVAR_ARCHIVE, "Extension to use when creating language files." );
// com_speeds times
@ -112,6 +116,8 @@ volatile int com_ticNumber; // 60 hz tics
int com_editors; // currently opened editor(s)
bool com_editorActive; // true if an editor has focus
bool com_debuggerSupported; // only set to true when the updateDebugger function is set. see GetAdditionalFunction()
#ifdef _WIN32
HWND com_hwndMsg = NULL;
bool com_outputMsg = false;
@ -245,6 +251,7 @@ idCommonLocal::idCommonLocal( void ) {
com_fullyInitialized = false;
com_refreshOnPrint = false;
com_errorEntered = 0;
com_debuggerSupported = false;
strcpy( errorMessage, "" );
@ -382,12 +389,17 @@ void idCommonLocal::VPrintf( const char *fmt, va_list args ) {
// remove any color codes
idStr::RemoveColors( msg );
// echo to dedicated console and early console
Sys_Printf( "%s", msg );
// print to script debugger server
// DebuggerServerPrint( msg );
if ( com_enableDebuggerServer.GetBool( ) ) {
// print to script debugger server
if ( com_editors & EDITOR_DEBUGGER )
DebuggerServerPrint( msg );
else
// only echo to dedicated console and early console when debugger is not running so no
// deadlocks occur if engine functions called from the debuggerthread trace stuff..
Sys_Printf( "%s", msg );
} else {
Sys_Printf( "%s", msg );
}
#if 0 // !@#
#if defined(_DEBUG) && defined(WIN32)
if ( strlen( msg ) < 512 ) {
@ -1134,8 +1146,14 @@ Com_ScriptDebugger_f
static void Com_ScriptDebugger_f( const idCmdArgs &args ) {
// Make sure it wasnt on the command line
if ( !( com_editors & EDITOR_DEBUGGER ) ) {
common->Printf( "Script debugger is currently disabled\n" );
// DebuggerClientLaunch();
//start debugger server if needed
if ( !com_enableDebuggerServer.GetBool() )
com_enableDebuggerServer.SetBool( true );
//start debugger client.
DebuggerClientLaunch();
}
}
@ -2020,6 +2038,7 @@ void Com_LocalizeMaps_f( const idCmdArgs &args ) {
strCount += LocalizeMap(args.Argv(2), strTable, listHash, excludeList, write);
} else {
idStrList files;
//wow, what now? a hardcoded path?
GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
for ( int i = 0; i < files.Num(); i++ ) {
idStr file = fileSystem->OSPathToRelativePath(files[i]);
@ -2398,6 +2417,14 @@ void idCommonLocal::Frame( void ) {
InitSIMD();
}
if ( com_enableDebuggerServer.IsModified() ) {
if ( com_enableDebuggerServer.GetBool() ) {
DebuggerServerInit();
} else {
DebuggerServerShutdown();
}
}
eventLoop->RunEventLoop();
com_frameTime = com_ticNumber * USERCMD_MSEC;
@ -2666,7 +2693,7 @@ void idCommonLocal::LoadGameDLL( void ) {
gameImport.AASFileManager = ::AASFileManager;
gameImport.collisionModelManager = ::collisionModelManager;
gameExport = *GetGameAPI( &gameImport );
gameExport = *GetGameAPI( &gameImport);
if ( gameExport.version != GAME_API_VERSION ) {
Sys_DLL_Unload( gameDLL );
@ -2709,6 +2736,7 @@ void idCommonLocal::UnloadGameDLL( void ) {
#endif
com_debuggerSupported = false; // HvG: Reset debugger availability.
gameCallbacks.Reset(); // DG: these callbacks are invalid now because DLL has been unloaded
}
@ -3175,14 +3203,15 @@ void idCommonLocal::InitGame( void ) {
// initialize the user interfaces
uiManager->Init();
// startup the script debugger
// DebuggerServerInit();
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04350" ) );
// load the game dll
LoadGameDLL();
// startup the script debugger
if ( com_enableDebuggerServer.GetBool( ) )
DebuggerServerInit( );
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04351" ) );
// init the session
@ -3214,7 +3243,8 @@ void idCommonLocal::ShutdownGame( bool reloading ) {
}
// shutdown the script debugger
// DebuggerServerShutdown();
if ( com_enableDebuggerServer.GetBool() )
DebuggerServerShutdown();
idAsyncNetwork::client.Shutdown();
@ -3277,11 +3307,21 @@ bool idCommonLocal::SetCallback(idCommon::CallbackType cbt, idCommon::FunctionPo
}
}
static bool isDemo(void)
static bool isDemo( void )
{
return sessLocal.IsDemoVersion();
}
static bool updateDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer )
{
if (com_editors & EDITOR_DEBUGGER)
{
DebuggerServerCheckBreakpoint( interpreter, program, instructionPointer );
return true;
}
return false;
}
// returns true if that function is available in this version of dhewm3
// *out_fnptr will be the function (you'll have to cast it probably)
// *out_userArg will be an argument you have to pass to the function, if appropriate (else NULL)
@ -3295,6 +3335,7 @@ bool idCommonLocal::GetAdditionalFunction(idCommon::FunctionType ft, idCommon::F
Warning("Called idCommon::GetAdditionalFunction() with out_fnptr == NULL!\n");
return false;
}
switch(ft)
{
case idCommon::FT_IsDemo:
@ -3302,6 +3343,11 @@ bool idCommonLocal::GetAdditionalFunction(idCommon::FunctionType ft, idCommon::F
// don't set *out_userArg, this function takes no arguments
return true;
case idCommon::FT_UpdateDebugger:
*out_fnptr = (idCommon::FunctionPointer)updateDebugger;
com_debuggerSupported = true;
return true;
default:
*out_fnptr = NULL;
Warning("Called idCommon::SetCallback() with unknown FunctionType %d!\n", ft);
@ -3309,7 +3355,6 @@ bool idCommonLocal::GetAdditionalFunction(idCommon::FunctionType ft, idCommon::F
}
}
idGameCallbacks gameCallbacks;
idGameCallbacks::idGameCallbacks()

View file

@ -73,6 +73,9 @@ extern idCVar com_showAsyncStats;
extern idCVar com_showSoundDecoders;
extern idCVar com_makingBuild;
extern idCVar com_updateLoadSize;
extern idCVar com_enableDebuggerServer;
extern idCVar com_dbgClientAdr;
extern idCVar com_dbgServerAdr;
extern int time_gameFrame; // game logic time
extern int time_gameDraw; // game present time
@ -84,6 +87,8 @@ extern volatile int com_ticNumber; // 60 hz tics, incremented by async functio
extern int com_editors; // current active editor(s)
extern bool com_editorActive; // true if an editor has focus
extern bool com_debuggerSupported; // only set to true when the updateDebugger function is set. see GetAdditionalFunction()
#ifdef _WIN32
const char DMAP_MSGID[] = "DMAPOutput";
const char DMAP_DONE[] = "DMAPDone";
@ -111,6 +116,8 @@ struct MemInfo_t {
};
class idLangDict;
class idInterpreter;
class idProgram;
class idCommon {
public:
@ -158,6 +165,7 @@ public:
// Writes cvars with the given flags to a file.
virtual void WriteFlaggedCVarsToFile( const char *filename, int flags, const char *setCmd ) = 0;
// Begins redirection of console output to the given buffer.
virtual void BeginRedirect( char *buffer, int buffersize, void (*flush)( const char * ) ) = 0;
@ -265,6 +273,11 @@ public:
// it returns true if we're currently running the doom3 demo
// not relevant for mods, only for game/ aka base.dll/base.so/...
FT_IsDemo = 1,
// the function's signature is bool fn(idInterpreter,idProgram,int) with arguments:
// idInterpreter *interpreter, idProgram *program, int instructionPointer
// it returns true if the game debugger is active.
// relevant for mods.
FT_UpdateDebugger,
};
// returns true if that function is available in this version of dhewm3

View file

@ -886,10 +886,20 @@ const char *idFileSystemLocal::OSPathToRelativePath( const char *OSPath ) {
}
if ( base ) {
s = strstr( base, "/" );
if ( !s ) {
s = strstr( base, "\\" );
// DG: on Windows base might look like "base\\pak008.pk4/script/doom_util.script"
// while on Linux it'll be more like "base/pak008.pk4/script/doom_util.script"
// I /think/ we want to get rid of the bla.pk4 part, at least that's what happens implicitly on Windows
// (I hope these problems don't exist if the file is not from a .pk4, so that case is handled like before)
s = strstr( base, ".pk4/" );
if ( s != NULL ) {
s += 4; // skip ".pk4", but *not* the following '/', that'll be skipped below
} else {
s = strchr( base, '/' );
if ( s == NULL ) {
s = strchr( base, '\\' );
}
}
if ( s ) {
strcpy( relativePath, s + 1 );
if ( fs_debug.GetInteger() > 1 ) {

View file

@ -31,6 +31,7 @@ If you have questions concerning this license or the applicable additional terms
#include "idlib/BitMsg.h"
#include "idlib/Dict.h"
#include "idlib/containers/StrList.h"
#include "framework/UsercmdGen.h"
#include "renderer/RenderWorld.h"
#include "sound/sound.h"
@ -232,6 +233,11 @@ enum {
class idEntity;
class idMD5Anim;
class idThread;
class function_t;
class idProgram;
class idInterpreter;
typedef struct prstack_s prstack_t;
// FIXME: this interface needs to be reworked but it properly separates code for the time being
class idGameEdit {
@ -294,7 +300,7 @@ public:
virtual void EntitySetColor( idEntity *ent, const idVec3 color );
// Player methods.
virtual bool PlayerIsValid() const;
virtual bool PlayerIsValid( ) const;
virtual void PlayerGetOrigin( idVec3 &org ) const;
virtual void PlayerGetAxis( idMat3 &axis ) const;
virtual void PlayerGetViewAngles( idAngles &angles ) const;
@ -310,11 +316,44 @@ public:
virtual int MapGetEntitiesMatchingClassWithString( const char *classname, const char *match, const char *list[], const int max ) const;
virtual void MapRemoveEntity( const char *name ) const;
virtual void MapEntityTranslate( const char *name, const idVec3 &v ) const;
};
extern idGameEdit * gameEdit;
// In game script Debugging Support
class idGameEditExt : public idGameEdit {
public:
virtual ~idGameEditExt( void ) { }
// IdProgram
virtual void GetLoadedScripts( idStrList ** result );
virtual bool IsLineCode( const char* filename, int linenumber) const;
virtual const char * GetFilenameForStatement( idProgram* program, int index ) const;
virtual int GetLineNumberForStatement( idProgram* program, int index ) const;
// idInterpreter
virtual bool CheckForBreakPointHit( const idInterpreter* interpreter, const function_t* function1, const function_t* function2, int depth ) const;
virtual bool ReturnedFromFunction( const idProgram* program, const idInterpreter* interpreter, int index ) const;
virtual bool GetRegisterValue( const idInterpreter* interpreter, const char* name, idStr& out, int scopeDepth ) const;
virtual const idThread* GetThread( const idInterpreter* interpreter ) const;
virtual int GetInterpreterCallStackDepth( const idInterpreter* interpreter );
virtual const function_t* GetInterpreterCallStackFunction( const idInterpreter* interpreter, int stackDepth = -1 );
// IdThread
virtual const char * ThreadGetName( const idThread* thread ) const;
virtual int ThreadGetNum( const idThread* thread ) const;
virtual bool ThreadIsDoneProcessing( const idThread* thread ) const;
virtual bool ThreadIsWaiting( const idThread* thread ) const;
virtual bool ThreadIsDying( const idThread* thread ) const;
virtual int GetTotalScriptThreads( ) const;
virtual const idThread* GetThreadByIndex( int index ) const;
// MSG helpers
virtual void MSG_WriteThreadInfo( idBitMsg* msg, const idThread* thread, const idInterpreter* interpreter );
virtual void MSG_WriteCallstackFunc( idBitMsg* msg, const prstack_t* stack, const idProgram* program, int instructionPtr );
virtual void MSG_WriteInterpreterInfo( idBitMsg* msg, const idInterpreter* interpreter, const idProgram* program, int instructionPtr );
virtual void MSG_WriteScriptList( idBitMsg* msg );
};
/*
===============================================================================

View file

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

View file

@ -670,7 +670,7 @@ void idEditEntities::DisplayEntities( void ) {
===============================================================================
*/
idGameEdit gameEditLocal;
idGameEditExt gameEditLocal;
idGameEdit * gameEdit = &gameEditLocal;
@ -1146,3 +1146,64 @@ void idGameEdit::MapEntityTranslate( const char *name, const idVec3 &v ) const {
}
}
}
/***********************************************************************
Debugger
***********************************************************************/
bool idGameEditExt::IsLineCode(const char* filename, int linenumber) const
{
idStr fileStr;
idProgram* program = &gameLocal.program;
for (int i = 0; i < program->NumStatements(); i++)
{
fileStr = program->GetFilename(program->GetStatement(i).file);
fileStr.BackSlashesToSlashes();
if (strcmp(filename, fileStr.c_str()) == 0
&& program->GetStatement(i).linenumber == linenumber
)
{
return true;
}
}
return false;
}
void idGameEditExt::GetLoadedScripts( idStrList** result )
{
(*result)->Clear();
idProgram* program = &gameLocal.program;
for (int i = 0; i < program->NumFilenames(); i++)
{
(*result)->AddUnique( idStr(program->GetFilename( i )) );
}
}
void idGameEditExt::MSG_WriteScriptList( idBitMsg* msg)
{
idProgram* program = &gameLocal.program;
msg->WriteInt( program->NumFilenames() );
for (int i = 0; i < program->NumFilenames(); i++)
{
idStr file = program->GetFilename(i);
//fix this. it seams that scripts triggered by the runtime are stored with a wrong path
//the use // instead of '\'
file.BackSlashesToSlashes();
msg->WriteString(file);
}
}
const char*idGameEditExt::GetFilenameForStatement(idProgram* program, int index) const
{
return program->GetFilenameForStatement(index);
}
int idGameEditExt::GetLineNumberForStatement(idProgram* program, int index) const
{
return program->GetLineNumberForStatement(index);
}

View file

@ -270,6 +270,14 @@ bool IsDoom3DemoVersion()
return ret;
}
static bool ( *updateDebuggerFnPtr )( idInterpreter *interpreter, idProgram *program, int instructionPointer ) = NULL;
bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ) {
bool ret = false;
if ( interpreter != nullptr && program != nullptr ) {
ret = updateDebuggerFnPtr ? updateDebuggerFnPtr( interpreter, program, instructionPointer ) : false;
}
return ret;
}
/*
@ -352,6 +360,8 @@ void idGameLocal::Init( void ) {
// DG: hack to support the Demo version of Doom3
common->GetAdditionalFunction(idCommon::FT_IsDemo, (idCommon::FunctionPointer*)&isDemoFnPtr, NULL);
//debugger support
common->GetAdditionalFunction(idCommon::FT_UpdateDebugger,(idCommon::FunctionPointer*) &updateDebuggerFnPtr,NULL);
}
/*
@ -1192,7 +1202,7 @@ void idGameLocal::MapPopulate( void ) {
idGameLocal::InitFromNewMap
===================
*/
void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randseed ) {
void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randseed) {
this->isServer = isServer;
this->isClient = isClient;
@ -2216,13 +2226,13 @@ idGameLocal::RunFrame
================
*/
gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) {
idEntity * ent;
int num;
float ms;
idTimer timer_think, timer_events, timer_singlethink;
gameReturn_t ret;
idPlayer *player;
const renderView_t *view;
idEntity * ent;
int num;
float ms;
idTimer timer_think, timer_events, timer_singlethink;
gameReturn_t ret;
idPlayer *player;
const renderView_t *view;
#ifdef _DEBUG
if ( isMultiplayer ) {

View file

@ -33,6 +33,11 @@ If you have questions concerning this license or the applicable additional terms
#include "script/Script_Interpreter.h"
#include "framework/FileSystem.h"
// HvG: Debugger support
extern bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer );
/*
================
idInterpreter::idInterpreter()
@ -183,7 +188,6 @@ idInterpreter::GetRegisterValue
Returns a string representation of the value of the register. This is
used primarily for the debugger and debugging
//FIXME: This is pretty much wrong. won't access data in most situations.
================
*/
bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDepth ) {
@ -191,17 +195,17 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
idVarDef *d;
char funcObject[ 1024 ];
char *funcName;
const idVarDef *scope;
const idVarDef *scope = NULL;
const idVarDef *scopeObj;
const idTypeDef *field;
const idScriptObject *obj;
const function_t *func;
out.Empty();
if ( scopeDepth == -1 ) {
scopeDepth = callStackDepth;
}
}
if ( scopeDepth == callStackDepth ) {
func = currentFunction;
} else {
@ -215,35 +219,44 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
funcName = strstr( funcObject, "::" );
if ( funcName ) {
*funcName = '\0';
scope = gameLocal.program.GetDef( NULL, funcObject, &def_namespace );
funcName += 2;
scopeObj = gameLocal.program.GetDef( NULL, funcObject, &def_namespace );
funcName += 2;
if ( scopeObj )
{
scope = gameLocal.program.GetDef( NULL, funcName, scopeObj );
}
} else {
funcName = funcObject;
scope = &def_namespace;
scope = gameLocal.program.GetDef( NULL, func->Name(), &def_namespace );
scopeObj = NULL;
}
// Get the function from the object
d = gameLocal.program.GetDef( NULL, funcName, scope );
if ( !d ) {
if ( !scope )
{
return false;
}
// Get the variable itself and check various namespaces
d = gameLocal.program.GetDef( NULL, name, d );
if ( !d ) {
if ( scope == &def_namespace ) {
return false;
}
d = gameLocal.program.GetDef( NULL, name, scope );
if ( !d ) {
d = gameLocal.program.GetDef( NULL, name, &def_namespace );
if ( !d ) {
return false;
d = gameLocal.program.GetDef( NULL, name, scope );
// Check the objects for it if it wasnt local to the function
if ( !d )
{
for ( ; scopeObj && scopeObj->TypeDef()->SuperClass(); scopeObj = scopeObj->TypeDef()->SuperClass()->def )
{
d = gameLocal.program.GetDef( NULL, name, scopeObj );
if ( d )
{
break;
}
}
}
}
if ( !d )
{
out = "???";
return false;
}
reg = GetVariable( d );
switch( d->Type() ) {
case ev_float:
@ -256,7 +269,7 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
break;
case ev_vector:
if ( reg.vectorPtr ) {
if ( reg.vectorPtr ) {
out = va( "%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z );
} else {
out = "0,0,0";
@ -274,30 +287,55 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
break;
case ev_field:
{
idEntity* entity;
idScriptObject* obj;
if ( scope == &def_namespace ) {
// should never happen, but handle it safely anyway
return false;
}
field = scope->TypeDef()->GetParmType( reg.ptrOffset )->FieldType();
obj = *reinterpret_cast<const idScriptObject **>( &localstack[ callStack[ callStackDepth ].stackbase ] );
if ( !field || !obj ) {
field = d->TypeDef()->FieldType();
entity = GetEntity ( *((int*)&localstack[ localstackBase ]) );
if ( !entity || !field )
{
return false;
}
obj = &entity->scriptObject;
if ( !obj ) {
return false;
}
switch ( field->Type() ) {
case ev_boolean:
out = va( "%d", *( reinterpret_cast<int *>( &obj->data[ reg.ptrOffset ] ) ) );
return true;
case ev_boolean:
out = va( "%d", *( reinterpret_cast<int *>( &obj->data[ reg.ptrOffset ] ) ) );
return true;
case ev_float:
out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) );
return true;
case ev_float:
out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) );
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:
return false;
default:
return false;
}
break;
}
case ev_string:
if ( reg.stringPtr ) {
@ -969,6 +1007,19 @@ bool idInterpreter::Execute( void ) {
// next statement
st = &gameLocal.program.GetStatement( instructionPointer );
if ( !updateGameDebugger( this, &gameLocal.program, instructionPointer )
&& g_debugScript.GetBool( ) )
{
static int lastLineNumber = -1;
if ( lastLineNumber != gameLocal.program.GetStatement ( instructionPointer ).linenumber ) {
gameLocal.Printf ( "%s (%d)\n",
gameLocal.program.GetFilename ( gameLocal.program.GetStatement ( instructionPointer ).file ),
gameLocal.program.GetStatement ( instructionPointer ).linenumber
);
lastLineNumber = gameLocal.program.GetStatement ( instructionPointer ).linenumber;
}
}
switch( st->op ) {
case OP_RETURN:
LeaveFunction( st->a );
@ -1833,3 +1884,98 @@ bool idInterpreter::Execute( void ) {
return threadDying;
}
bool idGameEditExt::CheckForBreakPointHit(const idInterpreter* interpreter, const function_t* function1, const function_t* function2, int depth) const
{
return ( ( interpreter->GetCurrentFunction ( ) == function1 ||
interpreter->GetCurrentFunction ( ) == function2)&&
( interpreter->GetCallstackDepth ( ) <= depth) );
}
bool idGameEditExt::ReturnedFromFunction(const idProgram* program, const idInterpreter* interpreter, int index) const
{
return ( const_cast<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 "gamesys/SysCvar.h"
#include "Player.h"
#include "Camera.h"
#include "game/gamesys/SysCvar.h"
#include "game/Player.h"
#include "game/Camera.h"
#include "script/Script_Thread.h"
#include "Script_Thread.h"
const idEventDef EV_Thread_Execute( "<execute>", NULL );
const idEventDef EV_Thread_SetCallback( "<script_setcallback>", NULL );
@ -1841,3 +1841,49 @@ void idThread::Event_InfluenceActive( void ) {
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 WriteUShort( int c );
void WriteInt( int c );
void WriteFloat( float f );
void WriteFloat( float f );
void WriteFloat( float f, int exponentBits, int mantissaBits );
void WriteAngle8( float f );
void WriteAngle16( float f );

View file

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

View file

@ -126,21 +126,21 @@ IDI_DBG_EMPTY ICON "res\\dbg_empty.ico"
IDR_DBG_ACCELERATORS ACCELERATORS
BEGIN
VK_F9, ID_DBG_DEBUG_QUICKWATCH, VIRTKEY, SHIFT, NOINVERT
VK_F5, ID_DBG_DEBUG_RUN, VIRTKEY, NOINVERT
VK_F11, ID_DBG_DEBUG_STEPINTO, VIRTKEY, NOINVERT
VK_F10, ID_DBG_DEBUG_STEPOVER, VIRTKEY, NOINVERT
VK_F9, ID_DBG_DEBUG_TOGGLEBREAKPOINT, VIRTKEY, NOINVERT
VK_F3, ID_DBG_EDIT_FINDNEXT, VIRTKEY, NOINVERT
VK_F3, ID_DBG_EDIT_FINDPREV, VIRTKEY, SHIFT, NOINVERT
VK_F3, ID_DBG_EDIT_FINDSELECTED, VIRTKEY, CONTROL, NOINVERT
VK_F3, ID_DBG_EDIT_FINDSELECTED, VIRTKEY, CONTROL, NOINVERT
VK_F3, ID_DBG_EDIT_FINDSELECTEDPREV, VIRTKEY, SHIFT, CONTROL,
NOINVERT
VK_F4, ID_DBG_FILE_CLOSE, VIRTKEY, CONTROL, NOINVERT
VK_TAB, ID_DBG_FILE_NEXT, VIRTKEY, CONTROL, NOINVERT
"O", ID_DBG_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
"F", ID_DBG_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT
VK_F9, ID_DBG_DEBUG_QUICKWATCH, VIRTKEY, SHIFT, NOINVERT
VK_F5, ID_DBG_DEBUG_RUN, VIRTKEY, NOINVERT
VK_F11, ID_DBG_DEBUG_STEPINTO, VIRTKEY, NOINVERT
VK_F10, ID_DBG_DEBUG_STEPOVER, VIRTKEY, NOINVERT
VK_F9, ID_DBG_DEBUG_TOGGLEBREAKPOINT, VIRTKEY, NOINVERT
VK_F3, ID_DBG_EDIT_FINDNEXT, VIRTKEY, NOINVERT
VK_F3, ID_DBG_EDIT_FINDPREV, VIRTKEY, SHIFT, NOINVERT
VK_F3, ID_DBG_EDIT_FINDSELECTED, VIRTKEY, CONTROL, NOINVERT
VK_F3, ID_DBG_EDIT_FINDSELECTEDPREV, VIRTKEY, SHIFT, CONTROL,NOINVERT
VK_F4, ID_DBG_FILE_CLOSE, VIRTKEY, CONTROL, NOINVERT
VK_TAB, ID_DBG_FILE_NEXT, VIRTKEY, CONTROL, NOINVERT
VK_RETURN, ID_DBG_SEND_COMMAND, VIRTKEY, NOINVERT
"O", ID_DBG_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
"F", ID_DBG_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT
END
@ -149,17 +149,17 @@ END
// Dialog
//
IDD_DBG_ABOUT DIALOGEX 0, 0, 222, 71
IDD_DBG_ABOUT DIALOGEX 0, 0, 250, 90
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP |
WS_CAPTION | WS_SYSMENU
CAPTION "About Script Debugger"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Script Debugger",IDC_STATIC,35,7,81,8
LTEXT "Version 0.01",IDC_STATIC,35,17,41,8
LTEXT "Original version by Raven",IDC_STATIC,35,
28,134,8
DEFPUSHBUTTON "OK",IDOK,165,50,50,14
LTEXT "Version 1.1",IDC_STATIC,35,17,41,8
LTEXT "Original version by Raven", IDC_STATIC, 35,28, 134, 8
LTEXT "Dhewm3 version by Harrie van Ginneken \n\t\t\t\tand\n\t\t\t Daniel Gibson", IDC_STATIC, 35, 38, 134, 32
DEFPUSHBUTTON "OK",IDOK,100,68,50,14
ICON 5098,IDC_STATIC,7,7,20,20
END

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

View file

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

View file

@ -673,7 +673,30 @@ Sys_DLL_GetProcAddress
=====================
*/
void *Sys_DLL_GetProcAddress( uintptr_t dllHandle, const char *procName ) {
return (void *)GetProcAddress( (HINSTANCE)dllHandle, procName );
void * adr = (void*)GetProcAddress((HINSTANCE)dllHandle, procName);
if (!adr)
{
DWORD e = GetLastError();
LPVOID msgBuf = nullptr;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
e,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&msgBuf,
0, NULL);
idStr errorStr = va("[%i (0x%X)]\t%s", e, e, msgBuf);
if (errorStr.Length())
common->Warning("GetProcAddress( %i %s) Failed ! %s", dllHandle, procName, errorStr.c_str());
::LocalFree(msgBuf);
}
return adr;
}
/*
@ -1031,7 +1054,10 @@ int main(int argc, char *argv[]) {
// Launch the script debugger
if ( strstr( GetCommandLine(), "+debugger" ) ) {
// DebuggerClientInit( lpCmdLine );
#ifdef ID_ALLOW_TOOLS
DebuggerClientInit(GetCommandLine());
#endif
return 0;
}

View file

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

View file

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

View file

@ -29,7 +29,7 @@ If you have questions concerning this license or the applicable additional terms
#define DEBUGGERAPP_H_
#include "../../sys/win32/win_local.h"
#include "../../framework/sync/Msg.h"
//#include "../../framework/sync/Msg.h"
#ifndef REGISTRYOPTIONS_H_
#include "../common/RegistryOptions.h"
@ -49,13 +49,15 @@ If you have questions concerning this license or the applicable additional terms
// These were changed to static by ID so to make it easy we just throw them
// in this header
const int MAX_MSGLEN = 1400;
// we need a lot to be able to list all threads in mars_city1
const int MAX_MSGLEN = 8600;
class rvDebuggerApp
{
public:
rvDebuggerApp ( );
~rvDebuggerApp();
bool Initialize ( HINSTANCE hInstance );
int Run ( void );

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

View file

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

View file

@ -73,7 +73,7 @@ bool rvDebuggerClient::Initialize ( void )
}
// Server must be running on the local host on port 28980
Sys_StringToNetAdr ( "localhost", &mServerAdrt, true );
Sys_StringToNetAdr ( com_dbgServerAdr.GetString( ), &mServerAdr, true );
mServerAdr.port = 27980;
// Attempt to let the server know we are here. The server may not be running so this
@ -110,25 +110,29 @@ Process all incomding messages from the debugger server
bool rvDebuggerClient::ProcessMessages ( void )
{
netadr_t adrFrom;
msg_t msg;
idBitMsg msg;
byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) );
msg.SetSize(MAX_MSGLEN);
msg.BeginReading();
int msgSize;
// Check for pending udp packets on the debugger port
while ( mPort.GetPacket ( adrFrom, msg.data, msg.cursize, msg.maxsize ) )
while ( mPort.GetPacket ( adrFrom, buffer,msgSize, MAX_MSGLEN) )
{
unsigned short command;
short command;
msg.Init(buffer, sizeof(buffer));
msg.SetSize(msgSize);
msg.BeginReading();
// Only accept packets from the debugger server for security reasons
if ( !Sys_CompareNetAdrBase ( adrFrom, mServerAdr ) )
{
continue;
}
command = msg.ReadShort ( );
command = (unsigned short) MSG_ReadShort ( &msg );
// Is this what we are waiting for?
// Is this what we are waiting for?
if ( command == mWaitFor )
{
mWaitFor = DBMSG_UNKNOWN;
@ -168,17 +172,39 @@ bool rvDebuggerClient::ProcessMessages ( void )
case DBMSG_INSPECTVARIABLE:
HandleInspectVariable ( &msg );
break;
case DBMSG_REMOVEBREAKPOINT:
HandleRemoveBreakpoint( &msg );
break;
case DBMSG_INSPECTSCRIPTS:
HandleInspectScripts( &msg );
break;
}
// Give the window a chance to process the message
msg.readcount = 0;
msg.bit = 0;
msg.SetReadCount(0);
msg.SetReadBit(0);
gDebuggerApp.GetWindow().ProcessNetMessage ( &msg );
}
return true;
}
void rvDebuggerClient::HandleRemoveBreakpoint(idBitMsg* msg)
{
long lineNumber;
char filename[MAX_PATH];
// Read the breakpoint info
lineNumber = msg->ReadInt();
msg->ReadString(filename, MAX_PATH);
rvDebuggerBreakpoint* bp = FindBreakpoint(filename, lineNumber);
if(bp)
RemoveBreakpoint(bp->GetID());
}
/*
================
rvDebuggerClient::HandleBreak
@ -187,19 +213,22 @@ Handle the DBMSG_BREAK message send from the server. This message is handled
by caching the file and linenumber where the break occured.
================
*/
void rvDebuggerClient::HandleBreak ( msg_t* msg )
void rvDebuggerClient::HandleBreak ( idBitMsg* msg )
{
char filename[MAX_PATH];
mBreak = true;
// Line number
mBreakLineNumber = MSG_ReadInt ( msg );
mBreakLineNumber = msg->ReadInt ( );
// Filename
MSG_ReadString ( msg, filename, MAX_PATH );
msg->ReadString ( filename, MAX_PATH );
mBreakFilename = filename;
//int64_t ptr64b = msg->ReadInt64();
//mBreakProgram = (idProgram*)ptr64b;
// Clear the variables
mVariables.Clear ( );
@ -211,6 +240,26 @@ void rvDebuggerClient::HandleBreak ( msg_t* msg )
WaitFor ( DBMSG_INSPECTTHREADS, 2000 );
}
/*
================
rvDebuggerClient::InspectScripts
Instructs the client to inspect the loaded scripts
================
*/
void rvDebuggerClient::InspectScripts ( void )
{
idBitMsg msg;
byte buffer[MAX_MSGLEN];
msg.Init(buffer, sizeof(buffer));
msg.BeginWriting();
msg.WriteShort((short)DBMSG_INSPECTSCRIPTS);
SendPacket(msg.GetData(), msg.GetSize());
}
/*
================
rvDebuggerClient::InspectVariable
@ -222,15 +271,41 @@ will in turn respond back to the client with the variable value
*/
void rvDebuggerClient::InspectVariable ( const char* name, int callstackDepth )
{
msg_t msg;
byte buffer[MAX_MSGLEN];
idBitMsg msg;
byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_INSPECTVARIABLE );
MSG_WriteShort ( &msg, (short)(mCallstack.Num()-callstackDepth) );
MSG_WriteString ( &msg, name );
msg.Init( buffer, sizeof( buffer ) );
msg.BeginWriting();
msg.WriteShort ( (short)DBMSG_INSPECTVARIABLE );
msg.WriteShort ( (short)(mCallstack.Num()-callstackDepth) );
msg.WriteString ( name );
SendPacket ( msg.data, msg.cursize );
SendPacket ( msg.GetData(), msg.GetSize());
}
/*
================
rvDebuggerClient::HandleInspectScripts
Handle the message DBMSG_INSPECTSCRIPTS being sent from the server. This message
is handled by adding the script entries to a list for later lookup.
================
*/
void rvDebuggerClient::HandleInspectScripts( idBitMsg* msg )
{
int totalScripts;
mServerScripts.Clear();
// Read all of the callstack entries specfied in the message
for (totalScripts = msg->ReadInt(); totalScripts > 0; totalScripts--)
{
char temp[1024];
// Script Name
msg->ReadString(temp, 1024);
mServerScripts.Append(temp);
}
}
/*
@ -241,29 +316,29 @@ Handle the message DBMSG_INSPECTCALLSTACK being sent from the server. This mess
is handled by adding the callstack entries to a list for later lookup.
================
*/
void rvDebuggerClient::HandleInspectCallstack ( msg_t* msg )
void rvDebuggerClient::HandleInspectCallstack ( idBitMsg* msg )
{
int depth;
ClearCallstack ( );
// Read all of the callstack entries specfied in the message
for ( depth = (short)MSG_ReadShort ( msg ) ; depth > 0; depth -- )
for ( depth = (short)msg->ReadShort ( ) ; depth > 0; depth -- )
{
rvDebuggerCallstack* entry = new rvDebuggerCallstack;
char temp[1024];
// Function name
MSG_ReadString ( msg, temp, 1024 );
entry->mFunction = temp;
msg->ReadString ( temp, 1024 );
entry->mFunction = idStr(temp);
// Filename
MSG_ReadString ( msg, temp, 1024 );
entry->mFilename = temp;
msg->ReadString ( temp, 1024 );
entry->mFilename = idStr(temp);
// Line Number
entry->mLineNumber = MSG_ReadInt ( msg );
entry->mLineNumber = msg->ReadInt ( );
// Add to list
mCallstack.Append ( entry );
@ -278,31 +353,31 @@ Handle the message DBMSG_INSPECTTHREADS being sent from the server. This messag
is handled by adding the list of threads to a list for later lookup.
================
*/
void rvDebuggerClient::HandleInspectThreads ( msg_t* msg )
void rvDebuggerClient::HandleInspectThreads ( idBitMsg* msg )
{
int count;
ClearThreads ( );
// Loop over the number of threads in the message
for ( count = (short)MSG_ReadShort ( msg ) ; count > 0; count -- )
for ( count = (short)msg->ReadShort ( ) ; count > 0; count -- )
{
rvDebuggerThread* entry = new rvDebuggerThread;
char temp[1024];
// Thread name
MSG_ReadString ( msg, temp, 1024 );
msg->ReadString ( temp, 1024 );
entry->mName = temp;
// Thread ID
entry->mID = MSG_ReadInt ( msg );
entry->mID = msg->ReadInt ( );
// Thread state
entry->mCurrent = MSG_ReadBits ( msg, 1 ) ? true : false;
entry->mDoneProcessing = MSG_ReadBits ( msg, 1 ) ? true : false;
entry->mWaiting = MSG_ReadBits ( msg, 1 ) ? true : false;
entry->mDying = MSG_ReadBits ( msg, 1 ) ? true : false;
entry->mCurrent = msg->ReadBits ( 1 ) ? true : false;
entry->mDoneProcessing = msg->ReadBits ( 1 ) ? true : false;
entry->mWaiting = msg->ReadBits ( 1 ) ? true : false;
entry->mDying = msg->ReadBits ( 1 ) ? true : false;
// Add thread to list
mThreads.Append ( entry );
@ -317,15 +392,15 @@ Handle the message DBMSG_INSPECTVARIABLE being sent from the server. This messa
is handled by adding the inspected variable to a dictionary for later lookup
================
*/
void rvDebuggerClient::HandleInspectVariable ( msg_t* msg )
void rvDebuggerClient::HandleInspectVariable ( idBitMsg* msg )
{
char var[1024];
char value[1024];
int callDepth;
callDepth = (short)MSG_ReadShort ( msg );
MSG_ReadString ( msg, var, 1024 );
MSG_ReadString ( msg, value, 1024 );
callDepth = (short)msg->ReadShort ( );
msg->ReadString ( var, 1024 );
msg->ReadString ( value, 1024 );
mVariables.Set ( va("%d:%s", mCallstack.Num()-callDepth, var), value );
}
@ -422,7 +497,7 @@ Adds a breakpoint to the client and server with the give nfilename and linenumbe
*/
int rvDebuggerClient::AddBreakpoint ( const char* filename, int lineNumber, bool onceOnly )
{
int index = mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber ) );
int index = mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber, -1, onceOnly ) );
SendAddBreakpoint ( *mBreakpoints[index] );
@ -463,13 +538,14 @@ Send a message with no data to the debugger server
*/
void rvDebuggerClient::SendMessage ( EDebuggerMessage dbmsg )
{
msg_t msg;
idBitMsg msg;
byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)dbmsg );
msg.Init ( buffer, sizeof( buffer ) );
msg.BeginWriting ( );
msg.WriteShort ( (short)dbmsg );
SendPacket ( msg.data, msg.cursize );
SendPacket ( msg.GetData(), msg.GetSize() );
}
/*
@ -502,9 +578,9 @@ rvDebuggerClient::SendAddBreakpoint
Send an individual breakpoint over to the debugger server
================
*/
void rvDebuggerClient::SendAddBreakpoint ( rvDebuggerBreakpoint& bp, bool onceOnly )
void rvDebuggerClient::SendAddBreakpoint ( rvDebuggerBreakpoint& bp )
{
msg_t msg;
idBitMsg msg;
byte buffer[MAX_MSGLEN];
if ( !mConnected )
@ -512,14 +588,15 @@ void rvDebuggerClient::SendAddBreakpoint ( rvDebuggerBreakpoint& bp, bool onceOn
return;
}
MSG_Init( &msg, buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_ADDBREAKPOINT );
MSG_WriteBits ( &msg, onceOnly?1:0, 1 );
MSG_WriteInt ( &msg, (unsigned long) bp.GetLineNumber ( ) );
MSG_WriteInt ( &msg, bp.GetID ( ) );
MSG_WriteString ( &msg, bp.GetFilename() );
msg.Init( buffer, sizeof( buffer ) );
msg.BeginWriting();
msg.WriteShort ( (short)DBMSG_ADDBREAKPOINT );
msg.WriteBits ( bp.GetOnceOnly() ? 1 : 0, 1 );
msg.WriteInt ( (unsigned long) bp.GetLineNumber ( ) );
msg.WriteInt ( bp.GetID ( ) );
msg.WriteString ( bp.GetFilename() ); // FIXME: this implies make7bit ?!
SendPacket ( msg.data, msg.cursize );
SendPacket ( msg.GetData(), msg.GetSize() );
}
/*
@ -531,7 +608,7 @@ Sends a remove breakpoint message to the debugger server
*/
void rvDebuggerClient::SendRemoveBreakpoint ( rvDebuggerBreakpoint& bp )
{
msg_t msg;
idBitMsg msg;
byte buffer[MAX_MSGLEN];
if ( !mConnected )
@ -539,11 +616,12 @@ void rvDebuggerClient::SendRemoveBreakpoint ( rvDebuggerBreakpoint& bp )
return;
}
MSG_Init( &msg, buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_REMOVEBREAKPOINT );
MSG_WriteInt ( &msg, bp.GetID() );
msg.Init ( buffer, sizeof( buffer ) );
msg.BeginWriting( );
msg.WriteShort ( (short)DBMSG_REMOVEBREAKPOINT );
msg.WriteInt ( bp.GetID() );
SendPacket ( msg.data, msg.cursize );
SendPacket ( msg.GetData(), msg.GetSize() );
}
/*
@ -583,3 +661,25 @@ void rvDebuggerClient::ClearThreads ( void )
mThreads.Clear ( );
}
/*
================
rvDebuggerClient::SendCommand
================
*/
void rvDebuggerClient::SendCommand( const char *cmdStr )
{
idBitMsg msg;
byte buffer[MAX_MSGLEN];
if ( !mConnected ) {
return;
}
msg.Init( buffer, sizeof( buffer ) );
msg.BeginWriting( );
msg.WriteShort( ( short ) DBMSG_EXECCOMMAND );
msg.WriteString( cmdStr ); // FIXME: this implies make7bit ?!
SendPacket( msg.GetData( ), msg.GetSize( ) );
}

View file

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

View file

@ -53,7 +53,7 @@ Launch the dialog
*/
bool rvDebuggerFindDlg::DoModal ( rvDebuggerWindow* parent )
{
if ( DialogBoxParam ( parent->GetInstance(), MAKEINTRESOURCE(IDD_DBG_FIND), parent->GetWindow(), DlgProc, (LONG)this ) )
if ( DialogBoxParam ( parent->GetInstance(), MAKEINTRESOURCE(IDD_DBG_FIND), parent->GetWindow(), DlgProc, (LPARAM)this ) )
{
return true;
}
@ -70,7 +70,7 @@ Dialog Procedure for the find dialog
*/
INT_PTR CALLBACK rvDebuggerFindDlg::DlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
rvDebuggerFindDlg* dlg = (rvDebuggerFindDlg*) GetWindowLong ( wnd, GWL_USERDATA );
rvDebuggerFindDlg* dlg = (rvDebuggerFindDlg*) GetWindowLongPtr ( wnd, GWLP_USERDATA);
switch ( msg )
{
@ -80,7 +80,8 @@ INT_PTR CALLBACK rvDebuggerFindDlg::DlgProc ( HWND wnd, UINT msg, WPARAM wparam,
case WM_INITDIALOG:
dlg = (rvDebuggerFindDlg*) lparam;
SetWindowLong ( wnd, GWL_USERDATA, (LONG) dlg );
SetWindowLongPtr ( wnd, GWLP_USERDATA, (LONG_PTR) dlg );
dlg->mWnd = wnd;
SetWindowText ( GetDlgItem ( dlg->mWnd, IDC_DBG_FIND ), dlg->mFindText );
return TRUE;

View file

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

View file

@ -55,7 +55,7 @@ bool rvDebuggerQuickWatchDlg::DoModal ( rvDebuggerWindow* window, int callstackD
mDebuggerWindow = window;
mVariable = variable?variable:"";
DialogBoxParam ( window->GetInstance(), MAKEINTRESOURCE(IDD_DBG_QUICKWATCH), window->GetWindow(), DlgProc, (LONG)this );
DialogBoxParam ( window->GetInstance(), MAKEINTRESOURCE(IDD_DBG_QUICKWATCH), window->GetWindow(), DlgProc, (LPARAM)this );
return true;
}
@ -69,7 +69,7 @@ Dialog Procedure for the quick watch dialog
*/
INT_PTR CALLBACK rvDebuggerQuickWatchDlg::DlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
rvDebuggerQuickWatchDlg* dlg = (rvDebuggerQuickWatchDlg*) GetWindowLong ( wnd, GWL_USERDATA );
rvDebuggerQuickWatchDlg* dlg = (rvDebuggerQuickWatchDlg*) GetWindowLongPtr ( wnd, GWLP_USERDATA);
switch ( msg )
{
@ -128,7 +128,7 @@ INT_PTR CALLBACK rvDebuggerQuickWatchDlg::DlgProc ( HWND wnd, UINT msg, WPARAM w
// Attach the dialog class pointer to the window
dlg = (rvDebuggerQuickWatchDlg*) lparam;
SetWindowLong ( wnd, GWL_USERDATA, lparam );
SetWindowLongPtr ( wnd, GWLP_USERDATA, lparam );
dlg->mWnd = wnd;
GetClientRect ( wnd, &client );

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 "DebuggerApp.h"
#else
#include "debugger_common.h"
#endif
#include "DebuggerScript.h"
#include "../../game/script/Script_Program.h"
#include "../../ui/Window.h"
#include "../../ui/UserInterfaceLocal.h"
@ -57,6 +59,7 @@ rvDebuggerScript::~rvDebuggerScript ( void )
Unload ( );
}
/*
================
rvDebuggerScript::Unload
@ -72,10 +75,6 @@ void rvDebuggerScript::Unload ( void )
{
delete mInterface;
}
else
{
delete mProgram;
}
mContents = NULL;
mProgram = NULL;
@ -116,60 +115,7 @@ bool rvDebuggerScript::Load ( const char* filename )
// Cleanup
fileSystem->FreeFile ( buffer );
// Now compile the script so we can tell what a valid line is, etc.. If its
// a gui file then we need to parse it using the userinterface system rather
// than the normal script compiler.
try
{
// Parse the script using the script compiler
mProgram = new idProgram;
mProgram->BeginCompilation ( );
mProgram->CompileFile ( SCRIPT_DEFAULT );
//BSM Nerve: Loads a game specific main script file
idStr gamedir = cvarSystem->GetCVarString( "fs_game" );
if(gamedir.Length() > 0) {
idStr scriptFile = va("script/%s_main.script", gamedir.c_str());
if(fileSystem->ReadFile(scriptFile.c_str(), NULL) > 0) {
mProgram.CompileFile(scriptFile.c_str());
}
}
// Make sure the file isnt already compiled before trying to compile it again
for ( int f = mProgram->NumFilenames() - 1; f >= 0; f -- )
{
idStr qpath;
qpath = fileSystem->OSPathToRelativePath ( mProgram->GetFilename ( f ) );
qpath.BackSlashesToSlashes ( );
if ( !qpath.Cmp ( filename ) )
{
break;
}
}
if ( f < 0 )
{
mProgram->CompileText ( filename, mContents, false );
}
mProgram->FinishCompilation ( );
}
catch ( idException& )
{
// Failed to parse the script so fail to load the file
delete mProgram;
mProgram = NULL;
delete[] mContents;
mContents = NULL;
// TODO: Should cache the error for the dialog box
return false;
}
return true;
}
@ -194,21 +140,8 @@ Determines whether or not the given line number within the script is a valid lin
*/
bool rvDebuggerScript::IsLineCode ( int linenumber )
{
int i;
assert ( mProgram );
// Run through all the statements in the program and see if any match the
// linenumber that we are checking.
for ( i = 0; i < mProgram->NumStatements ( ); i ++ )
{
if ( mProgram->GetStatement ( i ).linenumber == linenumber )
{
return true;
}
}
return false;
//we let server decide.
return true;
}
/*

View file

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

View file

@ -3,6 +3,8 @@
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 1999-2011 Raven Software
Copyright (C) 2021 Harrie van Ginneken
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
@ -26,17 +28,16 @@ If you have questions concerning this license or the applicable additional terms
===========================================================================
*/
#if defined( ID_ALLOW_TOOLS )
#include "tools/edit_gui_common.h"
#include "../../game/gamesys/Event.h"
#include "../../game/gamesys/Class.h"
#include "../../game/script/Script_Program.h"
#include "../../game/script/Script_Interpreter.h"
#include "../../game/script/Script_Thread.h"
#include "../../game/script/Script_Compiler.h"
#include "../../framework/sync/Msg.h"
#include "DebuggerApp.h"
#else
#include "debugger_common.h"
// we need a lot to be able to list all threads in mars_city1
const int MAX_MSGLEN = 8600;
#endif
#include "DebuggerServer.h"
/*
@ -51,10 +52,17 @@ rvDebuggerServer::rvDebuggerServer ( )
mBreak = false;
mBreakStepOver = false;
mBreakStepInto = false;
mGameThread = NULL;
mGameThreadBreakCond = NULL;
mGameThreadBreakLock = NULL;
mLastStatementLine = -1;
mBreakStepOverFunc1 = NULL;
mBreakStepOverFunc2 = NULL;
mBreakInstructionPointer = 0;
mBreakInterpreter = NULL;
mBreakProgram = NULL;
mGameDLLHandle = 0;
mBreakStepOverDepth = 0;
mCriticalSection = NULL;
}
/*
@ -82,15 +90,17 @@ bool rvDebuggerServer::Initialize ( void )
return false;
}
// Get a copy of the game thread handle so we can suspend the thread on a break
DuplicateHandle ( GetCurrentProcess(), GetCurrentThread ( ), GetCurrentProcess(), &mGameThread, 0, FALSE, DUPLICATE_SAME_ACCESS );
// we're using a condition variable to pause the game thread in rbDebuggerServer::Break()
// until rvDebuggerServer::Resume() is called (from another thread)
mGameThreadBreakCond = SDL_CreateCond();
mGameThreadBreakLock = SDL_CreateMutex();
// Create a critical section to ensure that the shared thread
// variables are protected
InitializeCriticalSection ( &mCriticalSection );
mCriticalSection = SDL_CreateMutex();
// Server must be running on the local host on port 28980
Sys_StringToNetAdr ( "localhost", &mClientAdr, true );
Sys_StringToNetAdr ( com_dbgClientAdr.GetString( ), &mClientAdr, true );
mClientAdr.port = 27981;
// Attempt to let the server know we are here. The server may not be running so this
@ -102,7 +112,7 @@ bool rvDebuggerServer::Initialize ( void )
void rvDebuggerServer::OSPathToRelativePath( const char *osPath, idStr &qpath )
{
if ( strchr( osPath, ':' ) )
if ( strchr( osPath, ':' ) ) // XXX: what about linux?
{
qpath = fileSystem->OSPathToRelativePath( osPath );
}
@ -130,8 +140,16 @@ void rvDebuggerServer::Shutdown ( void )
mPort.Close();
Resume(); // just in case we're still paused
// dont need the crit section anymore
DeleteCriticalSection ( &mCriticalSection );
SDL_DestroyMutex( mCriticalSection );
mCriticalSection = NULL;
SDL_DestroyCond( mGameThreadBreakCond );
mGameThreadBreakCond = NULL;
SDL_DestroyMutex( mGameThreadBreakLock );
mGameThreadBreakLock = NULL;
}
/*
@ -144,39 +162,46 @@ Process all incoming network messages from the debugger client
bool rvDebuggerServer::ProcessMessages ( void )
{
netadr_t adrFrom;
msg_t msg;
idBitMsg msg;
byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) );
// Check for pending udp packets on the debugger port
while ( mPort.GetPacket ( adrFrom, msg.data, msg.cursize, msg.maxsize ) )
int msgSize;
while ( mPort.GetPacket ( adrFrom, buffer, msgSize, MAX_MSGLEN) )
{
unsigned short command;
// Only accept packets from the debugger server for security reasons
if ( !Sys_CompareNetAdrBase ( adrFrom, mClientAdr ) )
{
continue;
short command;
msg.Init(buffer, sizeof(buffer));
msg.SetSize(msgSize);
msg.BeginReading();
if ( adrFrom.type != NA_LOOPBACK ) {
// Only accept packets from the debugger server for security reasons
if ( !Sys_CompareNetAdrBase( adrFrom, mClientAdr ) )
continue;
}
command = (unsigned short) MSG_ReadShort ( &msg );
command = msg.ReadShort( );
switch ( command )
{
case DBMSG_CONNECT:
mConnected = true;
SendMessage ( DBMSG_CONNECTED );
HandleInspectScripts ( nullptr );
com_editors |= EDITOR_DEBUGGER;
break;
case DBMSG_CONNECTED:
mConnected = true;
HandleInspectScripts( nullptr );
com_editors |= EDITOR_DEBUGGER;
break;
case DBMSG_DISCONNECT:
ClearBreakpoints ( );
Resume ( );
mConnected = false;
com_editors &= ~EDITOR_DEBUGGER;
break;
case DBMSG_ADDBREAKPOINT:
@ -188,7 +213,7 @@ bool rvDebuggerServer::ProcessMessages ( void )
break;
case DBMSG_RESUME:
Resume ( );
HandleResume ( &msg );
break;
case DBMSG_BREAK:
@ -197,11 +222,11 @@ bool rvDebuggerServer::ProcessMessages ( void )
case DBMSG_STEPOVER:
mBreakStepOver = true;
mBreakStepOverDepth = mBreakInterpreter->GetCallstackDepth ( );
mBreakStepOverFunc1 = mBreakInterpreter->GetCallstack()[mBreakInterpreter->GetCallstackDepth()].f;
if ( mBreakInterpreter->GetCallstackDepth() > 0 )
mBreakStepOverDepth = ((idGameEditExt*) gameEdit)->GetInterpreterCallStackDepth(mBreakInterpreter);
mBreakStepOverFunc1 = ((idGameEditExt*) gameEdit)->GetInterpreterCallStackFunction(mBreakInterpreter);
if (mBreakStepOverDepth)
{
mBreakStepOverFunc2 = mBreakInterpreter->GetCallstack()[mBreakInterpreter->GetCallstackDepth()-1].f;
mBreakStepOverFunc2 = ((idGameEditExt*) gameEdit)->GetInterpreterCallStackFunction(mBreakInterpreter,mBreakStepOverDepth - 1);
}
else
{
@ -226,6 +251,14 @@ bool rvDebuggerServer::ProcessMessages ( void )
case DBMSG_INSPECTTHREADS:
HandleInspectThreads ( &msg );
break;
case DBMSG_INSPECTSCRIPTS:
HandleInspectScripts( &msg );
break;
case DBMSG_EXECCOMMAND:
HandleExecCommand( &msg );
break;
}
}
@ -241,13 +274,14 @@ Send a message with no data to the debugger server.
*/
void rvDebuggerServer::SendMessage ( EDebuggerMessage dbmsg )
{
msg_t msg;
idBitMsg msg;
byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)dbmsg );
msg.Init( buffer, sizeof( buffer ) );
msg.BeginWriting();
msg.WriteShort ( (short)dbmsg );
SendPacket ( msg.data, msg.cursize );
SendPacket ( msg.GetData(), msg.GetSize() );
}
/*
@ -255,29 +289,44 @@ void rvDebuggerServer::SendMessage ( EDebuggerMessage dbmsg )
rvDebuggerServer::HandleAddBreakpoint
Handle the DBMSG_ADDBREAKPOINT message being sent by the debugger client. This
message is handled by adding a new breakpoint to the breakpoint list with the
message is handled by first checking if it is valid
and is added as a new breakpoint to the breakpoint list with the
data supplied in the message.
================
*/
void rvDebuggerServer::HandleAddBreakpoint ( msg_t* msg )
void rvDebuggerServer::HandleAddBreakpoint ( idBitMsg* msg )
{
bool onceOnly = false;
long lineNumber;
long id;
char filename[MAX_PATH];
char filename[2048]; // DG: randomly chose this size
// Read the breakpoint info
onceOnly = MSG_ReadBits ( msg, 1 ) ? true : false;
lineNumber = MSG_ReadInt ( msg );
id = MSG_ReadInt ( msg );
onceOnly = msg->ReadBits( 1 ) ? true : false;
lineNumber = msg->ReadInt ( );
id = msg->ReadInt ( );
MSG_ReadString ( msg, filename, MAX_PATH );
msg->ReadString ( filename, sizeof(filename) );
// Since breakpoints are used by both threads we need to
// protect them with a crit section
EnterCriticalSection ( &mCriticalSection );
mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber, id ) );
LeaveCriticalSection ( &mCriticalSection );
//check for statement on requested breakpoint location
if (!((idGameEditExt*) gameEdit)->IsLineCode(filename, lineNumber))
{
idBitMsg msgOut;
byte buffer[MAX_MSGLEN];
msgOut.Init(buffer, sizeof(buffer));
msgOut.BeginWriting();
msgOut.WriteShort((short)DBMSG_REMOVEBREAKPOINT);
msgOut.WriteInt(lineNumber);
msgOut.WriteString(filename);
SendPacket(msgOut.GetData(), msgOut.GetSize());
return;
}
SDL_LockMutex( mCriticalSection );
mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber, id, onceOnly ) );
SDL_UnlockMutex( mCriticalSection );
}
/*
@ -289,17 +338,17 @@ message is handled by removing the breakpoint that matches the given id from the
list.
================
*/
void rvDebuggerServer::HandleRemoveBreakpoint ( msg_t* msg )
void rvDebuggerServer::HandleRemoveBreakpoint ( idBitMsg* msg )
{
int i;
int id;
// ID that we are to remove
id = MSG_ReadInt ( msg );
id = msg->ReadInt ( );
// Since breakpoints are used by both threads we need to
// protect them with a crit section
EnterCriticalSection ( &mCriticalSection );
SDL_LockMutex( mCriticalSection );
// Find the breakpoint that matches the given id and remove it from the list
for ( i = 0; i < mBreakpoints.Num(); i ++ )
@ -312,52 +361,21 @@ void rvDebuggerServer::HandleRemoveBreakpoint ( msg_t* msg )
}
}
LeaveCriticalSection ( &mCriticalSection );
SDL_UnlockMutex( mCriticalSection );
}
/*
================
rvDebuggerServer::MSG_WriteCallstackFunc
rvDebuggerServer::HandleResume
Writes a single callstack entry to the given message
Resume the game thread.
================
*/
void rvDebuggerServer::MSG_WriteCallstackFunc ( msg_t* msg, const prstack_t* stack )
void rvDebuggerServer::HandleResume(idBitMsg* msg)
{
const statement_t* st;
const function_t* func;
func = stack->f;
// If the function is unknown then just fill in with default data.
if ( !func )
{
MSG_WriteString ( msg, "<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 );
}
//Empty msg
Resume();
}
/*
@ -368,31 +386,18 @@ Handle an incoming inspect callstack message by sending a message
back to the client with the callstack data.
================
*/
void rvDebuggerServer::HandleInspectCallstack ( msg_t* in_msg )
void rvDebuggerServer::HandleInspectCallstack ( idBitMsg* msg )
{
msg_t msg;
idBitMsg msgOut;
byte buffer[MAX_MSGLEN];
int i;
prstack_t temp;
MSG_Init( &msg, buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_INSPECTCALLSTACK );
msgOut.Init(buffer, sizeof( buffer ) );
msgOut.BeginWriting();
msgOut.WriteShort ( (short)DBMSG_INSPECTCALLSTACK );
MSG_WriteShort ( &msg, (int)mBreakInterpreter->GetCallstackDepth ( ) );
((idGameEditExt*) gameEdit)->MSG_WriteInterpreterInfo(&msgOut, mBreakInterpreter, mBreakProgram, mBreakInstructionPointer);
// write out the current function
temp.f = mBreakInterpreter->GetCurrentFunction ( );
temp.s = 0;
temp.stackbase = 0;
MSG_WriteCallstackFunc ( &msg, &temp );
// Run through all of the callstack and write each to the msg
for ( i = mBreakInterpreter->GetCallstackDepth ( ) - 1; i > 0; i -- )
{
MSG_WriteCallstackFunc ( &msg, mBreakInterpreter->GetCallstack ( ) + i );
}
SendPacket ( msg.data, msg.cursize );
SendPacket (msgOut.GetData(), msgOut.GetSize() );
}
/*
@ -402,35 +407,67 @@ rvDebuggerServer::HandleInspectThreads
Send the list of the current threads in the interpreter back to the debugger client
================
*/
void rvDebuggerServer::HandleInspectThreads ( msg_t* in_msg )
void rvDebuggerServer::HandleInspectThreads ( idBitMsg* msg )
{
msg_t msg;
byte buffer[MAX_MSGLEN];
int i;
idBitMsg msgOut;
byte buffer[MAX_MSGLEN];
int i;
// Initialize the message
MSG_Init( &msg, buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_INSPECTTHREADS );
msgOut.Init( buffer, sizeof( buffer ) );
msgOut.SetAllowOverflow(true);
msgOut.BeginWriting();
msgOut.WriteShort ( (short)DBMSG_INSPECTTHREADS );
// Write the number of threads to the message
MSG_WriteShort ( &msg, (int)idThread::GetThreads().Num() );
msgOut.WriteShort ((short)((idGameEditExt*) gameEdit)->GetTotalScriptThreads() );
// Loop through all of the threads and write their name and number to the message
for ( i = 0; i < idThread::GetThreads().Num(); i ++ )
for ( i = 0; i < ((idGameEditExt*) gameEdit)->GetTotalScriptThreads(); i ++ )
{
idThread* thread = idThread::GetThreads()[i];
MSG_WriteString ( &msg, thread->GetThreadName ( ) );
MSG_WriteInt ( &msg, thread->GetThreadNum ( ) );
MSG_WriteBits ( &msg, (int)(thread == mBreakInterpreter->GetThread ( )), 1 );
MSG_WriteBits ( &msg, (int)thread->IsDoneProcessing(), 1 );
MSG_WriteBits ( &msg, (int)thread->IsWaiting(), 1 );
MSG_WriteBits ( &msg, (int)thread->IsDying(), 1 );
((idGameEditExt*) gameEdit)->MSG_WriteThreadInfo(&msgOut,((idGameEditExt*) gameEdit)->GetThreadByIndex(i), mBreakInterpreter);
}
// Send off the inspect threads packet to the debugger client
SendPacket ( msg.data, msg.cursize );
SendPacket (msgOut.GetData(), msgOut.GetSize() );
}
/*
================
rvDebuggerServer::HandleExecCommand
Send the list of the current loaded scripts in the interpreter back to the debugger client
================
*/
void rvDebuggerServer::HandleExecCommand( idBitMsg *msg ) {
char cmdStr[2048]; // HvG: randomly chose this size
msg->ReadString( cmdStr, sizeof( cmdStr ) );
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, cmdStr ); // valid command
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "\n" );
}
/*
================
rvDebuggerServer::HandleInspectScripts
Send the list of the current loaded scripts in the interpreter back to the debugger client
================
*/
void rvDebuggerServer::HandleInspectScripts( idBitMsg* msg )
{
idBitMsg msgOut;
byte buffer[MAX_MSGLEN];
// Initialize the message
msgOut.Init(buffer, sizeof(buffer));
msgOut.BeginWriting();
msgOut.WriteShort((short)DBMSG_INSPECTSCRIPTS);
((idGameEditExt*) gameEdit)->MSG_WriteScriptList( &msgOut );
SendPacket(msgOut.GetData(), msgOut.GetSize());
}
/*
@ -440,7 +477,7 @@ rvDebuggerServer::HandleInspectVariable
Respondes to a request from the debugger client to inspect the value of a given variable
================
*/
void rvDebuggerServer::HandleInspectVariable ( msg_t* in_msg )
void rvDebuggerServer::HandleInspectVariable ( idBitMsg* msg )
{
char varname[256];
int scopeDepth;
@ -450,28 +487,29 @@ void rvDebuggerServer::HandleInspectVariable ( msg_t* in_msg )
return;
}
scopeDepth = (short)MSG_ReadShort ( in_msg );
MSG_ReadString ( in_msg, varname, 256 );
scopeDepth = (short)msg->ReadShort ( );
msg->ReadString ( varname, 256 );
idStr varvalue;
msg_t msg;
idBitMsg msgOut;
byte buffer[MAX_MSGLEN];
// Initialize the message
MSG_Init( &msg, buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_INSPECTVARIABLE );
msgOut.Init( buffer, sizeof( buffer ) );
msgOut.BeginWriting();
msgOut.WriteShort ( (short)DBMSG_INSPECTVARIABLE );
if ( !mBreakInterpreter->GetRegisterValue ( varname, varvalue, scopeDepth ) )
if (!((idGameEditExt*) gameEdit)->GetRegisterValue(mBreakInterpreter, varname, varvalue, scopeDepth ) )
{
varvalue = "???";
}
MSG_WriteShort ( &msg, (short)scopeDepth );
MSG_WriteString ( &msg, varname );
MSG_WriteString ( &msg, varvalue );
msgOut.WriteShort ( (short)scopeDepth );
msgOut.WriteString ( varname );
msgOut.WriteString ( varvalue );
SendPacket ( msg.data, msg.cursize );
SendPacket (msgOut.GetData(), msgOut.GetSize() );
}
/*
@ -484,7 +522,6 @@ Check to see if any breakpoints have been hit. This includes "break next",
*/
void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram* program, int instructionPointer )
{
const statement_t* st;
const char* filename;
int i;
@ -492,23 +529,24 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
return;
}
// Grab the current statement and the filename that it came from
st = &program->GetStatement ( instructionPointer );
filename = program->GetFilename ( st->file );
filename = ((idGameEditExt*) gameEdit)->GetFilenameForStatement(program, instructionPointer);
int linenumber = ((idGameEditExt*) gameEdit)->GetLineNumberForStatement(program, instructionPointer);
// Operate on lines, not statements
if ( mLastStatementLine == st->linenumber && mLastStatementFile == st->file )
if ( mLastStatementLine == linenumber && mLastStatementFile == filename)
{
return;
}
// Save the last visited line and file so we can prevent
// double breaks on lines with more than one statement
mLastStatementFile = idStr( st->file );
mLastStatementLine = st->linenumber;
mLastStatementFile = idStr(filename);
mLastStatementLine = linenumber;
// Reset stepping when the last function on the callstack is returned from
if ( st->op == OP_RETURN && interpreter->GetCallstackDepth ( ) <= 1 )
if ( ((idGameEditExt*) gameEdit)->ReturnedFromFunction(program, interpreter,instructionPointer))
{
mBreakStepOver = false;
mBreakStepInto = false;
@ -517,6 +555,7 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
// See if we are supposed to break on the next script line
if ( mBreakNext )
{
HandleInspectScripts(nullptr);
Break ( interpreter, program, instructionPointer );
return;
}
@ -524,9 +563,8 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
// Only break on the same callstack depth and thread as the break over
if ( mBreakStepOver )
{
if ( ( interpreter->GetCurrentFunction ( ) == mBreakStepOverFunc1 ||
interpreter->GetCurrentFunction ( ) == mBreakStepOverFunc2 )&&
( interpreter->GetCallstackDepth ( ) <= mBreakStepOverDepth ) )
//virtual bool CheckForBreakpointHit(interpreter,function1,function2,depth)
if (((idGameEditExt*) gameEdit)->CheckForBreakPointHit(interpreter, mBreakStepOverFunc1, mBreakStepOverFunc2, mBreakStepOverDepth))
{
Break ( interpreter, program, instructionPointer );
return;
@ -536,6 +574,7 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
// See if we are supposed to break on the next line
if ( mBreakStepInto )
{
HandleInspectScripts(nullptr);
// Break
Break ( interpreter, program, instructionPointer );
return;
@ -545,7 +584,7 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
OSPathToRelativePath(filename,qpath);
qpath.BackSlashesToSlashes ( );
EnterCriticalSection ( &mCriticalSection );
SDL_LockMutex( mCriticalSection );
// Check all the breakpoints
for ( i = 0; i < mBreakpoints.Num ( ); i ++ )
@ -553,30 +592,50 @@ void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram*
rvDebuggerBreakpoint* bp = mBreakpoints[i];
// Skip if not match of the line number
if ( st->linenumber != bp->GetLineNumber ( ) )
if ( linenumber != bp->GetLineNumber ( ) )
{
continue;
}
// Skip if no match of the filename
if ( idStr::Icmp ( bp->GetFilename(), qpath ) )
if ( idStr::Icmp ( bp->GetFilename(), qpath.c_str() ) )
{
continue;
}
// Pop out of the critical section so we dont get stuck
LeaveCriticalSection ( &mCriticalSection );
// DG: onceOnly support
if ( bp->GetOnceOnly() ) {
// we'll do the one Break() a few lines below; remove it here while mBreakpoints is unmodified
// (it can be modifed from the client while in Break() below)
mBreakpoints.RemoveIndex( i );
delete bp;
// also tell client to remove the breakpoint
idBitMsg msgOut;
byte buffer[MAX_MSGLEN];
msgOut.Init( buffer, sizeof( buffer ) );
msgOut.BeginWriting();
msgOut.WriteShort( (short)DBMSG_REMOVEBREAKPOINT );
msgOut.WriteInt( linenumber );
msgOut.WriteString( qpath.c_str() );
SendPacket( msgOut.GetData(), msgOut.GetSize() );
}
// DG end
// Pop out of the critical section so we dont get stuck
SDL_UnlockMutex( mCriticalSection );
HandleInspectScripts(nullptr);
// We hit a breakpoint, so break
Break ( interpreter, program, instructionPointer );
// Back into the critical section since we are going to have to leave it
EnterCriticalSection ( &mCriticalSection );
SDL_LockMutex( mCriticalSection );
break;
}
LeaveCriticalSection ( &mCriticalSection );
SDL_UnlockMutex( mCriticalSection );
}
/*
@ -589,9 +648,8 @@ the game has been halted
*/
void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, int instructionPointer )
{
msg_t msg;
idBitMsg msg;
byte buffer[MAX_MSGLEN];
const statement_t* st;
const char* filename;
// Clear all the break types
@ -600,12 +658,10 @@ void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, i
mBreakNext = false;
// Grab the current statement and the filename that it came from
st = &program->GetStatement ( instructionPointer );
filename = program->GetFilename ( st->file );
idStr qpath;
OSPathToRelativePath(filename, qpath);
qpath.BackSlashesToSlashes ( );
filename = ((idGameEditExt*) gameEdit)->GetFilenameForStatement(program,instructionPointer);
int linenumber = ((idGameEditExt*) gameEdit)->GetLineNumberForStatement(program, instructionPointer);
idStr fileStr = filename;
fileStr.BackSlashesToSlashes();
// Give the mouse cursor back to the world
Sys_GrabMouseCursor( false );
@ -617,19 +673,33 @@ void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, i
mBreakInstructionPointer = instructionPointer;
// Inform the debugger of the breakpoint hit
MSG_Init( &msg, buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_BREAK );
MSG_WriteInt ( &msg, st->linenumber );
MSG_WriteString ( &msg, qpath );
SendPacket ( msg.data, msg.cursize );
msg.Init( buffer, sizeof( buffer ) );
msg.BeginWriting();
msg.WriteShort ( (short)DBMSG_BREAK );
msg.WriteInt ( linenumber );
msg.WriteString ( fileStr.c_str() );
//msg.WriteInt64( (int64_t)mBreakProgram );
SendPacket ( msg.GetData(), msg.GetSize() );
// Suspend the game thread. Since this will be called from within the main game thread
// execution wont return until after the thread is resumed
SuspendThread ( mGameThread );
// DG: the original code used Win32 SuspendThread() here, but as there is no equivalent
// function in SDL and as this is only called within the main game thread anyway,
// just use a condition variable to put this thread to sleep until Resume() has set mBreak
SDL_LockMutex( mGameThreadBreakLock );
while ( mBreak ) {
SDL_CondWait( mGameThreadBreakCond, mGameThreadBreakLock );
}
SDL_UnlockMutex( mGameThreadBreakLock );
// Let the debugger client know that we have started back up again
SendMessage ( DBMSG_RESUMED );
// this should be platform specific
// TODO: maybe replace with SDL code? or does it not matter if debugger client runs on another machine?
#if defined( ID_ALLOW_TOOLS )
// This is to give some time between the keypress that
// told us to resume and the setforeground window. Otherwise the quake window
// would just flash
@ -640,8 +710,10 @@ void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, i
SetActiveWindow ( win32.hWnd );
UpdateWindow ( win32.hWnd );
SetFocus ( win32.hWnd );
#endif
// Give the mouse cursor back to the game
// HVG_Note : there be dragons here. somewhere.
Sys_GrabMouseCursor( true );
// Clear all commands that were generated before we went into suspended mode. This is
@ -664,10 +736,11 @@ void rvDebuggerServer::Resume ( void )
return;
}
mBreak = false;
// Start the game thread back up
ResumeThread ( mGameThread );
SDL_LockMutex( mGameThreadBreakLock );
mBreak = false;
SDL_CondSignal( mGameThreadBreakCond);
SDL_UnlockMutex( mGameThreadBreakLock );
}
/*
@ -703,12 +776,13 @@ void rvDebuggerServer::Print ( const char* text )
return;
}
msg_t msg;
idBitMsg msg;
byte buffer[MAX_MSGLEN];
MSG_Init( &msg, buffer, sizeof( buffer ) );
MSG_WriteShort ( &msg, (int)DBMSG_PRINT );
MSG_WriteString ( &msg, text );
msg.Init( buffer, sizeof( buffer ) );
msg.BeginWriting();
msg.WriteShort ( (short)DBMSG_PRINT );
msg.WriteString ( text );
SendPacket ( msg.data, msg.cursize );
SendPacket ( msg.GetData(), msg.GetSize() );
}

View file

@ -28,20 +28,16 @@ If you have questions concerning this license or the applicable additional terms
#ifndef DEBUGGERSERVER_H_
#define DEBUGGERSERVER_H_
#ifndef DEBUGGERMESSAGES_H_
#include "DebuggerMessages.h"
#endif
#ifndef DEBUGGERBREAKPOINT_H_
#include "DebuggerBreakpoint.h"
#endif
#include "framework/Game.h"
#include <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
{
@ -50,31 +46,52 @@ public:
rvDebuggerServer ( );
~rvDebuggerServer ( );
bool Initialize ( void );
void Shutdown ( void );
bool Initialize ( void );
void Shutdown ( void );
bool ProcessMessages ( void );
bool ProcessMessages ( void );
bool IsConnected ( void );
bool IsConnected ( void );
void CheckBreakpoints ( idInterpreter* interpreter, idProgram* program, int instructionPointer );
void CheckBreakpoints ( idInterpreter *interpreter, idProgram *program, int instructionPointer );
void Print ( const char* text );
void Print ( const char *text );
void OSPathToRelativePath( const char *osPath, idStr &qpath );
void OSPathToRelativePath ( const char *osPath, idStr &qpath );
protected:
bool GameSuspended ( void );
private:
void ClearBreakpoints ( void );
void Break ( idInterpreter *interpreter, idProgram *program, int instructionPointer );
void Resume ( void );
void SendMessage ( EDebuggerMessage dbmsg );
void SendPacket ( void* data, int datasize );
// Message handlers
void HandleAddBreakpoint ( idBitMsg *msg );
void HandleRemoveBreakpoint ( idBitMsg *msg );
void HandleResume ( idBitMsg *msg );
void HandleInspectVariable ( idBitMsg *msg );
void HandleInspectCallstack ( idBitMsg *msg );
void HandleInspectThreads ( idBitMsg *msg );
void HandleInspectScripts ( idBitMsg *msg );
void HandleExecCommand ( idBitMsg *msg );
////
// protected member variables
bool mConnected;
netadr_t mClientAdr;
idPort mPort;
idList<rvDebuggerBreakpoint*> mBreakpoints;
CRITICAL_SECTION mCriticalSection;
SDL_mutex* mCriticalSection;
HANDLE mGameThread;
SDL_cond* mGameThreadBreakCond;
SDL_mutex* mGameThreadBreakLock;
bool mBreak;
bool mBreakNext;
bool mBreakStepOver;
bool mBreakStepInto;
@ -87,27 +104,9 @@ protected:
idStr mLastStatementFile;
int mLastStatementLine;
uintptr_t mGameDLLHandle;
idStrList mScriptFileList;
private:
void ClearBreakpoints ( void );
void Break ( idInterpreter* interpreter, idProgram* program, int instructionPointer );
void Resume ( void );
void SendMessage ( EDebuggerMessage dbmsg );
void SendPacket ( void* data, int datasize );
// Message handlers
void HandleAddBreakpoint ( msg_t* msg );
void HandleRemoveBreakpoint ( msg_t* msg );
void HandleResume ( msg_t* msg );
void HandleInspectVariable ( msg_t* msg );
void HandleInspectCallstack ( msg_t* msg );
void HandleInspectThreads ( msg_t* msg );
// MSG helper routines
void MSG_WriteCallstackFunc ( msg_t* msg, const prstack_t* stack );
};
/*
@ -125,9 +124,19 @@ ID_INLINE bool rvDebuggerServer::IsConnected ( void )
rvDebuggerServer::SendPacket
================
*/
ID_INLINE void rvDebuggerServer::SendPacket ( void* data, int size )
ID_INLINE void rvDebuggerServer::SendPacket ( void *data, int size )
{
mPort.SendPacket ( mClientAdr, data, size );
}
/*
================
rvDebuggerServer::GameSuspended
================
*/
ID_INLINE bool rvDebuggerServer::GameSuspended( void )
{
return mBreak;
}
#endif // DEBUGGERSERVER_H_

View file

@ -35,7 +35,7 @@ If you have questions concerning this license or the applicable additional terms
#include "DebuggerQuickWatchDlg.h"
#include "DebuggerFindDlg.h"
#define DEBUGGERWINDOWCLASS "QUAKE4_DEBUGGER_WINDOW"
#define DEBUGGERWINDOWCLASS "DHEWM3_DEBUGGER_WINDOW"
#define ID_DBG_WINDOWMIN 18900
#define ID_DBG_WINDOWMAX 19900
@ -49,6 +49,9 @@ If you have questions concerning this license or the applicable additional terms
#define IDC_DBG_WATCH 31007
#define IDC_DBG_THREADS 31008
#define IDC_DBG_TOOLBAR 31009
#define IDC_DBG_SCRIPTLIST 31010
#define IDC_DBG_CONSOLEINPUT 31011
#define IDC_DBG_BREAKLIST 31012
#define ID_DBG_FILE_MRU1 10000
@ -167,7 +170,7 @@ bool rvDebuggerWindow::Create ( HINSTANCE instance )
UpdateTitle ( );
Printf ( "Quake 4 Script Debugger v0.1\n\n" );
Printf ( "Dhewm3 Script Debugger v1.1\n\n" );
ShowWindow ( mWnd, SW_SHOW );
UpdateWindow ( mWnd );
@ -248,7 +251,7 @@ LRESULT CALLBACK rvDebuggerWindow::ScriptWndProc ( HWND wnd, UINT msg, WPARAM wp
{
static int lastStart = -1;
static int lastEnd = -1;
rvDebuggerWindow* window = (rvDebuggerWindow*)GetWindowLong ( wnd, GWL_USERDATA );
rvDebuggerWindow* window = (rvDebuggerWindow*)GetWindowLongPtr ( wnd, GWLP_USERDATA );
WNDPROC wndproc = window->mOldScriptProc;
switch ( msg )
@ -347,6 +350,23 @@ LRESULT CALLBACK rvDebuggerWindow::ScriptWndProc ( HWND wnd, UINT msg, WPARAM wp
break;
}
case WM_SIZE:
{
float scaling_factor = Win_GetWindowScalingFactor(wnd);
int s18 = int(18 * scaling_factor);
int s10 = int(10 * scaling_factor);
RECT rect;
window->mMarginSize = window->mZoomScaleDem ? ((long)(s18 * (float)window->mZoomScaleNum / (float)window->mZoomScaleDem)) : s18;
GetWindowRect(window->mWndToolbar, &rect);
MoveWindow(window->mWndMargin, 0, 0, window->mMarginSize, window->mSplitterRect.top - (rect.bottom - rect.top), TRUE);
// FIXME: was *2.25, increased for line numbers up to 9999; but neither works particularly well
// if DPI scaling is involved, because script code text and linenumbers aren't DPI scaled
int lmargin = s18 * 3.5;
SendMessage(window->mWndScript, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(lmargin, s10));
}
}
return CallWindowProc ( wndproc, wnd, msg, wparam, lparam );
@ -354,7 +374,7 @@ LRESULT CALLBACK rvDebuggerWindow::ScriptWndProc ( HWND wnd, UINT msg, WPARAM wp
LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
rvDebuggerWindow* window = (rvDebuggerWindow*) GetWindowLong ( wnd, GWL_USERDATA );
rvDebuggerWindow* window = (rvDebuggerWindow*) GetWindowLongPtr ( wnd, GWLP_USERDATA );
switch ( msg )
{
@ -384,15 +404,57 @@ LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wp
case WM_PAINT:
{
HDC dc;
float scaling_factor = Win_GetWindowScalingFactor(wnd);
int s2 = int(2 * scaling_factor);
int s4 = int(4 * scaling_factor);
int width,height;
int size = window->mMarginSize - 2;
window->ResizeImageList(width,height);
PAINTSTRUCT ps;
RECT rect;
GetClientRect ( wnd, &rect );
dc = BeginPaint ( wnd, &ps );
FillRect ( dc, &rect, GetSysColorBrush ( COLOR_3DFACE ) );
dc = BeginPaint( wnd, &ps );
FillRect( dc, &rect, GetSysColorBrush( COLOR_3DSHADOW ) );
//draw line nrs
int iMaxNumberOfLines = ( (rect.bottom - rect.top ) / height ) + height;
int iFirstVisibleLine = SendMessage( window->mWndScript, EM_GETFIRSTVISIBLELINE, 0, 0 );
HFONT hf = CreateFont( height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Courier New" );
HFONT hfOld = ( HFONT ) SelectObject( dc, hf );
SetBkMode( dc, OPAQUE );
// I think it looks nicer when the line number background is white
SetBkColor( dc, RGB( 255, 255, 255 ) );
SetTextColor( dc, RGB( 0, 0, 255 ) );
int lnrWidth = 8;
GetCharWidth32( dc, '9', '9', &lnrWidth );
lnrWidth *= 4; // we want enough width for 4 chars ("9999"), not just one
lnrWidth += 2 * s4; // we want some space around the line number
RECT lnrRect = rect;
lnrRect.left = rect.right;
lnrRect.right = lnrRect.left + lnrWidth;
FillRect( dc, &lnrRect, WHITE_BRUSH );
for (int i = 0; i < iMaxNumberOfLines; ++i )
{
int c;
POINTL pos;
c = SendMessage( window->mWndScript, EM_LINEINDEX, iFirstVisibleLine + i , 0 );
SendMessage( window->mWndScript, EM_POSFROMCHAR, ( WPARAM ) &pos, c );
RECT t = lnrRect;
t.top = pos.y;
t.bottom = t.top + height;
t.right -= s4; // a little space between text and "border" to code part of window
idStr lntxt( iFirstVisibleLine + i + 1);
DrawText( dc, lntxt, lntxt.Length(), &t, DT_RIGHT );
}
DeleteObject( hf );
//draw breakpoints
if ( window->mScripts.Num ( ) )
{
for ( int i = 0; i < window->mClient->GetBreakpointCount(); i ++ )
@ -407,7 +469,8 @@ LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wp
c = SendMessage ( window->mWndScript, EM_LINEINDEX, bp->GetLineNumber ( ) - 1, 0 );
SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c );
ImageList_DrawEx ( window->mImageList, 2, dc, rect.left, pos.y, size, size, CLR_NONE, CLR_NONE, ILD_NORMAL );
ImageList_DrawEx ( window->mTmpImageList, 2, dc, rect.left, pos.y, width, height, CLR_NONE, CLR_NONE, ILD_NORMAL );
}
}
@ -421,7 +484,7 @@ LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wp
c = SendMessage ( window->mWndScript, EM_LINEINDEX, window->mClient->GetBreakLineNumber() - 1, 0 );
SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c );
ImageList_DrawEx ( window->mImageList, 3, dc, rect.left, pos.y, size, size, CLR_NONE, CLR_NONE, ILD_NORMAL );
ImageList_DrawEx ( window->mTmpImageList, 3, dc, rect.left, pos.y, width, height, CLR_NONE, CLR_NONE, ILD_NORMAL );
}
}
@ -434,17 +497,19 @@ LRESULT CALLBACK rvDebuggerWindow::MarginWndProc ( HWND wnd, UINT msg, WPARAM wp
c = SendMessage ( window->mWndScript, EM_LINEINDEX, window->mClient->GetCallstack()[window->mCurrentStackDepth]->mLineNumber - 1, 0 );
SendMessage ( window->mWndScript, EM_POSFROMCHAR, (WPARAM)&pos, c );
ImageList_DrawEx ( window->mImageList, 1, dc, rect.left, pos.y, size, size, CLR_NONE, CLR_NONE, ILD_NORMAL );
ImageList_DrawEx ( window->mTmpImageList, 1, dc, rect.left, pos.y, width, height, CLR_NONE, CLR_NONE, ILD_NORMAL );
}
}
}
RECT tmp = rect;
rect.right-=2;
rect.left = rect.right + 1;
HPEN pen = CreatePen ( PS_SOLID, 1, GetSysColor ( COLOR_3DSHADOW ) );
rect.right -= s2;
rect.left = rect.right + s2;
HPEN pen = CreatePen ( PS_SOLID, s2, GetSysColor ( COLOR_BACKGROUND ) );
HPEN old = (HPEN)SelectObject ( dc, pen );
MoveToEx ( dc, rect.right, rect.top, NULL );
LineTo ( dc, rect.right, rect.bottom );
SelectObject ( dc, old );
DeleteObject ( pen );
EndPaint ( wnd, &ps );
@ -466,7 +531,7 @@ void rvDebuggerWindow::UpdateTitle ( void )
{
idStr title;
title = "Quake 4 Script Debugger - ";
title = "Dhewm3 Script Debugger - ";
if ( mClient->IsConnected ( ) )
{
@ -487,7 +552,10 @@ void rvDebuggerWindow::UpdateTitle ( void )
if ( mScripts.Num ( ) )
{
title += " - [";
title += idStr( mScripts[mActiveScript]->GetFilename() ).StripPath ( );
if (mActiveScript != -1)
title += idStr( mScripts[mActiveScript]->GetFilename() ).StripPath ( );
else
title += "Load Error";
title += "]";
}
@ -590,6 +658,56 @@ void rvDebuggerWindow::UpdateCallstack ( void )
}
}
void rvDebuggerWindow::UpdateScriptList(void)
{
LVITEM item;
ListView_DeleteAllItems(mWndScriptList);
ZeroMemory(&item, sizeof(item));
item.mask = LVIF_TEXT | LVIF_IMAGE;
idStrList& scripts = mClient->GetServerScripts();
for (int i = 0; i < scripts.Num(); i++)
{
item.iItem = ListView_GetItemCount(mWndScriptList);
item.pszText = "";
//find in activeScripts
item.iImage = 0;
for (int j = 0; j < mScripts.Num(); j++)
{
if (!idStr::Icmp(mScripts[j]->GetFilename(), scripts[i]))
{
item.iImage = 1;
break;
}
}
ListView_InsertItem(mWndScriptList, &item);
ListView_SetItemText(mWndScriptList, item.iItem, 1, (LPSTR)scripts[i].c_str());
}
}
void rvDebuggerWindow::UpdateBreakpointList( void )
{
LVITEM item;
ListView_DeleteAllItems( mWndBreakList );
ZeroMemory( &item, sizeof( item ) );
item.mask = LVIF_TEXT | LVIF_IMAGE;
int numBreakPoints = mClient->GetBreakpointCount();
for ( int i = 0; i < numBreakPoints; i++ )
{
rvDebuggerBreakpoint* bp = mClient->GetBreakpoint( i );
item.iItem = ListView_GetItemCount( mWndBreakList );
item.pszText = "";
item.iImage = 2; // breakpoint
ListView_InsertItem( mWndBreakList, &item );
idStr lineStr( bp->GetLineNumber() );
ListView_SetItemText( mWndBreakList, item.iItem, 1, (LPSTR)bp->GetFilename() );
ListView_SetItemText( mWndBreakList, item.iItem, 2, (LPSTR)lineStr.c_str() );
}
}
/*
================
rvDebuggerWindow::UpdateWatch
@ -712,7 +830,7 @@ int rvDebuggerWindow::HandleInitMenu ( WPARAM wParam, LPARAM lParam )
case ID_DBG_DEBUG_STEPOVER:
case ID_DBG_DEBUG_STEPINTO:
case ID_DBG_DEBUG_SHOWNEXTSTATEMENT:
// case ID_DBG_DEBUG_QUICKWATCH:
case ID_DBG_DEBUG_QUICKWATCH:
if ( !mClient->IsConnected() || !mClient->IsStopped() )
{
EnableMenuItem ( hmenu, nPos, MF_GRAYED|MF_BYPOSITION );
@ -737,6 +855,33 @@ int rvDebuggerWindow::HandleInitMenu ( WPARAM wParam, LPARAM lParam )
return 0;
}
void rvDebuggerWindow::ResizeImageList(int& widthOut, int& heightOut)
{
//mTmpImageList
float scaling_factor = Win_GetWindowScalingFactor(mWnd);
int s16 = int(16 * scaling_factor);
TEXTMETRIC tm;
HDC dc;
dc = GetDC(mWndScript);
GetTextMetrics(dc, &tm);
int height = mZoomScaleDem ? (tm.tmHeight * (float)mZoomScaleNum / (float)mZoomScaleDem) : 16;
height *= scaling_factor;
int width = mZoomScaleDem ? (s16 * (float)mZoomScaleNum / (float)mZoomScaleDem) : s16;
ImageList_Destroy(mTmpImageList);
mTmpImageList = ImageList_Create(width, height, ILC_COLOR | ILC_MASK , 0, 2);
ImageList_AddIcon(mTmpImageList, (HICON)LoadImage(mInstance, MAKEINTRESOURCE(IDI_DBG_EMPTY), IMAGE_ICON, width, height, LR_DEFAULTSIZE | LR_DEFAULTCOLOR));
ImageList_AddIcon(mTmpImageList, (HICON)LoadImage(mInstance, MAKEINTRESOURCE(IDI_DBG_CURRENT), IMAGE_ICON, width, height, LR_DEFAULTSIZE | LR_DEFAULTCOLOR));
ImageList_AddIcon(mTmpImageList, (HICON)LoadImage(mInstance, MAKEINTRESOURCE(IDI_DBG_BREAKPOINT), IMAGE_ICON, width, height, LR_DEFAULTSIZE | LR_DEFAULTCOLOR));
ImageList_AddIcon(mTmpImageList, (HICON)LoadImage(mInstance, MAKEINTRESOURCE(IDI_DBG_CURRENTLINE), IMAGE_ICON, width, height, LR_DEFAULTSIZE | LR_DEFAULTCOLOR));
widthOut = width;
heightOut = height;
}
/*
================
rvDebuggerWindow::HandleCreate
@ -760,18 +905,18 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam )
// Create the script window
LoadLibrary ( "Riched20.dll" );
mWndScript = CreateWindow ( "RichEdit20A", "", WS_CHILD|WS_BORDER|ES_NOHIDESEL|ES_READONLY|ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|ES_AUTOHSCROLL|WS_VSCROLL|WS_HSCROLL, 0, 0, 100, 100, mWnd, (HMENU) IDC_DBG_SCRIPT, mInstance, 0 );
SendMessage ( mWndScript, EM_SETEVENTMASK, 0, ENM_SCROLL|ENM_CHANGE );
SendMessage ( mWndScript, EM_SETEVENTMASK, 0, ENM_SCROLL | ENM_CHANGE | ENM_UPDATE | ENM_SCROLLEVENTS | ENM_REQUESTRESIZE) ;
SendMessage ( mWndScript, EM_SETWORDBREAKPROC, 0, (LPARAM) ScriptWordBreakProc );
mOldScriptProc = (WNDPROC)GetWindowLong ( mWndScript, GWL_WNDPROC );
SetWindowLong ( mWndScript, GWL_USERDATA, (LONG)this );
SetWindowLong ( mWndScript, GWL_WNDPROC, (LONG)ScriptWndProc );
mOldScriptProc = (WNDPROC)GetWindowLongPtr ( mWndScript, GWLP_WNDPROC );
SetWindowLongPtr ( mWndScript, GWLP_USERDATA, (LONG_PTR)this );
SetWindowLongPtr ( mWndScript, GWLP_WNDPROC, (LONG_PTR)ScriptWndProc );
SendMessage ( mWndScript, EM_SETTABSTOPS, 1, (LPARAM)&tabsize );
dc = GetDC ( mWndScript );
GetTextMetrics ( dc, &tm );
ZeroMemory ( &lf, sizeof(lf) );
lf.lfHeight = tm.tmHeight;
lf.lfHeight = tm.tmHeight * Win_GetWindowScalingFactor( mWndScript );
strcpy ( lf.lfFaceName, "Courier New" );
SendMessage ( mWndScript, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );
@ -782,15 +927,22 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam )
SendMessage ( mWndOutput, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );
SendMessage ( mWndOutput, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) );
SendMessage ( mWndOutput, EM_SETBKGNDCOLOR, 0, GetSysColor ( COLOR_3DFACE ) );
SendMessage ( mWndOutput, EM_SETEVENTMASK, 0, ENM_SCROLL | ENM_CHANGE | ENM_UPDATE | ENM_SCROLLEVENTS);
mWndConsole = CreateWindow ( "RichEdit20A", "", WS_CHILD|ES_READONLY|ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|ES_AUTOHSCROLL|WS_VSCROLL|WS_HSCROLL, 0, 0, 100, 100, mWnd, (HMENU) IDC_DBG_CONSOLE, mInstance, 0 );
SendMessage ( mWndConsole, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 );
SendMessage ( mWndConsole, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(18,10) );
SendMessage ( mWndConsole, EM_SETBKGNDCOLOR, 0, GetSysColor ( COLOR_3DFACE ) );
mWndConsoleInput = CreateWindow( "RichEdit20A", "", WS_CHILD | ES_WANTRETURN | ES_AUTOVSCROLL | WS_VSCROLL | WS_BORDER, 0, 0, 100, 18, mWnd, ( HMENU ) IDC_DBG_CONSOLEINPUT, mInstance, 0 );
lf.lfHeight = -MulDiv( 8, GetDeviceCaps( dc, LOGPIXELSY ), 72 );
strcpy( lf.lfFaceName, "Arial" );
SendMessage( mWndConsoleInput, WM_SETFONT, ( WPARAM ) CreateFontIndirect( &lf ), 0 );
SendMessage( mWndConsoleInput, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG( 18, 10 ) );
mWndMargin = CreateWindow ( "STATIC", "", WS_VISIBLE|WS_CHILD, 0, 0, 0, 0, mWndScript, (HMENU)IDC_DBG_SPLITTER, mInstance, NULL );
SetWindowLong ( mWndMargin, GWL_USERDATA, (LONG)this );
SetWindowLong ( mWndMargin, GWL_WNDPROC, (LONG)MarginWndProc );
SetWindowLongPtr ( mWndMargin, GWLP_USERDATA, (LONG_PTR)this );
SetWindowLongPtr ( mWndMargin, GWLP_WNDPROC, (LONG_PTR)MarginWndProc );
mWndBorder = CreateWindow ( "STATIC", "", WS_VISIBLE|WS_CHILD|SS_GRAYFRAME, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_BORDER, mInstance, NULL );
@ -816,23 +968,52 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam )
TabCtrl_InsertItem ( mWndTabs, 3, &item );
item.pszText = "Threads";
TabCtrl_InsertItem ( mWndTabs, 4, &item );
item.pszText = "Scripts";
TabCtrl_InsertItem ( mWndTabs, 5, &item );
item.pszText = "Breakpoints";
TabCtrl_InsertItem ( mWndTabs, 6, &item );
mWndCallstack = CreateWindow ( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_SHAREIMAGELISTS, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_CALLSTACK, mInstance, NULL );
mWndWatch = CreateWindow ( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_EDITLABELS|LVS_OWNERDRAWFIXED, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_WATCH, mInstance, NULL );
mWndThreads = CreateWindow ( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_SHAREIMAGELISTS, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_THREADS, mInstance, NULL );
mWndScriptList = CreateWindow( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_SHAREIMAGELISTS, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_SCRIPTLIST, mInstance, NULL );
mWndBreakList = CreateWindow( WC_LISTVIEW, "", LVS_REPORT|WS_CHILD|LVS_SHAREIMAGELISTS, 0, 0, 0, 0, mWnd, (HMENU)IDC_DBG_BREAKLIST, mInstance, NULL );
LVCOLUMN col;
col.mask = LVCF_WIDTH|LVCF_TEXT;
col.cx = 20;
col.pszText = "";
ListView_InsertColumn( mWndBreakList, 0, &col );
#if 0 // TODO: figure out how to get the function name in UpdateBreakpointList()
col.cx = 150;
col.pszText = "Function";
ListView_InsertColumn( mWndBreakList, 1, &col );
#endif
col.cx = 350;
col.pszText = "Filename";
ListView_InsertColumn( mWndBreakList, 1, &col );
col.cx = 50;
col.pszText = "Line";
ListView_InsertColumn( mWndBreakList, 2, &col );
col.cx = 20;
col.pszText = "";
ListView_InsertColumn ( mWndScriptList, 0, &col);
col.cx = 350;
col.pszText = "Filename";
ListView_InsertColumn ( mWndScriptList, 1, &col );
col.cx = 20;
col.pszText = "";
ListView_InsertColumn ( mWndCallstack, 0, &col );
col.cx = 150;
col.pszText = "Function";
ListView_InsertColumn ( mWndCallstack, 1, &col );
col.cx = 150;
col.cx = 50;
col.pszText = "Line";
ListView_InsertColumn ( mWndCallstack, 2, &col );
col.cx = 150;
col.cx = 350;
col.pszText = "Filename";
ListView_InsertColumn ( mWndCallstack, 3, &col );
@ -863,13 +1044,21 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam )
ImageList_AddIcon ( mImageList, (HICON)LoadImage ( mInstance, MAKEINTRESOURCE(IDI_DBG_CURRENT), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_DEFAULTCOLOR) );
ImageList_AddIcon ( mImageList, (HICON)LoadImage ( mInstance, MAKEINTRESOURCE(IDI_DBG_BREAKPOINT), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_DEFAULTCOLOR) );
ImageList_AddIcon ( mImageList, (HICON)LoadImage ( mInstance, MAKEINTRESOURCE(IDI_DBG_CURRENTLINE), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_DEFAULTCOLOR) );
int w, h;
ResizeImageList(w, h);
ListView_SetImageList ( mWndScriptList, mTmpImageList, LVSIL_SMALL );
ListView_SetImageList ( mWndThreads, mImageList, LVSIL_SMALL );
ListView_SetImageList ( mWndCallstack, mImageList, LVSIL_SMALL );
ListView_SetImageList ( mWndBreakList, mImageList, LVSIL_SMALL );
EnableWindows ( FALSE );
EnableWindow ( mWndScriptList, true );
ListView_SetExtendedListViewStyle ( mWndCallstack, LVS_EX_FULLROWSELECT );
ListView_SetExtendedListViewStyle ( mWndThreads, LVS_EX_FULLROWSELECT );
ListView_SetExtendedListViewStyle ( mWndScriptList, LVS_EX_FULLROWSELECT );
ListView_SetExtendedListViewStyle ( mWndBreakList, LVS_EX_FULLROWSELECT );
gDebuggerApp.GetOptions().GetColumnWidths ( "cw_callstack", mWndCallstack );
gDebuggerApp.GetOptions().GetColumnWidths ( "cw_threads", mWndThreads );
@ -920,6 +1109,10 @@ int rvDebuggerWindow::HandleCreate ( WPARAM wparam, LPARAM lparam )
AddWatch ( s );
}
RECT t;
GetClientRect(mWndScript, &t);
SendMessage(mWndScript, WM_SIZE, 0, MAKELPARAM(t.right - t.left, t.bottom - t.top));
return 0;
}
@ -957,6 +1150,36 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
switch ( id )
{
case ID_DBG_SEND_COMMAND:
{
if ( mClient->IsConnected( ) && GetFocus( ) == mWndConsoleInput ) {
GETTEXTLENGTHEX textLen;
int chars;
textLen.flags = GTL_DEFAULT | GTL_USECRLF;
textLen.codepage = CP_ACP;
chars = SendMessage( mWndConsoleInput, EM_GETTEXTLENGTHEX, ( WPARAM ) &textLen, 0 );
char *text = new char[chars + 1];
GETTEXTEX getText;
getText.cb = chars + 1;
getText.codepage = CP_ACP;
getText.flags = GT_DEFAULT | GT_USECRLF;
getText.lpDefaultChar = NULL;
getText.lpUsedDefChar = NULL;
SendMessage( mWndConsoleInput, EM_GETTEXTEX, ( WPARAM ) &getText, ( LPARAM ) text );
idStr parse = text;
delete[] text;
mClient->SendCommand( parse.c_str() );
SendMessage( mWndConsoleInput, EM_SETSEL, 0, -1 );
SendMessage( mWndConsoleInput, EM_REPLACESEL, FALSE, ( LPARAM ) "" );
UpdateWindow( mWndConsoleInput );
}
break;
}
case ID_DBG_EDIT_FINDSELECTED:
{
idStr text;
@ -1025,7 +1248,7 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
GetCurrentDirectory ( MAX_PATH, curDir );
GetModuleFileName ( NULL, exeFile, MAX_PATH );
const char* s = va("%s +set fs_game %s +set fs_cdpath %s", exeFile, cvarSystem->GetCVarString( "fs_game" ), cvarSystem->GetCVarString( "fs_cdpath" ) );
const char* s = va("%s +set fs_game %s +set fs_cdpath %s +set com_enableDebuggerServer 1", exeFile, cvarSystem->GetCVarString( "fs_game" ), cvarSystem->GetCVarString( "fs_cdpath" ) );
CreateProcess ( NULL, (LPSTR)s,
NULL, NULL, FALSE, 0, NULL, curDir, &startup, &process );
@ -1049,13 +1272,13 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
LONG num;
LONG dem;
SendMessage ( mWndScript, EM_GETZOOM, (LONG)&num, (LONG)&dem );
SendMessage ( mWndScript, EM_GETZOOM, (WPARAM)&num, (LPARAM)&dem );
if ( num != mZoomScaleNum || dem != mZoomScaleDem )
{
mZoomScaleNum = num;
mZoomScaleDem = dem;
GetClientRect ( mWndScript, &t );
SendMessage ( mWnd, WM_SIZE, 0, MAKELPARAM(t.right-t.left,t.bottom-t.top) );
SendMessage ( mWndScript, WM_SIZE, 0, MAKELPARAM(t.right-t.left ,t.bottom-t.top) );
}
else
{
@ -1064,6 +1287,7 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
break;
}
case 111: // DG: Debugger.rc has 'MENUITEM "Toggle &Breakpoint\tF9", 111' for the context menu no idea why 111 but this works
case ID_DBG_DEBUG_TOGGLEBREAKPOINT:
ToggleBreakpoint ( );
break;
@ -1139,6 +1363,23 @@ int rvDebuggerWindow::HandleCommand ( WPARAM wparam, LPARAM lparam )
UpdateScript ( );
break;
}
// DG: support "Run To Cursor" from context menu
case ID_DBG_DEBUG_RUNTOCURSOR:
{
// Find the currently selected line
DWORD sel;
SendMessage( mWndScript, EM_GETSEL, (WPARAM)&sel, 0 );
int lineNumber = SendMessage( mWndScript, EM_LINEFROMCHAR, sel, 0 ) + 1;
const char* filename = mScripts[mActiveScript]->GetFilename();
mClient->AddBreakpoint( filename, lineNumber, true );
mClient->Resume();
break;
}
// TODO: case ID_DBG_DEBUG_SHOWNEXTSTATEMENT:
// whatever this is supposed to do (also from context menu)
}
return 0;
@ -1153,7 +1394,7 @@ Window procedure for the deubgger window
*/
LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
rvDebuggerWindow* window = (rvDebuggerWindow*) GetWindowLong ( wnd, GWL_USERDATA );
rvDebuggerWindow* window = (rvDebuggerWindow*) GetWindowLongPtr ( wnd, GWLP_USERDATA );
switch ( msg )
{
@ -1176,7 +1417,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
gDebuggerApp.GetOptions().SetString ( va("watch%d", i ), "" );
window->mWnd = NULL;
SetWindowLong ( wnd, GWL_USERDATA, 0 );
SetWindowLongPtr ( wnd, GWLP_USERDATA, 0 );
break;
}
@ -1202,8 +1443,13 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
case WM_SIZE:
{
float scaling_factor = Win_GetWindowScalingFactor(wnd);
int s18 = int(18 * scaling_factor);
int s4 = int(4 * scaling_factor);
int s10 = int(10 * scaling_factor);
RECT rect;
window->mMarginSize = window->mZoomScaleDem ? ((long)(18.0f * (float)window->mZoomScaleNum / (float)window->mZoomScaleDem)):18;
window->mMarginSize = window->mZoomScaleDem ? ((long)(s18 * (float)window->mZoomScaleNum / (float)window->mZoomScaleDem)): s18;
window->mSplitterRect.left = 0;
window->mSplitterRect.right = LOWORD(lparam);
@ -1215,14 +1461,27 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
SetRect ( &rect, 0, window->mSplitterRect.bottom, LOWORD(lparam), HIWORD(lparam) );
MoveWindow ( window->mWndTabs, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
SendMessage ( window->mWndTabs, TCM_ADJUSTRECT, FALSE, (LPARAM)&rect );
rect.bottom -= 4 ;
rect.bottom -= s4;
MoveWindow ( window->mWndBorder, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
InflateRect ( &rect, -1, -1 );
MoveWindow ( window->mWndOutput, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
MoveWindow ( window->mWndConsole, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
MoveWindow ( window->mWndConsole, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top - s18, TRUE );
MoveWindow ( window->mWndConsoleInput, rect.left, rect.bottom-s18, rect.right - rect.left, s18, TRUE );
MoveWindow ( window->mWndCallstack, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
MoveWindow ( window->mWndWatch, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
MoveWindow ( window->mWndThreads, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
MoveWindow ( window->mWndScriptList, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
MoveWindow ( window->mWndBreakList, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );
// FIXME: was *2.25, increased for line numbers up to 9999; but neither works particularly well
// if DPI scaling is involved, because script code text and linenumbers aren't DPI scaled
int lmargin = s18 * 3.5;
SendMessage(window->mWndScript, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(lmargin, s10));
SendMessage(window->mWndCallstack, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(s18, s10));
SendMessage(window->mWndOutput, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(s18, s10));
SendMessage(window->mWndConsole, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(s18, s10));
SendMessage( window->mWndConsoleInput, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG( s18, s10 ) );
break;
}
@ -1258,7 +1517,17 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
}
break;
}
case WM_MOUSEWHEEL:
{
HDC dc = GetDC(wnd);
DrawFocusRect(dc, &window->mSplitterRect);
ReleaseDC(wnd, dc);
RECT client;
GetClientRect(wnd, &client);
SendMessage(wnd, WM_SIZE, 0, MAKELPARAM(client.right - client.left, client.bottom - client.top));
}
case WM_LBUTTONUP:
if ( window->mSplitterDrag )
{
@ -1320,7 +1589,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
{
CREATESTRUCT* cs = (CREATESTRUCT*) lparam;
window = (rvDebuggerWindow*) cs->lpCreateParams;
SetWindowLong ( wnd, GWL_USERDATA, (LONG)cs->lpCreateParams );
SetWindowLongPtr ( wnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams );
window->mWnd = wnd;
window->HandleCreate ( wparam, lparam );
@ -1439,7 +1708,6 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
}
}
break;
case IDC_DBG_CALLSTACK:
if ( hdr->code == NM_DBLCLK )
{
@ -1454,14 +1722,74 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
}
}
break;
case IDC_DBG_SCRIPTLIST:
if ( hdr->code == NM_DBLCLK )
{
int sel = ListView_GetNextItem(hdr->hwndFrom, -1, LVNI_SELECTED);
if (sel != -1)
{
LVITEM item = { 0 };
char temp[1024] = { 0 };
item.mask = LVIF_TEXT;
item.pszText = temp;
item.cchTextMax = sizeof(temp) - 1;
item.iSubItem = 1;
item.iItem = sel;
ListView_GetItem(hdr->hwndFrom, &item);
if (strlen(item.pszText) > 0)
{
window->OpenScript(item.pszText);
window->UpdateScriptList();
}
}
}
break;
case IDC_DBG_BREAKLIST:
if ( hdr->code == NM_DBLCLK || hdr->code == NM_CLICK ) {
LPNMITEMACTIVATE ia = (LPNMITEMACTIVATE)lparam;
int sel = ia->iItem;
if ( sel != -1 ) {
rvDebuggerBreakpoint* bp = window->mClient->GetBreakpoint( sel );
if ( bp != NULL ) {
if ( hdr->code == NM_DBLCLK ) {
// double clicked breakpoint => show it in its file
window->OpenScript( bp->GetFilename(), bp->GetLineNumber() - 1 );
} else if( ia->iSubItem == 0 ) {
// clicked breakpoint symbol => delete breakpoint
window->mClient->RemoveBreakpoint( bp->GetID() );
window->UpdateBreakpointList();
}
}
}
} else if ( hdr->code == LVN_KEYDOWN ) {
// when user selects a breakpoints and presses the Del key, remove the breakpoint
int sel = ListView_GetNextItem( hdr->hwndFrom, -1, LVNI_SELECTED );
if ( sel != -1 ) {
LPNMLVKEYDOWN kd = (LPNMLVKEYDOWN)lparam;
rvDebuggerBreakpoint* bp = window->mClient->GetBreakpoint( sel );
if ( kd->wVKey == VK_DELETE && bp != NULL ) {
window->mClient->RemoveBreakpoint( bp->GetID() );
window->UpdateBreakpointList();
}
}
}
break;
case IDC_DBG_TABS:
if ( hdr->code == TCN_SELCHANGE )
{
ShowWindow ( window->mWndOutput, SW_HIDE );
ShowWindow ( window->mWndConsole, SW_HIDE );
ShowWindow ( window->mWndConsoleInput, SW_HIDE );
ShowWindow ( window->mWndCallstack, SW_HIDE );
ShowWindow ( window->mWndWatch, SW_HIDE );
ShowWindow ( window->mWndThreads, SW_HIDE );
ShowWindow ( window->mWndScriptList, SW_HIDE );
ShowWindow ( window->mWndBreakList, SW_HIDE );
switch ( TabCtrl_GetCurSel ( hdr->hwndFrom ) )
{
case 0:
@ -1470,6 +1798,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
case 1:
ShowWindow ( window->mWndConsole, SW_SHOW );
ShowWindow( window->mWndConsoleInput, SW_SHOW );
break;
case 2:
@ -1483,6 +1812,14 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
case 4:
ShowWindow ( window->mWndThreads, SW_SHOW );
break;
case 5:
ShowWindow(window->mWndScriptList, SW_SHOW);
break;
case 6:
ShowWindow( window->mWndBreakList, SW_SHOW );
break;
}
}
break;
@ -1493,7 +1830,7 @@ LRESULT CALLBACK rvDebuggerWindow::WndProc ( HWND wnd, UINT msg, WPARAM wparam,
case WM_CLOSE:
if ( window->mClient->IsConnected ( ) )
{
if ( IDNO == MessageBox ( wnd, "The debugger is currently connected to a running version of the game. Are you sure you want to close now?", "Quake 4 Script Debugger", MB_YESNO|MB_ICONQUESTION ) )
if ( IDNO == MessageBox ( wnd, "The debugger is currently connected to a running version of the game. Are you sure you want to close now?", "Dhewm3 Script Debugger", MB_YESNO|MB_ICONQUESTION ) )
{
return 0;
}
@ -1535,14 +1872,20 @@ rvDebuggerWindow::ProcessNetMessage
Process an incoming network message
================
*/
void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg )
void rvDebuggerWindow::ProcessNetMessage ( idBitMsg* msg )
{
unsigned short command;
short command;
command = (unsigned short)MSG_ReadShort ( msg );
command = msg->ReadShort( );
switch ( command )
{
case DBMSG_REMOVEBREAKPOINT:
MessageBeep(MB_ICONEXCLAMATION);
InvalidateRect(mWndScript, NULL, FALSE);
UpdateBreakpointList();
break;
case DBMSG_RESUMED:
UpdateTitle ( );
UpdateToolbar ( );
@ -1554,9 +1897,9 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg )
char temp2[1024];
int i;
MSG_ReadShort ( msg );
MSG_ReadString ( msg, temp, 1024 );
MSG_ReadString ( msg, temp2, 1024 );
msg->ReadShort ( );
msg->ReadString ( temp, 1024 );
msg->ReadString ( temp2, 1024 );
if ( mTooltipVar.Icmp ( temp ) == 0 )
{
mTooltipValue = temp2;
@ -1624,10 +1967,17 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg )
break;
case DBMSG_PRINT:
{
HWND prevFocus = GetFocus();
SetFocus ( mWndConsole );
SendMessage ( mWndConsole, EM_SETSEL, -1, -1 );
SendMessage ( mWndConsole, EM_REPLACESEL, 0, (LPARAM)(const char*)(msg->data) + msg->readcount );
SendMessage ( mWndConsole, EM_REPLACESEL, 0, (LPARAM)(const char*)(msg->GetData()) + msg->GetReadCount() );
SendMessage( mWndConsole, EM_SETSEL, -1, -1 );
SendMessage ( mWndConsole, EM_SCROLLCARET, 0, 0 );
UpdateWindow( mWndConsole );
SetFocus( prevFocus );
break;
}
case DBMSG_BREAK:
{
@ -1636,6 +1986,7 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg )
mCurrentStackDepth = 0;
mClient->InspectVariable ( mTooltipVar, mCurrentStackDepth );
UpdateWatch ( );
UpdateBreakpointList();
EnableWindows ( TRUE );
OpenScript ( mClient->GetBreakFilename(), mClient->GetBreakLineNumber() - 1 );
UpdateTitle ( );
@ -1643,7 +1994,11 @@ void rvDebuggerWindow::ProcessNetMessage ( msg_t* msg )
SetForegroundWindow ( mWnd );
break;
}
case DBMSG_INSPECTSCRIPTS:
{
UpdateScriptList ( );
break;
}
case DBMSG_INSPECTCALLSTACK:
{
UpdateCallstack ( );
@ -1719,7 +2074,7 @@ Opens the script with the given filename and will scroll to the given line
number if one is specified
================
*/
bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber )
bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber, idProgram* program )
{
int i;
@ -1748,7 +2103,6 @@ bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber )
// Load the script
if ( !script->Load ( filename ) )
{
delete script;
SetCursor ( LoadCursor ( NULL, IDC_ARROW ) );
return false;
}
@ -1777,7 +2131,7 @@ bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber )
// Move to a specific line number?
if ( lineNumber != -1 )
{
int c;
long c;
// Put the caret on the line number specified and scroll it into position.
// This is a bit of a hack since we set the selection twice, but setting the
@ -1785,10 +2139,10 @@ bool rvDebuggerWindow::OpenScript ( const char* filename, int lineNumber )
// and then scroll before going back to (c,c).
// NOTE: We scroll to the line before the one we want so its more visible
SetFocus ( mWndScript );
c = SendMessage ( mWndScript, EM_LINEINDEX, lineNumber - 1, 0 );
c = SendMessage ( mWndScript, EM_LINEINDEX, (long)lineNumber - 1, 0 );
SendMessage ( mWndScript, EM_SETSEL, c, c + 1 );
SendMessage ( mWndScript, EM_SCROLLCARET, 0, 0 );
c = SendMessage ( mWndScript, EM_LINEINDEX, lineNumber, 0 );
c = SendMessage ( mWndScript, EM_LINEINDEX, (long)lineNumber, 0 );
SendMessage ( mWndScript, EM_SETSEL, c, c );
}
else
@ -1843,6 +2197,8 @@ void rvDebuggerWindow::ToggleBreakpoint ( void )
// Force a repaint of the script window
InvalidateRect ( mWndScript, NULL, FALSE );
UpdateBreakpointList();
}
/*
@ -1895,6 +2251,8 @@ void rvDebuggerWindow::CreateToolbar ( void )
SendMessage( mWndToolbar, TB_ADDBITMAP, (WPARAM)4, (LPARAM) &tbab );
// Add the buttons to the toolbar
// FIXME: warning C4838: conversion from 'int' to 'BYTE' requires a narrowing conversion
// most probably because TBBUTTON has 4 more bytes in bReserved for alignment on _WIN64
TBBUTTON tbb[] = { { 0, 0, TBSTATE_ENABLED, BTNS_SEP, 0, 0, -1 },
{ 8, ID_DBG_FILE_OPEN, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0, -1 },
{ 0, 0, TBSTATE_ENABLED, BTNS_SEP, 0, 0, -1 },
@ -2013,6 +2371,7 @@ int rvDebuggerWindow::HandleActivate ( WPARAM wparam, LPARAM lparam )
}
}
}
UpdateBreakpointList();
return 1;
}
@ -2193,7 +2552,7 @@ then the last text used will be searched for.
*/
bool rvDebuggerWindow::FindNext ( const char* text )
{
int start;
long start;
FINDTEXT ft;
if ( text )
@ -2230,7 +2589,7 @@ bool rvDebuggerWindow::FindNext ( const char* text )
}
}
SendMessage ( mWndScript, EM_SETSEL, start, start + mFind.Length() );
SendMessage ( mWndScript, EM_SETSEL, start, start + (long)mFind.Length() );
SendMessage ( mWndScript, EM_SCROLLCARET, 0, 0 );
return true;
@ -2247,7 +2606,7 @@ then the last text used will be searched for.
*/
bool rvDebuggerWindow::FindPrev ( const char* text )
{
int start;
long start;
FINDTEXT ft;
if ( text )

View file

@ -52,38 +52,57 @@ public:
rvDebuggerWindow ( );
~rvDebuggerWindow ( );
bool Create ( HINSTANCE hInstance );
bool Create ( HINSTANCE hInstance );
static bool Activate ( void );
static bool Activate ( void );
void ProcessNetMessage ( msg_t* msg );
void ProcessNetMessage ( idBitMsg * msg );
void Printf ( const char* format, ... );
void Printf ( const char* format, ... );
HWND GetWindow ( void );
HWND GetWindow ( void );
void AddWatch ( const char* name, bool update = true );
void AddWatch ( const char* name, bool update = true );
HINSTANCE GetInstance ( void );
HINSTANCE GetInstance ( void );
protected:
private:
bool RegisterClass ( void );
void CreateToolbar ( void );
bool InitRecentFiles ( void );
bool FindPrev ( const char* text = NULL );
bool FindNext ( const char* text = NULL );
int HandleInitMenu ( WPARAM wParam, LPARAM lParam );
int HandleCommand ( WPARAM wParam, LPARAM lParam );
int HandleCreate ( WPARAM wparam, LPARAM lparam );
int HandleActivate ( WPARAM wparam, LPARAM lparam );
int HandleDrawItem ( WPARAM wparam, LPARAM lparam );
void HandleTooltipGetDispInfo( WPARAM wparam, LPARAM lparam );
void UpdateWatch ( void );
void UpdateWindowMenu ( void );
void UpdateScript ( void );
void UpdateToolbar ( void );
void UpdateTitle ( void );
void UpdateCallstack ( void );
void UpdateRecentFiles ( void );
bool OpenScript ( const char* filename, int lineNumber = -1 );
void EnableWindows ( bool state );
void ResizeImageList ( int& widthOut, int& heightOut);
static LRESULT CALLBACK WndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
static LRESULT CALLBACK MarginWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
static LRESULT CALLBACK ScriptWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
static INT_PTR CALLBACK AboutDlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
static int CALLBACK ScriptWordBreakProc( LPTSTR text, int current, int max, int action );
int GetSelectedText ( idStr& text );
bool FindPrev ( const char* text = NULL );
bool FindNext ( const char* text = NULL );
void ToggleBreakpoint ( void );
void UpdateBreakpointList( void );
void UpdateScriptList ( void );
void UpdateWatch ( void );
void UpdateWindowMenu ( void );
void UpdateScript ( void );
void UpdateToolbar ( void );
void UpdateTitle ( void );
void UpdateCallstack ( void );
void UpdateRecentFiles ( void );
bool OpenScript ( const char* filename, int lineNumber = -1, idProgram* program = nullptr );
void EnableWindows ( bool state );
int GetSelectedText ( idStr& text );
void ToggleBreakpoint ( void );
HWND mWnd;
HWND mWndScript;
@ -92,7 +111,10 @@ protected:
HWND mWndTabs;
HWND mWndBorder;
HWND mWndConsole;
HWND mWndConsoleInput;
HWND mWndCallstack;
HWND mWndScriptList;
HWND mWndBreakList; // list of breakpoints
HWND mWndWatch;
HWND mWndThreads;
HWND mWndToolTips;
@ -108,6 +130,7 @@ protected:
HINSTANCE mInstance;
HIMAGELIST mImageList;
HIMAGELIST mTmpImageList;
RECT mSplitterRect;
bool mSplitterDrag;
@ -129,25 +152,6 @@ protected:
rvDebuggerClient* mClient;
rvDebuggerWatchList mWatches;
private:
bool RegisterClass ( void );
void CreateToolbar ( void );
bool InitRecentFiles ( void );
int HandleInitMenu ( WPARAM wParam, LPARAM lParam );
int HandleCommand ( WPARAM wParam, LPARAM lParam );
int HandleCreate ( WPARAM wparam, LPARAM lparam );
int HandleActivate ( WPARAM wparam, LPARAM lparam );
int HandleDrawItem ( WPARAM wparam, LPARAM lparam );
void HandleTooltipGetDispInfo ( WPARAM wparam, LPARAM lparam );
static LRESULT CALLBACK WndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
static LRESULT CALLBACK MarginWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
static LRESULT CALLBACK ScriptWndProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
static INT_PTR CALLBACK AboutDlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam );
static int CALLBACK ScriptWordBreakProc ( LPTSTR text, int current, int max, int action );
};
/*

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

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 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" ); }