diff --git a/src/codsrc/clientscript/clientscript_public.cpp b/src/codsrc/clientscript/clientscript_public.cpp index 6637bac..0ab7cc4 100644 --- a/src/codsrc/clientscript/clientscript_public.cpp +++ b/src/codsrc/clientscript/clientscript_public.cpp @@ -61,7 +61,7 @@ std::string printStack() unsigned short frames; SYMBOL_INFO * symbol; HANDLE process; - std::string answer; + std::string answer{}; process = GetCurrentProcess(); @@ -76,7 +76,7 @@ std::string printStack() { SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol ); - answer += std::format("{}: {} - {:06x}\n", frames - i - 1, symbol->Name, symbol->Address); + answer += std::format("{}: {} - 0x{:06x}\n", frames - i - 1, symbol->Name, symbol->Address); } free( symbol ); @@ -84,17 +84,257 @@ std::string printStack() 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] = {}; + +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; + nlohmann::json answer{}; auto t = *game::gInst; *game::gInst = inst; answer["inst"] = inst; answer["gScrVarPub"] = game::gScrVarPub[inst]; - answer["stack"] = printStack(); + answer["codeCallStack"] = printStack(); + answer["gscCallStack"] = get_gsc_call_stack(inst); + answer["opHistory"] = build_op_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) +{ + inst = inst; + idx = idx; +} + +void push_codepos_history(game::scriptInstance_t inst, const char* pos) +{ + inst = inst; + pos = pos; +} diff --git a/src/codsrc/clientscript/cscr_stringlist.hpp b/src/codsrc/clientscript/cscr_stringlist.hpp index 3e6819a..284814e 100644 --- a/src/codsrc/clientscript/cscr_stringlist.hpp +++ b/src/codsrc/clientscript/cscr_stringlist.hpp @@ -38,6 +38,5 @@ namespace codsrc int SL_ConvertFromString(game::scriptInstance_t inst, const char* str); int SL_ConvertFromRefString(game::scriptInstance_t inst, game::RefString* refString); game::RefString* GetRefString_0(game::scriptInstance_t inst, const char* str); - const char* SL_DebugConvertToString(unsigned int stringValue, game::scriptInstance_t inst); const char* SL_ConvertToStringSafe(unsigned int id, game::scriptInstance_t inst); } diff --git a/src/codsrc/clientscript/cscr_vm.cpp b/src/codsrc/clientscript/cscr_vm.cpp index 1fcb73d..af4f937 100644 --- a/src/codsrc/clientscript/cscr_vm.cpp +++ b/src/codsrc/clientscript/cscr_vm.cpp @@ -1066,6 +1066,8 @@ namespace codsrc gScrVmPub[inst].outparamcount = outparamcount; }*/ + push_builtin_history(inst, builtinIndex); + assert(builtinIndex >= 0); assert(builtinIndex < 1024); @@ -1130,6 +1132,8 @@ namespace codsrc gScrVmPub[inst].top = localFs.top - 1; }*/ + push_builtin_history(inst, builtinIndex); + assert(builtinIndex >= 0); assert(builtinIndex < 1024); @@ -1292,6 +1296,10 @@ namespace codsrc } // + // our additions + push_codepos_history(inst, game::gFs[inst].pos); + // + assert(game::gFs[inst].pos); } @@ -2682,7 +2690,7 @@ namespace codsrc int currentCaseValue; const char* currentCodePos; - do + do // Scr_ReadIntArray(2 * game::gCaseCount[inst]) { currentCaseValue = game::Scr_ReadUnsignedInt(inst, &game::gFs[inst].pos); currentCodePos = game::Scr_ReadCodePos(inst, &game::gFs[inst].pos); @@ -2712,6 +2720,10 @@ 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]); + // + switch ( game::gOpcode[inst] ) { case game::OP_End: diff --git a/src/stdinc.hpp b/src/stdinc.hpp index 0b6b8d1..252ea30 100644 --- a/src/stdinc.hpp +++ b/src/stdinc.hpp @@ -70,5 +70,8 @@ #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); +void push_builtin_history(game::scriptInstance_t inst, int idx); +void push_codepos_history(game::scriptInstance_t inst, const char* pos); using namespace std::literals; \ No newline at end of file