From 935d44d4a34c22e3e59c8bb8faef12b73665253a Mon Sep 17 00:00:00 2001 From: fed <58637860+fedddddd@users.noreply.github.com> Date: Fri, 11 Nov 2022 21:15:26 +0100 Subject: [PATCH] Fix script leaks + increase memory --- src/client/component/gsc/script_error.cpp | 5 -- src/client/component/gsc/script_extension.cpp | 48 +++++----- src/client/component/gsc/script_loading.cpp | 90 ++++++++++++++----- src/client/component/party.cpp | 4 - src/client/game/structs.hpp | 22 +++++ src/client/game/symbols.hpp | 9 ++ 6 files changed, 123 insertions(+), 55 deletions(-) diff --git a/src/client/component/gsc/script_error.cpp b/src/client/component/gsc/script_error.cpp index 1c88774e..20af68f2 100644 --- a/src/client/component/gsc/script_error.cpp +++ b/src/client/component/gsc/script_error.cpp @@ -106,11 +106,6 @@ namespace gsc public: void post_unpack() override { - if (game::environment::is_sp()) - { - return; - } - scr_emit_function_hook.create(SELECT_VALUE(0x3BD680_b, 0x504660_b), &scr_emit_function_stub); utils::hook::call(SELECT_VALUE(0x3BD626_b, 0x504606_b), unknown_function_stub); // CompileError (LinkFile) diff --git a/src/client/component/gsc/script_extension.cpp b/src/client/component/gsc/script_extension.cpp index 0afe32dd..9afcfd0b 100644 --- a/src/client/component/gsc/script_extension.cpp +++ b/src/client/component/gsc/script_extension.cpp @@ -348,11 +348,6 @@ namespace gsc utils::hook::inject(SELECT_VALUE(0x3BDC36_b, 0x504C66_b) + 3, &meth_table); utils::hook::set(SELECT_VALUE(0x3BDC3F_b, 0x504C6F_b), sizeof(meth_table)); - if (game::environment::is_sp()) - { - return; - } - developer_script = dvars::register_bool("developer_script", false, 0, "Enable developer script comments"); utils::hook::nop(SELECT_VALUE(0x3CB723_b, 0x512783_b), 8); @@ -466,36 +461,39 @@ namespace gsc return scripting::script_value{}; }); - function::add("say", [](const function_args& args) - { - const auto message = args[0].as(); - game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data())); - - return scripting::script_value{}; - }); - function::add("typeof", typeof); function::add("type", typeof); - method::add("tell", [](const game::scr_entref_t ent, const function_args& args) + if (!game::environment::is_sp()) { - if (ent.classnum != 0) + function::add("say", [](const function_args& args) { - throw std::runtime_error("Invalid entity"); - } + const auto message = args[0].as(); + game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data())); - const auto client = ent.entnum; + return scripting::script_value{}; + }); - if (game::mp::g_entities[client].client == nullptr) + method::add("tell", [](const game::scr_entref_t ent, const function_args& args) { - throw std::runtime_error("Not a player entity"); - } + if (ent.classnum != 0) + { + throw std::runtime_error("Invalid entity"); + } - const auto message = args[0].as(); - game::SV_GameSendServerCommand(client, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data())); + const auto client = ent.entnum; - return scripting::script_value{}; - }); + if (game::mp::g_entities[client].client == nullptr) + { + throw std::runtime_error("Not a player entity"); + } + + const auto message = args[0].as(); + game::SV_GameSendServerCommand(client, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data())); + + return scripting::script_value{}; + }); + } } }; } diff --git a/src/client/component/gsc/script_loading.cpp b/src/client/component/gsc/script_loading.cpp index 510c4f03..8e1a00b2 100644 --- a/src/client/component/gsc/script_loading.cpp +++ b/src/client/component/gsc/script_loading.cpp @@ -39,13 +39,48 @@ namespace gsc std::unordered_map main_handles; std::unordered_map init_handles; + utils::memory::allocator scriptfile_allocator; std::unordered_map loaded_scripts; + struct + { + char* buf = nullptr; + char* pos = nullptr; + unsigned int size = 0x1000000; + } script_memory; + + char* allocate_buffer(size_t size) + { + if (script_memory.buf == nullptr) + { + script_memory.buf = game::PMem_AllocFromSource_NoDebug(script_memory.size, 4, 1, game::PMEM_SOURCE_SCRIPT); + script_memory.pos = script_memory.buf; + } + + if (script_memory.pos + size > script_memory.buf + script_memory.size) + { + game::Com_Error(game::ERR_FATAL, "Out of custom script memory"); + } + + const auto pos = script_memory.pos; + script_memory.pos += size; + return pos; + } + + void free_script_memory() + { + game::PMem_PopFromSource_NoDebug(script_memory.buf, script_memory.size, 4, 1, game::PMEM_SOURCE_SCRIPT); + script_memory.buf = nullptr; + script_memory.pos = nullptr; + } + void clear() { main_handles.clear(); init_handles.clear(); loaded_scripts.clear(); + scriptfile_allocator.clear(); + free_script_memory(); } bool read_script_file(const std::string& name, std::string* data) @@ -75,14 +110,13 @@ namespace gsc return false; } - char* allocate_buffer(size_t size) - { - // PMem_AllocFromSource_NoDebug - return utils::hook::invoke(SELECT_VALUE(0x41FB50_b, 0x5A4DC0_b), size, 4, 1, 5); - } - game::ScriptFile* load_custom_script(const char* file_name, const std::string& real_name) { + if (game::VirtualLobby_Loaded()) + { + return nullptr; + } + if (const auto itr = loaded_scripts.find(real_name); itr != loaded_scripts.end()) { return itr->second; @@ -134,7 +168,7 @@ namespace gsc return nullptr; } - const auto script_file_ptr = reinterpret_cast(allocate_buffer(sizeof(game::ScriptFile))); + const auto script_file_ptr = scriptfile_allocator.allocate(); script_file_ptr->name = file_name; const auto stack = assembler->output_stack(); @@ -143,15 +177,12 @@ namespace gsc const auto script = assembler->output_script(); script_file_ptr->bytecodeLen = static_cast(script.size()); - const auto script_size = script.size(); - const auto buffer_size = script_size + stack.size() + 2; + script_file_ptr->buffer = game::Hunk_AllocateTempMemoryHigh(stack.size() + 1); + std::memcpy(script_file_ptr->buffer, stack.data(), stack.size()); - const auto buffer = allocate_buffer(buffer_size); - std::memcpy(buffer, script.data(), script_size); - std::memcpy(&buffer[script_size], stack.data(), stack.size()); + script_file_ptr->bytecode = allocate_buffer(script.size() + 1); + std::memcpy(script_file_ptr->bytecode, script.data(), script.size()); - script_file_ptr->bytecode = &buffer[0]; - script_file_ptr->buffer = reinterpret_cast(&buffer[script.size()]); script_file_ptr->compressedLen = 0; loaded_scripts[real_name] = script_file_ptr; @@ -254,8 +285,6 @@ namespace gsc { utils::hook::invoke(SELECT_VALUE(0x2B9DA0_b, 0x18BC00_b), a1, a2); - clear(); - if (game::VirtualLobby_Loaded()) { return; @@ -286,6 +315,27 @@ namespace gsc utils::hook::invoke(SELECT_VALUE(0x1F1E00_b, 0x396080_b), rawfile, buf, size); } + + void pmem_init_stub() + { + utils::hook::invoke(SELECT_VALUE(0x420260_b, 0x5A5590_b)); + + const auto type_0 = &game::g_scriptmem[0]; + const auto type_1 = &game::g_scriptmem[1]; + + const auto size_0 = 0x100000; // default size + const auto size_1 = 0x100000 + script_memory.size; + + const auto block = reinterpret_cast(VirtualAlloc(NULL, size_0 + size_1, MEM_RESERVE, PAGE_READWRITE)); + + type_0->buf = block; + type_0->size = size_0; + + type_1->buf = block + size_0; + type_1->size = size_1; + + utils::hook::set(SELECT_VALUE(0x420252_b, 0x5A5582_b), size_0 + size_1); + } } void load_main_handles() @@ -329,11 +379,6 @@ namespace gsc public: void post_unpack() override { - if (game::environment::is_sp()) - { - return; - } - // allow custom scripts to include other custom scripts xsk::gsc::h1::resolver::init([](const auto& include_name) { @@ -369,6 +414,9 @@ namespace gsc // loads scripts with an uncompressed stack utils::hook::call(SELECT_VALUE(0x3C7280_b, 0x50E3C0_b), db_get_raw_buffer_stub); + // Increase script memory + utils::hook::call(SELECT_VALUE(0x38639C_b, 0x15C4D6_b), pmem_init_stub); + scripting::on_shutdown([](int free_scripts) { if (free_scripts) diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 4fd26ad5..cb033e50 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -343,8 +343,6 @@ namespace party const auto path = get_usermap_file_path(mapname, ext); const auto hash = get_file_hash(path); - console::debug("%s %s %s != %s\n", ext.data(), key.data(), hash.data(), source_hash.data()); - if ((!source_hash.empty() && hash != source_hash) || (source_hash.empty() && !opt)) { command::execute("disconnect"); @@ -423,8 +421,6 @@ namespace party } } - console::debug("%i %s\n", is_usermap, buffer.data()); - net_out_of_band_print_hook.invoke(sock, addr, buffer.data()); } } diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 0d75e633..66bf0eaa 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1599,6 +1599,28 @@ namespace game int ingame_cursor_visible; }; + enum PMem_Source + { + PMEM_SOURCE_EXTERNAL = 0x0, + PMEM_SOURCE_DATABASE = 0x1, + PMEM_SOURCE_DEFAULT_LOW = 0x2, + PMEM_SOURCE_DEFAULT_HIGH = 0x3, + PMEM_SOURCE_MOVIE = 0x4, + PMEM_SOURCE_SCRIPT = 0x5, + }; + + struct physical_memory + { + char __pad0[0x10]; + char* buf; + char __pad1[0x8]; + int unk1; + size_t size; + char __pad2[0x500]; + }; + + static_assert(sizeof(physical_memory) == 0x530); + namespace mp { struct cachedSnapshot_t diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index d7cf48fc..8d48b504 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -117,6 +117,8 @@ namespace game WEAK symbol GameInfo_ParseArenas{0x0, 0x4DE0B0}; + WEAK symbol Hunk_AllocateTempMemoryHigh{0x415DB0, 0x59DEC0}; + WEAK symbol I_CleanStr{0x4293E0, 0x5AF2E0}; WEAK symbol Key_KeynumToString{0x1AC410, 0x199990}; @@ -155,6 +157,11 @@ namespace game WEAK symbol Image_Setup{0x560740, 0x683890}; + WEAK symbol PMem_AllocFromSource_NoDebug{0x41FB50, 0x5A4DC0}; + WEAK symbol PMem_PopFromSource_NoDebug{0x4203D0, 0x5A5700}; + WEAK symbol VM_Execute{0x3C9E50, 0x510EB0}; @@ -299,6 +306,8 @@ namespace game WEAK symbol scr_VmPub{0xC3F4E20, 0xB7AE3C0}; WEAK symbol scr_function_stack{0xC4015C0, 0xB7B8940}; + WEAK symbol g_scriptmem{0xD5F3140, 0xC92EC40}; + WEAK symbol gfxDrawMethod{0xF7530B0, 0xE9213F0}; WEAK symbol dvarCount{0xC90E550, 0x2999C34};