diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 7e0f16ba..15e36cfb 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -160,7 +160,7 @@ namespace Components { Script::AddFunction("SetPing", [](Game::scr_entref_t id) // gsc: self SetPing() { - if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_INTEGER) + if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_INTEGER) { Game::Scr_Error("^1SetPing: Needs one integer parameter!\n"); return; @@ -250,7 +250,7 @@ namespace Components Script::AddFunction("botWeapon", [](Game::scr_entref_t id) // Usage: botWeapon(); { - if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING) + if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) { Game::Scr_Error("^1botWeapon: Needs one string parameter!\n"); return; @@ -293,7 +293,7 @@ namespace Components Script::AddFunction("botAction", [](Game::scr_entref_t id) // Usage: botAction(); { - if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING) + if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) { Game::Scr_Error("^1botAction: Needs one string parameter!\n"); return; @@ -346,7 +346,7 @@ namespace Components Script::AddFunction("botMovement", [](Game::scr_entref_t id) // Usage: botMovement(, ); { - if (Game::Scr_GetNumParam() != 2 || Game::Scr_GetType(0) != Game::VAR_INTEGER || Game::Scr_GetType(1) != Game::VAR_INTEGER) + if (Game::Scr_GetNumParam() != 2u || Game::Scr_GetType(0) != Game::VAR_INTEGER || Game::Scr_GetType(1) != Game::VAR_INTEGER) { Game::Scr_Error("^1botMovement: Needs two integer parameters!\n"); return; diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index d2bb9649..7d1d6f27 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -967,7 +967,7 @@ namespace Components Script::AddFunction("httpGet", [](Game::scr_entref_t) { if (!Dedicated::IsEnabled() && !Flags::HasFlag("scriptablehttp")) return; - if (Game::Scr_GetNumParam() < 1) return; + if (Game::Scr_GetNumParam() < 1u) return; std::string url = Game::Scr_GetString(0); unsigned int object = Game::AllocObject(); @@ -981,7 +981,7 @@ namespace Components Script::AddFunction("httpCancel", [](Game::scr_entref_t) { if (!Dedicated::IsEnabled() && !Flags::HasFlag("scriptablehttp")) return; - if (Game::Scr_GetNumParam() < 1) return; + if (Game::Scr_GetNumParam() < 1u) return; unsigned int object = Game::Scr_GetObject(0); for (auto& download : Download::ScriptDownloads) diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/Script.cpp index 730be18e..56bdd7ee 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/Script.cpp @@ -9,6 +9,8 @@ namespace Components unsigned short Script::FunctionName; std::unordered_map Script::ScriptStorage; std::unordered_map Script::ScriptBaseProgramNum; + std::unordered_map Script::ReplacedFunctions; + const char* Script::ReplacedPos = 0; int Script::LastFrameTime = -1; Utils::Signal Script::VMShutdownSignal; @@ -382,21 +384,109 @@ namespace Components Utils::Hook::Call(0x421EE0)(num); } - int Script::SetExpFogStub() + unsigned int Script::SetExpFogStub() { - if (Game::Scr_GetNumParam() == 6) + if (Game::Scr_GetNumParam() == 6u) { - std::memmove(&Game::scriptContainer->stack[-4], &Game::scriptContainer->stack[-5], sizeof(Game::VariableValue) * 6); - Game::scriptContainer->stack += 1; - Game::scriptContainer->stack[-6].type = Game::VAR_FLOAT; - Game::scriptContainer->stack[-6].u.floatValue = 0; + std::memmove(&Game::scrVmPub->top[-4], &Game::scrVmPub->top[-5], sizeof(Game::VariableValue) * 6); + Game::scrVmPub->top += 1; + Game::scrVmPub->top[-6].type = Game::VAR_FLOAT; + Game::scrVmPub->top[-6].u.floatValue = 0.0f; - ++Game::scriptContainer->numParam; + ++Game::scrVmPub->outparamcount; } return Game::Scr_GetNumParam(); } + const char* Script::GetCodePosForParam(int index) + { + if (static_cast(index) >= Game::scrVmPub->outparamcount) + { + Game::Scr_Error("^1GetCodePosForParam: Index is out of range!\n"); + return ""; + } + + const auto value = &Game::scrVmPub->top[-index]; + + if (value->type != Game::VAR_FUNCTION) + { + Game::Scr_Error("^1GetCodePosForParam: Expects a function as parameter!\n"); + return ""; + } + + return value->u.codePosValue; + } + + void Script::GetReplacedPos(const char* pos) + { + if (Script::ReplacedFunctions.find(pos) != Script::ReplacedFunctions.end()) + { + Script::ReplacedPos = Script::ReplacedFunctions[pos]; + } + } + + void Script::SetReplacedPos(const char* what, const char* with) + { + if (what[0] == '\0' || with[0] == '\0') + { + Logger::Print("Warning: Invalid paramters passed to ReplacedFunctions\n"); + return; + } + + if (Script::ReplacedFunctions.find(what) != Script::ReplacedFunctions.end()) + { + Logger::Print("Warning: ReplacedFunctions already contains codePosValue for a function\n"); + } + + Script::ReplacedFunctions[what] = with; + } + + __declspec(naked) void Script::VMExecuteInternalStub() + { + __asm + { + pushad + + push edx + call Script::GetReplacedPos + + pop edx + popad + + cmp Script::ReplacedPos, 0 + jne SetPos + + movzx eax, byte ptr [edx] + inc edx + + Loc1: + cmp eax, 0x8B + + push ecx + + mov ecx, 0x2045094 + mov [ecx], eax + + mov ecx, 0x2040CD4 + mov [ecx], edx + + pop ecx + + push 0x61E944 + retn + + SetPos: + mov edx, Script::ReplacedPos + mov Script::ReplacedPos, 0 + + movzx eax, byte ptr [edx] + inc edx + + jmp Loc1 + } + } + Game::gentity_t* Script::getEntFromEntRef(Game::scr_entref_t entref) { Game::gentity_t* gentity = &Game::g_entities[entref]; @@ -414,6 +504,20 @@ namespace Components void Script::AddFunctions() { + Script::AddFunction("ReplaceFunc", [](Game::scr_entref_t) // gsc: ReplaceFunc(, ) + { + if (Game::Scr_GetNumParam() != 2u) + { + Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n"); + return; + } + + const auto what = Script::GetCodePosForParam(0); + const auto with = Script::GetCodePosForParam(1); + + Script::SetReplacedPos(what, with); + }); + // System time Script::AddFunction("GetSystemTime", [](Game::scr_entref_t) // gsc: GetSystemTime() { @@ -434,7 +538,7 @@ namespace Components // Print to console, even without being in 'developer 1'. Script::AddFunction("PrintConsole", [](Game::scr_entref_t) // gsc: PrintConsole() { - if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING) + if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) { Game::Scr_Error("^1PrintConsole: Needs one string parameter!\n"); return; @@ -448,7 +552,7 @@ namespace Components // Executes command to the console Script::AddFunction("Exec", [](Game::scr_entref_t) // gsc: Exec() { - if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING) + if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) { Game::Scr_Error("^1Exec: Needs one string parameter!\n"); return; @@ -463,7 +567,7 @@ namespace Components // Script Storage Funcs Script::AddFunction("StorageSet", [](Game::scr_entref_t) // gsc: StorageSet(, ); { - if (Game::Scr_GetNumParam() != 2 || Game::Scr_GetType(0) != Game::VAR_STRING || Game::Scr_GetType(1) != Game::VAR_STRING) + if (Game::Scr_GetNumParam() != 2u || Game::Scr_GetType(0) != Game::VAR_STRING || Game::Scr_GetType(1) != Game::VAR_STRING) { Game::Scr_Error("^1StorageSet: Needs two string parameters!\n"); return; @@ -477,7 +581,7 @@ namespace Components Script::AddFunction("StorageRemove", [](Game::scr_entref_t) // gsc: StorageRemove(); { - if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING) + if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) { Game::Scr_Error("^1StorageRemove: Needs one string parameter!\n"); return; @@ -496,7 +600,7 @@ namespace Components Script::AddFunction("StorageGet", [](Game::scr_entref_t) // gsc: StorageGet(); { - if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING) + if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) { Game::Scr_Error("^1StorageGet: Needs one string parameter!\n"); return; @@ -516,7 +620,7 @@ namespace Components Script::AddFunction("StorageHas", [](Game::scr_entref_t) // gsc: StorageHas(); { - if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING) + if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) { Game::Scr_Error("^1StorageHas: Needs one string parameter!\n"); return; @@ -612,6 +716,9 @@ namespace Components Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x61E92E, Script::VMExecuteInternalStub, HOOK_JUMP).install()->quick(); + Utils::Hook::Nop(0x61E933, 1); + Utils::Hook(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); @@ -640,31 +747,10 @@ namespace Components Script::AddFunctions(); - // Script::AddFunction("playviewmodelfx", [](Game::scr_entref_t /*index*/) - // { - // /*auto Scr_Error = Utils::Hook::Call(0x42EF40); - // if (index >> 16) - // { - // Scr_Error("not an entity"); - // return; - // }*/ - - // // obtain FX name - // auto fxName = Game::Scr_GetString(0); - // auto fx = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FX, fxName).fx; - - // auto tagName = Game::Scr_GetString(1); - // auto tagIndex = Game::SL_GetString(tagName, 0); - - // /*char boneIndex = -2; - // if (!Game::CG_GetBoneIndex(2048, tagIndex, &boneIndex)) - // { - // Scr_Error(Utils::String::VA("Unknown bone %s.\n", tagName)); - // return; - // }*/ - - // Game::CG_PlayBoltedEffect(0, fx, 2048, tagIndex); - // }); + Script::OnVMShutdown([] + { + Script::ReplacedFunctions.clear(); + }); } Script::~Script() @@ -673,6 +759,7 @@ namespace Components Script::ScriptHandles.clear(); Script::ScriptNameStack.clear(); Script::ScriptFunctions.clear(); + Script::ReplacedFunctions.clear(); Script::VMShutdownSignal.clear(); Script::ScriptStorage.clear(); diff --git a/src/Components/Modules/Script.hpp b/src/Components/Modules/Script.hpp index dd429cde..c9125831 100644 --- a/src/Components/Modules/Script.hpp +++ b/src/Components/Modules/Script.hpp @@ -40,6 +40,8 @@ namespace Components static unsigned short FunctionName; static std::unordered_map ScriptStorage; static std::unordered_map ScriptBaseProgramNum; + static std::unordered_map ReplacedFunctions; + static const char* ReplacedPos; static int LastFrameTime; static Utils::Signal VMShutdownSignal; @@ -68,7 +70,12 @@ namespace Components static void Scr_PrintPrevCodePosStub(); static void Scr_PrintPrevCodePos(int); - static int SetExpFogStub(); + static unsigned int SetExpFogStub(); + + static const char* GetCodePosForParam(int index); + static void GetReplacedPos(const char* pos); + static void SetReplacedPos(const char* what, const char* with); + static void VMExecuteInternalStub(); static void AddFunctions(); }; diff --git a/src/Components/Modules/Slowmotion.cpp b/src/Components/Modules/Slowmotion.cpp index 0a15b94d..4879e142 100644 --- a/src/Components/Modules/Slowmotion.cpp +++ b/src/Components/Modules/Slowmotion.cpp @@ -38,12 +38,12 @@ namespace Components float start = Game::Scr_GetFloat(0); float end = 1.0f; - if (Game::Scr_GetNumParam() >= 2) + if (Game::Scr_GetNumParam() >= 2u) { end = Game::Scr_GetFloat(1); } - if (Game::Scr_GetNumParam() >= 3) + if (Game::Scr_GetNumParam() >= 3u) { duration = static_cast(Game::Scr_GetFloat(2) * 1000.0); } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 462c6d9c..d61e41fa 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -460,7 +460,7 @@ namespace Game XZone* g_zones = reinterpret_cast(0x14C0F80); unsigned short* db_hashTable = reinterpret_cast(0x12412B0); - ScriptContainer* scriptContainer = reinterpret_cast(0x2040D00); + scrVmPub_t* scrVmPub = reinterpret_cast(0x2040CF0); clientstate_t* clcState = reinterpret_cast(0xB2C540); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 7539ea18..6409c9db 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -660,7 +660,7 @@ namespace Game typedef unsigned int(__cdecl * Scr_GetObject_t)(int); extern Scr_GetObject_t Scr_GetObject; - typedef int(__cdecl * Scr_GetNumParam_t)(); + typedef unsigned int(__cdecl * Scr_GetNumParam_t)(); extern Scr_GetNumParam_t Scr_GetNumParam; typedef int(__cdecl * Scr_GetFunctionHandle_t)(const char*, const char*); @@ -687,7 +687,7 @@ namespace Game typedef bool(__cdecl * Scr_IsSystemActive_t)(); extern Scr_IsSystemActive_t Scr_IsSystemActive; - typedef int(__cdecl* Scr_GetType_t)(int); + typedef int(__cdecl* Scr_GetType_t)(unsigned int); extern Scr_GetType_t Scr_GetType; typedef void(__cdecl* Scr_Error_t)(const char*); @@ -969,7 +969,7 @@ namespace Game extern XZone* g_zones; extern unsigned short* db_hashTable; - extern ScriptContainer* scriptContainer; + extern scrVmPub_t* scrVmPub; extern clientstate_t* clcState; diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 2d8b443c..5ae14cc2 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -4886,18 +4886,38 @@ namespace Game struct VariableValue { VariableUnion u; - int type; + VariableType type; }; - struct ScriptContainer + struct function_stack_t { - VariableValue* stack; - char unk1; - char unk2; - char unk3; - char pad; - DWORD unk4; - int numParam; + const char* pos; + unsigned int localId; + unsigned int localVarCount; + VariableValue* top; + VariableValue* startTop; + }; + + struct function_frame_t + { + function_stack_t fs; + int topType; + }; + + struct scrVmPub_t + { + unsigned int* localVars; + VariableValue* maxStack; + int function_count; + function_frame_t* function_frame; + VariableValue* top; + bool debugCode; + bool abort_on_error; + bool terminal_error; + unsigned int inparamcount; + unsigned int outparamcount; + function_frame_t function_frame_start[32]; + VariableValue stack[2048]; }; enum UILocalVarType