From 508cc5d51e31e9831dcea06495f25310023b7e3a Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 13 Jan 2019 19:03:46 +0100 Subject: [PATCH] Prepare scripting environment --- src/game/game.cpp | 57 ++++++++++++++++++- src/game/game.hpp | 17 +++++- src/game/structs.hpp | 51 +++++++++++++++++ src/module/notification.cpp | 37 +++++++++++++ src/module/notification.hpp | 20 +++++++ src/module/scripting.cpp | 107 ++++++++++++++++++++++++++++++------ src/module/scripting.hpp | 39 +++++++++++++ src/utils/hook.cpp | 5 ++ src/utils/hook.hpp | 1 + 9 files changed, 316 insertions(+), 18 deletions(-) create mode 100644 src/module/notification.cpp create mode 100644 src/module/notification.hpp create mode 100644 src/module/scripting.hpp diff --git a/src/game/game.cpp b/src/game/game.cpp index f119dfc..9dccfa7 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -13,13 +13,61 @@ namespace game MSG_ReadData_t MSG_ReadData; + RemoveRefToValue_t RemoveRefToValue; + Sys_ShowConsole_t Sys_ShowConsole; + VM_Notify_t VM_Notify; + int* cmd_args; int* cmd_argc; const char*** cmd_argv; - } + short* scrVarGlob; + char** scrMemTreePub; + + void AddRefToValue(VariableValue* value) + { + if (value->type == SCRIPT_OBJECT) + { + ++scrVarGlob[4 * value->u.entityId]; + } + else if (value->type == SCRIPT_STRING) + { + static const auto size = is_sp() ? 16 : 12; + const auto ref_count = reinterpret_cast(*scrMemTreePub + size * value + ->u.stringValue); + InterlockedIncrement(ref_count); + } + else if (value->type == SCRIPT_VECTOR) + { + if (!*PBYTE(value->u.vectorValue - 1)) + { + ++*PWORD(value->u.vectorValue - 4); + } + } + } + + scr_entref_t Scr_GetEntityIdRef(const unsigned int id) + { + static auto class_array = reinterpret_cast(SELECT_VALUE(0x19AFC84, 0x1E72184, 0x1D3C804)); + static auto ent_array = reinterpret_cast(SELECT_VALUE(0x19AFC82, 0x1E72182, 0x1D3C802)); + + scr_entref_t result{}; + result.raw.classnum = static_cast(class_array[2 * id]) >> 8; + result.raw.entnum = ent_array[4 * id]; + + return result; + } + + const char* SL_ConvertToString(unsigned int stringValue) + { + if (!stringValue) return nullptr; + + static const auto size = is_sp() ? 16 : 12; + return *scrMemTreePub + size * stringValue + 4; + } + } launcher::mode mode = launcher::mode::none; @@ -50,10 +98,17 @@ namespace game native::MSG_ReadData = native::MSG_ReadData_t(SELECT_VALUE(0, 0x5592A0, 0)); + native::RemoveRefToValue = native::RemoveRefToValue_t(SELECT_VALUE(0x477EA0, 0x565730, 0x4E8A40)); + native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0)); + native::VM_Notify = native::VM_Notify_t(SELECT_VALUE(0x610200, 0x569720, 0x4EF450)); + native::cmd_args = reinterpret_cast(SELECT_VALUE(0x1750750, 0x1C978D0, 0x1B455F8)); native::cmd_argc = reinterpret_cast(SELECT_VALUE(0x1750794, 0x1C97914, 0x1B4563C)); native::cmd_argv = reinterpret_cast(SELECT_VALUE(0x17507B4, 0x1C97934, 0x1B4565C)); + + native::scrVarGlob = reinterpret_cast(SELECT_VALUE(0x19AFC80, 0x1E72180, 0x1D3C800)); + native::scrMemTreePub = reinterpret_cast(SELECT_VALUE(0x196FB00, 0x1E32000, 0x1C152A4)); } } diff --git a/src/game/game.hpp b/src/game/game.hpp index 74f3010..4b67fc2 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -18,15 +18,30 @@ namespace game typedef void (*DB_LoadXAssets_t)(XZoneInfo* zoneInfo, unsigned int zoneCount, int sync); extern DB_LoadXAssets_t DB_LoadXAssets; - typedef void(*MSG_ReadData_t)(msg_t *msg, void *data, int len); + typedef void (*MSG_ReadData_t)(msg_t* msg, void* data, int len); extern MSG_ReadData_t MSG_ReadData; + typedef void (*RemoveRefToValue_t)(scriptType_e type, VariableUnion u); + extern RemoveRefToValue_t RemoveRefToValue; + typedef void (*Sys_ShowConsole_t)(); extern Sys_ShowConsole_t Sys_ShowConsole; + typedef void (*VM_Notify_t)(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top); + extern VM_Notify_t VM_Notify; + extern int* cmd_args; extern int* cmd_argc; extern const char*** cmd_argv; + + extern short* scrVarGlob; + extern char** scrMemTreePub; + + void AddRefToValue(VariableValue* value); + + scr_entref_t Scr_GetEntityIdRef(unsigned int id); + + const char* SL_ConvertToString(unsigned int stringValue); } bool is_mp(); diff --git a/src/game/structs.hpp b/src/game/structs.hpp index 4beee2c..fd6df35 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -399,5 +399,56 @@ namespace game int allocFlags; int freeFlags; }; + + struct scr_entref_raw + { + unsigned __int16 entnum; + unsigned __int16 classnum; + }; + + union scr_entref_t + { + unsigned int val; + scr_entref_raw raw; + }; + + enum scriptType_e + { + SCRIPT_NONE = 0, + SCRIPT_OBJECT = 1, + SCRIPT_STRING = 2, + SCRIPT_VECTOR = 4, + SCRIPT_FLOAT = 5, + SCRIPT_INTEGER = 6, + SCRIPT_END = 8, // Custom + }; + + struct VariableStackBuffer + { + const char* pos; + unsigned __int16 size; + unsigned __int16 bufLen; + unsigned __int16 localId; + char time; + char buf[1]; + }; + + union VariableUnion + { + int intValue; + float floatValue; + unsigned int stringValue; + const float* vectorValue; + const char* codePosValue; + unsigned int pointerValue; + VariableStackBuffer* stackValue; + unsigned int entityId; + }; + + struct VariableValue + { + VariableUnion u; + scriptType_e type; + }; } } diff --git a/src/module/notification.cpp b/src/module/notification.cpp new file mode 100644 index 0000000..1ffb4d9 --- /dev/null +++ b/src/module/notification.cpp @@ -0,0 +1,37 @@ +#include +#include "loader/module_loader.hpp" +#include "notification.hpp" +#include "utils/hook.hpp" + +void notification::post_load() +{ + utils::hook(SELECT_VALUE(0x6109F3, 0x56B637, 0x4EDFF7), vm_notify_stub, HOOK_CALL).install()->quick(); + utils::hook(SELECT_VALUE(0x6128BE, 0x56D541, 0x4EFAF9), vm_notify_stub, HOOK_CALL).install()->quick(); + + if (game::is_sp()) + { + utils::hook(0x610970, vm_notify_stub, HOOK_JUMP).install()->quick(); + } +} + +void notification::vm_notify_stub(const unsigned int notify_id, const unsigned short type, + game::native::VariableValue* stack) +{ + event e; + e.name = game::native::SL_ConvertToString(type); + e.entity = game::native::Scr_GetEntityIdRef(notify_id); + + for (auto value = stack; value->type != game::native::SCRIPT_END; --value) + { + e.arguments.emplace_back(*value); + } + + if(!e.arguments.empty()) + { + printf(""); + } + + game::native::VM_Notify(notify_id, type, stack); +} + +REGISTER_MODULE(notification) diff --git a/src/module/notification.hpp b/src/module/notification.hpp new file mode 100644 index 0000000..df0e806 --- /dev/null +++ b/src/module/notification.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "loader/module_loader.hpp" +#include "scripting.hpp" + +class notification final : public module +{ +public: + class event final + { + public: + std::string name; + game::native::scr_entref_t entity; + std::vector arguments; + }; + + void post_load() override; + +private: + static void vm_notify_stub(unsigned int notify_id, unsigned short type, game::native::VariableValue* stack); +}; diff --git a/src/module/scripting.cpp b/src/module/scripting.cpp index 7b0d5b3..252313e 100644 --- a/src/module/scripting.cpp +++ b/src/module/scripting.cpp @@ -1,26 +1,101 @@ #include -#include "loader/module_loader.hpp" -#include "game/game.hpp" +#include "scripting.hpp" -class scripting final : public module +utils::hook scripting::start_hook_; +utils::hook scripting::stop_hook_; + +std::mutex scripting::mutex_; +std::vector> scripting::start_callbacks_; +std::vector> scripting::stop_callbacks_; + +scripting::variable::variable(game::native::VariableValue value) : value_(value) { -public: - void post_load() override + game::native::AddRefToValue(&value); +} + +scripting::variable::~variable() +{ + game::native::RemoveRefToValue(this->value_.type, this->value_.u); +} + +scripting::variable::operator game::native::VariableValue() const +{ + return this->value_; +} + +void scripting::post_load() +{ + start_hook_.initialize(SELECT_VALUE(0x50C575, 0x50D4F2, 0x48A026), []() { - this->chai_.add(chaiscript::fun(&function), "function"); - - double d = this->chai_.eval("function(3, 4.75);"); - printf("Result: %f", d); + start_execution(); + static_cast(start_hook_.get_original())(); + }, HOOK_CALL)->install()->quick(); + + stop_hook_.initialize(SELECT_VALUE(0x528B04, 0x569E46, 0x4F03FA), []() + { + stop_execution(); + static_cast(stop_hook_.get_original())(); + }, HOOK_CALL)->install()->quick(); + + on_start([this]() + { + this->chai_ = std::make_unique(); + }); + + on_stop([this]() + { + this->chai_ = {}; + }); +} + +void scripting::pre_destroy() +{ + this->chai_ = {}; + start_callbacks_.clear(); + stop_callbacks_.clear(); +} + +void scripting::on_start(const std::function& callback) +{ + std::lock_guard _(mutex_); + start_callbacks_.push_back(callback); +} + +void scripting::on_stop(const std::function& callback) +{ + std::lock_guard _(mutex_); + stop_callbacks_.push_back(callback); +} + +void scripting::start_execution() +{ + std::vector> copy; + + { + std::lock_guard _(mutex_); + copy = start_callbacks_; } -private: - chaiscript::ChaiScript chai_; + for (const auto& callback : copy) + { + callback(); + } +} - static double function(int i, double j) - { - return i * j; - } +void scripting::stop_execution() +{ + std::vector> copy; + + { + std::lock_guard _(mutex_); + copy = stop_callbacks_; + } + + for (const auto& callback : copy) + { + callback(); + } +} -}; REGISTER_MODULE(scripting) diff --git a/src/module/scripting.hpp b/src/module/scripting.hpp new file mode 100644 index 0000000..df413ca --- /dev/null +++ b/src/module/scripting.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "loader/module_loader.hpp" +#include "game/game.hpp" +#include "utils/hook.hpp" + +class scripting final : public module +{ +public: + class variable final + { + public: + variable(game::native::VariableValue value); + ~variable(); + + operator game::native::VariableValue() const; + + private: + game::native::VariableValue value_; + }; + + void post_load() override; + void pre_destroy() override; + + static void on_start(const std::function& callback); + static void on_stop(const std::function& callback); + +private: + std::unique_ptr chai_; + + static utils::hook start_hook_; + static utils::hook stop_hook_; + + static std::mutex mutex_; + static std::vector> start_callbacks_; + static std::vector> stop_callbacks_; + + static void start_execution(); + static void stop_execution(); +}; diff --git a/src/utils/hook.cpp b/src/utils/hook.cpp index 50c52f2..5149852 100644 --- a/src/utils/hook.cpp +++ b/src/utils/hook.cpp @@ -156,6 +156,11 @@ namespace utils return this->place_; } + void* hook::get_original() const + { + return this->original_; + } + void hook::nop(void* place, const size_t length) { DWORD old_protect; diff --git a/src/utils/hook.hpp b/src/utils/hook.hpp index 448b9b4..f044f76 100644 --- a/src/utils/hook.hpp +++ b/src/utils/hook.hpp @@ -80,6 +80,7 @@ namespace utils hook* uninstall(bool unprotect = true); void* get_address() const; + void* get_original() const; void quick(); static bool iat(nt::module module, const std::string& target_module, const std::string& process, void* stub);