diff --git a/src/codsrc/clientscript/clientscript_public.cpp b/src/codsrc/clientscript/clientscript_public.cpp index a188334..2644169 100644 --- a/src/codsrc/clientscript/clientscript_public.cpp +++ b/src/codsrc/clientscript/clientscript_public.cpp @@ -1,418 +1,2 @@ #include #include "clientscript_public.hpp" -#include - -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(&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; - } -} diff --git a/src/codsrc/clientscript/cscr_vm.cpp b/src/codsrc/clientscript/cscr_vm.cpp index 01dee68..1ec8ec0 100644 --- a/src/codsrc/clientscript/cscr_vm.cpp +++ b/src/codsrc/clientscript/cscr_vm.cpp @@ -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] ) diff --git a/src/component/exception.cpp b/src/component/exception.cpp index d8cf8f7..c139a3d 100644 --- a/src/component/exception.cpp +++ b/src/component/exception.cpp @@ -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) diff --git a/src/game/game.hpp b/src/game/game.hpp index 623db96..90f6c60 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -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 diff --git a/src/stdinc.cpp b/src/stdinc.cpp index 43b100a..80d0b1d 100644 --- a/src/stdinc.cpp +++ b/src/stdinc.cpp @@ -1 +1,418 @@ #include +#include "codsrc/clientscript/clientscript_public.hpp" +#include + +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(&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; + } +} diff --git a/src/stdinc.hpp b/src/stdinc.hpp index 252ea30..a5c967c 100644 --- a/src/stdinc.hpp +++ b/src/stdinc.hpp @@ -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);