iw4x-client/src/Components/Modules/GSC/ScriptError.cpp
2023-03-07 22:09:02 +00:00

1124 lines
31 KiB
C++

#include <STDInclude.hpp>
#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<int>(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<unsigned int>(-1))
{
assert(scrParserGlob.delayedSourceIndex == -1);
assert(type & Game::SOURCE_TYPE_BREAKPOINT);
scrParserGlob.delayedSourceIndex = static_cast<int>(sourcePosLookupIndex);
}
else if (sourcePos == static_cast<unsigned int>(-2))
{
scrParserGlob.threadStartSourceIndex = static_cast<int>(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<unsigned short>(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<unsigned short>(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<int>(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<int>(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, "<frozen thread>\n", 0);
return;
}
if (codePos == Game::g_EndPos)
{
Game::Com_PrintMessage(channel, "<removed thread>\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<int>(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::OpcodeLookup*>(Game::Z_VirtualReserve(Game::MAX_OPCODE_LOOKUP_SIZE));
scrParserGlob.sourcePosLookupMaxSize = 0;
scrParserGlob.sourcePosLookupLen = 0;
scrParserGlob.sourcePosLookup = static_cast<Game::SourceLookup*>(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::SourceBufferInfo*>(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<char*>(Game::Engine::Hunk_AllocDebugMem(static_cast<int>(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<char*>(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<char*>(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<char*>(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<unsigned short>(name)));
const auto* oldSourceBuf = scrParserPub.sourceBuf;
auto* sourceBuffer = Scr_AddSourceBuffer(Game::SL_ConvertToString(static_cast<unsigned short>(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<void()>(0x4D9620)();
Scr_InitOpcodeLookup();
Game::Engine::Hunk_InitDebugMemory();
}
void ScriptError::SL_ShutdownSystem_Stub(unsigned int user)
{
Utils::Hook::Call<void(unsigned int)>(0x4F46D0)(user);
Scr_ShutdownOpcodeLookup();
Game::Engine::Hunk_ShutdownDebugMemory();
}
ScriptError::ScriptError()
{
#ifdef SCRIPT_ERROR_PATCH
std::vector<std::pair<std::size_t, void*>> 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
}
}