mirror of
https://github.com/JezuzLizard/T4SP-Server-Plugin.git
synced 2025-04-19 13:12:53 +00:00
Cleanup and move gsc dump stuff
This commit is contained in:
parent
1f66cb6a43
commit
2e50e3274d
@ -1,418 +1,2 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "clientscript_public.hpp"
|
||||
#include <dbghelp.h>
|
||||
|
||||
namespace game
|
||||
{
|
||||
#define QUICK_TO_JSON_FIELD(j, v, membername) j[#membername] = v.membername
|
||||
|
||||
#define QUICK_TO_JSON_FIELD_SAFE_CSTR(j, v, membername) \
|
||||
if (v.membername) \
|
||||
j[#membername] = v.membername; \
|
||||
else \
|
||||
j[#membername] = "(NULL)"
|
||||
|
||||
#define QUICK_TO_JSON_FIELD_PTR_ADDR(j, v, membername) j[#membername] = reinterpret_cast<size_t>(&v.membername)
|
||||
|
||||
#define QUICK_TO_JSON_FIELD_ARRAY(j, v, membername) \
|
||||
for (auto i = 0; i < ARRAY_COUNT(v.membername); i++) \
|
||||
{ \
|
||||
j[#membername][i] = v.membername[i]; \
|
||||
}
|
||||
|
||||
#define QUICK_TO_JSON_FIELD_SL_STRING(j, v, membername) j[#membername "Str"] = SL_ConvertToStringSafe(v.membername, *gInst)
|
||||
|
||||
void to_json(nlohmann::json& j, const scrVarPub_t& v)
|
||||
{
|
||||
QUICK_TO_JSON_FIELD_SAFE_CSTR(j, v, fieldBuffer);
|
||||
QUICK_TO_JSON_FIELD(j, v, canonicalStrCount);
|
||||
QUICK_TO_JSON_FIELD(j, v, developer);
|
||||
QUICK_TO_JSON_FIELD(j, v, developer_script);
|
||||
QUICK_TO_JSON_FIELD(j, v, evaluate);
|
||||
QUICK_TO_JSON_FIELD_SAFE_CSTR(j, v, error_message);
|
||||
QUICK_TO_JSON_FIELD(j, v, error_index);
|
||||
QUICK_TO_JSON_FIELD(j, v, time);
|
||||
QUICK_TO_JSON_FIELD(j, v, timeArrayId);
|
||||
QUICK_TO_JSON_FIELD(j, v, pauseArrayId);
|
||||
QUICK_TO_JSON_FIELD(j, v, levelId);
|
||||
QUICK_TO_JSON_FIELD(j, v, gameId);
|
||||
QUICK_TO_JSON_FIELD(j, v, animId);
|
||||
QUICK_TO_JSON_FIELD(j, v, freeEntList);
|
||||
QUICK_TO_JSON_FIELD(j, v, tempVariable);
|
||||
QUICK_TO_JSON_FIELD(j, v, bInited);
|
||||
QUICK_TO_JSON_FIELD(j, v, savecount);
|
||||
QUICK_TO_JSON_FIELD(j, v, checksum);
|
||||
QUICK_TO_JSON_FIELD(j, v, entId);
|
||||
QUICK_TO_JSON_FIELD(j, v, entFieldName);
|
||||
QUICK_TO_JSON_FIELD_SL_STRING(j, v, entFieldName);
|
||||
QUICK_TO_JSON_FIELD_PTR_ADDR(j, v, programHunkUser);
|
||||
QUICK_TO_JSON_FIELD_PTR_ADDR(j, v, programBuffer);
|
||||
QUICK_TO_JSON_FIELD_PTR_ADDR(j, v, endScriptBuffer);
|
||||
QUICK_TO_JSON_FIELD_ARRAY(j, v, saveIdMap);
|
||||
QUICK_TO_JSON_FIELD_ARRAY(j, v, saveIdMapRev);
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/5693192/win32-backtrace-from-c-code
|
||||
std::string printStack()
|
||||
{
|
||||
unsigned int i;
|
||||
void * stack[ 100 ];
|
||||
unsigned short frames;
|
||||
SYMBOL_INFO * symbol;
|
||||
HANDLE process;
|
||||
std::string answer{};
|
||||
|
||||
process = GetCurrentProcess();
|
||||
|
||||
SymInitialize( process, NULL, TRUE );
|
||||
|
||||
frames = CaptureStackBackTrace( 0, 100, stack, NULL );
|
||||
symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
|
||||
symbol->MaxNameLen = 255;
|
||||
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
|
||||
|
||||
for( i = 0; i < frames; i++ )
|
||||
{
|
||||
SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );
|
||||
|
||||
answer += std::format("{}: {} - 0x{:06x}\n", frames - i - 1, symbol->Name, symbol->Address);
|
||||
}
|
||||
|
||||
free( symbol );
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
int op_idx[game::SCRIPT_INSTANCE_MAX] = { 0, 0 };
|
||||
bool op_idx_rolled_over[game::SCRIPT_INSTANCE_MAX] = { false, false };
|
||||
game::OpcodeVM op_history[game::SCRIPT_INSTANCE_MAX][128] = {};
|
||||
|
||||
int builtin_idx[game::SCRIPT_INSTANCE_MAX] = { 0, 0 };
|
||||
bool builtin_idx_rolled_over[game::SCRIPT_INSTANCE_MAX] = { false, false };
|
||||
int builtin_history[game::SCRIPT_INSTANCE_MAX][128] = {};
|
||||
|
||||
int codepos_idx[game::SCRIPT_INSTANCE_MAX] = { 0, 0 };
|
||||
bool codepos_idx_rolled_over[game::SCRIPT_INSTANCE_MAX] = { false, false };
|
||||
const char* codepos_history[game::SCRIPT_INSTANCE_MAX][128] = {};
|
||||
|
||||
std::string build_builtin_history(game::scriptInstance_t inst)
|
||||
{
|
||||
std::string answer{};
|
||||
|
||||
int count = builtin_idx_rolled_over[inst] ? ARRAY_COUNT(builtin_history[inst]) : builtin_idx[inst];
|
||||
|
||||
for (auto i = 0; i < count; i++)
|
||||
{
|
||||
auto idx = builtin_idx[inst] - 1 - i;
|
||||
if (idx < 0)
|
||||
{
|
||||
idx += ARRAY_COUNT(builtin_history[inst]);
|
||||
}
|
||||
|
||||
// todo, convert to builtin name
|
||||
answer += std::format("{}\n", builtin_history[inst][idx]);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string build_codepos_history(game::scriptInstance_t inst)
|
||||
{
|
||||
std::string answer{};
|
||||
int bufferIndex;
|
||||
int prevSourcePos;
|
||||
int col;
|
||||
char line[1024];
|
||||
int lineNum;
|
||||
const char* fileName;
|
||||
|
||||
int count = codepos_idx_rolled_over[inst] ? ARRAY_COUNT(codepos_history[inst]) : codepos_idx[inst];
|
||||
|
||||
for (auto i = 0; i < count; i++)
|
||||
{
|
||||
auto idx = codepos_idx[inst] - 1 - i;
|
||||
if (idx < 0)
|
||||
{
|
||||
idx += ARRAY_COUNT(codepos_history[inst]);
|
||||
}
|
||||
|
||||
bufferIndex = game::Scr_GetSourceBuffer(inst, codepos_history[inst][idx]);
|
||||
prevSourcePos = game::Scr_GetPrevSourcePos(inst, codepos_history[inst][idx], 0);
|
||||
lineNum = game::Scr_GetLineInfo(&col, game::gScrParserPub[inst].sourceBufferLookup[bufferIndex].sourceBuf, prevSourcePos, line);
|
||||
fileName = game::gScrParserPub[inst].sourceBufferLookup[bufferIndex].buf;
|
||||
|
||||
answer += std::format("{}({}, {}): '{}'\n", fileName, lineNum, col, line);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string build_op_history(game::scriptInstance_t inst)
|
||||
{
|
||||
static const char* OpcodeVMToString[] = {
|
||||
"OP_End",
|
||||
"OP_Return",
|
||||
"OP_GetUndefined",
|
||||
"OP_GetZero",
|
||||
"OP_GetByte",
|
||||
"OP_GetNegByte",
|
||||
"OP_GetUnsignedShort",
|
||||
"OP_GetNegUnsignedShort",
|
||||
"OP_GetInteger",
|
||||
"OP_GetFloat",
|
||||
"OP_GetString",
|
||||
"OP_GetIString",
|
||||
"OP_GetVector",
|
||||
"OP_GetLevelObject",
|
||||
"OP_GetAnimObject",
|
||||
"OP_GetSelf",
|
||||
"OP_GetLevel",
|
||||
"OP_GetGame",
|
||||
"OP_GetAnim",
|
||||
"OP_GetAnimation",
|
||||
"OP_GetGameRef",
|
||||
"OP_GetFunction",
|
||||
"OP_CreateLocalVariable",
|
||||
"OP_RemoveLocalVariables",
|
||||
"OP_EvalLocalVariableCached0",
|
||||
"OP_EvalLocalVariableCached1",
|
||||
"OP_EvalLocalVariableCached2",
|
||||
"OP_EvalLocalVariableCached3",
|
||||
"OP_EvalLocalVariableCached4",
|
||||
"OP_EvalLocalVariableCached5",
|
||||
"OP_EvalLocalVariableCached",
|
||||
"OP_EvalLocalArrayCached",
|
||||
"OP_EvalArray",
|
||||
"OP_EvalLocalArrayRefCached0",
|
||||
"OP_EvalLocalArrayRefCached",
|
||||
"OP_EvalArrayRef",
|
||||
"OP_ClearArray",
|
||||
"OP_EmptyArray",
|
||||
"OP_GetSelfObject",
|
||||
"OP_EvalLevelFieldVariable",
|
||||
"OP_EvalAnimFieldVariable",
|
||||
"OP_EvalSelfFieldVariable",
|
||||
"OP_EvalFieldVariable",
|
||||
"OP_EvalLevelFieldVariableRef",
|
||||
"OP_EvalAnimFieldVariableRef",
|
||||
"OP_EvalSelfFieldVariableRef",
|
||||
"OP_EvalFieldVariableRef",
|
||||
"OP_ClearFieldVariable",
|
||||
"OP_SafeCreateVariableFieldCached",
|
||||
"OP_SafeSetVariableFieldCached0",
|
||||
"OP_SafeSetVariableFieldCached",
|
||||
"OP_SafeSetWaittillVariableFieldCached",
|
||||
"OP_clearparams",
|
||||
"OP_checkclearparams",
|
||||
"OP_EvalLocalVariableRefCached0",
|
||||
"OP_EvalLocalVariableRefCached",
|
||||
"OP_SetLevelFieldVariableField",
|
||||
"OP_SetVariableField",
|
||||
"OP_SetAnimFieldVariableField",
|
||||
"OP_SetSelfFieldVariableField",
|
||||
"OP_SetLocalVariableFieldCached0",
|
||||
"OP_SetLocalVariableFieldCached",
|
||||
"OP_CallBuiltin0",
|
||||
"OP_CallBuiltin1",
|
||||
"OP_CallBuiltin2",
|
||||
"OP_CallBuiltin3",
|
||||
"OP_CallBuiltin4",
|
||||
"OP_CallBuiltin5",
|
||||
"OP_CallBuiltin",
|
||||
"OP_CallBuiltinMethod0",
|
||||
"OP_CallBuiltinMethod1",
|
||||
"OP_CallBuiltinMethod2",
|
||||
"OP_CallBuiltinMethod3",
|
||||
"OP_CallBuiltinMethod4",
|
||||
"OP_CallBuiltinMethod5",
|
||||
"OP_CallBuiltinMethod",
|
||||
"OP_wait",
|
||||
"OP_waittillFrameEnd",
|
||||
"OP_PreScriptCall",
|
||||
"OP_ScriptFunctionCall2",
|
||||
"OP_ScriptFunctionCall",
|
||||
"OP_ScriptFunctionCallPointer",
|
||||
"OP_ScriptMethodCall",
|
||||
"OP_ScriptMethodCallPointer",
|
||||
"OP_ScriptThreadCall",
|
||||
"OP_ScriptThreadCallPointer",
|
||||
"OP_ScriptMethodThreadCall",
|
||||
"OP_ScriptMethodThreadCallPointer",
|
||||
"OP_DecTop",
|
||||
"OP_CastFieldObject",
|
||||
"OP_EvalLocalVariableObjectCached",
|
||||
"OP_CastBool",
|
||||
"OP_BoolNot",
|
||||
"OP_BoolComplement",
|
||||
"OP_JumpOnFalse",
|
||||
"OP_JumpOnTrue",
|
||||
"OP_JumpOnFalseExpr",
|
||||
"OP_JumpOnTrueExpr",
|
||||
"OP_jump",
|
||||
"OP_jumpback",
|
||||
"OP_inc",
|
||||
"OP_dec",
|
||||
"OP_bit_or",
|
||||
"OP_bit_ex_or",
|
||||
"OP_bit_and",
|
||||
"OP_equality",
|
||||
"OP_inequality",
|
||||
"OP_less",
|
||||
"OP_greater",
|
||||
"OP_less_equal",
|
||||
"OP_greater_equal",
|
||||
"OP_shift_left",
|
||||
"OP_shift_right",
|
||||
"OP_plus",
|
||||
"OP_minus",
|
||||
"OP_multiply",
|
||||
"OP_divide",
|
||||
"OP_mod",
|
||||
"OP_size",
|
||||
"OP_waittillmatch",
|
||||
"OP_waittill",
|
||||
"OP_notify",
|
||||
"OP_endon",
|
||||
"OP_voidCodepos",
|
||||
"OP_switch",
|
||||
"OP_endswitch",
|
||||
"OP_vector",
|
||||
"OP_NOP",
|
||||
"OP_abort",
|
||||
"OP_object",
|
||||
"OP_thread_object",
|
||||
"OP_EvalLocalVariable",
|
||||
"OP_EvalLocalVariableRef",
|
||||
"OP_prof_begin",
|
||||
"OP_prof_end",
|
||||
"OP_breakpoint",
|
||||
"OP_assignmentBreakpoint",
|
||||
"OP_manualAndAssignmentBreakpoint",
|
||||
"OP_count"
|
||||
};
|
||||
|
||||
std::string answer{};
|
||||
|
||||
int count = op_idx_rolled_over[inst] ? ARRAY_COUNT(op_history[inst]) : op_idx[inst];
|
||||
|
||||
for (auto i = 0; i < count; i++)
|
||||
{
|
||||
auto idx = op_idx[inst] - 1 - i;
|
||||
if (idx < 0)
|
||||
{
|
||||
idx += ARRAY_COUNT(op_history[inst]);
|
||||
}
|
||||
|
||||
if ((int)op_history[inst][idx] >= 0 && op_history[inst][idx] < game::OP_count)
|
||||
{
|
||||
answer += std::format("{}\n", OpcodeVMToString[op_history[inst][idx]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
answer += std::format("0x{:02x}\n", (int)op_history[inst][idx]);
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string get_gsc_call_stack(game::scriptInstance_t inst)
|
||||
{
|
||||
std::string answer{};
|
||||
|
||||
int bufferIndex;
|
||||
int prevSourcePos;
|
||||
int col;
|
||||
char line[1024];
|
||||
int lineNum;
|
||||
const char* fileName;
|
||||
|
||||
if (!game::gFs[inst].pos || !game::Scr_IsInOpcodeMemory(inst, game::gFs[inst].pos))
|
||||
{
|
||||
return answer;
|
||||
}
|
||||
|
||||
for (auto frame = game::gScrVmPub[inst].function_frame_start;; frame++)
|
||||
{
|
||||
if (!frame->fs.pos || !game::Scr_IsInOpcodeMemory(inst, frame->fs.pos))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bufferIndex = game::Scr_GetSourceBuffer(inst, frame->fs.pos - 1);
|
||||
prevSourcePos = game::Scr_GetPrevSourcePos(inst, frame->fs.pos - 1, 0);
|
||||
lineNum = game::Scr_GetLineInfo(&col, game::gScrParserPub[inst].sourceBufferLookup[bufferIndex].sourceBuf, prevSourcePos, line);
|
||||
fileName = game::gScrParserPub[inst].sourceBufferLookup[bufferIndex].buf;
|
||||
|
||||
answer += std::format("{}({}, {}): '{}'\n", fileName, lineNum, col, line);
|
||||
|
||||
if (frame == game::gScrVmPub[inst].function_frame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string get_full_gsc_state_str(game::scriptInstance_t inst)
|
||||
{
|
||||
nlohmann::json answer{};
|
||||
auto t = *game::gInst;
|
||||
*game::gInst = inst;
|
||||
|
||||
answer["inst"] = inst;
|
||||
answer["gScrVarPub"] = game::gScrVarPub[inst];
|
||||
answer["codeCallStack"] = printStack();
|
||||
answer["gscCallStack"] = get_gsc_call_stack(inst);
|
||||
answer["opHistory"] = build_op_history(inst);
|
||||
answer["builtinHistory"] = build_builtin_history(inst);
|
||||
answer["codeposHistory"] = build_codepos_history(inst);
|
||||
|
||||
*game::gInst = t;
|
||||
|
||||
return answer.dump(2);
|
||||
}
|
||||
|
||||
void push_opcode_to_history(game::scriptInstance_t inst, game::OpcodeVM op)
|
||||
{
|
||||
assert(inst == 0 || inst == 1);
|
||||
//assert((int)op >= 0 && op < game::OP_count);
|
||||
|
||||
op_history[inst][op_idx[inst]++] = op;
|
||||
|
||||
if (op_idx[inst] >= ARRAY_COUNT(op_history[inst]))
|
||||
{
|
||||
op_idx_rolled_over[inst] = true;
|
||||
op_idx[inst] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void push_builtin_history(game::scriptInstance_t inst, int idx)
|
||||
{
|
||||
assert(inst == 0 || inst == 1);
|
||||
assert(idx >= 0 && idx < 1024);
|
||||
|
||||
builtin_history[inst][builtin_idx[inst]++] = idx;
|
||||
|
||||
if (builtin_idx[inst] >= ARRAY_COUNT(builtin_history[inst]))
|
||||
{
|
||||
builtin_idx_rolled_over[inst] = true;
|
||||
builtin_idx[inst] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void push_codepos_history(game::scriptInstance_t inst, const char* pos)
|
||||
{
|
||||
assert(inst == 0 || inst == 1);
|
||||
assert(game::Scr_IsInOpcodeMemory(inst, pos));
|
||||
|
||||
codepos_history[inst][codepos_idx[inst]++] = pos;
|
||||
|
||||
if (codepos_idx[inst] >= ARRAY_COUNT(codepos_history[inst]))
|
||||
{
|
||||
codepos_idx_rolled_over[inst] = true;
|
||||
codepos_idx[inst] = 0;
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ namespace codsrc
|
||||
game::Cmd_AddCommand("dump_gsc_state", []()
|
||||
{
|
||||
game::scriptInstance_t inst = game::SCRIPTINSTANCE_SERVER;
|
||||
utils::io::write_file("t4sp-server-plugin/gsc_state.json", get_full_gsc_state_str(inst));
|
||||
utils::io::write_file("t4sp-server-plugin/gsc_state.json", build_gsc_dump(inst));
|
||||
});
|
||||
}
|
||||
//
|
||||
@ -2725,7 +2725,7 @@ namespace codsrc
|
||||
game::gOpcode[inst] = (game::OpcodeVM)game::Scr_ReadUnsignedByte(inst, &game::gFs[inst].pos);
|
||||
interrupt_return:
|
||||
// our addition
|
||||
push_opcode_to_history(inst, game::gOpcode[inst]);
|
||||
push_opcode_history(inst, game::gOpcode[inst]);
|
||||
//
|
||||
|
||||
switch ( game::gOpcode[inst] )
|
||||
|
@ -71,7 +71,7 @@ namespace exception
|
||||
line(utils::string::va("Address: 0x%lX", exceptioninfo->ExceptionRecord->ExceptionAddress));
|
||||
|
||||
line("");
|
||||
line(get_full_gsc_state_str(game::SCRIPTINSTANCE_SERVER));
|
||||
line(build_gsc_dump(game::SCRIPTINSTANCE_SERVER));
|
||||
line("");
|
||||
|
||||
#pragma warning(push)
|
||||
|
@ -15,7 +15,7 @@
|
||||
#define assert(expr) \
|
||||
if (!!!(expr)) \
|
||||
{ \
|
||||
utils::io::write_file("t4sp-server-plugin/gsc_state_assert.json", get_full_gsc_state_str(game::SCRIPTINSTANCE_SERVER)); \
|
||||
utils::io::write_file("t4sp-server-plugin/gsc_state_assert.json", build_gsc_dump(game::SCRIPTINSTANCE_SERVER)); \
|
||||
_wassert(_CRT_WIDE(#expr), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)); \
|
||||
}
|
||||
#endif
|
||||
|
417
src/stdinc.cpp
417
src/stdinc.cpp
@ -1 +1,418 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "codsrc/clientscript/clientscript_public.hpp"
|
||||
#include <dbghelp.h>
|
||||
|
||||
namespace game
|
||||
{
|
||||
#define QUICK_TO_JSON_FIELD(j, v, membername) j[#membername] = v.membername
|
||||
|
||||
#define QUICK_TO_JSON_FIELD_SAFE_CSTR(j, v, membername) \
|
||||
if (v.membername) \
|
||||
j[#membername] = v.membername; \
|
||||
else \
|
||||
j[#membername] = "(NULL)"
|
||||
|
||||
#define QUICK_TO_JSON_FIELD_PTR_ADDR(j, v, membername) j[#membername] = reinterpret_cast<size_t>(&v.membername)
|
||||
|
||||
#define QUICK_TO_JSON_FIELD_ARRAY(j, v, membername) \
|
||||
for (auto i = 0; i < ARRAY_COUNT(v.membername); i++) \
|
||||
{ \
|
||||
j[#membername][i] = v.membername[i]; \
|
||||
}
|
||||
|
||||
#define QUICK_TO_JSON_FIELD_SL_STRING(j, v, membername) j[#membername "Str"] = SL_ConvertToStringSafe(v.membername, *gInst)
|
||||
|
||||
void to_json(nlohmann::json& j, const scrVarPub_t& v)
|
||||
{
|
||||
QUICK_TO_JSON_FIELD_SAFE_CSTR(j, v, fieldBuffer);
|
||||
QUICK_TO_JSON_FIELD(j, v, canonicalStrCount);
|
||||
QUICK_TO_JSON_FIELD(j, v, developer);
|
||||
QUICK_TO_JSON_FIELD(j, v, developer_script);
|
||||
QUICK_TO_JSON_FIELD(j, v, evaluate);
|
||||
QUICK_TO_JSON_FIELD_SAFE_CSTR(j, v, error_message);
|
||||
QUICK_TO_JSON_FIELD(j, v, error_index);
|
||||
QUICK_TO_JSON_FIELD(j, v, time);
|
||||
QUICK_TO_JSON_FIELD(j, v, timeArrayId);
|
||||
QUICK_TO_JSON_FIELD(j, v, pauseArrayId);
|
||||
QUICK_TO_JSON_FIELD(j, v, levelId);
|
||||
QUICK_TO_JSON_FIELD(j, v, gameId);
|
||||
QUICK_TO_JSON_FIELD(j, v, animId);
|
||||
QUICK_TO_JSON_FIELD(j, v, freeEntList);
|
||||
QUICK_TO_JSON_FIELD(j, v, tempVariable);
|
||||
QUICK_TO_JSON_FIELD(j, v, bInited);
|
||||
QUICK_TO_JSON_FIELD(j, v, savecount);
|
||||
QUICK_TO_JSON_FIELD(j, v, checksum);
|
||||
QUICK_TO_JSON_FIELD(j, v, entId);
|
||||
QUICK_TO_JSON_FIELD(j, v, entFieldName);
|
||||
QUICK_TO_JSON_FIELD_SL_STRING(j, v, entFieldName);
|
||||
QUICK_TO_JSON_FIELD_PTR_ADDR(j, v, programHunkUser);
|
||||
QUICK_TO_JSON_FIELD_PTR_ADDR(j, v, programBuffer);
|
||||
QUICK_TO_JSON_FIELD_PTR_ADDR(j, v, endScriptBuffer);
|
||||
QUICK_TO_JSON_FIELD_ARRAY(j, v, saveIdMap);
|
||||
QUICK_TO_JSON_FIELD_ARRAY(j, v, saveIdMapRev);
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/5693192/win32-backtrace-from-c-code
|
||||
std::string build_code_stack()
|
||||
{
|
||||
unsigned int i;
|
||||
void * stack[ 100 ];
|
||||
unsigned short frames;
|
||||
SYMBOL_INFO * symbol;
|
||||
HANDLE process;
|
||||
std::string answer{};
|
||||
|
||||
process = GetCurrentProcess();
|
||||
|
||||
SymInitialize( process, NULL, TRUE );
|
||||
|
||||
frames = CaptureStackBackTrace( 0, 100, stack, NULL );
|
||||
symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
|
||||
symbol->MaxNameLen = 255;
|
||||
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
|
||||
|
||||
for( i = 0; i < frames; i++ )
|
||||
{
|
||||
SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );
|
||||
|
||||
answer += std::format("{}: {} - 0x{:06x}\n", frames - i - 1, symbol->Name, symbol->Address);
|
||||
}
|
||||
|
||||
free( symbol );
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
int op_idx[game::SCRIPT_INSTANCE_MAX] = { 0, 0 };
|
||||
bool op_idx_rolled_over[game::SCRIPT_INSTANCE_MAX] = { false, false };
|
||||
game::OpcodeVM op_history[game::SCRIPT_INSTANCE_MAX][128] = {};
|
||||
|
||||
int builtin_idx[game::SCRIPT_INSTANCE_MAX] = { 0, 0 };
|
||||
bool builtin_idx_rolled_over[game::SCRIPT_INSTANCE_MAX] = { false, false };
|
||||
int builtin_history[game::SCRIPT_INSTANCE_MAX][128] = {};
|
||||
|
||||
int codepos_idx[game::SCRIPT_INSTANCE_MAX] = { 0, 0 };
|
||||
bool codepos_idx_rolled_over[game::SCRIPT_INSTANCE_MAX] = { false, false };
|
||||
const char* codepos_history[game::SCRIPT_INSTANCE_MAX][128] = {};
|
||||
|
||||
std::string build_builtin_history(game::scriptInstance_t inst)
|
||||
{
|
||||
std::string answer{};
|
||||
|
||||
int count = builtin_idx_rolled_over[inst] ? ARRAY_COUNT(builtin_history[inst]) : builtin_idx[inst];
|
||||
|
||||
for (auto i = 0; i < count; i++)
|
||||
{
|
||||
auto idx = builtin_idx[inst] - 1 - i;
|
||||
if (idx < 0)
|
||||
{
|
||||
idx += ARRAY_COUNT(builtin_history[inst]);
|
||||
}
|
||||
|
||||
// todo, convert to builtin name
|
||||
answer += std::format("{}\n", builtin_history[inst][idx]);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string build_codepos_history(game::scriptInstance_t inst)
|
||||
{
|
||||
std::string answer{};
|
||||
int bufferIndex;
|
||||
int prevSourcePos;
|
||||
int col;
|
||||
char line[1024];
|
||||
int lineNum;
|
||||
const char* fileName;
|
||||
|
||||
int count = codepos_idx_rolled_over[inst] ? ARRAY_COUNT(codepos_history[inst]) : codepos_idx[inst];
|
||||
|
||||
for (auto i = 0; i < count; i++)
|
||||
{
|
||||
auto idx = codepos_idx[inst] - 1 - i;
|
||||
if (idx < 0)
|
||||
{
|
||||
idx += ARRAY_COUNT(codepos_history[inst]);
|
||||
}
|
||||
|
||||
bufferIndex = game::Scr_GetSourceBuffer(inst, codepos_history[inst][idx]);
|
||||
prevSourcePos = game::Scr_GetPrevSourcePos(inst, codepos_history[inst][idx], 0);
|
||||
lineNum = game::Scr_GetLineInfo(&col, game::gScrParserPub[inst].sourceBufferLookup[bufferIndex].sourceBuf, prevSourcePos, line);
|
||||
fileName = game::gScrParserPub[inst].sourceBufferLookup[bufferIndex].buf;
|
||||
|
||||
answer += std::format("{}({}, {}): '{}'\n", fileName, lineNum, col, line);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string build_op_history(game::scriptInstance_t inst)
|
||||
{
|
||||
static const char* OpcodeVMToString[] = {
|
||||
"OP_End",
|
||||
"OP_Return",
|
||||
"OP_GetUndefined",
|
||||
"OP_GetZero",
|
||||
"OP_GetByte",
|
||||
"OP_GetNegByte",
|
||||
"OP_GetUnsignedShort",
|
||||
"OP_GetNegUnsignedShort",
|
||||
"OP_GetInteger",
|
||||
"OP_GetFloat",
|
||||
"OP_GetString",
|
||||
"OP_GetIString",
|
||||
"OP_GetVector",
|
||||
"OP_GetLevelObject",
|
||||
"OP_GetAnimObject",
|
||||
"OP_GetSelf",
|
||||
"OP_GetLevel",
|
||||
"OP_GetGame",
|
||||
"OP_GetAnim",
|
||||
"OP_GetAnimation",
|
||||
"OP_GetGameRef",
|
||||
"OP_GetFunction",
|
||||
"OP_CreateLocalVariable",
|
||||
"OP_RemoveLocalVariables",
|
||||
"OP_EvalLocalVariableCached0",
|
||||
"OP_EvalLocalVariableCached1",
|
||||
"OP_EvalLocalVariableCached2",
|
||||
"OP_EvalLocalVariableCached3",
|
||||
"OP_EvalLocalVariableCached4",
|
||||
"OP_EvalLocalVariableCached5",
|
||||
"OP_EvalLocalVariableCached",
|
||||
"OP_EvalLocalArrayCached",
|
||||
"OP_EvalArray",
|
||||
"OP_EvalLocalArrayRefCached0",
|
||||
"OP_EvalLocalArrayRefCached",
|
||||
"OP_EvalArrayRef",
|
||||
"OP_ClearArray",
|
||||
"OP_EmptyArray",
|
||||
"OP_GetSelfObject",
|
||||
"OP_EvalLevelFieldVariable",
|
||||
"OP_EvalAnimFieldVariable",
|
||||
"OP_EvalSelfFieldVariable",
|
||||
"OP_EvalFieldVariable",
|
||||
"OP_EvalLevelFieldVariableRef",
|
||||
"OP_EvalAnimFieldVariableRef",
|
||||
"OP_EvalSelfFieldVariableRef",
|
||||
"OP_EvalFieldVariableRef",
|
||||
"OP_ClearFieldVariable",
|
||||
"OP_SafeCreateVariableFieldCached",
|
||||
"OP_SafeSetVariableFieldCached0",
|
||||
"OP_SafeSetVariableFieldCached",
|
||||
"OP_SafeSetWaittillVariableFieldCached",
|
||||
"OP_clearparams",
|
||||
"OP_checkclearparams",
|
||||
"OP_EvalLocalVariableRefCached0",
|
||||
"OP_EvalLocalVariableRefCached",
|
||||
"OP_SetLevelFieldVariableField",
|
||||
"OP_SetVariableField",
|
||||
"OP_SetAnimFieldVariableField",
|
||||
"OP_SetSelfFieldVariableField",
|
||||
"OP_SetLocalVariableFieldCached0",
|
||||
"OP_SetLocalVariableFieldCached",
|
||||
"OP_CallBuiltin0",
|
||||
"OP_CallBuiltin1",
|
||||
"OP_CallBuiltin2",
|
||||
"OP_CallBuiltin3",
|
||||
"OP_CallBuiltin4",
|
||||
"OP_CallBuiltin5",
|
||||
"OP_CallBuiltin",
|
||||
"OP_CallBuiltinMethod0",
|
||||
"OP_CallBuiltinMethod1",
|
||||
"OP_CallBuiltinMethod2",
|
||||
"OP_CallBuiltinMethod3",
|
||||
"OP_CallBuiltinMethod4",
|
||||
"OP_CallBuiltinMethod5",
|
||||
"OP_CallBuiltinMethod",
|
||||
"OP_wait",
|
||||
"OP_waittillFrameEnd",
|
||||
"OP_PreScriptCall",
|
||||
"OP_ScriptFunctionCall2",
|
||||
"OP_ScriptFunctionCall",
|
||||
"OP_ScriptFunctionCallPointer",
|
||||
"OP_ScriptMethodCall",
|
||||
"OP_ScriptMethodCallPointer",
|
||||
"OP_ScriptThreadCall",
|
||||
"OP_ScriptThreadCallPointer",
|
||||
"OP_ScriptMethodThreadCall",
|
||||
"OP_ScriptMethodThreadCallPointer",
|
||||
"OP_DecTop",
|
||||
"OP_CastFieldObject",
|
||||
"OP_EvalLocalVariableObjectCached",
|
||||
"OP_CastBool",
|
||||
"OP_BoolNot",
|
||||
"OP_BoolComplement",
|
||||
"OP_JumpOnFalse",
|
||||
"OP_JumpOnTrue",
|
||||
"OP_JumpOnFalseExpr",
|
||||
"OP_JumpOnTrueExpr",
|
||||
"OP_jump",
|
||||
"OP_jumpback",
|
||||
"OP_inc",
|
||||
"OP_dec",
|
||||
"OP_bit_or",
|
||||
"OP_bit_ex_or",
|
||||
"OP_bit_and",
|
||||
"OP_equality",
|
||||
"OP_inequality",
|
||||
"OP_less",
|
||||
"OP_greater",
|
||||
"OP_less_equal",
|
||||
"OP_greater_equal",
|
||||
"OP_shift_left",
|
||||
"OP_shift_right",
|
||||
"OP_plus",
|
||||
"OP_minus",
|
||||
"OP_multiply",
|
||||
"OP_divide",
|
||||
"OP_mod",
|
||||
"OP_size",
|
||||
"OP_waittillmatch",
|
||||
"OP_waittill",
|
||||
"OP_notify",
|
||||
"OP_endon",
|
||||
"OP_voidCodepos",
|
||||
"OP_switch",
|
||||
"OP_endswitch",
|
||||
"OP_vector",
|
||||
"OP_NOP",
|
||||
"OP_abort",
|
||||
"OP_object",
|
||||
"OP_thread_object",
|
||||
"OP_EvalLocalVariable",
|
||||
"OP_EvalLocalVariableRef",
|
||||
"OP_prof_begin",
|
||||
"OP_prof_end",
|
||||
"OP_breakpoint",
|
||||
"OP_assignmentBreakpoint",
|
||||
"OP_manualAndAssignmentBreakpoint",
|
||||
"OP_count"
|
||||
};
|
||||
|
||||
std::string answer{};
|
||||
|
||||
int count = op_idx_rolled_over[inst] ? ARRAY_COUNT(op_history[inst]) : op_idx[inst];
|
||||
|
||||
for (auto i = 0; i < count; i++)
|
||||
{
|
||||
auto idx = op_idx[inst] - 1 - i;
|
||||
if (idx < 0)
|
||||
{
|
||||
idx += ARRAY_COUNT(op_history[inst]);
|
||||
}
|
||||
|
||||
if ((int)op_history[inst][idx] >= 0 && op_history[inst][idx] < game::OP_count)
|
||||
{
|
||||
answer += std::format("{}\n", OpcodeVMToString[op_history[inst][idx]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
answer += std::format("0x{:02x}\n", (int)op_history[inst][idx]);
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string build_gsc_stack(game::scriptInstance_t inst)
|
||||
{
|
||||
std::string answer{};
|
||||
|
||||
int bufferIndex;
|
||||
int prevSourcePos;
|
||||
int col;
|
||||
char line[1024];
|
||||
int lineNum;
|
||||
const char* fileName;
|
||||
|
||||
if (!game::gFs[inst].pos || !game::Scr_IsInOpcodeMemory(inst, game::gFs[inst].pos))
|
||||
{
|
||||
return answer;
|
||||
}
|
||||
|
||||
for (auto frame = game::gScrVmPub[inst].function_frame_start;; frame++)
|
||||
{
|
||||
if (!frame->fs.pos || !game::Scr_IsInOpcodeMemory(inst, frame->fs.pos))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bufferIndex = game::Scr_GetSourceBuffer(inst, frame->fs.pos - 1);
|
||||
prevSourcePos = game::Scr_GetPrevSourcePos(inst, frame->fs.pos - 1, 0);
|
||||
lineNum = game::Scr_GetLineInfo(&col, game::gScrParserPub[inst].sourceBufferLookup[bufferIndex].sourceBuf, prevSourcePos, line);
|
||||
fileName = game::gScrParserPub[inst].sourceBufferLookup[bufferIndex].buf;
|
||||
|
||||
answer += std::format("{}({}, {}): '{}'\n", fileName, lineNum, col, line);
|
||||
|
||||
if (frame == game::gScrVmPub[inst].function_frame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string build_gsc_dump(game::scriptInstance_t inst)
|
||||
{
|
||||
nlohmann::json answer{};
|
||||
auto t = *game::gInst;
|
||||
*game::gInst = inst;
|
||||
|
||||
answer["inst"] = inst;
|
||||
answer["gScrVarPub"] = game::gScrVarPub[inst];
|
||||
answer["codeCallStack"] = build_code_stack();
|
||||
answer["gscCallStack"] = build_gsc_stack(inst);
|
||||
answer["opHistory"] = build_op_history(inst);
|
||||
answer["builtinHistory"] = build_builtin_history(inst);
|
||||
answer["codeposHistory"] = build_codepos_history(inst);
|
||||
|
||||
*game::gInst = t;
|
||||
|
||||
return answer.dump(2);
|
||||
}
|
||||
|
||||
void push_opcode_history(game::scriptInstance_t inst, game::OpcodeVM op)
|
||||
{
|
||||
assert(inst == 0 || inst == 1);
|
||||
//assert((int)op >= 0 && op < game::OP_count);
|
||||
|
||||
op_history[inst][op_idx[inst]++] = op;
|
||||
|
||||
if (op_idx[inst] >= ARRAY_COUNT(op_history[inst]))
|
||||
{
|
||||
op_idx_rolled_over[inst] = true;
|
||||
op_idx[inst] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void push_builtin_history(game::scriptInstance_t inst, int idx)
|
||||
{
|
||||
assert(inst == 0 || inst == 1);
|
||||
assert(idx >= 0 && idx < 1024);
|
||||
|
||||
builtin_history[inst][builtin_idx[inst]++] = idx;
|
||||
|
||||
if (builtin_idx[inst] >= ARRAY_COUNT(builtin_history[inst]))
|
||||
{
|
||||
builtin_idx_rolled_over[inst] = true;
|
||||
builtin_idx[inst] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void push_codepos_history(game::scriptInstance_t inst, const char* pos)
|
||||
{
|
||||
assert(inst == 0 || inst == 1);
|
||||
assert(game::Scr_IsInOpcodeMemory(inst, pos));
|
||||
|
||||
codepos_history[inst][codepos_idx[inst]++] = pos;
|
||||
|
||||
if (codepos_idx[inst] >= ARRAY_COUNT(codepos_history[inst]))
|
||||
{
|
||||
codepos_idx_rolled_over[inst] = true;
|
||||
codepos_idx[inst] = 0;
|
||||
}
|
||||
}
|
||||
|
@ -69,8 +69,9 @@
|
||||
#include "game/enums.hpp"
|
||||
#include "game/structs.hpp"
|
||||
#include "game/symbols.hpp"
|
||||
std::string get_full_gsc_state_str(game::scriptInstance_t inst);
|
||||
void push_opcode_to_history(game::scriptInstance_t inst, game::OpcodeVM op);
|
||||
|
||||
std::string build_gsc_dump(game::scriptInstance_t inst);
|
||||
void push_opcode_history(game::scriptInstance_t inst, game::OpcodeVM op);
|
||||
void push_builtin_history(game::scriptInstance_t inst, int idx);
|
||||
void push_codepos_history(game::scriptInstance_t inst, const char* pos);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user