iw4x-client/src/Components/Modules/Script.cpp

338 lines
7.4 KiB
C++
Raw Normal View History

2017-01-19 16:23:59 -05:00
#include "STDInclude.hpp"
namespace Components
{
std::string Script::ScriptName;
std::vector<int> Script::ScriptHandles;
std::vector<Script::Function> Script::ScriptFunctions;
2017-01-19 16:23:59 -05:00
std::vector<std::string> Script::ScriptNameStack;
unsigned short Script::FunctionName;
void Script::FunctionError()
{
std::string funcName = Game::SL_ConvertToString(Script::FunctionName);
Game::Scr_ShutdownAllocNode();
Logger::Print(23, "\n");
Logger::Print(23, "******* script compile error *******\n");
Logger::Print(23, "Error: unknown function %s in %s\n", funcName.data(), Script::ScriptName.data());
Logger::Print(23, "************************************\n");
Logger::Error(5, "script compile error\nunknown function %s\n%s\n\n", funcName.data(), Script::ScriptName.data());
}
__declspec(naked) void Script::StoreFunctionNameStub()
{
__asm
{
mov eax, [esp - 8h]
mov Script::FunctionName, ax
sub esp, 0Ch
push 0
push edi
mov eax, 612DB6h
jmp eax
}
}
void Script::StoreScriptName(const char* name)
{
Script::ScriptNameStack.push_back(Script::ScriptName);
Script::ScriptName = name;
if (!Utils::String::EndsWith(Script::ScriptName, ".gsc"))
{
Script::ScriptName.append(".gsc");
}
}
__declspec(naked) void Script::StoreScriptNameStub()
{
__asm
{
2017-02-01 07:44:25 -05:00
pushad
lea ecx, [esp + 30h]
2017-01-19 16:23:59 -05:00
push ecx
call Script::StoreScriptName
add esp, 4h
2017-02-01 07:44:25 -05:00
popad
2017-01-19 16:23:59 -05:00
push ebp
mov ebp, ds:1CDEAA8h
2017-02-01 07:44:25 -05:00
push 427DC3h
retn
2017-01-19 16:23:59 -05:00
}
}
void Script::RestoreScriptName()
{
Script::ScriptName = Script::ScriptNameStack.back();
Script::ScriptNameStack.pop_back();
}
__declspec(naked) void Script::RestoreScriptNameStub()
{
__asm
{
2017-02-01 07:44:25 -05:00
pushad
2017-01-19 16:23:59 -05:00
call Script::RestoreScriptName
2017-02-01 07:44:25 -05:00
popad
2017-01-19 16:23:59 -05:00
mov ds:1CDEAA8h, ebp
2017-02-01 07:44:25 -05:00
push 427E77h
retn
2017-01-19 16:23:59 -05:00
}
}
void Script::PrintSourcePos(const char* filename, unsigned int offset)
{
FileSystem::File script(filename);
if (script.exists())
{
std::string buffer = script.getBuffer();
Utils::String::Replace(buffer, "\t", " ");
int line = 1;
int lineOffset = 0;
int inlineOffset = 0;
for (unsigned int i = 0; i < buffer.size(); ++i)
{
// Terminate line
if (i == offset)
{
while (buffer[i] != '\r' && buffer[i] != '\n' && buffer[i] != '\0')
{
++i;
}
buffer[i] = '\0';
break;
}
if (buffer[i] == '\n')
{
++line;
lineOffset = i; // Includes the line break!
inlineOffset = 0;
}
else
{
++inlineOffset;
}
}
Logger::Print(23, "in file %s, line %d:", filename, line);
Logger::Print(23, "%s\n", buffer.data() + lineOffset);
for (int i = 0; i < (inlineOffset - 1); ++i)
{
Logger::Print(23, " ");
}
Logger::Print(23, "*\n");
}
else
{
Logger::Print(23, "in file %s, offset %d\n", filename, offset);
}
}
void Script::CompileError(unsigned int offset, const char* message, ...)
{
char msgbuf[1024] = { 0 };
va_list v;
va_start(v, message);
_vsnprintf_s(msgbuf, sizeof(msgbuf), message, v);
va_end(v);
Game::Scr_ShutdownAllocNode();
Logger::Print(23, "\n");
Logger::Print(23, "******* script compile error *******\n");
Logger::Print(23, "Error: %s ", msgbuf);
Script::PrintSourcePos(Script::ScriptName.data(), offset);
Logger::Print(23, "************************************\n\n");
Logger::Error(5, "script compile error\n%s\n%s\n(see console for actual details)\n", msgbuf, Script::ScriptName.data());
}
int Script::LoadScriptAndLabel(std::string script, std::string label)
{
Logger::Print("Loading script %s.gsc...\n", script.data());
if (!Game::Scr_LoadScript(script.data()))
{
Logger::Print("Script %s encountered an error while loading. (doesn't exist?)", script.data());
Logger::Error(1, reinterpret_cast<char*>(0x70B810), script.data());
2017-01-19 16:23:59 -05:00
}
else
{
Logger::Print("Script %s.gsc loaded successfully.\n", script.data());
}
Logger::Print("Finding script handle %s::%s...\n", script.data(), label.data());
int handle = Game::Scr_GetFunctionHandle(script.data(), label.data());
if (handle)
{
Logger::Print("Script handle %s::%s loaded successfully.\n", script.data(), label.data());
return handle;
}
Logger::Print("Script handle %s::%s couldn't be loaded. (file with no entry point?)\n", script.data(), label.data());
return handle;
}
void Script::LoadGameType()
{
for (auto handle : Script::ScriptHandles)
{
Game::Scr_FreeThread(Game::Scr_ExecThread(handle, 0));
}
Game::Scr_LoadGameType();
}
void Script::LoadGameTypeScript()
{
Script::ScriptHandles.clear();
auto list = FileSystem::GetFileList("scripts/", "gsc");
for (auto file : list)
{
file = "scripts/" + file;
if (Utils::String::EndsWith(file, ".gsc"))
{
file = file.substr(0, file.size() - 4);
}
int handle = Script::LoadScriptAndLabel(file, "init");
if (handle) Script::ScriptHandles.push_back(handle);
else
2017-01-19 16:23:59 -05:00
{
handle = Script::LoadScriptAndLabel(file, "main");
if (handle) Script::ScriptHandles.push_back(handle);
2017-01-19 16:23:59 -05:00
}
}
Game::GScr_LoadGameTypeScript();
}
void Script::AddFunction(std::string name, Game::scr_function_t function, bool isDev)
{
for(auto i = Script::ScriptFunctions.begin(); i != Script::ScriptFunctions.end();)
{
if(i->getName() == name)
{
i = Script::ScriptFunctions.erase(i);
continue;
}
++i;
}
Script::ScriptFunctions.push_back({ name, function, isDev });
}
Game::scr_function_t Script::GetFunction(const char** name, int* isDev)
{
if (name && *name) OutputDebugStringA(*name);
for (auto& function : Script::ScriptFunctions)
{
if (name)
{
if(std::string(*name) == function.getName())
{
*name = function.getName();
*isDev = function.isDev();
return function.getFunction();
}
}
else
{
Game::Scr_RegisterFunction(function.getFunction());
}
}
return nullptr;
}
__declspec(naked) void Script::GetFunctionStub()
{
__asm
{
push [esp + 8h]
push [esp + 8h]
mov eax, 5FA2B0h
call eax
test eax, eax
jnz returnSafe
call Script::GetFunction
returnSafe:
add esp, 8h
retn
}
}
2017-04-29 17:08:41 -04:00
int Script::SetExpFogStub()
{
if(Game::Scr_GetNumParam() == 6)
{
Game::VariableValue*& scr_stack = *reinterpret_cast<Game::VariableValue**>(0x2040D00);
if(scr_stack)
{
2017-05-07 13:04:57 -04:00
std::memmove(&scr_stack[-4], &scr_stack[-5], sizeof(Game::VariableValue) * 6);
scr_stack += 1;
scr_stack[-6].type = Game::VAR_FLOAT;
scr_stack[-6].u.floatValue = 0;
++*reinterpret_cast<DWORD*>(0x2040D0C);
2017-04-29 17:08:41 -04:00
}
}
return Game::Scr_GetNumParam();
}
2017-01-19 16:23:59 -05:00
Script::Script()
{
Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x427E71, Script::RestoreScriptNameStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x427DBC, Script::StoreScriptNameStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick();
Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick();
Utils::Hook(0x434260, Script::CompileError, HOOK_JUMP).install()->quick();
Utils::Hook(0x48EFFE, Script::LoadGameType, HOOK_CALL).install()->quick();
Utils::Hook(0x45D44A, Script::LoadGameTypeScript, HOOK_CALL).install()->quick();
2017-04-29 17:08:41 -04:00
Utils::Hook(0x44E72E, Script::GetFunctionStub, HOOK_CALL).install()->quick();
2017-04-29 17:08:41 -04:00
Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick();
2017-01-19 16:23:59 -05:00
}
Script::~Script()
{
Script::ScriptName.clear();
Script::ScriptHandles.clear();
Script::ScriptNameStack.clear();
Script::ScriptFunctions.clear();
2017-01-19 16:23:59 -05:00
}
}