From 45907301eec9233946222ace7969f31cb5d998c6 Mon Sep 17 00:00:00 2001 From: diamante0018 Date: Wed, 18 Dec 2024 12:39:55 +0100 Subject: [PATCH] Christmas Day Update --- deps/GSL | 2 +- deps/WinToast | 2 +- deps/asmjit | 2 +- deps/gsc-tool | 2 +- deps/libtomcrypt | 2 +- deps/libtommath | 2 +- deps/minhook | 2 +- deps/rapidjson | 2 +- deps/zlib | 2 +- src/client/component/asset_restrict.cpp | 35 ++++ src/client/component/auth.cpp | 25 ++- src/client/component/command.cpp | 37 +++-- src/client/component/dedicated_info.cpp | 12 +- src/client/component/dvar_cheats.cpp | 2 +- src/client/component/fastfiles.cpp | 24 +-- src/client/component/filesystem.cpp | 5 + src/client/component/game_log.cpp | 9 +- src/client/component/gsc/script_error.cpp | 32 ++-- src/client/component/gsc/script_extension.cpp | 128 +++++++++++--- src/client/component/gsc/script_extension.hpp | 27 +++ src/client/component/gsc/script_loading.cpp | 135 +++++++++++---- src/client/component/mods.cpp | 157 ++++++++++++++++++ src/client/component/mods.hpp | 7 + src/client/component/notifies.cpp | 22 +-- src/client/component/party.cpp | 85 ++++++++-- src/client/component/scripting.cpp | 17 +- src/client/component/scripting.hpp | 2 +- src/client/game/dvars.cpp | 4 +- src/client/game/dvars.hpp | 2 + src/client/game/game.cpp | 7 + src/client/game/game.hpp | 2 + src/client/game/structs.hpp | 43 ++++- src/client/game/symbols.hpp | 37 +++-- src/client/std_include.hpp | 2 + src/common/utils/properties.cpp | 25 +++ src/common/utils/properties.hpp | 7 + 36 files changed, 724 insertions(+), 184 deletions(-) create mode 100644 src/client/component/asset_restrict.cpp create mode 100644 src/client/component/mods.cpp create mode 100644 src/client/component/mods.hpp create mode 100644 src/common/utils/properties.cpp create mode 100644 src/common/utils/properties.hpp diff --git a/deps/GSL b/deps/GSL index f1a494c..2724630 160000 --- a/deps/GSL +++ b/deps/GSL @@ -1 +1 @@ -Subproject commit f1a494cfd2ce55fe88b5134eab985f5852667b8d +Subproject commit 272463043ef3e442f6c80a530d3dd38ee6781381 diff --git a/deps/WinToast b/deps/WinToast index 821c481..a78ce46 160000 --- a/deps/WinToast +++ b/deps/WinToast @@ -1 +1 @@ -Subproject commit 821c4818ade1aa4da56ac753285c159ce26fd597 +Subproject commit a78ce469b456c06103b3b30d4bd37e7bb80da30c diff --git a/deps/asmjit b/deps/asmjit index bfa0bf6..cfc9f81 160000 --- a/deps/asmjit +++ b/deps/asmjit @@ -1 +1 @@ -Subproject commit bfa0bf690c2e90cc0844f2f012efa41b916bde7e +Subproject commit cfc9f813cc6ccda63cad872edb32b38e0662bedb diff --git a/deps/gsc-tool b/deps/gsc-tool index b8e30e6..9ff3b87 160000 --- a/deps/gsc-tool +++ b/deps/gsc-tool @@ -1 +1 @@ -Subproject commit b8e30e6334aa33ea731caf8d0700a9e6c7794c09 +Subproject commit 9ff3b871ef59b14eb8b32d258bf18347e14a3e18 diff --git a/deps/libtomcrypt b/deps/libtomcrypt index 7e863d2..c900951 160000 --- a/deps/libtomcrypt +++ b/deps/libtomcrypt @@ -1 +1 @@ -Subproject commit 7e863d21429f94ed6a720e24499a12a3f852bb31 +Subproject commit c900951dab1bb94bab803fc57688dac18e3b71f9 diff --git a/deps/libtommath b/deps/libtommath index 8314bde..5809141 160000 --- a/deps/libtommath +++ b/deps/libtommath @@ -1 +1 @@ -Subproject commit 8314bde5e5c8e5d9331460130a9d1066e324f091 +Subproject commit 5809141a3a6ec1bf3443c927c02b955e19224016 diff --git a/deps/minhook b/deps/minhook index f5485b8..c1a7c38 160000 --- a/deps/minhook +++ b/deps/minhook @@ -1 +1 @@ -Subproject commit f5485b8454544c2f034c78f8f127c1d03dea3636 +Subproject commit c1a7c3843bd1a5fe3eb779b64c0d823bca3dc339 diff --git a/deps/rapidjson b/deps/rapidjson index 6089180..d621dc9 160000 --- a/deps/rapidjson +++ b/deps/rapidjson @@ -1 +1 @@ -Subproject commit 6089180ecb704cb2b136777798fa1be303618975 +Subproject commit d621dc9e9c77f81e5c8a35b8dcc16dcd63351321 diff --git a/deps/zlib b/deps/zlib index 9f0f2d4..ef24c4c 160000 --- a/deps/zlib +++ b/deps/zlib @@ -1 +1 @@ -Subproject commit 9f0f2d4f9f1f28be7e16d8bf3b4e9d4ada70aa9f +Subproject commit ef24c4c7502169f016dcd2a26923dbaf3216748c diff --git a/src/client/component/asset_restrict.cpp b/src/client/component/asset_restrict.cpp new file mode 100644 index 0000000..5637ddf --- /dev/null +++ b/src/client/component/asset_restrict.cpp @@ -0,0 +1,35 @@ +#include +#include +#include "game/game.hpp" + +#include + +namespace asset_restrict +{ + namespace + { + void reallocate_asset_pool(game::XAssetType type, const int size) + { + const auto asset_size = game::DB_GetXAssetTypeSize(type); + const auto new_size = size * game::g_poolSize[type]; + + const auto new_allocation_size = static_cast(new_size * asset_size); + + const game::XAssetHeader pool_entry = { .data = utils::memory::allocate(new_allocation_size) }; + game::DB_XAssetPool[type] = pool_entry.data; + game::g_poolSize[type] = new_size; + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + reallocate_asset_pool(game::ASSET_TYPE_XANIMPARTS, 2); + reallocate_asset_pool(game::ASSET_TYPE_ATTACHMENT, 2); + } + }; +} + +REGISTER_COMPONENT(asset_restrict::component) diff --git a/src/client/component/auth.cpp b/src/client/component/auth.cpp index 74115b2..3840601 100644 --- a/src/client/component/auth.cpp +++ b/src/client/component/auth.cpp @@ -8,11 +8,13 @@ #include "console.hpp" #include "network.hpp" -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include namespace auth { @@ -31,13 +33,26 @@ namespace auth std::string get_hw_profile_guid() { + auto hw_profile_path = (utils::properties::get_appdata_path() / "iw6-guid.dat").generic_string(); + if (utils::io::file_exists(hw_profile_path)) + { + std::string hw_profile_info; + if (utils::io::read_file(hw_profile_path, &hw_profile_info) && !hw_profile_info.empty()) + { + return hw_profile_info; + } + } + HW_PROFILE_INFO info; if (!GetCurrentHwProfileA(&info)) { return {}; } - return std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) }; + auto hw_profile_info = std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) }; + utils::io::write_file(hw_profile_path, hw_profile_info); + + return hw_profile_info; } std::string get_protected_data() diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 32127b6..c7dc997 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -232,7 +232,10 @@ namespace command void execute(std::string command, const bool sync) { - command += "\n"; + if (!command.ends_with('\n')) + { + command += "\n"; + } if (sync) { @@ -553,12 +556,12 @@ namespace command { if (!dvars::sv_cheats->current.enabled) { - game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\""); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); return; } game::mp::g_entities[client_num].flags ^= game::FL_GODMODE; - game::SV_GameSendServerCommand(client_num, 1, + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, utils::string::va("f \"godmode %s\"", game::mp::g_entities[client_num].flags & game::FL_GODMODE ? "^2on" @@ -569,12 +572,12 @@ namespace command { if (!dvars::sv_cheats->current.enabled) { - game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\""); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); return; } game::mp::g_entities[client_num].flags ^= game::FL_NOTARGET; - game::SV_GameSendServerCommand(client_num, 1, + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, utils::string::va("f \"notarget %s\"", game::mp::g_entities[client_num].flags & game::FL_NOTARGET ? "^2on" @@ -585,12 +588,12 @@ namespace command { if (!dvars::sv_cheats->current.enabled) { - game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\""); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); return; } game::mp::g_entities[client_num].client->flags ^= 1; - game::SV_GameSendServerCommand(client_num, 1, + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, utils::string::va("f \"noclip %s\"", game::mp::g_entities[client_num].client->flags & 1 ? "^2on" @@ -601,12 +604,12 @@ namespace command { if (!dvars::sv_cheats->current.enabled) { - game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\""); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); return; } game::mp::g_entities[client_num].client->flags ^= 2; - game::SV_GameSendServerCommand(client_num, 1, + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, utils::string::va("f \"ufo %s\"", game::mp::g_entities[client_num].client->flags & 2 ? "^2on" @@ -617,13 +620,13 @@ namespace command { if (!dvars::sv_cheats->current.enabled) { - game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\""); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); return; } if (params.size() < 4) { - game::SV_GameSendServerCommand(client_num, 1, + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify the correct number of coordinates\""); return; } @@ -637,13 +640,13 @@ namespace command { if (!dvars::sv_cheats->current.enabled) { - game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\""); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); return; } if (params.size() < 4) { - game::SV_GameSendServerCommand(client_num, 1, + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify the correct number of coordinates\""); return; } @@ -657,13 +660,13 @@ namespace command { if (!dvars::sv_cheats->current.enabled) { - game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\""); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); return; } if (params.size() < 2) { - game::SV_GameSendServerCommand(client_num, 1, "f \"You did not specify a weapon name\""); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\""); return; } @@ -680,13 +683,13 @@ namespace command { if (!dvars::sv_cheats->current.enabled) { - game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\""); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); return; } if (params.size() < 2) { - game::SV_GameSendServerCommand(client_num, 1, "f \"You did not specify a weapon name\""); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\""); return; } diff --git a/src/client/component/dedicated_info.cpp b/src/client/component/dedicated_info.cpp index 9f0de69..5dacf01 100644 --- a/src/client/component/dedicated_info.cpp +++ b/src/client/component/dedicated_info.cpp @@ -25,28 +25,28 @@ namespace dedicated_info scheduler::loop([]() { - auto* sv_running = game::Dvar_FindVar("sv_running"); + const auto* sv_running = game::Dvar_FindVar("sv_running"); if (!sv_running || !sv_running->current.enabled) { console::set_title("iw6-mod Dedicated Server"); return; } - auto* const sv_hostname = game::Dvar_FindVar("sv_hostname"); - auto* const sv_maxclients = game::Dvar_FindVar("sv_maxclients"); - auto* const mapname = game::Dvar_FindVar("mapname"); + const auto* sv_hostname = game::Dvar_FindVar("sv_hostname"); + const auto* sv_maxclients = game::Dvar_FindVar("sv_maxclients"); + const auto* mapname = game::Dvar_FindVar("mapname"); auto client_count = 0; auto bot_count = 0; - for (auto i = 0; i < sv_maxclients->current.integer; i++) + for (auto i = 0; i < sv_maxclients->current.integer; ++i) { auto* client = &game::mp::svs_clients[i]; auto* self = &game::mp::g_entities[i]; if (client->header.state > game::CS_FREE && self && self->client) { - client_count++; + ++client_count; if (game::SV_BotIsBot(i)) { ++bot_count; diff --git a/src/client/component/dvar_cheats.cpp b/src/client/component/dvar_cheats.cpp index 19450f2..04c4654 100644 --- a/src/client/component/dvar_cheats.cpp +++ b/src/client/component/dvar_cheats.cpp @@ -136,7 +136,7 @@ namespace dvar_cheats const auto* dvar = game::Scr_GetString(0); // grab the original dvar again since it's never stored on stack const auto* command = utils::string::va("q %s \"%s\"", dvar, value); - game::SV_GameSendServerCommand(entity_num, 1, command); + game::SV_GameSendServerCommand(entity_num, game::SV_CMD_RELIABLE, command); } const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a) diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index 16b93a0..f5bb265 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -1,12 +1,12 @@ #include #include "loader/component_loader.hpp" - #include "game/game.hpp" #include "game/dvars.hpp" -#include "fastfiles.hpp" #include "command.hpp" #include "console.hpp" +#include "fastfiles.hpp" + #include #include @@ -22,7 +22,7 @@ namespace fastfiles void db_try_load_x_file_internal(const char* zone_name, const int zone_flags, const int is_base_map) { console::info("Loading fastfile %s\n", zone_name); - return db_try_load_x_file_internal_hook.invoke(zone_name, zone_flags, is_base_map); + db_try_load_x_file_internal_hook.invoke(zone_name, zone_flags, is_base_map); } void dump_gsc_script(const std::string& name, game::XAssetHeader header) @@ -114,21 +114,9 @@ namespace fastfiles utils::hook::call(SELECT_VALUE(0x1402752DF, 0x140156350), p_mem_free_stub); utils::hook::call(SELECT_VALUE(0x140276004, 0x140324259), p_mem_free_stub); - command::add("loadzone", [](const command::params& params) - { - if (params.size() < 2) - { - console::info("usage: loadzone \n"); - return; - } - - game::XZoneInfo info; - info.name = params.get(1); - info.allocFlags = 1; - info.freeFlags = 0; - - game::DB_LoadXAssets(&info, 1, game::DBSyncMode::DB_LOAD_SYNC); - }); + // Allow loading of unsigned fastfiles + utils::hook::set(0x1402FBF23, 0xEB); // DB_LoadXFile + utils::hook::nop(0x1402FC445, 2); // DB_SetFileLoadCompressor command::add("materiallist", [](const command::params& params) { diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp index 9bcac55..dee28a3 100644 --- a/src/client/component/filesystem.cpp +++ b/src/client/component/filesystem.cpp @@ -86,6 +86,11 @@ namespace filesystem register_path("iw6"); register_path(get_binary_directory() + "\\data"); + if (get_binary_directory() != std::filesystem::current_path()) + { + register_path(std::filesystem::current_path() / "data"); + } + // game's search paths register_path("devraw"); register_path("devraw_shared"); diff --git a/src/client/component/game_log.cpp b/src/client/component/game_log.cpp index 5c46d91..e8262b0 100644 --- a/src/client/component/game_log.cpp +++ b/src/client/component/game_log.cpp @@ -102,9 +102,14 @@ namespace game_log g_log_printf("InitGame\n"); }); - scripting::on_shutdown([](int free_scripts) + scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void { - console::info("==== ShutdownGame (%d) ====\n", free_scripts); + if (post_shutdown) + { + return; + } + + console::info("==== ShutdownGame (%d) ====\n", clear_scripts); g_log_printf("ShutdownGame:\n"); g_log_printf("------------------------------------------------------------\n"); diff --git a/src/client/component/gsc/script_error.cpp b/src/client/component/gsc/script_error.cpp index 9bd853c..62027a7 100644 --- a/src/client/component/gsc/script_error.cpp +++ b/src/client/component/gsc/script_error.cpp @@ -113,7 +113,6 @@ namespace gsc return res; } - } unsigned int scr_get_object(unsigned int index) @@ -284,28 +283,23 @@ namespace gsc public: void post_unpack() override { - if (game::environment::is_sp()) - { - return; - } + scr_emit_function_hook.create(SELECT_VALUE(0x1403D3350, 0x14042E150), &scr_emit_function_stub); - scr_emit_function_hook.create(0x14042E150, &scr_emit_function_stub); - - utils::hook::call(0x14042E0E4, compile_error_stub); // LinkFile - utils::hook::call(0x14042E138, compile_error_stub); // LinkFile - utils::hook::call(0x14042E22B, find_variable_stub); // Scr_EmitFunction + utils::hook::call(SELECT_VALUE(0x1403D32E4, 0x14042E0E4), compile_error_stub); // LinkFile + utils::hook::call(SELECT_VALUE(0x1403D3338, 0x14042E138), compile_error_stub); // LinkFile + utils::hook::call(SELECT_VALUE(0x1403D342A, 0x14042E22B), find_variable_stub); // Scr_EmitFunction // Restore basic error messages to scr functions - utils::hook::jump(0x140438ED0, scr_get_object); - utils::hook::jump(0x140438AD0, scr_get_const_string); - utils::hook::jump(0x1404388B0, scr_get_const_istring); - utils::hook::jump(0x1404393D0, scr_get_vector); - utils::hook::jump(0x140438E10, scr_get_int); - utils::hook::jump(0x140438D60, scr_get_float); + utils::hook::jump(game::Scr_GetObject, scr_get_object); + utils::hook::jump(game::Scr_GetConstString, scr_get_const_string); + utils::hook::jump(game::Scr_GetConstIString, scr_get_const_istring); + utils::hook::jump(game::Scr_GetVector, scr_get_vector); + utils::hook::jump(game::Scr_GetInt, scr_get_int); + utils::hook::jump(game::Scr_GetFloat, scr_get_float); - utils::hook::jump(0x1404390B0, scr_get_pointer_type); - utils::hook::jump(0x140439280, scr_get_type); - utils::hook::jump(0x1404392F0, scr_get_type_name); + utils::hook::jump(SELECT_VALUE(0x1403DE150, 0x1404390B0), scr_get_pointer_type); + utils::hook::jump(SELECT_VALUE(0x1403DE320, 0x140439280), scr_get_type); + utils::hook::jump(SELECT_VALUE(0x1403DE390, 0x1404392F0), scr_get_type_name); } void pre_destroy() override diff --git a/src/client/component/gsc/script_extension.cpp b/src/client/component/gsc/script_extension.cpp index 753a861..bab3816 100644 --- a/src/client/component/gsc/script_extension.cpp +++ b/src/client/component/gsc/script_extension.cpp @@ -37,9 +37,60 @@ namespace gsc bool force_error_print = false; std::optional gsc_error_msg; + std::vector devmap_entries{}; + + std::optional get_devmap_entry(const std::uint8_t* codepos) + { + const auto itr = std::ranges::find_if(devmap_entries, [codepos](const devmap_entry& entry) -> bool + { + return codepos >= entry.bytecode && codepos < entry.bytecode + entry.size; + }); + + if (itr != devmap_entries.end()) + { + return *itr; + } + + return {}; + } + + std::optional> get_line_and_col_for_codepos(const std::uint8_t* codepos) + { + const auto entry = get_devmap_entry(codepos); + + if (!entry.has_value()) + { + return {}; + } + + std::optional> best_line_info{}; + std::uint32_t best_codepos = 0; + + assert(codepos >= entry->bytecode); + const std::uint32_t codepos_offset = static_cast(codepos - entry->bytecode); + + for (const auto& instruction : entry->devmap) + { + if (instruction.codepos > codepos_offset) + { + continue; + } + + if (best_line_info.has_value() && codepos_offset - instruction.codepos > codepos_offset - best_codepos) + { + continue; + } + + best_line_info = { { instruction.line, instruction.col } }; + best_codepos = instruction.codepos; + } + + return best_line_info; + } + unsigned int scr_get_function_stub(const char** p_name, int* type) { - const auto result = utils::hook::invoke(0x1403CD9F0, p_name, type); + const auto result = game::Scr_GetFunction(p_name, type); for (const auto& [name, func] : functions) { @@ -120,27 +171,36 @@ namespace gsc const auto pos = frame == game::scr_VmPub->function_frame ? game::scr_function_stack->pos : frame->fs.pos; const auto function = find_function(frame->fs.pos); + const char* location; if (function.has_value()) { - console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data()); + location = utils::string::va("function \"%s\" in file \"%s\"", function.value().first.data(), function.value().second.data()); } else { - console::warn("\tat unknown location %p\n", pos); + location = utils::string::va("unknown location %p", pos); } + + const auto line_info = get_line_and_col_for_codepos(reinterpret_cast(pos)); + if (line_info.has_value()) + { + location = utils::string::va("%s line \"%d\" column \"%d\"", location, line_info->first, line_info->second); + } + + console::warn("\tat %s\n", location); } } - void vm_error_stub(int mark_pos) + void vm_error_stub(const unsigned __int64 mark_pos) { if (!dvars::com_developer_script->current.enabled && !force_error_print) { - utils::hook::invoke(0x1404E4D00, mark_pos); + game::LargeLocalResetToMark(mark_pos); return; } console::warn("******* script runtime error ********\n"); - const auto opcode_id = *reinterpret_cast(0x144D57840); + const auto opcode_id = *reinterpret_cast(SELECT_VALUE(0x1455BE740, 0x144D57840)); const std::string error = gsc_error_msg.has_value() ? std::format(": {}", gsc_error_msg.value()) : std::string(); @@ -166,7 +226,7 @@ namespace gsc print_callstack(); console::warn("************************************\n"); - utils::hook::invoke(0x1404E4D00, mark_pos); + game::LargeLocalResetToMark(mark_pos); } void inc_in_param() @@ -228,6 +288,11 @@ namespace gsc scr_error(utils::string::va("Assert fail: %s", game::Scr_GetString(0))); } + void scr_cmd_is_dedicated_server() + { + game::Scr_AddInt(game::environment::is_dedi()); + } + const char* get_code_pos(const int index) { if (static_cast(index) >= game::scr_VmPub->outparamcount) @@ -248,6 +313,22 @@ namespace gsc } } + void add_devmap_entry(std::uint8_t* codepos, std::size_t size, const std::string& name, xsk::gsc::buffer devmap_buf) + { + std::vector devmap{}; + const auto* devmap_ptr = reinterpret_cast(devmap_buf.data); + + devmap.resize(devmap_ptr->num_instructions); + std::memcpy(devmap.data(), devmap_ptr->instructions, sizeof(dev_map_instruction) * devmap_ptr->num_instructions); + + devmap_entries.emplace_back(codepos, size, name, std::move(devmap)); + } + + void clear_devmap() + { + devmap_entries.clear(); + } + void add_function(const std::string& name, game::BuiltinFunction function) { ++function_id_start; @@ -271,27 +352,18 @@ namespace gsc utils::hook::set(SELECT_VALUE(0x14086F468, 0x1409E6CE8), scr_print); utils::hook::set(SELECT_VALUE(0x14086F480, 0x1409E6D00), scr_print_ln); - utils::hook::set(SELECT_VALUE(0x1403D353C, 0x14042E33C), 0x1000); // Scr_RegisterFunction + utils::hook::set(SELECT_VALUE(0x1403D353B + 1, 0x14042E33B + 1), 0x1000); // Scr_RegisterFunction utils::hook::set(SELECT_VALUE(0x1403D3542 + 4, 0x14042E342 + 4), RVA(&func_table)); // Scr_RegisterFunction - utils::hook::set(SELECT_VALUE(0x1403E0BDD + 3, 0x14043BBBE + 3), RVA(&func_table)); // VM_Execute_0 + // utils::hook::set(SELECT_VALUE(0x1403E0BDD + 3, 0x14043BBBE + 3), RVA(&func_table)); // VM_Execute_0 utils::hook::inject(SELECT_VALUE(0x1403D38E4 + 3, 0x14042E734 + 3), &func_table); // Scr_BeginLoadScripts - if (game::environment::is_sp()) - { - return; - } + utils::hook::nop(SELECT_VALUE(0x1403E0BDD + 5, 0x14043BBBE + 5), 2); + utils::hook::call(SELECT_VALUE(0x1403E0BDD, 0x14043BBBE), vm_call_builtin_function); - utils::hook::nop(0x14043BBBE + 5, 2); - utils::hook::call(0x14043BBBE, vm_call_builtin_function); + utils::hook::call(SELECT_VALUE(0x1403D391F, 0x14042E76F), scr_get_function_stub); - utils::hook::call(0x14043CEB1, vm_error_stub); - - utils::hook::call(0x14042E76F, scr_get_function_stub); - - utils::hook::set(0x1409E6E38, assert_ex_cmd); - utils::hook::set(0x1409E6E50, assert_msg_cmd); - utils::hook::set(0x1409E6E20, assert_cmd); + utils::hook::call(SELECT_VALUE(0x1403E1ED0, 0x14043CEB1), vm_error_stub); add_function("replacefunc", [] { @@ -309,10 +381,16 @@ namespace gsc command::execute(cmd); }); - add_function("isdedicated", [] + if (game::environment::is_sp()) { - game::Scr_AddInt(game::environment::is_dedi()); - }); + return; + } + + utils::hook::set(0x1409E6E38, assert_ex_cmd); + utils::hook::set(0x1409E6E50, assert_msg_cmd); + utils::hook::set(0x1409E6E20, assert_cmd); + + utils::hook::set(0x1409E94D0, scr_cmd_is_dedicated_server); } }; } diff --git a/src/client/component/gsc/script_extension.hpp b/src/client/component/gsc/script_extension.hpp index c8cc2f4..0205896 100644 --- a/src/client/component/gsc/script_extension.hpp +++ b/src/client/component/gsc/script_extension.hpp @@ -1,9 +1,36 @@ #pragma once +#include namespace gsc { extern void* func_table[0x1000]; +#pragma pack(push, 1) + struct dev_map_instruction + { + std::uint32_t codepos; + std::uint16_t line; + std::uint16_t col; + }; + + struct dev_map + { + std::uint32_t num_instructions; + dev_map_instruction instructions[1]; + }; +#pragma pack(pop) + + struct devmap_entry + { + const std::uint8_t* bytecode; + std::size_t size; + std::string script_name; + std::vector devmap; + }; + + void add_devmap_entry(std::uint8_t*, std::size_t, const std::string&, xsk::gsc::buffer); + void clear_devmap(); + void add_function(const std::string& name, game::BuiltinFunction function); void scr_error(const char* error); diff --git a/src/client/component/gsc/script_loading.cpp b/src/client/component/gsc/script_loading.cpp index 7532bee..fa5a9ec 100644 --- a/src/client/component/gsc/script_loading.cpp +++ b/src/client/component/gsc/script_loading.cpp @@ -3,15 +3,16 @@ #include "game/game.hpp" #include "game/dvars.hpp" -#include -#include -#include #include +#include +#include +#include -#include "component/filesystem.hpp" #include "component/console.hpp" +#include "component/filesystem.hpp" #include "component/scripting.hpp" +#include "script_extension.hpp" #include "script_loading.hpp" namespace gsc @@ -32,6 +33,7 @@ namespace gsc init_handles.clear(); loaded_scripts.clear(); script_allocator.clear(); + clear_devmap(); } bool read_raw_script_file(const std::string& name, std::string* data) @@ -114,6 +116,12 @@ namespace gsc loaded_scripts[real_name] = script_file_ptr; + const auto devmap = std::get<2>(output_script); + if (devmap.size > 0 && (gsc_ctx->build() & xsk::gsc::build::dev_maps) != xsk::gsc::build::prod) + { + add_devmap_entry(script_file_ptr->bytecode, byte_code_size, real_name, devmap); + } + return script_file_ptr; } catch (const std::exception& ex) @@ -180,13 +188,9 @@ namespace gsc } } - void load_scripts(const std::filesystem::path& root_dir) + void load_scripts_from_folder(const std::filesystem::path& root_dir, const std::filesystem::path& script_dir) { - const std::filesystem::path script_dir = root_dir / "scripts"; - if (!utils::io::directory_exists(script_dir.generic_string())) - { - return; - } + console::info("Scanning directory '%s' for custom GSC scripts...\n", script_dir.generic_string().data()); const auto scripts = utils::io::list_files(script_dir.generic_string()); for (const auto& script : scripts) @@ -204,6 +208,44 @@ namespace gsc } } + void load_scripts(const std::filesystem::path& root_dir) + { + const auto load = [&root_dir](const std::filesystem::path& folder) -> void + { + const std::filesystem::path script_dir = root_dir / folder; + if (utils::io::directory_exists(script_dir.generic_string())) + { + load_scripts_from_folder(root_dir, script_dir); + } + }; + + const std::filesystem::path base_dir = "scripts"; + + load(base_dir); + + const auto* map_name = game::Dvar_FindVar("mapname"); + + if (game::environment::is_sp()) + { + const std::filesystem::path game_folder = "sp"; + + load(base_dir / game_folder); + + load(base_dir / game_folder / map_name->current.string); + } + else + { + const std::filesystem::path game_folder = "mp"; + + load(base_dir / game_folder); + + load(base_dir / game_folder / map_name->current.string); + + const auto* game_type = game::Dvar_FindVar("g_gametype"); + load(base_dir / game_folder / game_type->current.string); + } + } + int db_is_x_asset_default(game::XAssetType type, const char* name) { if (loaded_scripts.contains(name)) @@ -218,12 +260,22 @@ namespace gsc { utils::hook::invoke(0x1403CCB10); - clear(); + for (const auto& path : filesystem::get_search_paths()) + { + load_scripts(path); + } + } + + int g_scr_load_script_and_label_stub(game::ScriptFunctions* functions) + { + const auto result = utils::hook::invoke(0x140349790, functions); for (const auto& path : filesystem::get_search_paths()) { load_scripts(path); } + + return result; } void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size) @@ -250,7 +302,26 @@ namespace gsc utils::hook::invoke(0x1403D2CA0); } - void scr_load_level_stub() + void scr_load_level_singleplayer_stub() + { + for (auto& function_handle : main_handles) + { + console::info("Executing '%s::main'\n", function_handle.first.data()); + const auto thread = game::Scr_ExecThread(static_cast(function_handle.second), 0); + game::RemoveRefToObject(thread); + } + + utils::hook::invoke(0x1403401B0); + + for (auto& function_handle : init_handles) + { + console::info("Executing '%s::init'\n", function_handle.first.data()); + const auto thread = game::Scr_ExecThread(static_cast(function_handle.second), 0); + game::RemoveRefToObject(thread); + } + } + + void scr_load_level_multiplayer_stub() { utils::hook::invoke(0x1403CDC60); @@ -345,31 +416,35 @@ namespace gsc utils::hook::call(SELECT_VALUE(0x14032D1E0, 0x1403CCED9), scr_begin_load_scripts_stub); // GScr_LoadScripts utils::hook::call(SELECT_VALUE(0x14032D345, 0x1403CD08D), scr_end_load_scripts_stub); // GScr_LoadScripts + // ProcessScript + utils::hook::call(SELECT_VALUE(0x1403DC887, 0x1404378D7), find_script); + utils::hook::call(SELECT_VALUE(0x1403DC897, 0x1404378E7), db_is_x_asset_default); + dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments"); - if (game::environment::is_sp()) + scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void { - return; - } - - // ProcessScript - utils::hook::call(0x1404378D7, find_script); - utils::hook::call(0x1404378E7, db_is_x_asset_default); - - // GScr_LoadScripts - utils::hook::call(0x1403CD009, gscr_load_game_type_script_stub); - - // Exec script handles - utils::hook::call(0x14039F64E, g_load_structs_stub); - utils::hook::call(0x14039F653, scr_load_level_stub); - - scripting::on_shutdown([](int free_scripts) - { - if (free_scripts) + if (clear_scripts && post_shutdown) { clear(); } }); + + if (game::environment::is_sp()) + { + utils::hook::call(0x14034996F, g_scr_load_script_and_label_stub); + + utils::hook::call(0x140316591, scr_load_level_singleplayer_stub); + } + else + { + // GScr_LoadScripts + utils::hook::call(0x1403CD009, gscr_load_game_type_script_stub); + + // Exec script handles + utils::hook::call(0x14039F64E, g_load_structs_stub); + utils::hook::call(0x14039F653, scr_load_level_multiplayer_stub); + } } }; } diff --git a/src/client/component/mods.cpp b/src/client/component/mods.cpp new file mode 100644 index 0000000..fd588b4 --- /dev/null +++ b/src/client/component/mods.cpp @@ -0,0 +1,157 @@ +#include +#include "loader/component_loader.hpp" +#include "game/game.hpp" +#include "game/dvars.hpp" + +#include "mods.hpp" + +#include + +namespace mods +{ + namespace + { + utils::hook::detour sys_create_file_hook; + + void db_build_os_path_from_source(const char* zone_name, game::FF_DIR source, unsigned int size, char* filename) + { + char user_map[MAX_PATH]{}; + + switch (source) + { + case game::FFD_DEFAULT: + (void)sprintf_s(filename, size, "%s\\%s.ff", std::filesystem::current_path().string().c_str(), zone_name); + break; + case game::FFD_MOD_DIR: + assert(mods::is_using_mods()); + + (void)sprintf_s(filename, size, "%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), (*dvars::fs_gameDirVar)->current.string, zone_name); + break; + case game::FFD_USER_MAP: + strncpy_s(user_map, zone_name, _TRUNCATE); + + (void)sprintf_s(filename, size, "%s\\%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), "usermaps", user_map, zone_name); + break; + default: + assert(false && "inconceivable"); + break; + } + } + + game::Sys_File sys_create_file_stub(const char* dir, const char* filename) + { + auto result = sys_create_file_hook.invoke(dir, filename); + + if (result.handle != INVALID_HANDLE_VALUE) + { + return result; + } + + if (!is_using_mods()) + { + return result; + } + + // .ff extension was added previously + if (!std::strcmp(filename, "mod.ff") && mods::db_mod_file_exists()) + { + char file_path[MAX_PATH]{}; + db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(file_path), file_path); + result.handle = game::Sys_OpenFileReliable(file_path); + } + + return result; + } + + void db_load_x_assets_stub(game::XZoneInfo* zone_info, unsigned int zone_count, game::DBSyncMode sync_mode) + { + std::vector zones(zone_info, zone_info + zone_count); + + if (db_mod_file_exists()) + { + zones.emplace_back("mod", game::DB_ZONE_COMMON | game::DB_ZONE_CUSTOM, 0); + } + + game::DB_LoadXAssets(zones.data(), static_cast(zones.size()), sync_mode); + } + + const auto skip_extra_zones_stub = utils::hook::assemble([](utils::hook::assembler& a) + { + const auto skip = a.newLabel(); + const auto original = a.newLabel(); + + a.pushad64(); + a.test(ebp, game::DB_ZONE_CUSTOM); // allocFlags + a.jnz(skip); + + a.bind(original); + a.popad64(); + a.mov(rdx, 0x140835F28); + a.mov(rcx, rsi); + a.call_aligned(strcmp); + a.jmp(0x1403217C0); + + a.bind(skip); + a.popad64(); + a.mov(r15d, 0x80); + a.not_(r15d); + a.and_(ebp, r15d); + a.jmp(0x1403217F6); + }); + } + + bool is_using_mods() + { + return (*dvars::fs_gameDirVar) && *(*dvars::fs_gameDirVar)->current.string; + } + + bool db_mod_file_exists() + { + if (!*(*dvars::fs_gameDirVar)->current.string) + { + return false; + } + + char filename[MAX_PATH]{}; + db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(filename), filename); + + if (auto zone_file = game::Sys_OpenFileReliable(filename); zone_file != INVALID_HANDLE_VALUE) + { + ::CloseHandle(zone_file); + return true; + } + + return false; + } + + class component final : public component_interface + { + public: + static_assert(sizeof(game::Sys_File) == 8); + + void post_unpack() override + { + dvars::fs_gameDirVar = reinterpret_cast(SELECT_VALUE(0x145856D38, 0x147876000)); + + // Remove DVAR_INIT from fs_game + utils::hook::set(SELECT_VALUE(0x14041C085 + 2, 0x1404DDA45 + 2), SELECT_VALUE(game::DVAR_FLAG_NONE, game::DVAR_FLAG_SERVERINFO)); + + if (game::environment::is_sp()) + { + return; + } + + // Don't load eng_ + patch_ with loadzone + utils::hook::nop(0x1403217B1, 15); + utils::hook::jump(0x1403217B1, skip_extra_zones_stub, true); + + // Add custom zone paths + sys_create_file_hook.create(game::Sys_CreateFile, sys_create_file_stub); + + // Load mod.ff + utils::hook::call(0x1405E7113, db_load_x_assets_stub); // R_LoadGraphicsAssets According to myself but I don't remember where I got it from + } + }; +} + +REGISTER_COMPONENT(mods::component) diff --git a/src/client/component/mods.hpp b/src/client/component/mods.hpp new file mode 100644 index 0000000..66ac5a1 --- /dev/null +++ b/src/client/component/mods.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace mods +{ + bool is_using_mods(); + bool db_mod_file_exists(); +} diff --git a/src/client/component/notifies.cpp b/src/client/component/notifies.cpp index 72f35ff..7fc89e4 100644 --- a/src/client/component/notifies.cpp +++ b/src/client/component/notifies.cpp @@ -126,7 +126,7 @@ namespace notifies a.lea(eax, dword_ptr(r15, -0x17)); a.mov(dword_ptr(rbp, 0x60), r15d); - a.jmp(0x14043A593); + a.jmp(SELECT_VALUE(0x1403DF5B3, 0x14043A593)); a.bind(replace); @@ -174,22 +174,22 @@ namespace notifies public: void post_unpack() override { + utils::hook::jump(SELECT_VALUE(0x1403DF5A4, 0x14043A584), utils::hook::assemble(vm_execute_stub), true); + + scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void + { + if (clear_scripts && post_shutdown) + { + vm_execute_hooks.clear(); + } + }); + if (game::environment::is_sp()) { return; } utils::hook::call(0x1404724DD, client_command_stub); - - utils::hook::jump(0x14043A584, utils::hook::assemble(vm_execute_stub), true); - - scripting::on_shutdown([](const bool free_scripts) - { - if (free_scripts) - { - vm_execute_hooks.clear(); - } - }); } }; } diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index bc073e6..f6ec909 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -81,6 +81,26 @@ namespace party a.mov(ecx, 2); a.jmp(0x1402C617D); }); + + utils::info_string get_info() + { + utils::info_string info; + + info.set("gamename", "IW6"); + info.set("hostname", dvars::get_string("sv_hostname")); + info.set("gametype", dvars::get_string("g_gametype")); + info.set("sv_motd", dvars::get_string("sv_motd")); + info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits)); + info.set("mapname", dvars::get_string("mapname")); + info.set("isPrivate", dvars::get_string("g_password").empty() ? "0" : "1"); + info.set("clients", std::to_string(get_client_count())); + info.set("bots", std::to_string(get_bot_count())); + info.set("sv_maxclients", std::to_string(*game::mp::svs_clientCount)); + info.set("protocol", std::to_string(PROTOCOL)); + info.set("shortversion", SHORTVERSION); + + return info; + } } void switch_gamemode_if_necessary(const std::string& gametype) @@ -360,7 +380,7 @@ namespace party const auto message = params.join(2); const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string; - game::SV_GameSendServerCommand(client_num, 0, utils::string::va("%c \"%s: %s\"", 84, name, message.data())); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data())); console::info("%s -> %i: %s\n", name, client_num, message.data()); }); @@ -374,7 +394,7 @@ namespace party const auto client_num = atoi(params.get(1)); const auto message = params.join(2); - game::SV_GameSendServerCommand(client_num, 0, utils::string::va("%c \"%s\"", 84, message.data())); + game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data())); console::info("%i: %s\n", client_num, message.data()); }); @@ -388,7 +408,7 @@ namespace party const auto message = params.join(1); const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string; - game::SV_GameSendServerCommand(-1, 0, utils::string::va("%c \"%s: %s\"", 84, name, message.data())); + game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data())); console::info("%s: %s\n", name, message.data()); }); @@ -401,30 +421,61 @@ namespace party const auto message = params.join(1); - game::SV_GameSendServerCommand(-1, 0, utils::string::va("%c \"%s\"", 84, message.data())); + game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data())); console::info("%s\n", message.data()); }); network::on("getInfo", [](const game::netadr_s& target, const std::string& data) { - utils::info_string info; + utils::info_string info = get_info(); info.set("challenge", data); - info.set("gamename", "IW6"); - info.set("hostname", dvars::get_string("sv_hostname")); - info.set("gametype", dvars::get_string("g_gametype")); - info.set("sv_motd", dvars::get_string("sv_motd")); - info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits)); - info.set("mapname", dvars::get_string("mapname")); - info.set("isPrivate", dvars::get_string("g_password").empty() ? "0" : "1"); - info.set("clients", std::to_string(get_client_count())); - info.set("bots", std::to_string(get_bot_count())); - info.set("sv_maxclients", std::to_string(*game::mp::svs_clientCount)); - info.set("protocol", std::to_string(PROTOCOL)); - info.set("shortversion", SHORTVERSION); network::send(target, "infoResponse", info.build(), '\n'); }); + network::on("getStatus", [](const game::netadr_s& target, const std::string& data) + { + std::string player_list; + + utils::info_string info = get_info(); + info.set("challenge", data); + + const auto* sv_running = game::Dvar_FindVar("sv_running"); + if (!sv_running || !sv_running->current.enabled) + { + return; + } + + for (auto i = 0; i < game::Dvar_FindVar("sv_maxclients")->current.integer; ++i) + { + auto* client = &game::mp::svs_clients[i]; + auto* self = &game::mp::g_entities[i]; + + if (client->header.state < game::CS_ACTIVE) + { + continue; + } + + if (!self || !self->client) + { + continue; + } + + if (game::SV_BotIsBot(i)) + { + continue; + } + + const auto score = game::G_GetClientScore(i); + const auto ping = game::mp::svs_clients[i].ping; + const std::string name = game::mp::svs_clients[i].name; + + player_list.append(std::format("{} {} \"{}\"\n", score, ping, name)); + } + + network::send(target, "statusResponse", info.build() + "\n"s + player_list + "\n"s, '\n'); + }); + if (game::environment::is_dedi()) { return; diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp index bf535a4..8b1b48c 100644 --- a/src/client/component/scripting.cpp +++ b/src/client/component/scripting.cpp @@ -34,7 +34,7 @@ namespace scripting std::unordered_map canonical_string_table; - std::vector> shutdown_callbacks; + std::vector> shutdown_callbacks; std::vector> init_callbacks; void scr_load_level_stub() @@ -47,9 +47,9 @@ namespace scripting } } - void g_shutdown_game_stub(const int free_scripts) + void g_shutdown_game_stub(const int clear_scripts) { - if (free_scripts) + if (clear_scripts) { script_function_table_sort.clear(); script_function_table.clear(); @@ -59,10 +59,15 @@ namespace scripting for (const auto& callback : shutdown_callbacks) { - callback(free_scripts); + callback(clear_scripts ,false); } - return g_shutdown_game_hook.invoke(free_scripts); + g_shutdown_game_hook.invoke(clear_scripts); + + for (const auto& callback : shutdown_callbacks) + { + callback(clear_scripts, true); + } } void process_script_stub(const char* filename) @@ -165,7 +170,7 @@ namespace scripting return find_token(id); } - void on_shutdown(const std::function& callback) + void on_shutdown(const std::function& callback) { shutdown_callbacks.push_back(callback); } diff --git a/src/client/component/scripting.hpp b/src/client/component/scripting.hpp index c841903..8f615bd 100644 --- a/src/client/component/scripting.hpp +++ b/src/client/component/scripting.hpp @@ -8,7 +8,7 @@ namespace scripting extern std::string current_file; - void on_shutdown(const std::function& callback); + void on_shutdown(const std::function& callback); void on_init(const std::function& callback); std::optional get_canonical_string(unsigned int id); diff --git a/src/client/game/dvars.cpp b/src/client/game/dvars.cpp index 63b4eb5..19a64c1 100644 --- a/src/client/game/dvars.cpp +++ b/src/client/game/dvars.cpp @@ -46,7 +46,9 @@ namespace dvars game::dvar_t* cg_legacyCrashHandling = nullptr; game::dvar_t* com_developer_script = nullptr; - game::dvar_t** com_developer; + game::dvar_t** com_developer = nullptr; + + game::dvar_t** fs_gameDirVar = nullptr; std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain) { diff --git a/src/client/game/dvars.hpp b/src/client/game/dvars.hpp index c273a6b..f25e287 100644 --- a/src/client/game/dvars.hpp +++ b/src/client/game/dvars.hpp @@ -47,6 +47,8 @@ namespace dvars extern game::dvar_t* com_developer_script; extern game::dvar_t** com_developer; + extern game::dvar_t** fs_gameDirVar; + std::string dvar_get_vector_domain(int components, const game::dvar_limits& domain); std::string dvar_get_domain(game::dvar_type type, const game::dvar_limits& domain); } diff --git a/src/client/game/game.cpp b/src/client/game/game.cpp index ed33f04..16229ad 100644 --- a/src/client/game/game.cpp +++ b/src/client/game/game.cpp @@ -25,6 +25,13 @@ namespace game return sv_cmd_args->argv[sv_cmd_args->nesting][index]; } + HANDLE Sys_OpenFileReliable(const char* filename) + { + return ::CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr); + } + namespace environment { launcher::mode mode = launcher::mode::none; diff --git a/src/client/game/game.hpp b/src/client/game/game.hpp index d0c6188..33dcb60 100644 --- a/src/client/game/game.hpp +++ b/src/client/game/game.hpp @@ -63,6 +63,8 @@ namespace game [[nodiscard]] int SV_Cmd_Argc(); [[nodiscard]] const char* SV_Cmd_Argv(int index); + [[nodiscard]] HANDLE Sys_OpenFileReliable(const char* filename); + [[nodiscard]] bool is_headless(); void show_error(const std::string& text, const std::string& title = "Error"); diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 87e6847..475fedb 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -47,6 +47,12 @@ namespace game PMF_UNK3 = 0x4000, }; + enum svscmd_type + { + SV_CMD_CAN_IGNORE, + SV_CMD_RELIABLE, + }; + enum XAssetType { ASSET_TYPE_PHYSPRESET = 0x0, @@ -127,6 +133,19 @@ namespace game DB_LOAD_SYNC_SKIP_ALWAYS_LOADED = 0x5, }; + enum + { + DB_ZONE_COMMON = 0x1, + DB_ZONE_UI = 0x2, + DB_ZONE_GAME = 0x4, + DB_ZONE_LOAD = 0x8, + DB_ZONE_DEV = 0x10, + DB_ZONE_BASEMAP = 0x20, + DB_ZONE_TRANSIENT_POOL = 0x40, + DB_ZONE_TRANSIENT_MASK = 0x40, + DB_ZONE_CUSTOM = 0x80, + }; + enum { THREAD_CONTEXT_MAIN = 0x0, @@ -1106,6 +1125,7 @@ namespace game DVAR_FLAG_LATCHED = 0x2, DVAR_FLAG_CHEAT = 0x4, DVAR_FLAG_REPLICATED = 0x8, + DVAR_FLAG_SERVERINFO = 0x400, DVAR_FLAG_WRITE = 0x800, DVAR_FLAG_READ = 0x2000, }; @@ -1553,6 +1573,13 @@ namespace game VAR_TOTAL_COUNT = 0x1B, }; + struct ScriptFunctions + { + int maxSize; + int count; + int* address; + }; + struct VariableStackBuffer { const char* pos; @@ -1952,8 +1979,8 @@ namespace game SurfaceFxTable *surfaceFx;*/ RawFile* rawfile; ScriptFile* scriptfile; - /*StringTable *stringTable; - LeaderboardDef *leaderboardDef; + StringTable* stringTable; + /*LeaderboardDef *leaderboardDef; StructuredDataDefSet *structuredDataDefSet; TracerDef *tracerDef; VehicleDef *vehDef; @@ -2015,6 +2042,18 @@ namespace game const void* weapCompleteDef; }; + enum FF_DIR + { + FFD_DEFAULT = 0x0, + FFD_MOD_DIR = 0x1, + FFD_USER_MAP = 0x2, + }; + + struct Sys_File + { + HANDLE handle; + }; + namespace sp { // very shit structures for the moment cuz i cba mapping the whole thing out right now... diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 69b3007..90f3897 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -8,12 +8,12 @@ namespace game * Functions **************************************************************/ - WEAK symbol AddRefToObject{0, 0x1404326D0}; + WEAK symbol AddRefToObject{0x1403D7A10, 0x1404326D0}; WEAK symbol AddRefToValue{0x1403D7740, 0x1404326E0}; WEAK symbol AllocThread{0, 0x1404329B0}; WEAK symbol AllocVariable{0x1403D7A70, 0x140432A10}; WEAK symbol RemoveRefToValue{0x1403D90F0, 0x1404340C0}; - WEAK symbol RemoveRefToObject{0, 0x140433FB0}; + WEAK symbol RemoveRefToObject{0x1403D8FE0, 0x140433FB0}; WEAK symbol RemoveVariableValue{0x1403D91C0, 0x140434190}; WEAK symbol AimAssist_AddToTargetList{0, 0x140139D80}; @@ -54,11 +54,11 @@ namespace game WEAK symbol DB_FindXAssetHeader{0x140272300, 0x14031F3A0}; WEAK symbol DB_XAssetExists{0x140276200, 0x1403245E0}; WEAK symbol DB_IsXAssetDefault{0x140273480 , 0x1403204D0}; - WEAK symbol DB_GetRawFileLen{0x0140272E80, 0x14031FF80}; + WEAK symbol DB_GetRawFileLen{0x140272E80, 0x14031FF80}; WEAK symbol DB_GetRawBuffer{0x140272D50, 0x14031FE50}; WEAK symbol DB_IsLocalized{0x140273210, 0x140320360}; - WEAK symbol PMem_AllocFromSource_NoDebug{0x0140430B80, 0x001404F46C0}; + WEAK symbol PMem_AllocFromSource_NoDebug{0x140430B80, 0x1404F46C0}; WEAK symbol PMem_Free{0x140430EC0 , 0x1404F4A30}; WEAK symbol Hunk_AllocateTempMemoryHighInternal{0x140423C70, 0x1404E4E20}; @@ -103,6 +103,7 @@ namespace game WEAK symbol G_FindConfigstringIndex{0x0, 0x140161F90}; WEAK symbol G_RunFrame{0x0, 0x1403A05E0}; + WEAK symbol G_GetClientScore{0x0, 0x14039EF60}; WEAK symbol HudElem_Alloc{0x0, 0x1403997E0}; @@ -132,8 +133,9 @@ namespace game WEAK symbolStructuredDataDef_GetAsset{0, 0x1404E6560}; WEAK symbolStringTable_GetAsset{0, 0x1404E6170}; - WEAK symbol StringTable_GetColumnValueForRow{0, 0x1404E61A0}; - WEAK symbol StringTable_LookupRowNumForValue{0, 0x1404E6260}; + WEAK symbol StringTable_GetColumnValueForRow{0, 0x1404E61A0}; + WEAK symbol StringTable_LookupRowNumForValue{0, 0x1404E6260}; + WEAK symbol StringTable_HashString{0x1404259A0, 0x1404E6320}; WEAK symbol LUI_OpenMenu{0x1403FD460, 0x1404B3610}; // Made up name, replaced by ScopedCriticalSection on Black Ops 3 @@ -163,13 +165,17 @@ namespace game WEAK symbol FindVariable{0x1403D84F0, 0x1404334A0}; WEAK symbol FindEntityId{0, 0x1404333A0}; WEAK symbol GetVariableName{0x1403D8E90, 0x140433E60}; - WEAK symbol GetEntityFieldValue{0x1403DC810, 0x140437860}; + WEAK symbol GetEntityFieldValue{0x1403DC810, 0x140437860}; WEAK symbol Scr_AllocVector{0x1403D9AF0, 0x140434A10}; - WEAK symbol Scr_GetString{0, 0x140439160}; + WEAK symbol Scr_GetString{0x1403DE200, 0x140439160}; + WEAK symbol Scr_GetConstString{0x1403DDAC0, 0x140438AD0}; + WEAK symbol Scr_GetConstIString{0x1403DD8A0, 0x1404388B0}; WEAK symbol Scr_AddInt{0x0, 0x140437E70}; WEAK symbol Scr_AddString{0x0, 0x1404381D0}; - WEAK symbol Scr_GetInt{0x0, 0x140438E10}; - WEAK symbol Scr_GetFloat{0, 0x140438D60}; + WEAK symbol Scr_GetInt{0x1403DDEB0, 0x140438E10}; + WEAK symbol Scr_GetFloat{0x1403DDD50, 0x140438D60}; + WEAK symbol Scr_GetVector{0x1403DE470, 0x1404393D0}; + WEAK symbol Scr_GetObject{0x1403DDF70, 0x140438ED0}; WEAK symbol Scr_GetNumParam{0x1403DDF60, 0x140438EC0}; WEAK symbol Scr_ClearOutParams{0x1403DD500, 0x140438600}; WEAK symbol Scr_GetEntityIdRef{0x1403DBDC0, 0x140436E10}; @@ -178,15 +184,16 @@ namespace game WEAK symbol Scr_NotifyId{0x1403DE730, 0x140439700}; WEAK symbol Scr_NotifyLevel{0x0, 0x1404397D0}; WEAK symbol Scr_GetEntityId{0x0, 0x140436D60}; - WEAK symbol Scr_CastString{0x0, 0x140434AC0}; + WEAK symbol Scr_CastString{0x1403D9BA0, 0x140434AC0}; WEAK symbol Scr_ExecThread{0x1403DD600, 0x1404386E0}; WEAK symbol Scr_LoadScript{0x1403D3C50, 0x14042EAA0}; WEAK symbol Scr_GetFunctionHandle{0x1403D3AD0 , 0x14042E920}; WEAK symbol Scr_RegisterFunction{0x1403D3530, 0x14042E330}; - WEAK symbol Scr_ErrorInternal{0x0, 0x140438660}; + WEAK symbol Scr_GetFunction{0x14034A950, 0x1403CD9F0}; + WEAK symbol Scr_ErrorInternal{0x1403DD580 , 0x140438660}; - WEAK symbol GetObjectType{0x0, 0x140433CF0}; + WEAK symbol GetObjectType{0x1403D8D30, 0x140433CF0}; WEAK symbol VM_Execute{0, 0x14043A280}; @@ -204,7 +211,6 @@ namespace game WEAK symbol SV_MatchEnd{0x0, 0x14047A090}; WEAK symbol SV_DirectConnect{0, 0x140471390}; - WEAK symbol SV_GameSendServerCommand{0x140490F40, 0x1404758C0}; WEAK symbol SV_Loaded{0x140491820, 0x1404770C0}; WEAK symbol SV_StartMap{0, 0x140470170}; WEAK symbol SV_StartMapForParty{0, 0x1404702F0}; @@ -220,6 +226,7 @@ namespace game WEAK symbol SV_GetGuid{0, 0x140475990}; WEAK symbol SV_KickClientNum{0, 0x14046F730}; WEAK symbol SV_SetConfigstring{0, 0x140477450}; + WEAK symbol SV_GameSendServerCommand{0x140490F40, 0x1404758C0}; WEAK symbol Sys_Error{0x14043AC20, 0x1404FF510}; WEAK symbol Sys_IsDatabaseReady2{0x1403C2D40, 0x140423920}; @@ -246,6 +253,8 @@ namespace game WEAK symbol Jump_ClearState{0x0, 0x140213120}; + WEAK symbol LargeLocalResetToMark{0x140423B50, 0x1404E4D00}; + WEAK symbol longjmp{0x14062E030, 0x140738060}; WEAK symbol _setjmp{0x14062F030, 0x140739060}; diff --git a/src/client/std_include.hpp b/src/client/std_include.hpp index 3f7c182..11b7eb2 100644 --- a/src/client/std_include.hpp +++ b/src/client/std_include.hpp @@ -5,7 +5,9 @@ #define BINARY_PAYLOAD_SIZE 0x0B500000 // Decide whether to load the game as lib or to inject it +#if 1 #define INJECT_HOST_AS_LIB +#endif #pragma warning(push) #pragma warning(disable: 4100) diff --git a/src/common/utils/properties.cpp b/src/common/utils/properties.cpp new file mode 100644 index 0000000..8c85bfd --- /dev/null +++ b/src/common/utils/properties.cpp @@ -0,0 +1,25 @@ +#include "properties.hpp" + +#include + +#include + +namespace utils::properties +{ + std::filesystem::path get_appdata_path() + { + PWSTR path; + if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path))) + { + throw std::runtime_error("Failed to read APPDATA path!"); + } + + auto _ = gsl::finally([&path] + { + CoTaskMemFree(path); + }); + + static auto appdata = std::filesystem::path(path) / "alterware"; + return appdata; + } +} diff --git a/src/common/utils/properties.hpp b/src/common/utils/properties.hpp new file mode 100644 index 0000000..92aff82 --- /dev/null +++ b/src/common/utils/properties.hpp @@ -0,0 +1,7 @@ +#pragma once +#include + +namespace utils::properties +{ + std::filesystem::path get_appdata_path(); +}