Port script debugger support from dhewm3

all that work in dhewm3 was done by https://github.com/HarrievG/
This commit is contained in:
Daniel Gibson 2022-05-17 04:58:38 +02:00
parent 0942905731
commit ced3b8f74e
10 changed files with 654 additions and 61 deletions

View file

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

@ -300,6 +300,17 @@ void idGameLocal::Clear( void ) {
#endif
}
// DG: for script debugger
static bool ( *updateDebuggerFnPtr )( idInterpreter *interpreter, idProgram *program, int instructionPointer ) = NULL;
bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ) {
bool ret = false;
if ( interpreter != NULL && program != NULL ) {
ret = updateDebuggerFnPtr ? updateDebuggerFnPtr( interpreter , program, instructionPointer ) : false;
}
return ret;
}
/*
===========
idGameLocal::Init
@ -408,6 +419,11 @@ void idGameLocal::Init( void ) {
gamestate = GAMESTATE_NOMAP;
Printf( "...%d aas types\n", aasList.Num() );
// debugger support - Note: a side effect of requesting this function pointer
// is that now the engine knows that this mod supports script debugging.
common->GetAdditionalFunction( idCommon::FT_UpdateDebugger,( idCommon::FunctionPointer * ) &updateDebuggerFnPtr,NULL);
}
/*

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,9 +195,9 @@ 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();
@ -215,34 +219,43 @@ 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 );
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 ) {
return false;
}
// Get the variable itself and check various namespaces
d = gameLocal.program.GetDef( NULL, name, d );
if ( !d ) {
if ( scope == &def_namespace ) {
if ( !scope )
{
return false;
}
d = gameLocal.program.GetDef( NULL, name, scope );
if ( !d ) {
d = gameLocal.program.GetDef( NULL, name, &def_namespace );
if ( !d ) {
// 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() ) {
@ -274,14 +287,24 @@ 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;
}
@ -294,10 +317,25 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
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;
}
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,100 @@ 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,50 @@ 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

@ -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";
@ -266,6 +271,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

@ -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,12 @@ enum {
class idEntity;
class idMD5Anim;
// DG: for idGameEditExt (for script debugger)
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 {
@ -315,6 +322,40 @@ public:
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

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

@ -256,6 +256,18 @@ void idGameLocal::Clear( void ) {
memset( lagometer, 0, sizeof( lagometer ) );
}
// DG: for script debugger support
static bool ( *updateDebuggerFnPtr )( idInterpreter *interpreter, idProgram *program, int instructionPointer ) = NULL;
bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ) {
bool ret = false;
if ( interpreter != NULL && program != NULL ) {
ret = updateDebuggerFnPtr ? updateDebuggerFnPtr( interpreter, program, instructionPointer ) : false;
}
return ret;
}
/*
===========
idGameLocal::Init
@ -332,6 +344,10 @@ void idGameLocal::Init( void ) {
gamestate = GAMESTATE_NOMAP;
Printf( "...%d aas types\n", aasList.Num() );
// debugger support - Note: a side effect of requesting this function pointer
// is that now the engine knows that this mod supports script debugging.
common->GetAdditionalFunction(idCommon::FT_UpdateDebugger,(idCommon::FunctionPointer*) &updateDebuggerFnPtr,NULL);
}
/*

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,9 +195,9 @@ 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();
@ -215,34 +219,43 @@ 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 );
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 ) {
return false;
}
// Get the variable itself and check various namespaces
d = gameLocal.program.GetDef( NULL, name, d );
if ( !d ) {
if ( scope == &def_namespace ) {
if ( !scope )
{
return false;
}
d = gameLocal.program.GetDef( NULL, name, scope );
if ( !d ) {
d = gameLocal.program.GetDef( NULL, name, &def_namespace );
if ( !d ) {
// 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() ) {
@ -274,14 +287,24 @@ 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;
}
@ -294,10 +317,25 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
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;
}
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

@ -1841,3 +1841,50 @@ 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);
}