#include #include "Game/Engine/Hunk.hpp" #include "ScriptError.hpp" #define SCRIPT_ERROR_PATCH namespace Components::GSC { using namespace Utils::String; int ScriptError::developer_; Game::scrParserGlob_t ScriptError::scrParserGlob; Game::scrParserPub_t ScriptError::scrParserPub; int ScriptError::Scr_IsInOpcodeMemory(const char* pos) { assert(Game::scrVarPub->programBuffer); assert(pos); return pos - Game::scrVarPub->programBuffer < static_cast(Game::scrCompilePub->programLen); } void ScriptError::AddOpcodePos(unsigned int sourcePos, int type) { Game::OpcodeLookup* opcodeLookup; Game::SourceLookup* sourcePosLookup; if (!developer_) { return; } if (Game::scrCompilePub->developer_statement == 2) { assert(!Game::scrVarPub->developer_script); return; } if (Game::scrCompilePub->developer_statement == 3) { return; } if (!Game::scrCompilePub->allowedBreakpoint) { type &= ~Game::SOURCE_TYPE_BREAKPOINT; } assert(scrParserGlob.opcodeLookup); assert(scrParserGlob.sourcePosLookup); assert(Game::scrCompilePub->opcodePos); auto size = sizeof(Game::OpcodeLookup) * (scrParserGlob.opcodeLookupLen + 1); if (size > scrParserGlob.opcodeLookupMaxSize) { if (scrParserGlob.opcodeLookupMaxSize >= Game::MAX_OPCODE_LOOKUP_SIZE) { Game::Sys_Error("MAX_OPCODE_LOOKUP_SIZE exceeded"); } Game::Z_VirtualCommit((char*)scrParserGlob.opcodeLookup + scrParserGlob.opcodeLookupMaxSize, 0x20000); scrParserGlob.opcodeLookupMaxSize += 0x20000; assert(size <= scrParserGlob.opcodeLookupMaxSize); } size = sizeof(Game::SourceLookup) * (scrParserGlob.sourcePosLookupLen + 1); if (size > scrParserGlob.sourcePosLookupMaxSize) { if (scrParserGlob.sourcePosLookupMaxSize >= Game::MAX_SOURCEPOS_LOOKUP_SIZE) { Game::Sys_Error("MAX_SOURCEPOS_LOOKUP_SIZE exceeded"); } Game::Z_VirtualCommit((char*)scrParserGlob.sourcePosLookup + scrParserGlob.sourcePosLookupMaxSize, 0x20000); scrParserGlob.sourcePosLookupMaxSize += 0x20000; assert(size <= scrParserGlob.sourcePosLookupMaxSize); } if (scrParserGlob.currentCodePos == Game::scrCompilePub->opcodePos) { assert(scrParserGlob.currentSourcePosCount); --scrParserGlob.opcodeLookupLen; opcodeLookup = &scrParserGlob.opcodeLookup[scrParserGlob.opcodeLookupLen]; assert(opcodeLookup->sourcePosIndex + scrParserGlob.currentSourcePosCount == scrParserGlob.sourcePosLookupLen); assert(opcodeLookup->codePos == (char*)scrParserGlob.currentCodePos); } else { scrParserGlob.currentSourcePosCount = 0; scrParserGlob.currentCodePos = Game::scrCompilePub->opcodePos; opcodeLookup = &scrParserGlob.opcodeLookup[scrParserGlob.opcodeLookupLen]; opcodeLookup->sourcePosIndex = scrParserGlob.sourcePosLookupLen; opcodeLookup->codePos = scrParserGlob.currentCodePos; } auto sourcePosLookupIndex = scrParserGlob.currentSourcePosCount + opcodeLookup->sourcePosIndex; sourcePosLookup = &scrParserGlob.sourcePosLookup[sourcePosLookupIndex]; sourcePosLookup->sourcePos = sourcePos; if (sourcePos == static_cast(-1)) { assert(scrParserGlob.delayedSourceIndex == -1); assert(type & Game::SOURCE_TYPE_BREAKPOINT); scrParserGlob.delayedSourceIndex = static_cast(sourcePosLookupIndex); } else if (sourcePos == static_cast(-2)) { scrParserGlob.threadStartSourceIndex = static_cast(sourcePosLookupIndex); } else if (scrParserGlob.delayedSourceIndex >= 0 && (type & Game::SOURCE_TYPE_BREAKPOINT)) { scrParserGlob.sourcePosLookup[scrParserGlob.delayedSourceIndex].sourcePos = sourcePos; scrParserGlob.delayedSourceIndex = -1; } sourcePosLookup->type |= type; ++scrParserGlob.currentSourcePosCount; opcodeLookup->sourcePosCount = static_cast(scrParserGlob.currentSourcePosCount); ++scrParserGlob.opcodeLookupLen; ++scrParserGlob.sourcePosLookupLen; } void ScriptError::RemoveOpcodePos() { if (!developer_) { return; } if (Game::scrCompilePub->developer_statement == 2) { assert(!Game::scrVarPub->developer_script); return; } assert(scrParserGlob.opcodeLookup); assert(scrParserGlob.sourcePosLookup); assert(Game::scrCompilePub->opcodePos); assert(scrParserGlob.sourcePosLookupLen); --scrParserGlob.sourcePosLookupLen; assert(scrParserGlob.opcodeLookupLen); --scrParserGlob.opcodeLookupLen; assert(scrParserGlob.currentSourcePosCount); --scrParserGlob.currentSourcePosCount; auto* opcodeLookup = &scrParserGlob.opcodeLookup[scrParserGlob.opcodeLookupLen]; assert(scrParserGlob.currentCodePos == Game::scrCompilePub->opcodePos); assert(opcodeLookup->sourcePosIndex + scrParserGlob.currentSourcePosCount == scrParserGlob.sourcePosLookupLen); assert(opcodeLookup->codePos == (char*)scrParserGlob.currentCodePos); if (!scrParserGlob.currentSourcePosCount) { scrParserGlob.currentCodePos = nullptr; } opcodeLookup->sourcePosCount = static_cast(scrParserGlob.currentSourcePosCount); } void ScriptError::AddThreadStartOpcodePos(unsigned int sourcePos) { if (!developer_) { return; } if (Game::scrCompilePub->developer_statement == 2) { assert(!Game::scrVarPub->developer_script); } else { assert(scrParserGlob.threadStartSourceIndex >= 0); auto* sourcePosLookup = &scrParserGlob.sourcePosLookup[scrParserGlob.threadStartSourceIndex]; sourcePosLookup->sourcePos = sourcePos; assert(!sourcePosLookup->type); sourcePosLookup->type = 8; scrParserGlob.threadStartSourceIndex = -1; } } int ScriptError::Scr_GetLineNum(unsigned int bufferIndex, unsigned int sourcePos) { const char* startLine; int col; assert(developer_); return Scr_GetLineNumInternal(scrParserPub.sourceBufferLookup[bufferIndex].sourceBuf, sourcePos, &startLine, &col, nullptr); } unsigned int ScriptError::Scr_GetPrevSourcePos(const char* codePos, unsigned int index) { return scrParserGlob.sourcePosLookup[index + Scr_GetPrevSourcePosOpcodeLookup(codePos)->sourcePosIndex].sourcePos; } Game::OpcodeLookup* ScriptError::Scr_GetPrevSourcePosOpcodeLookup(const char* codePos) { assert(Scr_IsInOpcodeMemory(codePos)); assert(scrParserGlob.opcodeLookup); unsigned int low = 0; unsigned int high = scrParserGlob.opcodeLookupLen - 1; while (low <= high) { unsigned int middle = (high + low) >> 1; if (codePos < scrParserGlob.opcodeLookup[middle].codePos) { high = middle - 1; } else { low = middle + 1; if (low == scrParserGlob.opcodeLookupLen || codePos < scrParserGlob.opcodeLookup[low].codePos) { return &scrParserGlob.opcodeLookup[middle]; } } } AssertUnreachable; return nullptr; } void ScriptError::Scr_CopyFormattedLine(char* line, const char* rawLine) { auto len = static_cast(std::strlen(rawLine)); if (len >= 1024) { len = 1024 - 1; } for (auto i = 0; i < len; ++i) { if (rawLine[i] == '\t') { line[i] = ' '; } else { line[i] = rawLine[i]; } } if (line[len - 1] == '\r') { line[len - 1] = '\0'; } line[len] = '\0'; } int ScriptError::Scr_GetLineNumInternal(const char* buf, unsigned int sourcePos, const char** startLine, int* col, [[maybe_unused]] Game::SourceBufferInfo* binfo) { assert(buf); *startLine = buf; unsigned int lineNum = 0; while (sourcePos) { if (!*buf) { *startLine = buf + 1; ++lineNum; } ++buf; --sourcePos; } *col = buf - *startLine; return static_cast(lineNum); } unsigned int ScriptError::Scr_GetSourceBuffer(const char* codePos) { unsigned int bufferIndex; assert(Scr_IsInOpcodeMemory(codePos)); assert(scrParserPub.sourceBufferLookupLen > 0); for (bufferIndex = scrParserPub.sourceBufferLookupLen - 1; bufferIndex; --bufferIndex) { if (!scrParserPub.sourceBufferLookup[bufferIndex].codePos) { continue; } if (scrParserPub.sourceBufferLookup[bufferIndex].codePos > codePos) { continue; } break; } return bufferIndex; } void ScriptError::Scr_PrintPrevCodePos(int channel, const char* codePos, unsigned int index) { if (!codePos) { Game::Com_PrintMessage(channel, "\n", 0); return; } if (codePos == Game::g_EndPos) { Game::Com_PrintMessage(channel, "\n", 0); return; } if (!developer_) { if (Scr_IsInOpcodeMemory(codePos - 1)) { Game::Com_PrintMessage(channel, VA("@ %d\n", codePos - Game::scrVarPub->programBuffer), 0); return; } } else { if (Game::scrVarPub->programBuffer && Scr_IsInOpcodeMemory(codePos)) { auto bufferIndex = Scr_GetSourceBuffer(codePos - 1); Scr_PrintSourcePos(channel, scrParserPub.sourceBufferLookup[bufferIndex].buf, scrParserPub.sourceBufferLookup[bufferIndex].sourceBuf, Scr_GetPrevSourcePos(codePos - 1, index)); return; } } Game::Com_PrintMessage(channel, VA("%s\n\n", codePos), 0); } int ScriptError::Scr_GetLineInfo(const char* buf, unsigned int sourcePos, int* col, char* line, Game::SourceBufferInfo* binfo) { const char* startLine; unsigned int lineNum; if (buf) { lineNum = Scr_GetLineNumInternal(buf, sourcePos, &startLine, col, binfo); } else { lineNum = 0; startLine = ""; *col = 0; } Scr_CopyFormattedLine(line, startLine); return static_cast(lineNum); } void ScriptError::Scr_PrintSourcePos(int channel, const char* filename, const char* buf, unsigned int sourcePos) { char line[1024]; int col; assert(filename); auto lineNum = Scr_GetLineInfo(buf, sourcePos, &col, line, nullptr); Game::Com_PrintMessage(channel, VA("(file '%s'%s, line %d)\n", filename, scrParserGlob.saveSourceBufferLookup ? " (savegame)" : "", lineNum + 1), 0); Game::Com_PrintMessage(channel, VA("%s\n", line), 0); for (auto i = 0; i < col; ++i) { Game::Com_PrintMessage(channel, " ", 0); } Game::Com_PrintMessage(channel, "*\n", 0); } void ScriptError::RuntimeErrorInternal(int channel, const char* codePos, unsigned int index, const char* msg) { assert(Scr_IsInOpcodeMemory(codePos)); Game::Com_PrintError(channel, "\n******* script runtime error *******\n%s: ", msg); Scr_PrintPrevCodePos(channel, codePos, index); if (Game::scrVmPub->function_count) { for (auto i = Game::scrVmPub->function_count - 1; i >= 1; --i) { Game::Com_PrintError(channel, "called from:\n"); Scr_PrintPrevCodePos(0, Game::scrVmPub->function_frame_start[i].fs.pos, Game::scrVmPub->function_frame_start[i].fs.localId == 0); } Game::Com_PrintError(channel, "started from:\n"); Scr_PrintPrevCodePos(0, Game::scrVmPub->function_frame_start[0].fs.pos, 1); } Game::Com_PrintError(channel, "************************************\n"); } void ScriptError::RuntimeError(const char* codePos, unsigned int index, const char* msg, const char* dialogMessage) { bool abort_on_error; const char* dialogMessageSeparator; if (!developer_) { assert(Scr_IsInOpcodeMemory(codePos)); if (!(*Game::com_developer)->current.enabled) { return; } } if (Game::scrVmPub->debugCode) { Game::Com_Printf(Game::CON_CHANNEL_PARSERSCRIPT, "%s\n", msg); if (!Game::scrVmPub->terminal_error) { return; } goto error; } abort_on_error = Game::scrVmPub->terminal_error; RuntimeErrorInternal(abort_on_error ? Game::CON_CHANNEL_LOGFILEONLY : Game::CON_CHANNEL_PARSERSCRIPT, codePos, index, msg); if (abort_on_error) { error: if (!dialogMessage) { dialogMessage = ""; dialogMessageSeparator = ""; } else { dialogMessageSeparator = "\n"; } Game::Com_Error(Game::scrVmPub->terminal_error ? Game::ERR_SCRIPT_DROP : Game::ERR_SCRIPT, "\x15script runtime error\n(see console for details)\n%s%s%s", msg, dialogMessageSeparator, dialogMessage); } } void ScriptError::CompileError(unsigned int sourcePos, const char* msg, ...) { char line[1024]; char text[1024]; int col; va_list argptr; va_start(argptr, msg); vsnprintf_s(text, _TRUNCATE, msg, argptr); va_end(argptr); if (Game::scrVarPub->evaluate) { if (!Game::scrVarPub->error_message) { Game::scrVarPub->error_message = VA("%s", text); } } else { Game::Scr_ShutdownAllocNode(); Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "\n"); Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n"); if (!developer_ || !scrParserPub.sourceBuf) { Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "%s\n", text); line[0] = '\0'; Game::Com_Printf(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n"); Game::Com_Error(Game::ERR_SCRIPT_DROP, "\x15" "script compile error\n%s\n%s\n(see console for details)\n", text, line); } else { assert(scrParserPub.sourceBuf); Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "%s: ", text); Scr_PrintSourcePos(Game::CON_CHANNEL_PARSERSCRIPT, scrParserPub.scriptfilename, scrParserPub.sourceBuf, sourcePos); auto lineNumber = Scr_GetLineInfo(scrParserPub.sourceBuf, sourcePos, &col, line, nullptr); Game::Com_Error(Game::ERR_SCRIPT_DROP, "\x15" "script compile error\n%s\n%s(%d):\n %s\n(see console for details)\n", text, scrParserPub.scriptfilename, lineNumber, line); } } } void ScriptError::CompileError2(const char* codePos, const char* msg, ...) { char line[1024]; char text[1024]; va_list argptr; assert(!Game::scrVarPub->evaluate); assert(Scr_IsInOpcodeMemory(codePos)); Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "\n"); Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n"); va_start(argptr, msg); vsnprintf_s(text, _TRUNCATE, msg, argptr); va_end(argptr); Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "%s: ", text); Scr_PrintPrevCodePos(Game::CON_CHANNEL_PARSERSCRIPT, codePos, 0); Game::Com_Printf(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n"); Scr_GetTextSourcePos(scrParserPub.sourceBuf, codePos, line); Game::Com_Error(Game::ERR_SCRIPT_DROP, "\x15" "script compile error\n%s\n%s\n(see console for details)\n", text, line); } void ScriptError::Scr_GetTextSourcePos([[maybe_unused]] const char* buf, const char* codePos, char* line) { int col; if (developer_ && codePos && codePos != Game::g_EndPos && Game::scrVarPub->programBuffer && Scr_IsInOpcodeMemory(codePos)) { auto bufferIndex = Scr_GetSourceBuffer(codePos - 1); Scr_GetLineInfo(scrParserPub.sourceBufferLookup[bufferIndex].sourceBuf, Scr_GetPrevSourcePos(codePos - 1, 0), &col, line, nullptr); } else { *line = '\0'; } } void ScriptError::Scr_InitOpcodeLookup() { assert(!scrParserGlob.opcodeLookup); assert(!scrParserGlob.sourcePosLookup); assert(!scrParserPub.sourceBufferLookup); if (!developer_) { return; } scrParserGlob.delayedSourceIndex = -1; scrParserGlob.opcodeLookupMaxSize = 0; scrParserGlob.opcodeLookupLen = 0; scrParserGlob.opcodeLookup = static_cast(Game::Z_VirtualReserve(Game::MAX_OPCODE_LOOKUP_SIZE)); scrParserGlob.sourcePosLookupMaxSize = 0; scrParserGlob.sourcePosLookupLen = 0; scrParserGlob.sourcePosLookup = static_cast(Game::Z_VirtualReserve(Game::MAX_SOURCEPOS_LOOKUP_SIZE)); scrParserGlob.currentCodePos = nullptr; scrParserGlob.currentSourcePosCount = 0; scrParserGlob.sourceBufferLookupMaxSize = 0; scrParserPub.sourceBufferLookupLen = 0; scrParserPub.sourceBufferLookup = static_cast(Game::Z_VirtualReserve(Game::MAX_SOURCEBUF_LOOKUP_SIZE)); } void ScriptError::Scr_ShutdownOpcodeLookup() { if (scrParserGlob.opcodeLookup) { Z_VirtualFree(scrParserGlob.opcodeLookup); scrParserGlob.opcodeLookup = nullptr; } if (scrParserGlob.sourcePosLookup) { Z_VirtualFree(scrParserGlob.sourcePosLookup); scrParserGlob.sourcePosLookup = nullptr; } if (scrParserPub.sourceBufferLookup) { for (unsigned int i = 0; i < scrParserPub.sourceBufferLookupLen; ++i) { Game::Engine::Hunk_FreeDebugMem(scrParserPub.sourceBufferLookup[i].buf); } Z_VirtualFree(scrParserPub.sourceBufferLookup); scrParserPub.sourceBufferLookup = nullptr; } if (scrParserGlob.saveSourceBufferLookup) { for (unsigned int i = 0; i < scrParserGlob.saveSourceBufferLookupLen; ++i) { if (scrParserGlob.saveSourceBufferLookup[i].sourceBuf) { Game::Engine::Hunk_FreeDebugMem(scrParserGlob.saveSourceBufferLookup[i].buf); } } Game::Engine::Hunk_FreeDebugMem(scrParserGlob.saveSourceBufferLookup); scrParserGlob.saveSourceBufferLookup = nullptr; } } __declspec(naked) void ScriptError::EmitThreadInternal_Stub() { __asm { pushad push [esp + 0x20 + 0x8] // sourcePos call AddThreadStartOpcodePos add esp, 0x4 popad cmp ds:0x1CFEEF0, 2 push 0x61A687 ret } } Game::SourceBufferInfo* ScriptError::Scr_GetNewSourceBuffer() { assert(scrParserPub.sourceBufferLookup); auto size = sizeof(Game::SourceBufferInfo) * (scrParserPub.sourceBufferLookupLen + 1); if (size > scrParserGlob.sourceBufferLookupMaxSize) { if (scrParserGlob.sourceBufferLookupMaxSize >= Game::MAX_SOURCEBUF_LOOKUP_SIZE) { Game::Sys_Error("MAX_SOURCEBUF_LOOKUP_SIZE exceeded"); } Game::Z_VirtualCommit((char*)scrParserPub.sourceBufferLookup + scrParserGlob.sourceBufferLookupMaxSize, 0x20000); scrParserGlob.sourceBufferLookupMaxSize += 0x20000; assert(size <= scrParserGlob.sourceBufferLookupMaxSize); } return &scrParserPub.sourceBufferLookup[scrParserPub.sourceBufferLookupLen++]; } void ScriptError::Scr_AddSourceBufferInternal(const char* extFilename, const char* codePos, char* sourceBuf, int len, bool doEolFixup, bool archive) { int i; if (!scrParserPub.sourceBufferLookup) { scrParserPub.sourceBuf = nullptr; return; } assert((len >= -1)); assert((len >= 0) || !sourceBuf); auto strLen = std::strlen(extFilename) + 1; auto newLen = strLen + len + 2; auto* buf = static_cast(Game::Engine::Hunk_AllocDebugMem(static_cast(newLen))); // Scr_AddSourceBufferInternal strcpy(buf, extFilename); auto* sourceBuf2 = sourceBuf ? buf + strLen : nullptr; auto* source = sourceBuf; auto* dest = sourceBuf2; if (doEolFixup) { for (i = 0; i <= len; ++i) { const auto c = *source++; if (c == '\n' || c == '\r' && *source != '\n') { *dest = 0; } else { *dest = c; } ++dest; } } else { for (i = 0; i <= len; ++i) { const auto c = *source; ++source; *dest = c; ++dest; } } auto* bufferInfo = Scr_GetNewSourceBuffer(); bufferInfo->codePos = codePos; bufferInfo->buf = buf; bufferInfo->sourceBuf = sourceBuf2; bufferInfo->len = len; bufferInfo->sortedIndex = -1; bufferInfo->archive = archive; if (sourceBuf2) { scrParserPub.sourceBuf = sourceBuf2; } } char* ScriptError::Scr_ReadFile_FastFile([[maybe_unused]] const char* filename, const char* extFilename, const char* codePos, bool archive) { auto* rawfile = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_RAWFILE, extFilename).rawfile; if (Game::DB_IsXAssetDefault(Game::ASSET_TYPE_RAWFILE, extFilename)) { Scr_AddSourceBufferInternal(extFilename, codePos, nullptr, -1, true, true); return nullptr; } const auto len = Game::DB_GetRawFileLen(rawfile); auto* sourceBuf = static_cast(Game::Hunk_AllocateTempMemoryHigh(len)); // Scr_ReadFile_FastFile Game::DB_GetRawBuffer(rawfile, sourceBuf, len); Scr_AddSourceBufferInternal(extFilename, codePos, sourceBuf, len, true, archive); return sourceBuf; } char* ScriptError::Scr_ReadFile_LoadObj([[maybe_unused]] const char* filename, const char* extFilename, const char* codePos, bool archive) { int f; auto len = Game::FS_FOpenFileByMode(extFilename, &f, Game::FS_READ); if (len < 0) { Scr_AddSourceBufferInternal(extFilename, codePos, nullptr, -1, true, archive); return nullptr; } *Game::g_loadedImpureScript = true; auto* sourceBuf = static_cast(Game::Hunk_AllocateTempMemoryHigh(len + 1)); Game::FS_Read(sourceBuf, len, f); sourceBuf[len] = '\0'; Game::FS_FCloseFile(f); Scr_AddSourceBufferInternal(extFilename, codePos, sourceBuf, len, true, archive); return sourceBuf; } char* ScriptError::Scr_ReadFile(const char* filename, const char* extFilename, const char* codePos, bool archive) { int file; if (Game::FS_FOpenFileRead(extFilename, &file) < 0) { return Scr_ReadFile_FastFile(filename, extFilename, codePos, archive); } Game::FS_FCloseFile(file); return Scr_ReadFile_LoadObj(filename, extFilename, codePos, archive); } char* ScriptError::Scr_AddSourceBuffer(const char* filename, const char* extFilename, const char* codePos, bool archive) { char* sourceBuf; if (archive && scrParserGlob.saveSourceBufferLookup) { assert(scrParserGlob.saveSourceBufferLookupLen > 0); --scrParserGlob.saveSourceBufferLookupLen; auto* saveSourceBuffer = scrParserGlob.saveSourceBufferLookup + scrParserGlob.saveSourceBufferLookupLen; const auto len = saveSourceBuffer->len; assert(len >= -1); if (len < 0) { Scr_AddSourceBufferInternal(extFilename, codePos, nullptr, -1, true, archive); sourceBuf = nullptr; } else { sourceBuf = static_cast(Game::Hunk_AllocateTempMemoryHigh(len + 1)); const char* source = saveSourceBuffer->sourceBuf; auto* dest = sourceBuf; for (int i = 0; i < len; ++i) { const auto c = *source++; *dest = c ? c : '\n'; dest++; } *dest = '\0'; Scr_AddSourceBufferInternal(extFilename, codePos, sourceBuf, len, false, archive); } return sourceBuf; } return Scr_ReadFile(filename, extFilename, codePos, archive); } unsigned int ScriptError::Scr_LoadScriptInternal_Hk(const char* filename, Game::PrecacheEntry* entries, int entriesCount) { char extFilename[MAX_QPATH]; Game::sval_u parseData; assert(Game::scrCompilePub->script_loading); assert(std::strlen(filename) < MAX_QPATH); const auto name = Game::Scr_CreateCanonicalFilename(filename); if (Game::FindVariable(Game::scrCompilePub->loadedscripts, name)) { Game::SL_RemoveRefToString(name); auto filePosPtr = Game::FindVariable(Game::scrCompilePub->scriptsPos, name); return filePosPtr ? Game::FindObject(Game::scrCompilePub->scriptsPos, filePosPtr) : 0; } const auto scriptId = Game::GetNewVariable(Game::scrCompilePub->loadedscripts, name); Game::SL_RemoveRefToString(name); sprintf_s(extFilename, "%s.gsc", Game::SL_ConvertToString(static_cast(name))); const auto* oldSourceBuf = scrParserPub.sourceBuf; auto* sourceBuffer = Scr_AddSourceBuffer(Game::SL_ConvertToString(static_cast(name)), extFilename, Game::TempMalloc(0), true); if (!sourceBuffer) { return 0; } const auto oldAnimTreeNames = Game::scrAnimPub->animTreeNames; Game::scrAnimPub->animTreeNames = 0; Game::scrCompilePub->far_function_count = 0; const auto* oldFilename = scrParserPub.scriptfilename; scrParserPub.scriptfilename = extFilename; Game::scrCompilePub->in_ptr = "+"; Game::scrCompilePub->in_ptr_valid = false; Game::scrCompilePub->parseBuf = sourceBuffer; Game::ScriptParse(&parseData, 0); Game::scrCompilePub->parseBuf = nullptr; const auto filePosId = Game::GetObject(Game::scrCompilePub->scriptsPos, Game::GetVariable(Game::scrCompilePub->scriptsPos, name)); const auto fileCountId = Game::GetObject(Game::scrCompilePub->scriptsCount, Game::GetVariable(Game::scrCompilePub->scriptsCount, name)); Game::ScriptCompile(parseData.node, filePosId, fileCountId, scriptId, entries, entriesCount); Game::RemoveVariable(Game::scrCompilePub->scriptsCount, name); scrParserPub.scriptfilename = oldFilename; scrParserPub.sourceBuf = oldSourceBuf; Game::scrAnimPub->animTreeNames = oldAnimTreeNames; return filePosId; } void ScriptError::Scr_Settings_Hk([[maybe_unused]] int developer, int developer_script, int abort_on_error) { assert(!abort_on_error || developer); developer_ = (*Game::com_developer)->current.enabled; Game::scrVarPub->developer_script = developer_script != 0; Game::scrVmPub->abort_on_error = abort_on_error != 0; } void ScriptError::MT_Reset_Stub() { Utils::Hook::Call(0x4D9620)(); Scr_InitOpcodeLookup(); Game::Engine::Hunk_InitDebugMemory(); } void ScriptError::SL_ShutdownSystem_Stub(unsigned int user) { Utils::Hook::Call(0x4F46D0)(user); Scr_ShutdownOpcodeLookup(); Game::Engine::Hunk_ShutdownDebugMemory(); } ScriptError::ScriptError() { #ifdef SCRIPT_ERROR_PATCH std::vector> patches; patches.reserve(200); const auto p = [&patches](const std::size_t a, void* b) { patches.emplace_back(a, b); }; p(0x44AC44, Scr_Settings_Hk); p(0x60BE0B, Scr_Settings_Hk); p(0x4656A7, MT_Reset_Stub); // Scr_BeginLoadScripts p(0x42904B, SL_ShutdownSystem_Stub); // Scr_FreeScripts p(0x426C8A, Scr_LoadScriptInternal_Hk); // ScriptCompile p(0x45D959, Scr_LoadScriptInternal_Hk); // Scr_LoadScript p(0x61E3AD, RuntimeError); // VM_Notify p(0x621976, RuntimeError); // VM_Execute p(0x62246E, RuntimeError); // VM_Execute_0 p(0x611F7C, CompileError2); // Scr_CheckAnimsDefined p(0x612E76, CompileError2); // LinkThread p(0x612E8D, CompileError2); // ^^ p(0x612EA2, CompileError2); // ^^ Utils::Hook(0x4227E0, Scr_PrintPrevCodePos, HOOK_JUMP).install()->quick(); Utils::Hook(0x61A680, EmitThreadInternal_Stub, HOOK_JUMP).install()->quick(); Utils::Hook::Nop(0x61A680 + 5, 2); p(0x61228C, AddOpcodePos); // EmitGetInteger p(0x6122C0, AddOpcodePos); // ^^ p(0x6121FF, AddOpcodePos); // ^^ p(0x612223, AddOpcodePos); // ^^ p(0x612259, AddOpcodePos); // ^^ p(0x6122ED, AddOpcodePos); // ^^ p(0x6128BE, AddOpcodePos); // EmitValue p(0x6128F3, AddOpcodePos); // EmitGetFloat p(0x612702, AddOpcodePos); // EmitGetString p(0x612772, AddOpcodePos); // EmitGetIString p(0x6127E4, AddOpcodePos); // EmitGetVector p(0x612843, AddOpcodePos); // EmitGetAnimation p(0x6166DD, AddOpcodePos); // EmitPreFunctionCall p(0x617D10, AddOpcodePos); // EmitOrEvalVariableExpression p(0x615160, AddOpcodePos); // EmitOrEvalLocalVariable p(0x6155B3, AddOpcodePos); // EmitArrayVariable p(0x6155BF, AddOpcodePos); // ^^ p(0x6170D7, AddOpcodePos); // EmitBoolAndExpression p(0x614971, AddOpcodePos); // ?? p(0x6151B6, AddOpcodePos); // EmitLocalVariableRef p(0x619CC1, AddOpcodePos); // EmitArrayVariableRef p(0x619CC9, AddOpcodePos); // ^^ p(0x615261, AddOpcodePos); // EmitGameRef p(0x615309, AddOpcodePos); // EmitClearArray Utils::Hook(0x615319, AddOpcodePos, HOOK_JUMP).install()->quick(); // EmitClearArray p(0x614D79, AddOpcodePos); // ?? p(0x6153B9, AddOpcodePos); // ?? p(0x614B74, AddOpcodePos); // ?? p(0x614ED9, AddOpcodePos); // ?? p(0x614E29, AddOpcodePos); // ?? p(0x614CC9, AddOpcodePos); // ?? p(0x614C19, AddOpcodePos); // ?? p(0x6167A5, AddOpcodePos); // ?? p(0x614AB1, AddOpcodePos); // ?? p(0x614A11, AddOpcodePos); // ?? p(0x616FB7, AddOpcodePos); // EmitBoolOrExpression p(0x6171EE, AddOpcodePos); // EmitOrEvalBinaryOperatorExpression p(0x616E6B, AddOpcodePos); // EmitOrEvalPrimitiveExpressionList p(0x618199, AddOpcodePos); // EmitReturnStatement p(0x61A2F2, AddOpcodePos); // EmitEndStatement p(0x61826A, AddOpcodePos); // EmitWaitStatement p(0x618272, AddOpcodePos); // ^^ p(0x61827E, AddOpcodePos); // ^^ p(0x61832E, RemoveOpcodePos); // EmitIfStatement (EmitOpcode inlined?) p(0x618366, AddOpcodePos); // ^^ p(0x6183C8, AddOpcodePos); // ^^ p(0x6184A6, RemoveOpcodePos); // EmitIfElseStatement (EmitOpcode inlined?) p(0x6184DD, AddOpcodePos); // ^^ p(0x618601, AddOpcodePos); // ^^ p(0x618569, AddOpcodePos); // ^^ p(0x618685, AddOpcodePos); // ^^ p(0x618876, RemoveOpcodePos); // EmitWhileStatement (EmitOpcode inlined?) p(0x6188AD, AddOpcodePos); // ^^ p(0x6189F5, AddOpcodePos); // ^^ p(0x618A09, AddOpcodePos); // ^^ p(0x618CC2, RemoveOpcodePos); // EmitForStatement (EmitOpcode inlined?) p(0x618CF9, AddOpcodePos); // ^^ p(0x618EAE, AddOpcodePos); // ^^ p(0x618EC2, AddOpcodePos); // ^^ p(0x61A0C7, AddOpcodePos); // EmitIncStatement p(0x61A0DA, AddOpcodePos); // ^^ p(0x61A1A7, AddOpcodePos); // EmitDecStatement p(0x61A1BA, AddOpcodePos); // ^^ p(0x61A239, AddOpcodePos); // EmitBinaryEqualsOperatorExpression p(0x61A253, AddOpcodePos); // ^^ p(0x61909D, AddOpcodePos); // EmitWaittillStatement p(0x6190A5, AddOpcodePos); // ^^ p(0x6190B1, AddOpcodePos); // ^^ p(0x6190BE, AddOpcodePos); // ^^ p(0x6148D0, AddOpcodePos); // EmitSafeSetWaittillVariableField p(0x6192B3, AddOpcodePos); // EmitWaittillmatchStatement p(0x6192BB, AddOpcodePos); // ^^ p(0x6192C7, AddOpcodePos); // ^^ p(0x6192D5, AddOpcodePos); // ^^ p(0x6192EC, AddOpcodePos); // ^^ p(0x617412, AddOpcodePos); // EmitWaittillFrameEnd p(0x61741A, AddOpcodePos); // ^^ p(0x61944B, AddOpcodePos); // EmitNotifyStatement p(0x619551, AddOpcodePos); // ^^ p(0x61955F, AddOpcodePos); // ^^ p(0x61956B, AddOpcodePos); // ^^ p(0x61965E, AddOpcodePos); // EmitEndOnStatement p(0x61966A, AddOpcodePos); // ^^ p(0x619835, AddOpcodePos); // EmitSwitchStatement p(0x617860, AddOpcodePos); // EmitBreakStatement p(0x617990, AddOpcodePos); // EmitContinueStatement p(0x61A70D, AddOpcodePos); // EmitThreadInternal p(0x61A716, AddOpcodePos); // ^^ p(0x617BB8, AddOpcodePos); // InitThread p(0x617BC0, AddOpcodePos); // ^^ p(0x6157B7, AddOpcodePos); // EmitGetFunction p(0x615997, AddOpcodePos); // ^^ p(0x6158D7, AddOpcodePos); // ^^ p(0x616BBD, AddOpcodePos); // EmitMethod p(0x616C2E, AddOpcodePos); // ^^ p(0x615A42, RemoveOpcodePos); // ?? (EmitOpcode inlined?) p(0x615AF7, RemoveOpcodePos); // ^^ p(0x615B78, AddOpcodePos); // ^^ p(0x615D0D, AddOpcodePos); // ?? p(0x615D19, AddOpcodePos); // ^^ p(0x6163FD, AddOpcodePos); // ?? p(0x616420, AddOpcodePos); // ^^ p(0x615E5B, RemoveOpcodePos); // ?? (EmitOpcode inlined?) p(0x615E93, AddOpcodePos); // ^^ p(0x6161C5, AddOpcodePos); // ?? p(0x61645D, AddOpcodePos); // ?? p(0x616480, AddOpcodePos); // ^^ p(0x615FEB, RemoveOpcodePos); // ?? (EmitOpcode inlined?) p(0x616023, AddOpcodePos); // ^^ p(0x616365, AddOpcodePos); // ?? p(0x616605, AddOpcodePos); // ?? p(0x6167F2, AddOpcodePos); // EmitCallBuiltinMethodOpcode p(0x617C6F, AddOpcodePos); // EmitClearFieldVariable p(0x617482, AddOpcodePos); // EmitSafeSetVariableField p(0x617B61, AddOpcodePos); // EmitFormalParameterList p(0x6154F4, AddOpcodePos); // ?? p(0x61551B, AddOpcodePos); // ^^ p(0x61554D, AddOpcodePos); // ^^ p(0x6150C1, AddOpcodePos); // EmitAnimObject p(0x615021, AddOpcodePos); // EmitLevelObject p(0x614F81, AddOpcodePos); // ?? p(0x612CF2, AddOpcodePos); // EmitFunction p(0x619FFA, AddOpcodePos); // ?? p(0x61407E, RemoveOpcodePos); // EmitOpcode p(0x61409F, RemoveOpcodePos); // ^^ p(0x6140C0, RemoveOpcodePos); // ^^ p(0x6140D7, RemoveOpcodePos); // ^^ p(0x614164, RemoveOpcodePos); // ^^ p(0x61417A, RemoveOpcodePos); // ^^ p(0x61419D, RemoveOpcodePos); // ^^ p(0x6141B4, RemoveOpcodePos); // ^^ p(0x6141CE, RemoveOpcodePos); // ^^ p(0x6141F9, RemoveOpcodePos); // ^^ p(0x614214, RemoveOpcodePos); // ^^ p(0x614277, RemoveOpcodePos); // ^^ p(0x61428E, RemoveOpcodePos); // ^^ p(0x6142CD, RemoveOpcodePos); // ^^ for (const auto& patch : patches) { Utils::Hook(patch.first, patch.second, HOOK_CALL).install()->quick(); } Utils::Hook(0x434260, CompileError, HOOK_JUMP).install()->quick(); #endif } }