diff --git a/.gitmodules b/.gitmodules index a6a9bd10..615d9285 100644 --- a/.gitmodules +++ b/.gitmodules @@ -50,7 +50,7 @@ [submodule "deps/gsc-tool"] path = deps/gsc-tool url = https://github.com/xensik/gsc-tool.git - branch = xlabs + branch = dev [submodule "deps/stb"] path = deps/stb url = https://github.com/nothings/stb.git diff --git a/deps/extra/gsc-tool/interface.cpp b/deps/extra/gsc-tool/interface.cpp deleted file mode 100644 index 28b7ad73..00000000 --- a/deps/extra/gsc-tool/interface.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "stdafx.hpp" - -#include - -#include "interface.hpp" - -namespace gsc -{ - std::unique_ptr compiler() - { - auto compiler = std::make_unique(); - compiler->mode(xsk::gsc::build::prod); - return compiler; - } - - std::unique_ptr decompiler() - { - return std::make_unique(); - } - - std::unique_ptr assembler() - { - return std::make_unique(); - } - - std::unique_ptr disassembler() - { - return std::make_unique(); - } -} diff --git a/deps/extra/gsc-tool/interface.hpp b/deps/extra/gsc-tool/interface.hpp deleted file mode 100644 index 133e6ae2..00000000 --- a/deps/extra/gsc-tool/interface.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -namespace gsc -{ - std::unique_ptr compiler(); - std::unique_ptr decompiler(); - std::unique_ptr assembler(); - std::unique_ptr disassembler(); -} diff --git a/deps/gsc-tool b/deps/gsc-tool index 7d374025..e05d853b 160000 --- a/deps/gsc-tool +++ b/deps/gsc-tool @@ -1 +1 @@ -Subproject commit 7d374025b7675bada64c247ebe9378dd335a33da +Subproject commit e05d853ba6448212166b18847b430768d99d39e3 diff --git a/deps/premake/gsc-tool.lua b/deps/premake/gsc-tool.lua index 08da07de..87cc40c5 100644 --- a/deps/premake/gsc-tool.lua +++ b/deps/premake/gsc-tool.lua @@ -1,5 +1,5 @@ gsc_tool = { - source = path.join(dependencies.basePath, "gsc-tool/src") + source = path.join(dependencies.basePath, "gsc-tool") } function gsc_tool.import() @@ -9,60 +9,54 @@ end function gsc_tool.includes() includedirs { - path.join(gsc_tool.source, "utils"), - path.join(gsc_tool.source, "h1"), - path.join(dependencies.basePath, "extra/gsc-tool") -- https://github.com/GEEKiDoS/open-teknomw3/blob/master/deps/extra/gsc-tool + path.join(gsc_tool.source, "include") } end --- https://github.com/xensik/gsc-tool/blob/dev/premake5.lua#L95 function gsc_tool.project() project "xsk-gsc-utils" - kind "StaticLib" - language "C++" + kind "StaticLib" + language "C++" + warnings "Off" - pchheader "stdafx.hpp" - pchsource(path.join(gsc_tool.source, "utils/stdafx.cpp")) + files { + path.join(gsc_tool.source, "include/xsk/utils/*.hpp"), + path.join(gsc_tool.source, "src/utils/*.cpp") + } - files { - path.join(gsc_tool.source, "utils/**.h"), - path.join(gsc_tool.source, "utils/**.hpp"), - path.join(gsc_tool.source, "utils/**.cpp") - } + includedirs { + path.join(gsc_tool.source, "include") + } - includedirs { - path.join(gsc_tool.source, "utils"), - gsc_tool.source - } - - zlib.includes() + zlib.includes() project "xsk-gsc-h1" - kind "StaticLib" - language "C++" + kind "StaticLib" + language "C++" + warnings "Off" - pchheader "stdafx.hpp" - pchsource(path.join(gsc_tool.source, "h1/stdafx.cpp")) - - files { - path.join(gsc_tool.source, "h1/**.h"), - path.join(gsc_tool.source, "h1/**.hpp"), - path.join(gsc_tool.source, "h1/**.cpp"), - path.join(dependencies.basePath, "extra/gsc-tool/interface.cpp") - } - - includedirs { - path.join(gsc_tool.source, "h1"), - gsc_tool.source, - path.join(dependencies.basePath, "extra/gsc-tool") - } - - -- https://github.com/xensik/gsc-tool/blob/dev/premake5.lua#L25 - -- adding these build options fixes a bunch of parser stuff filter "action:vs*" - buildoptions "/bigobj" buildoptions "/Zc:__cplusplus" filter {} + + files { + path.join(gsc_tool.source, "include/xsk/stdinc.hpp"), + + path.join(gsc_tool.source, "include/xsk/gsc/engine/h1.hpp"), + path.join(gsc_tool.source, "src/gsc/engine/h1.cpp"), + + path.join(gsc_tool.source, "src/gsc/engine/h1_code.cpp"), + path.join(gsc_tool.source, "src/gsc/engine/h1_func.cpp"), + path.join(gsc_tool.source, "src/gsc/engine/h1_meth.cpp"), + path.join(gsc_tool.source, "src/gsc/engine/h1_token.cpp"), path.join(gsc_tool.source, "src/gsc/*.cpp"), + + path.join(gsc_tool.source, "src/gsc/common/*.cpp"), + path.join(gsc_tool.source, "include/xsk/gsc/common/*.hpp") + } + + includedirs { + path.join(gsc_tool.source, "include") + } end table.insert(dependencies, gsc_tool) diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index db87cf78..af429d91 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -28,7 +28,7 @@ namespace fastfiles game::dvar_t* g_dump_scripts; game::dvar_t* db_print_default_assets; - std::vector fastfile_handles; + utils::concurrency::container> fastfile_handles; bool is_mod_pre_gfx = false; void db_try_load_x_file_internal(const char* zone_name, const int flags) @@ -226,7 +226,10 @@ namespace fastfiles FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr); if (handle != INVALID_HANDLE_VALUE) { - fastfile_handles.push_back(handle); + fastfile_handles.access([&](std::vector& handles) + { + handles.push_back(handle); + }); } return handle; @@ -1035,7 +1038,7 @@ namespace fastfiles void reallocate_asset_pools() { - reallocate_attachment_and_weapon(); + //reallocate_attachment_and_weapon(); reallocate_asset_pool_multiplier(); reallocate_asset_pool_multiplier(); reallocate_asset_pool_multiplier(); @@ -1105,10 +1108,14 @@ namespace fastfiles void close_fastfile_handles() { - for (const auto& handle : fastfile_handles) + fastfile_handles.access([&](std::vector& handles) { - CloseHandle(handle); - } + for (const auto& handle : handles) + { + CloseHandle(handle); + } + }); + } void set_usermap(const std::string& usermap) diff --git a/src/client/component/gsc/script_error.cpp b/src/client/component/gsc/script_error.cpp index 15b71d72..4062f845 100644 --- a/src/client/component/gsc/script_error.cpp +++ b/src/client/component/gsc/script_error.cpp @@ -2,11 +2,15 @@ #include "loader/component_loader.hpp" #include "game/game.hpp" +#include "script_extension.hpp" #include "script_error.hpp" #include "component/scripting.hpp" #include +#include + +using namespace utils::string; namespace gsc { @@ -18,6 +22,37 @@ namespace gsc std::string unknown_function_error; + std::array var_typename = + { + "undefined", + "object", + "string", + "localized string", + "vector", + "float", + "int", + "codepos", + "precodepos", + "function", + "builtin function", + "builtin method", + "stack", + "animation", + "pre animation", + "thread", + "thread", + "thread", + "thread", + "struct", + "removed entity", + "entity", + "array", + "removed thread", + "", + "thread list", + "endon list", + }; + void scr_emit_function_stub(std::uint32_t filename, std::uint32_t thread_name, char* code_pos) { current_filename = filename; @@ -43,8 +78,7 @@ namespace gsc { const auto& pos = function.value(); unknown_function_error = std::format( - "while processing function '{}' in script '{}':\nunknown script '{}'", - pos.first, pos.second, scripting::current_file + "while processing function '{}' in script '{}':\nunknown script '{}'", pos.first, pos.second, scripting::current_file ); } else @@ -59,30 +93,192 @@ namespace gsc const auto name = scripting::get_token(thread_name); unknown_function_error = std::format( - "while processing script '{}':\nunknown function '{}::{}'", - scripting::current_file, filename, name + "while processing script '{}':\nunknown function '{}::{}'", scripting::current_file, filename, name ); } - void unknown_function_stub(const char* code_pos) + void compile_error_stub(const char* code_pos, [[maybe_unused]] const char* msg) { get_unknown_function_error(code_pos); - game::Com_Error(game::ERR_DROP, "script link error\n%s", - unknown_function_error.data()); + game::Com_Error(game::ERR_SCRIPT_DROP, "script link error\n%s", unknown_function_error.data()); } - + std::uint32_t find_variable_stub(std::uint32_t parent_id, std::uint32_t thread_name) { const auto res = game::FindVariable(parent_id, thread_name); if (!res) { get_unknown_function_error(thread_name); - game::Com_Error(game::ERR_DROP, "script link error\n%s", - unknown_function_error.data()); + game::Com_Error(game::ERR_SCRIPT_DROP, "script link error\n%s", unknown_function_error.data()); } return res; } + unsigned int scr_get_object(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + auto* value = game::scr_VmPub->top - index; + if (value->type == game::VAR_POINTER) + { + return value->u.pointerValue; + } + + scr_error(va("Type %s is not an object", var_typename[value->type])); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0; + } + + unsigned int scr_get_const_string(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + auto* value = game::scr_VmPub->top - index; + if (game::Scr_CastString(value)) + { + assert(value->type == game::VAR_STRING); + return value->u.stringValue; + } + + game::Scr_ErrorInternal(); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0; + } + + unsigned int scr_get_const_istring(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + auto* value = game::scr_VmPub->top - index; + if (value->type == game::VAR_ISTRING) + { + return value->u.stringValue; + } + + scr_error(va("Type %s is not a localized string", var_typename[value->type])); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0; + } + + void scr_validate_localized_string_ref(int parm_index, const char* token, int token_len) + { + assert(token); + assert(token_len >= 0); + + if (token_len < 2) + { + return; + } + + for (auto char_iter = 0; char_iter < token_len; ++char_iter) + { + if (!std::isalnum(static_cast(token[char_iter])) && token[char_iter] != '_') + { + scr_error(va("Illegal localized string reference: %s must contain only alpha-numeric characters and underscores", token)); + } + } + } + + void scr_get_vector(unsigned int index, float* vector_value) + { + if (index < game::scr_VmPub->outparamcount) + { + auto* value = game::scr_VmPub->top - index; + if (value->type == game::VAR_VECTOR) + { + std::memcpy(vector_value, value->u.vectorValue, sizeof(std::float_t[3])); + return; + } + + scr_error(va("Type %s is not a vector", var_typename[value->type])); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + } + + int scr_get_int(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + auto* value = game::scr_VmPub->top - index; + if (value->type == game::VAR_INTEGER) + { + return value->u.intValue; + } + + scr_error(va("Type %s is not an int", var_typename[value->type])); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0; + } + + float scr_get_float(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + auto* value = game::scr_VmPub->top - index; + if (value->type == game::VAR_FLOAT) + { + return value->u.floatValue; + } + + if (value->type == game::VAR_INTEGER) + { + return static_cast(value->u.intValue); + } + + scr_error(va("Type %s is not a float", var_typename[value->type])); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0.0f; + } + + int scr_get_pointer_type(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + if ((game::scr_VmPub->top - index)->type == game::VAR_POINTER) + { + return static_cast(game::GetObjectType((game::scr_VmPub->top - index)->u.uintValue)); + } + + scr_error(va("Type %s is not an object", var_typename[(game::scr_VmPub->top - index)->type])); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0; + } + + int scr_get_type(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + return (game::scr_VmPub->top - index)->type; + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0; + } + + const char* scr_get_type_name(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + return var_typename[(game::scr_VmPub->top - index)->type]; + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return nullptr; + } + template void safe_func() { @@ -128,11 +324,27 @@ namespace gsc { 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) - utils::hook::call(SELECT_VALUE(0x3BD672_b, 0x504652_b), unknown_function_stub); // ^ - utils::hook::call(SELECT_VALUE(0x3BD75A_b, 0x50473A_b), find_variable_stub); // Scr_EmitFunction + utils::hook::call(SELECT_VALUE(0x3BD626_b, 0x504606_b), compile_error_stub); // CompileError (LinkFile) + utils::hook::call(SELECT_VALUE(0x3BD672_b, 0x504652_b), compile_error_stub); // ^ + utils::hook::call(SELECT_VALUE(0x3BD75A_b, 0x50473A_b), find_variable_stub); // Scr_EmitFunction - safe_func<0xBA7A0>(); // fix vlobby cac crash + // Restore basic error messages for commonly used scr functions + utils::hook::jump(SELECT_VALUE(0x3C89F0_b, 0x50F9E0_b), scr_get_object); + utils::hook::jump(SELECT_VALUE(0x3C84C0_b, 0x50F560_b), scr_get_const_string); + utils::hook::jump(SELECT_VALUE(0x3C8280_b, 0x50F320_b), scr_get_const_istring); + utils::hook::jump(SELECT_VALUE(0x2D6950_b, 0x452EF0_b), scr_validate_localized_string_ref); + utils::hook::jump(SELECT_VALUE(0x3C8F30_b, 0x50FF20_b), scr_get_vector); + utils::hook::jump(SELECT_VALUE(0x3C8930_b, 0x50F920_b), scr_get_int); + utils::hook::jump(SELECT_VALUE(0x3C87D0_b, 0x50F870_b), scr_get_float); + + utils::hook::jump(SELECT_VALUE(0x3C8C10_b, 0x50FC00_b), scr_get_pointer_type); + utils::hook::jump(SELECT_VALUE(0x3C8DE0_b, 0x50FDD0_b), scr_get_type); + utils::hook::jump(SELECT_VALUE(0x3C8E50_b, 0x50FE40_b), scr_get_type_name); + + if (!game::environment::is_sp()) + { + safe_func<0xBA7A0>(); // fix vlobby cac crash + } } void pre_destroy() override diff --git a/src/client/component/gsc/script_error.hpp b/src/client/component/gsc/script_error.hpp index e8742026..1cee00d2 100644 --- a/src/client/component/gsc/script_error.hpp +++ b/src/client/component/gsc/script_error.hpp @@ -1,4 +1,3 @@ - #pragma once namespace gsc diff --git a/src/client/component/gsc/script_extension.cpp b/src/client/component/gsc/script_extension.cpp index fb736884..afe0e679 100644 --- a/src/client/component/gsc/script_extension.cpp +++ b/src/client/component/gsc/script_extension.cpp @@ -3,23 +3,17 @@ #include "game/dvars.hpp" #include "game/game.hpp" -#include "game/scripting/execution.hpp" -#include "game/scripting/function.hpp" -#include "game/scripting/functions.hpp" -#include "game/scripting/lua/error.hpp" - -#include +#include "component/logfile.hpp" #include "component/command.hpp" #include "component/console.hpp" #include "component/scripting.hpp" -#include "component/logfile.hpp" -#include -#include - -#include "script_extension.hpp" #include "script_error.hpp" +#include "script_extension.hpp" +#include "script_loading.hpp" + +#include namespace gsc { @@ -33,8 +27,8 @@ namespace gsc namespace { - std::unordered_map functions; - std::unordered_map methods; + std::unordered_map functions; + std::unordered_map methods; bool force_error_print = false; std::optional gsc_error_msg; @@ -70,10 +64,15 @@ namespace gsc reinterpret_cast(pos - 2)); } + game::scr_entref_t get_entity_id_stub(std::uint32_t ent_id) + { + const auto ref = game::Scr_GetEntityIdRef(ent_id); + saved_ent_ref = ref; + return ref; + } + void execute_custom_function(const std::uint16_t id) { - auto error = false; - try { const auto& function = functions[id]; @@ -85,23 +84,33 @@ namespace gsc return_value(result); } } - catch (const std::exception& e) + catch (const std::exception& ex) { - error = true; - force_error_print = true; - gsc_error_msg = e.what(); + scr_error(ex.what()); + } + } + + void vm_call_builtin_function_stub(builtin_function func) + { + const auto function_id = get_function_id(); + const auto custom = functions.contains(static_cast(function_id)); + if (custom) + { + execute_custom_function(function_id); + return; } - if (error) + if (func == nullptr) { - game::Scr_ErrorInternal(); + scr_error("function doesn't exist"); + return; } + + func(); } void execute_custom_method(const std::uint16_t id) { - auto error = false; - try { const auto& method = methods[id]; @@ -113,70 +122,29 @@ namespace gsc return_value(result); } } - catch (const std::exception& e) + catch (const std::exception& ex) { - error = true; - force_error_print = true; - gsc_error_msg = e.what(); - } - - if (error) - { - game::Scr_ErrorInternal(); + scr_error(ex.what()); } } - void vm_call_builtin_function_stub(builtin_function function) + void vm_call_builtin_method_stub(builtin_method meth) { - const auto function_id = get_function_id(); - - if (!functions.contains(function_id)) + const auto method_id = get_function_id(); + const auto custom = methods.contains(static_cast(method_id)); + if (custom) { - if (function == nullptr) - { - force_error_print = true; - gsc_error_msg = "function doesn't exist"; - game::Scr_ErrorInternal(); - } - else - { - function(); - } + execute_custom_method(method_id); + return; } - else - { - execute_custom_function(function_id); - } - } - game::scr_entref_t get_entity_id_stub(std::uint32_t ent_id) - { - const auto ref = game::Scr_GetEntityIdRef(ent_id); - saved_ent_ref = ref; - return ref; - } - - void vm_call_builtin_method_stub(builtin_method method) - { - const auto function_id = get_function_id(); - - if (!methods.contains(function_id)) + if (meth == nullptr) { - if (method == nullptr) - { - force_error_print = true; - gsc_error_msg = "method doesn't exist"; - game::Scr_ErrorInternal(); - } - else - { - method(saved_ent_ref); - } - } - else - { - execute_custom_method(function_id); + scr_error("function doesn't exist"); + return; } + + meth(saved_ent_ref); } void builtin_call_error(const std::string& error) @@ -185,13 +153,11 @@ namespace gsc if (function_id > 0x1000) { - console::warn("in call to builtin method \"%s\"%s", - xsk::gsc::h1::resolver::method_name(function_id).data(), error.data()); + console::warn("in call to builtin method \"%s\"%s", gsc_ctx->meth_name(function_id).data(), error.data()); } else { - console::warn("in call to builtin function \"%s\"%s", - xsk::gsc::h1::resolver::function_name(function_id).data(), error.data()); + console::warn("in call to builtin function \"%s\"%s", gsc_ctx->func_name(function_id).data(), error.data()); } } @@ -199,7 +165,8 @@ namespace gsc { try { - return {xsk::gsc::h1::resolver::opcode_name(opcode)}; + const auto index = gsc_ctx->opcode_enum(opcode); + return { gsc_ctx->opcode_name(index) }; } catch (...) { @@ -227,7 +194,8 @@ namespace gsc void vm_error_stub(int mark_pos) { - if (!developer_script->current.enabled && !force_error_print) + const bool dev_script = developer_script ? developer_script->current.enabled : false; + if (!dev_script && !force_error_print) { utils::hook::invoke(SELECT_VALUE(0x415C90_b, 0x59DDA0_b), mark_pos); return; @@ -284,19 +252,27 @@ namespace gsc } } + void scr_error(const char* error) + { + force_error_print = true; + gsc_error_msg = error; + + game::Scr_ErrorInternal(); + } + namespace function { void add(const std::string& name, script_function function) { - if (xsk::gsc::h1::resolver::find_function(name)) + if (gsc_ctx->func_exists(name)) { - const auto id = xsk::gsc::h1::resolver::function_id(name); + const auto id = gsc_ctx->func_id(name); functions[id] = function; } else { const auto id = ++function_id_start; - xsk::gsc::h1::resolver::add_function(name, static_cast(id)); + gsc_ctx->func_add(name, static_cast(id)); functions[id] = function; } } @@ -306,15 +282,15 @@ namespace gsc { void add(const std::string& name, script_method method) { - if (xsk::gsc::h1::resolver::find_method(name)) + if (gsc_ctx->meth_exists(name)) { - const auto id = xsk::gsc::h1::resolver::method_id(name); + const auto id = gsc_ctx->meth_id(name); methods[id] = method; } else { const auto id = ++method_id_start; - xsk::gsc::h1::resolver::add_method(name, static_cast(id)); + gsc_ctx->meth_add(name, static_cast(id)); methods[id] = method; } } @@ -350,6 +326,8 @@ namespace gsc public: void post_unpack() override { + developer_script = dvars::register_bool("developer_script", false, 0, "Enable developer script comments"); + utils::hook::set(SELECT_VALUE(0x3BD86C_b, 0x50484C_b), 0x1000); // change builtin func count utils::hook::set(SELECT_VALUE(0x3BD872_b, 0x504852_b) + 4, @@ -376,7 +354,7 @@ namespace gsc utils::hook::nop(SELECT_VALUE(0x3CBA4E_b, 0x512AAE_b), 2); utils::hook::call(SELECT_VALUE(0x3CBA46_b, 0x512AA6_b), vm_call_builtin_method_stub); - utils::hook::call(SELECT_VALUE(0x3CC9F3_b, 0x513A53_b), vm_error_stub); + utils::hook::call(SELECT_VALUE(0x3CC9F3_b, 0x513A53_b), vm_error_stub); // LargeLocalResetToMark if (game::environment::is_dedi()) { @@ -442,7 +420,7 @@ namespace gsc if (what.type != game::VAR_FUNCTION || with.type != game::VAR_FUNCTION) { - throw std::runtime_error("replaceFunc: parameter 1 must be a function"); + throw std::runtime_error("replacefunc: parameter 1 must be a function"); } logfile::set_gsc_hook(what.u.codePosValue, with.u.codePosValue); @@ -473,8 +451,7 @@ namespace gsc function::add("executecommand", [](const function_args& args) { - const auto cmd = args[0].as(); - command::execute(cmd, false); + command::execute(args[0].as(), false); return scripting::script_value{}; }); diff --git a/src/client/component/gsc/script_extension.hpp b/src/client/component/gsc/script_extension.hpp index 2aae4a2e..93c1b3e7 100644 --- a/src/client/component/gsc/script_extension.hpp +++ b/src/client/component/gsc/script_extension.hpp @@ -34,6 +34,8 @@ namespace gsc extern const game::dvar_t* developer_script; + void scr_error(const char* error); + namespace function { void add(const std::string& name, script_function function); diff --git a/src/client/component/gsc/script_loading.cpp b/src/client/component/gsc/script_loading.cpp index 5dd76f54..7cd16ea1 100644 --- a/src/client/component/gsc/script_loading.cpp +++ b/src/client/component/gsc/script_loading.cpp @@ -6,7 +6,7 @@ #include "component/filesystem.hpp" #include "component/logfile.hpp" #include "component/scripting.hpp" -#include "component/gsc/script_loading.hpp" +#include "component/memory.hpp" #include "game/dvars.hpp" @@ -14,27 +14,22 @@ #include "game/scripting/execution.hpp" #include "game/scripting/function.hpp" -#include -#include -#include -#include -#include -#include -#include -#include +#include "script_extension.hpp" +#include "script_loading.hpp" +#include #include #include #include namespace gsc { + std::unique_ptr gsc_ctx = std::make_unique();; + namespace { - auto compiler = ::gsc::compiler(); - auto decompiler = ::gsc::decompiler(); - auto assembler = ::gsc::assembler(); - auto disassembler = ::gsc::disassembler(); + utils::hook::detour scr_begin_load_scripts_hook; + utils::hook::detour scr_end_load_scripts_hook; std::unordered_map main_handles; std::unordered_map init_handles; @@ -46,7 +41,7 @@ namespace gsc { char* buf = nullptr; char* pos = nullptr; - unsigned int size = 0x1000000; + const unsigned int size = memory::custom_script_mem_size; } script_memory; char* allocate_buffer(size_t size) @@ -83,14 +78,14 @@ namespace gsc free_script_memory(); } - bool read_scriptfile(const std::string& name, std::string* data) + bool read_raw_script_file(const std::string& name, std::string* data) { if (filesystem::read_file(name, data)) { return true; } - const auto name_str = name.data(); + const auto* name_str = name.data(); if (game::DB_XAssetExists(game::ASSET_TYPE_RAWFILE, name_str) && !game::DB_IsXAssetDefault(game::ASSET_TYPE_RAWFILE, name_str)) { @@ -113,9 +108,9 @@ namespace gsc game::ScriptFile* load_custom_script(const char* file_name, const std::string& real_name) { - if (loaded_scripts.contains(real_name)) + if (const auto itr = loaded_scripts.find(real_name); itr != loaded_scripts.end()) { - return loaded_scripts[real_name]; + return itr->second; } if (game::VirtualLobby_Loaded() && !force_load) @@ -123,28 +118,61 @@ namespace gsc return nullptr; } - std::string source_buffer; - if (!read_scriptfile(real_name + ".gsc", &source_buffer) || source_buffer.empty()) + std::string source_buffer{}; + if (!read_raw_script_file(real_name + ".gsc", &source_buffer) || source_buffer.empty()) { return nullptr; } - if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, file_name) && + // filter out "GSC rawfiles" that were used for development usage and are not meant for us. + // each "GSC rawfile" has a ScriptFile counterpart to be used instead + if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, file_name) && !game::DB_IsXAssetDefault(game::ASSET_TYPE_SCRIPTFILE, file_name)) { - // filter out gsc rawfiles that contain developer code (has ScriptFile counterparts for ship, won't compile either) - if ((real_name.starts_with("maps/createfx") || real_name.starts_with("maps/createart") || real_name.starts_with("maps/mp")) + if ((real_name.starts_with("maps/createfx") || real_name.starts_with("maps/createart") || real_name.starts_with("maps/mp")) && (real_name.ends_with("_fx") || real_name.ends_with("_fog") || real_name.ends_with("_hdr"))) { + console::debug("Refusing to compile rawfile '%s'\n", real_name.data()); return game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, file_name, false).scriptfile; } } - auto data = std::vector{source_buffer.begin(), source_buffer.end()}; + console::debug("Loading custom gsc '%s.gsc'", real_name.data()); try { - compiler->compile(real_name, data); + auto& compiler = gsc_ctx->compiler(); + auto& assembler = gsc_ctx->assembler(); + + std::vector data; + data.assign(source_buffer.begin(), source_buffer.end()); + + const auto assembly_ptr = compiler.compile(real_name, data); + const auto output_script = assembler.assemble(*assembly_ptr); + + const auto bytecode = output_script.first; // formerly named "script" + const auto stack = output_script.second; + + const auto script_file_ptr = static_cast(scriptfile_allocator.allocate(sizeof(game::ScriptFile))); + script_file_ptr->name = file_name; + + script_file_ptr->len = static_cast(stack.size); + script_file_ptr->bytecodeLen = static_cast(bytecode.size); + + const auto stack_size = static_cast(stack.size + 1); + const auto byte_code_size = static_cast(bytecode.size + 1); + + script_file_ptr->buffer = static_cast(scriptfile_allocator.allocate(stack_size)); + std::memcpy(const_cast(script_file_ptr->buffer), stack.data, stack.size); + + script_file_ptr->bytecode = allocate_buffer(byte_code_size); + std::memcpy(script_file_ptr->bytecode, bytecode.data, bytecode.size); + + script_file_ptr->compressedLen = 0; + + loaded_scripts[real_name] = script_file_ptr; + + return script_file_ptr; } catch (const std::exception& e) { @@ -153,46 +181,21 @@ namespace gsc console::error("**********************************************\n"); return nullptr; } + } - auto assembly = compiler->output(); - - try + std::string get_raw_script_file_name(const std::string& name) + { + if (name.ends_with(".gsh")) { - assembler->assemble(real_name, assembly); - } - catch (const std::exception& e) - { - console::error("*********** script compile error *************\n"); - console::error("failed to assemble '%s':\n%s", real_name.data(), e.what()); - console::error("**********************************************\n"); - return nullptr; + return name; } - const auto script_file_ptr = scriptfile_allocator.allocate(); - script_file_ptr->name = file_name; - - const auto stack = assembler->output_stack(); - script_file_ptr->len = static_cast(stack.size()); - - const auto script = assembler->output_script(); - script_file_ptr->bytecodeLen = static_cast(script.size()); - - script_file_ptr->buffer = game::Hunk_AllocateTempMemoryHigh(stack.size() + 1); - std::memcpy(script_file_ptr->buffer, 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->compressedLen = 0; - - loaded_scripts[real_name] = script_file_ptr; - - return script_file_ptr; + return name + ".gsc"; } std::string get_script_file_name(const std::string& name) { - const auto id = xsk::gsc::h1::resolver::token_id(name); + const auto id = gsc_ctx->token_id(name); if (!id) { return name; @@ -201,7 +204,7 @@ namespace gsc return std::to_string(id); } - std::vector decompile_scriptfile(const std::string& name, const std::string& real_name) + std::pair> read_compiled_script_file(const std::string& name, const std::string& real_name) { const auto* script_file = game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, name.data(), false).scriptfile; if (script_file == nullptr) @@ -209,19 +212,17 @@ namespace gsc throw std::runtime_error(std::format("Could not load scriptfile '{}'", real_name)); } - console::info("Decompiling scriptfile '%s'\n", real_name.data()); + console::debug("Decompiling scriptfile '%s'\n", real_name.data()); - std::vector stack{script_file->buffer, script_file->buffer + script_file->len}; - std::vector bytecode{script_file->bytecode, script_file->bytecode + script_file->bytecodeLen}; + const auto len = script_file->compressedLen; + const std::string stack{script_file->buffer, static_cast(len)}; - auto decompressed_stack = xsk::utils::zlib::decompress(stack, static_cast(stack.size())); + const auto decompressed_stack = utils::compression::zlib::decompress(stack); - disassembler->disassemble(name, bytecode, decompressed_stack); - auto output = disassembler->output(); + std::vector stack_data; + stack_data.assign(decompressed_stack.begin(), decompressed_stack.end()); - decompiler->decompile(name, output); - - return decompiler->output(); + return {{reinterpret_cast(script_file->bytecode), static_cast(script_file->bytecodeLen)}, stack_data}; } void load_script(const std::string& name) @@ -231,31 +232,31 @@ namespace gsc return; } - const auto main_handle = game::Scr_GetFunctionHandle(name.data(), xsk::gsc::h1::resolver::token_id("main")); - const auto init_handle = game::Scr_GetFunctionHandle(name.data(), xsk::gsc::h1::resolver::token_id("init")); + const auto main_handle = game::Scr_GetFunctionHandle(name.data(), gsc_ctx->token_id("main")); + const auto init_handle = game::Scr_GetFunctionHandle(name.data(), gsc_ctx->token_id("init")); if (main_handle) { - console::info("Loaded '%s::main'\n", name.data()); + console::debug("Loaded '%s::main'\n", name.data()); main_handles[name] = main_handle; } if (init_handle) { - console::info("Loaded '%s::init'\n", name.data()); + console::debug("Loaded '%s::init'\n", name.data()); init_handles[name] = init_handle; } } - void load_scripts(const std::filesystem::path& root_dir, const std::filesystem::path& script_dir) + void load_scripts(const std::filesystem::path& root_dir, const std::filesystem::path& subfolder) { - std::filesystem::path script_dir_path = root_dir / script_dir; - if (!utils::io::directory_exists(script_dir_path.generic_string())) + std::filesystem::path script_dir = root_dir / subfolder; + if (!utils::io::directory_exists(script_dir.generic_string())) { return; } - const auto scripts = utils::io::list_files(script_dir_path.generic_string()); + const auto scripts = utils::io::list_files(script_dir.generic_string()); for (const auto& script : scripts) { if (!script.ends_with(".gsc")) @@ -281,7 +282,7 @@ namespace gsc return game::DB_IsXAssetDefault(type, name); } - void gscr_load_gametype_script_stub(void* a1, void* a2) + void load_gametype_script_stub(void* a1, void* a2) { utils::hook::invoke(SELECT_VALUE(0x2B9DA0_b, 0x18BC00_b), a1, a2); @@ -319,28 +320,48 @@ namespace gsc return; } - utils::hook::invoke(SELECT_VALUE(0x1F1E00_b, 0x396080_b), rawfile, buf, size); + game::DB_GetRawBuffer(rawfile, buf, size); } - void pmem_init_stub() + void scr_begin_load_scripts_stub() { - utils::hook::invoke(SELECT_VALUE(0x420260_b, 0x5A5590_b)); + const bool dev_script = developer_script ? developer_script->current.enabled : false; + const auto comp_mode = dev_script ? + xsk::gsc::build::dev: + xsk::gsc::build::prod; - const auto type_0 = &game::g_scriptmem[0]; - const auto type_1 = &game::g_scriptmem[1]; + gsc_ctx->init(comp_mode, [](const std::string& include_name) + -> std::pair> + { + const auto real_name = get_raw_script_file_name(include_name); - const auto size_0 = 0x100000; // default size - const auto size_1 = 0x100000 + script_memory.size; + std::string file_buffer; + if (!read_raw_script_file(real_name, &file_buffer) || file_buffer.empty()) + { + const auto name = get_script_file_name(include_name); + if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, name.data())) + { + return read_compiled_script_file(name, real_name); + } - const auto block = reinterpret_cast(VirtualAlloc(NULL, size_0 + size_1, MEM_RESERVE, PAGE_READWRITE)); + throw std::runtime_error(std::format("Could not load gsc file '{}'", real_name)); + } - type_0->buf = block; - type_0->size = size_0; + std::vector script_data; + script_data.assign(file_buffer.begin(), file_buffer.end()); - type_1->buf = block + size_0; - type_1->size = size_1; + return {{}, script_data}; + }); - utils::hook::set(SELECT_VALUE(0x420252_b, 0x5A5582_b), size_0 + size_1); + scr_begin_load_scripts_hook.invoke(); + } + + void scr_end_load_scripts_stub() + { + // cleanup the compiler + gsc_ctx->cleanup(); + + scr_end_load_scripts_hook.invoke(); } } @@ -348,7 +369,7 @@ namespace gsc { for (auto& function_handle : main_handles) { - console::info("Executing '%s::main'\n", function_handle.first.data()); + console::debug("Executing '%s::main'\n", function_handle.first.data()); game::RemoveRefToObject(game::Scr_ExecThread(function_handle.second, 0)); } } @@ -357,7 +378,7 @@ namespace gsc { for (auto& function_handle : init_handles) { - console::info("Executing '%s::init'\n", function_handle.first.data()); + console::debug("Executing '%s::init'\n", function_handle.first.data()); game::RemoveRefToObject(game::Scr_ExecThread(function_handle.second, 0)); } } @@ -368,7 +389,7 @@ namespace gsc const auto id = static_cast(std::atoi(name)); if (id) { - real_name = xsk::gsc::h1::resolver::token_name(id); + real_name = gsc_ctx->token_name(id); } auto* script = load_custom_script(name, real_name); @@ -385,49 +406,26 @@ namespace gsc public: void post_unpack() override { - // allow custom scripts to include other custom scripts - xsk::gsc::h1::resolver::init([](const auto& include_name) - { - const auto real_name = include_name + ".gsc"; + // Load our scripts with an uncompressed stack + utils::hook::call(SELECT_VALUE(0x3C7280_b, 0x50E3C0_b), db_get_raw_buffer_stub); - std::string file_buffer; - if (!read_scriptfile(real_name, &file_buffer) || file_buffer.empty()) - { - const auto name = get_script_file_name(include_name); - if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, name.data())) - { - return decompile_scriptfile(name, real_name); - } - else - { - throw std::runtime_error(std::format("Could not load gsc file '{}'", real_name)); - } - } + scr_begin_load_scripts_hook.create(SELECT_VALUE(0x3BDB90_b, 0x504BC0_b), scr_begin_load_scripts_stub); + scr_end_load_scripts_hook.create(SELECT_VALUE(0x3BDCC0_b, 0x504CF0_b), scr_end_load_scripts_stub); - std::vector result; - result.assign(file_buffer.begin(), file_buffer.end()); - - return result; - }); - - // hook xasset functions to return our own custom scripts + // ProcessScript: hook xasset functions to return our own custom scripts utils::hook::call(SELECT_VALUE(0x3C7217_b, 0x50E357_b), find_script); utils::hook::call(SELECT_VALUE(0x3C7227_b, 0x50E367_b), db_is_x_asset_default); - // GScr_LoadScripts - utils::hook::call(SELECT_VALUE(0x2BA152_b, 0x18C325_b), gscr_load_gametype_script_stub); + // GScr_LoadScripts: initial loading of scripts + utils::hook::call(SELECT_VALUE(0x2BA152_b, 0x18C325_b), load_gametype_script_stub); - // 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); + // main is called from scripting.cpp + // init is called from scripting.cpp scripting::on_shutdown([](bool free_scripts, bool post_shutdown) { if (free_scripts && post_shutdown) { - xsk::gsc::h1::resolver::cleanup(); clear(); } }); diff --git a/src/client/component/gsc/script_loading.hpp b/src/client/component/gsc/script_loading.hpp index e42a7e45..8fcdff2c 100644 --- a/src/client/component/gsc/script_loading.hpp +++ b/src/client/component/gsc/script_loading.hpp @@ -1,7 +1,10 @@ #pragma once +#include namespace gsc { + extern std::unique_ptr gsc_ctx; + void load_main_handles(); void load_init_handles(); game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default); diff --git a/src/client/component/mapents.cpp b/src/client/component/mapents.cpp index eee4297f..bb834fb9 100644 --- a/src/client/component/mapents.cpp +++ b/src/client/component/mapents.cpp @@ -7,13 +7,7 @@ #include #include -#include -#include -#include -#include -#include -#include -#include +#include "gsc/script_loading.hpp" namespace mapents { @@ -128,7 +122,7 @@ namespace mapents } const auto key_ = key.substr(1, key.size() - 2); - const auto id = xsk::gsc::h1::resolver::token_id(key_); + const auto id = gsc::gsc_ctx->token_id(key_); if (id == 0) { console::warn("[map_ents parser] Key '%s' not found, on line %i (%s)\n", key_.data(), i, line.data()); diff --git a/src/client/component/memory.cpp b/src/client/component/memory.cpp new file mode 100644 index 00000000..e8fdaf8e --- /dev/null +++ b/src/client/component/memory.cpp @@ -0,0 +1,143 @@ +#include +#include "loader/component_loader.hpp" + +#include "memory.hpp" + +#include "game/game.hpp" + +#include +#include + +namespace memory +{ + namespace + { + constexpr auto mem_low_size = 0x80000000ui64 * 2; // default: 0x80000000 + constexpr auto mem_high_size = 0x80000000ui64 * 2; // default: 0x80000000 + + constexpr auto script_mem_low_size = 0x100000ui64; // default: 0x100000 + constexpr auto script_mem_high_size = 0x100000ui64 + custom_script_mem_size; // default: 0x100000 + + constexpr auto phys_mem_low_size = 0x700000000ui64; // default: 0x700000000 + constexpr auto phys_mem_high_size = 0x300000000i64; // default: 0x300000000 + + constexpr auto pmem_alloc_size = + mem_low_size + + mem_high_size + + script_mem_low_size + + script_mem_high_size + + phys_mem_low_size + + phys_mem_high_size; + + constexpr auto mem_low_buf = 0; + constexpr auto mem_high_buf = mem_low_buf + mem_low_size; + + constexpr auto script_mem_low_buf = mem_high_buf + mem_high_size; + constexpr auto script_mem_high_buf = script_mem_low_buf + script_mem_low_size; + + constexpr auto phys_mem_low_buf = script_mem_high_buf + script_mem_high_size; + constexpr auto phys_mem_high_buf = phys_mem_low_buf + phys_mem_low_size; + + constexpr auto stream_mem_size = 0x2000000ui64; + + void pmem_init() + { + const auto size = pmem_alloc_size; + const auto allocated_buffer = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_READWRITE); + auto buffer = reinterpret_cast(allocated_buffer); + *game::pmem_size = size; + *game::pmem_buffer = buffer; + + memset(game::g_mem, 0, sizeof(*game::g_mem)); + + game::g_mem->prim[game::PHYS_ALLOC_LOW].buf = buffer + mem_low_buf; + game::g_mem->prim[game::PHYS_ALLOC_LOW].pos = mem_low_size; + game::g_mem->prim[game::PHYS_ALLOC_HIGH].buf = buffer + mem_high_buf; + game::g_mem->prim[game::PHYS_ALLOC_HIGH].pos = mem_high_size; + + game::g_mem->prim[game::PHYS_ALLOC_LOW].unk1 = 0; + game::g_mem->prim[game::PHYS_ALLOC_HIGH].unk1 = 0; + + memset(game::g_scriptmem, 0, sizeof(*game::g_scriptmem)); + + game::g_scriptmem->prim[game::PHYS_ALLOC_LOW].buf = buffer + script_mem_low_buf; + game::g_scriptmem->prim[game::PHYS_ALLOC_LOW].pos = script_mem_low_size; + game::g_scriptmem->prim[game::PHYS_ALLOC_HIGH].buf = buffer + script_mem_high_buf; + game::g_scriptmem->prim[game::PHYS_ALLOC_HIGH].pos = script_mem_high_size; + + game::g_scriptmem->prim[game::PHYS_ALLOC_LOW].unk1 = 0; + game::g_scriptmem->prim[game::PHYS_ALLOC_HIGH].unk1 = 0; + + memset(game::g_physmem, 0, sizeof(*game::g_physmem)); + + game::g_physmem->prim[game::PHYS_ALLOC_LOW].buf = buffer + phys_mem_low_buf; + game::g_physmem->prim[game::PHYS_ALLOC_LOW].pos = phys_mem_low_size; + game::g_physmem->prim[game::PHYS_ALLOC_HIGH].buf = buffer + phys_mem_high_buf; + game::g_physmem->prim[game::PHYS_ALLOC_HIGH].pos = phys_mem_high_size; + + game::g_physmem->prim[game::PHYS_ALLOC_LOW].unk1 = 2; + game::g_physmem->prim[game::PHYS_ALLOC_HIGH].unk1 = 2; + + *game::stream_size = stream_mem_size; + *game::stream_buffer = reinterpret_cast(VirtualAlloc(NULL, *game::stream_size, MEM_COMMIT, PAGE_READWRITE)); + } + + void pmem_init_stub() + { + // call our own init + pmem_init(); + + const auto script_mem_size = script_mem_low_size + script_mem_high_size; + utils::hook::set(SELECT_VALUE(0x420252_b, 0x5A5582_b), static_cast(script_mem_size)); + } + } + + namespace + { + int out_of_memory_text_stub(char* dest, int size, const char* fmt, ...) + { + fmt = "%s (%d)\n\n" + "Disable shader caching, lower graphic settings, free up RAM, or update your GPU drivers.\n\n" + "If this still occurs, try using the '-memoryfix' parameter to generate the 'players2' folder."; + + char buffer[2048]; + + { + va_list ap; + va_start(ap, fmt); + + vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap); + + va_end(ap); + } + + return utils::hook::invoke(SELECT_VALUE(0x429200_b, 0x5AF0F0_b), dest, size, "%s", buffer); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + // patch PMem_Init, so we can use whatever memory size we want + utils::hook::call(SELECT_VALUE(0x38639C_b, 0x15C4D6_b), pmem_init_stub); + + // Com_sprintf for "Out of memory. You are probably low on disk space." + utils::hook::call(SELECT_VALUE(0x457BC9_b, 0x1D8E09_b), out_of_memory_text_stub); + + // "fix" for rare 'Out of memory error' error + // this will *at least* generate the configs for mp/sp, which is the #1 issue + if (utils::flags::has_flag("memoryfix")) + { + utils::hook::jump(SELECT_VALUE(0x5110D0_b, 0x6200C0_b), malloc); + utils::hook::jump(SELECT_VALUE(0x510FF0_b, 0x61FFE0_b), _aligned_malloc); + utils::hook::jump(SELECT_VALUE(0x511130_b, 0x620120_b), free); + utils::hook::jump(SELECT_VALUE(0x511220_b, 0x620210_b), realloc); + utils::hook::jump(SELECT_VALUE(0x511050_b, 0x620040_b), _aligned_realloc); + } + } + }; +} + +REGISTER_COMPONENT(memory::component) \ No newline at end of file diff --git a/src/client/component/memory.hpp b/src/client/component/memory.hpp new file mode 100644 index 00000000..2d430224 --- /dev/null +++ b/src/client/component/memory.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace memory +{ + constexpr auto custom_script_mem_size = 0x1000000ui64; +} diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index e17e4066..c0d78011 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -244,26 +244,6 @@ namespace patches } } - int out_of_memory_text_stub(char* dest, int size, const char* fmt, ...) - { - fmt = "%s (%d)\n\n" - "Disable shader caching, lower graphic settings, free up RAM, or update your GPU drivers.\n\n" - "If this still occurs, try using the '-memoryfix' parameter to generate the 'players2' folder."; - - char buffer[2048]; - - { - va_list ap; - va_start(ap, fmt); - - vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap); - - va_end(ap); - } - - return utils::hook::invoke(SELECT_VALUE(0x429200_b, 0x5AF0F0_b), dest, size, "%s", buffer); - } - void create_2d_texture_stub_1(const char* fmt, ...) { fmt = "Create2DTexture( %s, %i, %i, %i, %i ) failed\n\n" @@ -377,18 +357,6 @@ namespace patches utils::hook::call(SELECT_VALUE(0x55E919_b, 0x681A69_b), create_2d_texture_stub_1); // Sys_Error for "Create2DTexture( %s, %i, %i, %i, %i ) failed" utils::hook::call(SELECT_VALUE(0x55EACB_b, 0x681C1B_b), create_2d_texture_stub_2); // Com_Error for ^ utils::hook::call(SELECT_VALUE(0x5B35BA_b, 0x6CB1BC_b), swap_chain_stub); // Com_Error for "IDXGISwapChain::Present failed: %s" - utils::hook::call(SELECT_VALUE(0x457BC9_b, 0x1D8E09_b), out_of_memory_text_stub); // Com_sprintf for "Out of memory. You are probably low on disk space." - - // "fix" for rare 'Out of memory error' error - // this will *at least* generate the configs for mp/sp, which is the #1 issue - if (utils::flags::has_flag("memoryfix")) - { - utils::hook::jump(SELECT_VALUE(0x5110D0_b, 0x6200C0_b), malloc); - utils::hook::jump(SELECT_VALUE(0x510FF0_b, 0x61FFE0_b), _aligned_malloc); - utils::hook::jump(SELECT_VALUE(0x511130_b, 0x620120_b), free); - utils::hook::jump(SELECT_VALUE(0x511220_b, 0x620210_b), realloc); - utils::hook::jump(SELECT_VALUE(0x511050_b, 0x620040_b), _aligned_realloc); - } // Uncheat protect gamepad-related dvars dvars::override::register_float("gpad_button_deadzone", 0.13f, 0, 1, game::DVAR_FLAG_SAVED); diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp index 90f3f330..4efc6df4 100644 --- a/src/client/component/scripting.cpp +++ b/src/client/component/scripting.cpp @@ -252,12 +252,12 @@ namespace scripting std::optional get_canonical_string(const unsigned int id) { - if (canonical_string_table.find(id) == canonical_string_table.end()) + if (const auto itr = canonical_string_table.find(id); itr != canonical_string_table.end()) { - return {}; + return itr->second; } - return {canonical_string_table[id]}; + return {}; } class component final : public component_interface diff --git a/src/client/component/weapon.cpp b/src/client/component/weapon.cpp index fa67abdc..330525f0 100644 --- a/src/client/component/weapon.cpp +++ b/src/client/component/weapon.cpp @@ -39,7 +39,7 @@ namespace weapon // precache items for (std::size_t i = 0; i < weapons.size(); i++) { - console::debug("precaching weapon \"%s\"\n", weapons[i]->name); + //console::debug("precaching weapon \"%s\"\n", weapons[i]->name); game::G_GetWeaponForName(weapons[i]->name); } } diff --git a/src/client/game/scripting/functions.cpp b/src/client/game/scripting/functions.cpp index 165e56a9..daf766ee 100644 --- a/src/client/game/scripting/functions.cpp +++ b/src/client/game/scripting/functions.cpp @@ -1,80 +1,79 @@ #include #include "functions.hpp" -#include "component/console.hpp" -#include "component/gsc/script_extension.hpp" - -#include -#include - #include +#include "component/gsc/script_extension.hpp" +#include "component/gsc/script_loading.hpp" + namespace scripting { namespace { - int find_function_index(const std::string& name, const bool prefer_global) + int find_function_index(const std::string& name, [[maybe_unused]] const bool prefer_global) { const auto target = utils::string::to_lower(name); - auto first = xsk::gsc::h1::resolver::function_id; - auto second = xsk::gsc::h1::resolver::method_id; + auto const& first = gsc::gsc_ctx->func_map(); + auto const& second = gsc::gsc_ctx->meth_map(); + if (!prefer_global) { - std::swap(first, second); + if (const auto itr = second.find(name); itr != second.end()) + { + return static_cast(itr->second); + } + + if (const auto itr = first.find(name); itr != first.end()) + { + return static_cast(itr->second); + } } - const auto first_res = first(target); - if (first_res) + if (const auto itr = first.find(name); itr != first.end()) { - return first_res; + return static_cast(itr->second); } - const auto second_res = second(target); - if (second_res) + if (const auto itr = second.find(name); itr != second.end()) { - return second_res; + return static_cast(itr->second); } return -1; } - - script_function get_function_by_index(const unsigned index) - { - if (index < 0x1000) - { - return reinterpret_cast(gsc::func_table)[index - 1]; - } - - return reinterpret_cast(gsc::meth_table)[index - 0x8000]; - } - - unsigned int parse_token_id(const std::string& name) - { - if (name.starts_with("_ID")) - { - return static_cast(std::strtol(name.substr(3).data(), nullptr, 10)); - } - - if (name.starts_with("_id_")) - { - return static_cast(std::strtol(name.substr(4).data(), nullptr, 16)); - } - - return 0; - } } - std::string find_token(unsigned int id) + std::uint32_t parse_token_id(const std::string& name) { - return xsk::gsc::h1::resolver::token_name(static_cast(id)); + if (name.starts_with("_ID")) + { + return static_cast(std::strtol(name.substr(3).data(), nullptr, 10)); + } + + if (name.starts_with("_id_")) + { + return static_cast(std::strtol(name.substr(4).data(), nullptr, 16)); + } + + return 0; + } + + std::string find_token(std::uint32_t id) + { + return gsc::gsc_ctx->token_name(id); + } + + std::string find_token_single(std::uint32_t id) + { + return gsc::gsc_ctx->token_name(id); } unsigned int find_token_id(const std::string& name) { - const auto result = xsk::gsc::h1::resolver::token_id(name); - if (result) + const auto id = gsc::gsc_ctx->token_id(name); + if (id) { - return result; + return id; } const auto parsed_id = parse_token_id(name); @@ -86,14 +85,24 @@ namespace scripting return game::SL_GetCanonicalString(name.data()); } + script_function get_function_by_index(const std::uint32_t index) + { + static const auto function_table = &gsc::func_table; + static const auto method_table = &gsc::meth_table; + + if (index < 0x1000) + { + return reinterpret_cast(function_table)[index - 1]; + } + + return reinterpret_cast(method_table)[index - 0x8000]; + } + script_function find_function(const std::string& name, const bool prefer_global) { const auto index = find_function_index(name, prefer_global); - if (index < 0) - { - return nullptr; - } + if (index < 0) return nullptr; return get_function_by_index(index); } -} +} \ No newline at end of file diff --git a/src/client/game/scripting/functions.hpp b/src/client/game/scripting/functions.hpp index 0685a92e..5bd1c764 100644 --- a/src/client/game/scripting/functions.hpp +++ b/src/client/game/scripting/functions.hpp @@ -5,8 +5,10 @@ namespace scripting { using script_function = void(*)(game::scr_entref_t); - std::string find_token(unsigned int id); + std::string find_token(std::uint32_t id); + std::string find_token_single(std::uint32_t id); unsigned int find_token_id(const std::string& name); + script_function get_function_by_index(std::uint32_t index); script_function find_function(const std::string& name, const bool prefer_global); } diff --git a/src/client/game/scripting/lua/context.cpp b/src/client/game/scripting/lua/context.cpp index 8a719e0e..8c483551 100644 --- a/src/client/game/scripting/lua/context.cpp +++ b/src/client/game/scripting/lua/context.cpp @@ -11,8 +11,7 @@ #include "component/fastfiles.hpp" #include "component/scheduler.hpp" -#include -#include +#include "component/gsc/script_loading.hpp" #include #include @@ -217,7 +216,7 @@ namespace scripting::lua auto entity_type = state.new_usertype("entity"); - for (const auto& func : xsk::gsc::h1::resolver::get_methods()) + for (auto const& func : gsc::gsc_ctx->meth_map()) { const auto name = std::string(func.first); entity_type[name] = [name](const entity& entity, const sol::this_state s, sol::variadic_args va) @@ -336,7 +335,7 @@ namespace scripting::lua auto game_type = state.new_usertype("game_"); state["game"] = game(); - for (const auto& func : xsk::gsc::h1::resolver::get_functions()) + for (auto const& func : gsc::gsc_ctx->func_map()) { const auto name = std::string(func.first); game_type[name] = [name](const game&, const sol::this_state s, sol::variadic_args va) diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index df9d5ad8..0ebd5057 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1844,6 +1844,13 @@ namespace game int ingame_cursor_visible; }; + enum PMem_Direction + { + PHYS_ALLOC_LOW = 0x0, + PHYS_ALLOC_HIGH = 0x1, + PHYS_ALLOC_COUNT = 0x2, + }; + enum PMem_Source { PMEM_SOURCE_EXTERNAL = 0x0, @@ -1852,19 +1859,38 @@ namespace game PMEM_SOURCE_DEFAULT_HIGH = 0x3, PMEM_SOURCE_MOVIE = 0x4, PMEM_SOURCE_SCRIPT = 0x5, + PMEM_SOURCE_UNK5 = 0x5, + PMEM_SOURCE_UNK6 = 0x6, + PMEM_SOURCE_UNK7 = 0x7, + PMEM_SOURCE_UNK8 = 0x8, + PMEM_SOURCE_CUSTOMIZATION = 0x9, }; - struct physical_memory + struct PhysicalMemoryAllocation { - char __pad0[0x10]; - char* buf; - char __pad1[0x8]; - int unk1; - size_t size; - char __pad2[0x500]; - }; + const char* name; + char __pad0[16]; + unsigned __int64 pos; + char __pad1[8]; + }; static_assert(sizeof(PhysicalMemoryAllocation) == 40); - static_assert(sizeof(physical_memory) == 0x530); + struct PhysicalMemoryPrim + { + const char* name; + unsigned int allocListCount; + char __pad0[4]; + unsigned char* buf; + char __pad1[8]; + int unk1; + char __pad2[4]; + unsigned __int64 pos; + PhysicalMemoryAllocation allocList[32]; + }; static_assert(sizeof(PhysicalMemoryPrim) == 1328); + + struct PhysicalMemory + { + PhysicalMemoryPrim prim[2]; + }; static_assert(sizeof(PhysicalMemory) == 0xA60); namespace mp { diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 3d76ac9a..7f8b2ea0 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -96,6 +96,7 @@ namespace game WEAK symbol FS_Startup{0x40D890, 0x0}; WEAK symbol FS_AddLocalizedGameDirectory{0x40B1E0, 0x1878F0}; + WEAK symbol GetObjectType{0x3C3680, 0x50A810}; WEAK symbol GetVariable{0x3C3740, 0x50A8D0}; WEAK symbol GetNewVariable{0x3C3360, 0x50A4F0}; WEAK symbol GetNewArrayVariable{0x3C31E0, 0x50A370}; @@ -177,6 +178,7 @@ namespace game WEAK symbol Scr_GetFloat{0x3C87D0, 0x50F870}; WEAK symbol Scr_GetString{0x3C8CC0, 0x50FCB0}; WEAK symbol Scr_GetNumParam{0x3C89E0, 0x50F9D0}; + WEAK symbol Scr_CastString{0x3C4450, 0x50B4B0}; WEAK symbol Scr_ClearOutParams{0x3C7EF0, 0x50F070}; WEAK symbol Scr_GetEntityIdRef{0x3C6760, 0x50D8E0}; WEAK symbol Scr_GetEntityId{0x3C66B0, 0x50D830}; @@ -307,7 +309,15 @@ namespace game WEAK symbol scr_VmPub{0xC3F4E20, 0xB7AE3C0}; WEAK symbol scr_function_stack{0xC4015C0, 0xB7B8940}; - WEAK symbol g_scriptmem{0xD5F3140, 0xC92EC40}; + WEAK game::symbol pmem_size{0xD5F26D8, 0xC92E1D8}; + WEAK game::symbol pmem_buffer{0xD5F26D0, 0xC92E1D0}; + + WEAK game::symbol g_mem{0xD5F26E0, 0xC92E1E0}; + WEAK game::symbol g_scriptmem{0xD5F3140, 0xC92EC40}; + WEAK game::symbol g_physmem{0xD5F3BA0, 0xC92F6A0}; + + WEAK game::symbol stream_size{0x1DAD810, 0x258AA10}; + WEAK game::symbol stream_buffer{0x1DAD808, 0x258AA08}; WEAK symbol gfxDrawMethod{0xF7530B0, 0xE9213F0}; diff --git a/src/common/utils/memory.cpp b/src/common/utils/memory.cpp index 12b36e70..bc93d644 100644 --- a/src/common/utils/memory.cpp +++ b/src/common/utils/memory.cpp @@ -43,7 +43,7 @@ namespace utils { std::lock_guard _(this->mutex_); - const auto data = memory::allocate(length); + auto* data = memory::allocate(length); this->pool_.push_back(data); return data; } @@ -57,7 +57,7 @@ namespace utils { std::lock_guard _(this->mutex_); - const auto data = memory::duplicate_string(string); + auto* data = memory::duplicate_string(string); this->pool_.push_back(data); return data; }