diff --git a/src/codsrc/clientscript/clientscript_public.cpp b/src/codsrc/clientscript/clientscript_public.cpp index 2644169..6637bac 100644 --- a/src/codsrc/clientscript/clientscript_public.cpp +++ b/src/codsrc/clientscript/clientscript_public.cpp @@ -1,2 +1,100 @@ #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("{}: {} - {:06x}\n", frames - i - 1, symbol->Name, symbol->Address); + } + + free( symbol ); + + 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["stack"] = printStack(); + + *game::gInst = t; + + return answer.dump(2); +} diff --git a/src/codsrc/clientscript/cscr_stringlist.hpp b/src/codsrc/clientscript/cscr_stringlist.hpp index 426fd26..3e6819a 100644 --- a/src/codsrc/clientscript/cscr_stringlist.hpp +++ b/src/codsrc/clientscript/cscr_stringlist.hpp @@ -39,4 +39,5 @@ namespace codsrc 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 3c25670..cb258a2 100644 --- a/src/codsrc/clientscript/cscr_vm.cpp +++ b/src/codsrc/clientscript/cscr_vm.cpp @@ -47,6 +47,17 @@ namespace codsrc game::DVAR_FLAG_NONE, "Used to toggle systems in script on and off on the server."); } + + // our additions + if (!game::Cmd_FindCommand("dump_gsc_state")) + { + 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)); + }); + } + // } // Completed diff --git a/src/component/exception.cpp b/src/component/exception.cpp index ed6c572..d8cf8f7 100644 --- a/src/component/exception.cpp +++ b/src/component/exception.cpp @@ -70,6 +70,10 @@ namespace exception line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode)); line(utils::string::va("Address: 0x%lX", exceptioninfo->ExceptionRecord->ExceptionAddress)); + line(""); + line(get_full_gsc_state_str(game::SCRIPTINSTANCE_SERVER)); + line(""); + #pragma warning(push) #pragma warning(disable: 4996) OSVERSIONINFOEXA version_info; @@ -85,7 +89,7 @@ namespace exception void write_minidump(const LPEXCEPTION_POINTERS exceptioninfo) { - const std::string crash_name = utils::string::va("minidumps/plutonium-t4-crash-%s.zip", + const std::string crash_name = utils::string::va("t4sp-server-plugin/minidumps/plutonium-t4-crash-%s.zip", utils::string::get_timestamp().data()); utils::compression::zip::archive zip_file{}; diff --git a/src/game/clientscript/cscr_stringlist.hpp b/src/game/clientscript/cscr_stringlist.hpp index 701ba65..4891619 100644 --- a/src/game/clientscript/cscr_stringlist.hpp +++ b/src/game/clientscript/cscr_stringlist.hpp @@ -61,4 +61,5 @@ namespace game int SL_ConvertFromString(scriptInstance_t inst, const char* str); int SL_ConvertFromRefString(scriptInstance_t inst, RefString* refString); RefString* GetRefString_0(scriptInstance_t inst, const char* str); + const char* SL_ConvertToStringSafe(unsigned int id, game::scriptInstance_t inst); } \ No newline at end of file diff --git a/src/game/clientscript/cscr_stringlist_w.cpp b/src/game/clientscript/cscr_stringlist_w.cpp index 71730fa..51326c7 100644 --- a/src/game/clientscript/cscr_stringlist_w.cpp +++ b/src/game/clientscript/cscr_stringlist_w.cpp @@ -362,4 +362,9 @@ namespace game { return codsrc::GetRefString_0(inst, str); } + + const char* SL_ConvertToStringSafe(unsigned int id, game::scriptInstance_t inst) + { + return codsrc::SL_ConvertToStringSafe(id, inst); + } } \ No newline at end of file diff --git a/src/game/game.cpp b/src/game/game.cpp index d903ed1..5cb2a87 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -188,6 +188,33 @@ namespace game return answer; } + // cmd_function_s *__usercall Cmd_FindCommand@(const char *cmdName@) + cmd_function_s* Cmd_FindCommand(const char* cmdName, void* call_addr) + { + cmd_function_s* answer; + + __asm + { + mov esi, cmdName; + call call_addr; + mov answer, eax; + } + + return answer; + } + + void Cmd_AddCommand(const char* name, void (__cdecl *function)()) + { + cmd_function_s* newCmd = utils::memory::allocate(); + + *newCmd = {}; + newCmd->next = *cmd_functions; + newCmd->function = function; + newCmd->name = utils::memory::duplicate_string(name); + + *cmd_functions = newCmd; + } + // restored void Sys_EnterCriticalSection(CriticalSection critSect) { diff --git a/src/game/game.hpp b/src/game/game.hpp index bbdbf08..f3ae068 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -10,6 +10,16 @@ #define ARRAY_COUNT(arrayn) \ ((sizeof(arrayn)) / (sizeof(arrayn[0]))) +#ifndef NDEBUG +#undef assert +#define assert(expr) \ + if (!!!(expr)) \ + { \ + utils::io::write_file("t4sp-server-plugin/gsc_state_assert.json", get_full_gsc_state_str(game::SCRIPTINSTANCE_SERVER)); \ + _wassert(_CRT_WIDE(#expr), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)); \ + } +#endif + namespace game { enum gamemode diff --git a/src/game/symbols.hpp b/src/game/symbols.hpp index 4bdee7e..5bffda5 100644 --- a/src/game/symbols.hpp +++ b/src/game/symbols.hpp @@ -55,6 +55,10 @@ namespace game inline void* XAnimGetAnimDebugName_ADDR() { return CALL_ADDR(0x0, 0x60F850); } const char * XAnimGetAnimDebugName(unsigned int animIndex, XAnim_s * anims, void* call_addr = XAnimGetAnimDebugName_ADDR()); + inline void* Cmd_FindCommand_ADDR() { return CALL_ADDR(0x0, 0x594DB0); } + cmd_function_s* Cmd_FindCommand(const char* cmdName, void* call_addr = Cmd_FindCommand_ADDR()); + void Cmd_AddCommand(const char* name, void(__cdecl* function)()); + void Sys_EnterCriticalSection(CriticalSection critSect); void Sys_LeaveCriticalSection(CriticalSection critSect); @@ -69,6 +73,7 @@ namespace game WEAK symbol g_allocNodeUser{ 0x0, 0x3882B20 }; WEAK symbol g_user{ 0x0, 0x3882B48 }; WEAK symbol fs_searchpaths{ 0x0, 0x46E5044 }; + WEAK symbol cmd_functions{ 0x0, 0x1F416F4 }; namespace plutonium { diff --git a/src/stdinc.hpp b/src/stdinc.hpp index bccc044..0b6b8d1 100644 --- a/src/stdinc.hpp +++ b/src/stdinc.hpp @@ -61,6 +61,7 @@ #pragma comment(lib, "Winmm.lib") #include "utils/hexrays_defs.h" +#include "utils/io.hpp" #undef GetObject @@ -68,5 +69,6 @@ #include "game/enums.hpp" #include "game/structs.hpp" #include "game/symbols.hpp" +std::string get_full_gsc_state_str(game::scriptInstance_t inst); using namespace std::literals; \ No newline at end of file