diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index 19a9e071..a69c5bc6 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -10,6 +10,7 @@ #include #include #include +#include //#define XFILE_DEBUG @@ -56,6 +57,28 @@ namespace fastfiles return db_load_x_zone_hook.invoke(parent_name, zone_flags, is_base_map, failure_mode); } + game::dvar_t* g_dump_scripts; + void dump_gsc_script(const std::string& name, game::XAssetHeader header) + { + if (!g_dump_scripts->current.enabled) + { + return; + } + + std::string buffer; + buffer.append(header.scriptfile->name, strlen(header.scriptfile->name) + 1); + buffer.append(reinterpret_cast(&header.scriptfile->compressedLen), 4); + buffer.append(reinterpret_cast(&header.scriptfile->len), 4); + buffer.append(reinterpret_cast(&header.scriptfile->bytecodeLen), 4); + buffer.append(header.scriptfile->buffer, header.scriptfile->compressedLen); + buffer.append(header.scriptfile->bytecode, header.scriptfile->bytecodeLen); + + const auto out_name = utils::string::va("gsc_dump/%s.gscbin", name.data()); + utils::io::write_file(out_name, buffer); + + console::info("Dumped %s\n", out_name); + } + game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, const int allow_create_default) { auto result = db_find_xasset_header_hook.invoke(type, name, allow_create_default); @@ -65,6 +88,10 @@ namespace fastfiles game::g_assetNames[static_cast(type)], name); } + if (type == game::ASSET_TYPE_SCRIPTFILE && result.scriptfile) + { + dump_gsc_script(name, result); + } return result; } } @@ -98,6 +125,8 @@ namespace fastfiles db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub); + g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts"); + command::add("listassetpool", [](const command::params& params) { if (params.size() < 2) diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp index e01ed850..225807e0 100644 --- a/src/client/component/filesystem.cpp +++ b/src/client/component/filesystem.cpp @@ -25,21 +25,44 @@ namespace filesystem return search_paths; } + void fs_display_path() + { + console::info("Current language: %s\n", game::SEH_GetLanguageName(*reinterpret_cast(0x74C6420_b))); + console::info("Current search paths:\n"); + + if (game::fs_searchpaths.get()) + { + for (auto i = game::fs_searchpaths.get()->next; i; i = i->next) + { + console::info("%s/%s\n", i->dir->path, i->dir->gamedir); + } + } + + for (auto path : filesystem::get_search_paths()) + { + console::info("%s\n", path.data()); + } + } + void fs_startup_stub(const char* name) { - console::info("[FS] Startup\n"); + console::info("----- FS_Startup -----\n"); initialized = true; filesystem::register_path(L"."); filesystem::register_path(L"iw7-mod"); - filesystem::register_path(L"devraw"); filesystem::register_path(L"devraw_shared"); + filesystem::register_path(L"devraw"); filesystem::register_path(L"raw_shared"); filesystem::register_path(L"raw"); + filesystem::register_path(L"main_shared"); filesystem::register_path(L"main"); fs_startup_hook.invoke(name); + + fs_display_path(); + console::info("----------------------\n"); } std::vector get_paths(const std::filesystem::path& path) @@ -143,7 +166,7 @@ namespace filesystem { if (can_insert_path(path_)) { - console::info("[FS] Registering path '%s'\n", path_.generic_string().data()); + console::debug("[FS] Registering path '%s'\n", path_.generic_string().data()); get_search_paths_internal().push_front(path_); } } diff --git a/src/client/component/gsc/script_error.cpp b/src/client/component/gsc/script_error.cpp index 456aadd9..208bf21f 100644 --- a/src/client/component/gsc/script_error.cpp +++ b/src/client/component/gsc/script_error.cpp @@ -279,26 +279,6 @@ namespace gsc scr_error(va("Parameter %u does not exist", index + 1)); return nullptr; } - - template - void safe_func() - { - static utils::hook::detour hook; - static const auto stub = []() - { - __try - { - hook.invoke(); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - game::Scr_ErrorInternal(); - } - }; - - const auto ptr = rva + 0_b; - hook.create(reinterpret_cast(ptr), stub); - } } std::optional> find_function(const char* pos) @@ -342,11 +322,6 @@ namespace gsc utils::hook::jump(0xC0BDE0_b, scr_get_type); utils::hook::jump(0xC0BE50_b, scr_get_type_name); } - - void pre_destroy() override - { - scr_emit_function_hook.clear(); - } }; } diff --git a/src/client/component/gsc/script_extension.cpp b/src/client/component/gsc/script_extension.cpp index 934f48ef..088f6dea 100644 --- a/src/client/component/gsc/script_extension.cpp +++ b/src/client/component/gsc/script_extension.cpp @@ -193,8 +193,7 @@ namespace gsc void vm_error_stub(int mark_pos) { - //const bool dev_script = developer_script ? developer_script->current.enabled : false; - bool dev_script = true; + const bool dev_script = developer_script ? developer_script->current.enabled : false; if (!dev_script && !force_error_print) { @@ -327,7 +326,7 @@ namespace gsc public: void post_unpack() override { - //developer_script = dvars::register_bool("developer_script", false, 0, "Enable developer script comments"); + developer_script = game::Dvar_RegisterBool("developer_script", true, 0, "Enable developer script comments"); // enable by default for now /* utils::hook::set(0xBFD16C_b, 0x1000); // change builtin func count @@ -476,4 +475,4 @@ namespace gsc }; } -//REGISTER_COMPONENT(gsc::extension) +REGISTER_COMPONENT(gsc::extension) diff --git a/src/client/component/gsc/script_loading.cpp b/src/client/component/gsc/script_loading.cpp index 6116b6ef..cb3788c1 100644 --- a/src/client/component/gsc/script_loading.cpp +++ b/src/client/component/gsc/script_loading.cpp @@ -6,6 +6,8 @@ #include "component/filesystem.hpp" #include "component/scripting.hpp" +#include "script_extension.hpp" + #include "game/game.hpp" #include "script_loading.hpp" @@ -121,21 +123,18 @@ namespace gsc return nullptr; } - // TODO: check if needed in IW7 // 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)) { - 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"))) + if (real_name.starts_with(utils::string::va("scripts/%s/maps/", game::Com_GameMode_GetActiveGameModeStr())) + && real_name.ends_with("_fx")) { console::debug("Refusing to compile rawfile '%s'\n", real_name.data()); return game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, file_name, false).scriptfile; } } - */ console::debug("Loading custom gsc '%s.gsc'", real_name.data()); @@ -150,7 +149,7 @@ namespace gsc 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 bytecode = output_script.first; const auto stack = output_script.second; const auto script_file_ptr = static_cast(scriptfile_allocator.allocate(sizeof(game::ScriptFile))); @@ -272,17 +271,7 @@ namespace gsc } } - int db_is_x_asset_default(game::XAssetType type, const char* name) - { - if (loaded_scripts.contains(name)) - { - return 0; - } - - return game::DB_IsXAssetDefault(type, name); - } - - void load_scripts_stub() + void load_scripts() { if (!game::Com_FrontEnd_IsInFrontEnd()) { @@ -293,27 +282,13 @@ namespace gsc load_scripts(path, "custom_scripts/"s + game::Com_GameMode_GetActiveGameModeStr() + "/"); } } - - utils::hook::invoke(0xB50670_b); } - void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size) + void init_compiler() { - if (rawfile->len > 0 && rawfile->compressedLen == 0) - { - std::memset(buf, 0, size); - std::memcpy(buf, rawfile->buffer, std::min(rawfile->len, size)); - return; - } - - game::DB_GetRawBuffer(rawfile, buf, size); - } - - void scr_begin_load_scripts_stub(bool a1) - { - const bool dev_script = true; + const bool dev_script = developer_script ? developer_script->current.enabled : false; const auto comp_mode = dev_script ? - xsk::gsc::build::dev: + xsk::gsc::build::dev : xsk::gsc::build::prod; gsc_ctx->init(comp_mode, [](const std::string& include_name) @@ -336,18 +311,27 @@ namespace gsc std::vector script_data; script_data.assign(file_buffer.begin(), file_buffer.end()); - return {{}, script_data}; + return { {}, script_data }; }); - - scr_begin_load_scripts_hook.invoke(a1); } - void scr_end_load_scripts_stub() + void scr_begin_load_scripts_stub(bool a1) + { + // start the compiler + init_compiler(); + + scr_begin_load_scripts_hook.invoke(a1); + + // load scripts + load_scripts(); + } + + void scr_end_load_scripts_stub(const char* a1) { // cleanup the compiler gsc_ctx->cleanup(); - scr_end_load_scripts_hook.invoke(); + scr_end_load_scripts_hook.invoke(a1); } utils::hook::detour g_load_structs_hook; @@ -370,17 +354,30 @@ namespace gsc console::debug("Executing '%s::init'\n", function_handle.first.data()); game::RemoveRefToObject(game::Scr_ExecThread(function_handle.second, 0)); } + scr_load_level_hook.invoke(); } - utils::hook::detour g_shutdown_game_hook; - void g_shutdown_game_stub(int full_clear, int a2) + int db_is_x_asset_default(game::XAssetType type, const char* name) { - if (full_clear && a2) + if (loaded_scripts.contains(name)) { - clear(); + return 0; } - g_shutdown_game_hook.invoke(full_clear, a2); + + return game::DB_IsXAssetDefault(type, name); + } + + void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size) + { + if (rawfile->len > 0 && rawfile->compressedLen == 0) + { + std::memset(buf, 0, size); + std::memcpy(buf, rawfile->buffer, std::min(rawfile->len, size)); + return; + } + + game::DB_GetRawBuffer(rawfile, buf, size); } // donetsk developers will paste this in days @@ -436,9 +433,15 @@ namespace gsc // Allocate script memory (PMem doesn't work) db_alloc_x_zone_memory_internal_hook.create(0xA75450_b, db_alloc_x_zone_memory_internal_stub); + // Increase allocated script memory + utils::hook::set(0xA75B5C_b + 1, 0x480000 + static_cast(script_memory.size)); + utils::hook::set(0xA75BAA_b + 4, 0x480 + (static_cast(script_memory.size) >> 12)); + utils::hook::set(0xA75BBE_b + 6, 0x480 + (static_cast(script_memory.size) >> 12)); + // Load our scripts with an uncompressed stack utils::hook::call(0xC09DA7_b, db_get_raw_buffer_stub); + // Compiler start and cleanup, also loads scripts scr_begin_load_scripts_hook.create(0xBFD500_b, scr_begin_load_scripts_stub); scr_end_load_scripts_hook.create(0xBFD630_b, scr_end_load_scripts_stub); @@ -446,23 +449,19 @@ namespace gsc utils::hook::call(0xC09D37_b, find_script); utils::hook::call(0xC09D47_b, db_is_x_asset_default); - // GScr_LoadScripts: initial loading of scripts - utils::hook::call(0xB5CB70_b, load_scripts_stub); - - // execute main handle after G_LoadStructs (now called G_Spawn_LoadStructs) - //g_load_structs_hook.create(0x409FB0_b, g_load_structs_stub); + // execute main handle + g_load_structs_hook.create(0x409FB0_b, g_load_structs_stub); // execute init handle scr_load_level_hook.create(0xB51B40_b, scr_load_level_stub); - // clear memory (SV_GameMP_ShutdownGameVM) - g_shutdown_game_hook.create(0xBB36D86_b, g_shutdown_game_stub); - } - - void pre_destroy() override - { - scr_begin_load_scripts_hook.clear(); - scr_end_load_scripts_hook.clear(); + scripting::on_shutdown([](bool free_scripts, bool post_shutdown) + { + if (free_scripts && post_shutdown) + { + clear(); + } + }); } }; } diff --git a/src/client/component/logger.cpp b/src/client/component/logger.cpp index e127c244..b53515c8 100644 --- a/src/client/component/logger.cpp +++ b/src/client/component/logger.cpp @@ -7,6 +7,8 @@ #include +#include "version.h" + namespace logger { namespace @@ -77,6 +79,17 @@ namespace logger vsnprintf(buffer, buffer_length, msg, va); console::warn(buffer); } + + void com_init_stub() + { + console::info("%s %s build %s %s\n", "IW7", VERSION, "win64", __DATE__); + + console::info("--- Common Initialization ---\n"); + utils::hook::invoke(0xB8EF90_b); + console::info("--- Common Initialization Complete ---\n"); + + console::info("Working directory: %s\n", game::Sys_Cwd()); + } } class component final : public component_interface @@ -93,6 +106,8 @@ namespace logger // Com_Printf utils::hook::jump(0x343080_b, print_info); + utils::hook::call(0xD4D8D8_b, com_init_stub); + if (!game::environment::is_dedi()) { // R_WarnOncePerFrame diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp index ad1d70cc..b7c65d9f 100644 --- a/src/client/component/scripting.cpp +++ b/src/client/component/scripting.cpp @@ -24,17 +24,11 @@ namespace scripting std::unordered_map>> script_function_table_sort; std::unordered_map> script_function_table_rev; - utils::concurrency::container shared_table; - std::string current_file; namespace { utils::hook::detour vm_notify_hook; - utils::hook::detour vm_execute_hook; - utils::hook::detour g_load_structs_hook; - utils::hook::detour scr_load_level_hook; - utils::hook::detour g_shutdown_game_hook; utils::hook::detour scr_add_class_field_hook; @@ -53,7 +47,7 @@ namespace scripting void vm_notify_stub(const unsigned int notify_list_owner_id, const game::scr_string_t string_value, game::VariableValue* top) { - if (!game::Com_FrontEndScene_IsActive()) + if (!game::Com_FrontEnd_IsInFrontEnd()) { const auto* string = game::SL_ConvertToString(string_value); if (string) @@ -72,31 +66,6 @@ namespace scripting vm_notify_hook.invoke(notify_list_owner_id, string_value, top); } - void g_shutdown_game_stub(const int free_scripts) - { - if (free_scripts) - { - script_function_table_sort.clear(); - script_function_table.clear(); - script_function_table_rev.clear(); - canonical_string_table.clear(); - } - - for (const auto& callback : shutdown_callbacks) - { - callback(free_scripts, false); - } - - scripting::notify(*game::levelEntityId, "shutdownGame_called", {1}); - - g_shutdown_game_hook.invoke(); - - for (const auto& callback : shutdown_callbacks) - { - callback(free_scripts, true); - } - } - void scr_add_class_field_stub(unsigned int classnum, game::scr_string_t name, unsigned int canonical_string, unsigned int offset) { const auto name_str = game::SL_ConvertToString(name); @@ -180,6 +149,91 @@ namespace scripting canonical_string_table[result] = str; return result; } + + void shutdown_game_pre(const int free_scripts) + { + if (free_scripts) + { + script_function_table_sort.clear(); + script_function_table.clear(); + script_function_table_rev.clear(); + canonical_string_table.clear(); + } + + for (const auto& callback : shutdown_callbacks) + { + callback(free_scripts, false); + } + + scripting::notify(*game::levelEntityId, "shutdownGame_called", { 1 }); + } + + void shutdown_game_post(const int free_scripts) + { + for (const auto& callback : shutdown_callbacks) + { + callback(free_scripts, true); + } + } + + namespace mp + { + utils::hook::detour sv_initgame_vm_hook; + utils::hook::detour sv_shutdowngame_vm_hook; + + void sv_initgame_vm_stub(game::sv::SvServerInitSettings* init_settings) + { + if (!game::Com_FrontEnd_IsInFrontEnd()) + { + console::info("------- Game Initialization -------\n"); + console::info("gamename: %s\n", "Call of Duty Infinite Warfare"); + console::info("gamedate: %s\n", __DATE__); + + //G_LogPrintf("------------------------------------------------------------\n"); + //G_LogPrintf("InitGame: %s\n", serverinfo); + } + + sv_initgame_vm_hook.invoke(init_settings); + + if (!game::Com_FrontEnd_IsInFrontEnd()) + { + console::info("-----------------------------------\n"); + } + } + + void sv_shutdowngame_vm_stub(int full_clear, int a2) + { + if (!game::Com_FrontEnd_IsInFrontEnd()) + { + console::info("==== ShutdownGame (%d) ====\n", full_clear); + + //G_LogPrintf("ShutdownGame:\n"); + //G_LogPrintf("------------------------------------------------------------\n"); + } + + shutdown_game_pre(full_clear); + sv_shutdowngame_vm_hook.invoke(full_clear, a2); + shutdown_game_post(full_clear); + } + } + + namespace sp + { + utils::hook::detour sv_initgame_vm_hook; + utils::hook::detour sv_shutdowngame_vm_hook; + + void sv_initgame_vm_stub(int random_seed, int restart, int* savegame, void** save, int load_scripts) + { + sv_initgame_vm_hook.invoke(random_seed, restart, savegame, save, load_scripts); + } + + void sv_shutdowngame_vm_stub(int full_clear, int a2) + { + shutdown_game_pre(full_clear); + sv_shutdowngame_vm_hook.invoke(full_clear, a2); + shutdown_game_post(full_clear); + } + } } std::string get_token(unsigned int id) @@ -219,6 +273,11 @@ namespace scripting scr_set_thread_position_hook.create(0xBFD190_b, scr_set_thread_position_stub); process_script_hook.create(0xC09D20_b, process_script_stub); sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, sl_get_canonical_string_stub); + + mp::sv_initgame_vm_hook.create(0xBA3428D_b, mp::sv_initgame_vm_stub); + sp::sv_initgame_vm_hook.create(0xBED4A96_b, sp::sv_initgame_vm_stub); + mp::sv_shutdowngame_vm_hook.create(0xBB36D86_b, mp::sv_shutdowngame_vm_stub); + sp::sv_shutdowngame_vm_hook.create(0x12159B6_b, sp::sv_shutdowngame_vm_stub); } }; } diff --git a/src/client/component/scripting.hpp b/src/client/component/scripting.hpp index 12598b9c..46048217 100644 --- a/src/client/component/scripting.hpp +++ b/src/client/component/scripting.hpp @@ -3,15 +3,11 @@ namespace scripting { - using shared_table_t = std::unordered_map; - extern std::unordered_map> fields_table; extern std::unordered_map> script_function_table; extern std::unordered_map>> script_function_table_sort; extern std::unordered_map> script_function_table_rev; - extern utils::concurrency::container shared_table; - extern std::string current_file; void on_shutdown(const std::function& callback); diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index ddfbb152..27aea825 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -3246,6 +3246,22 @@ namespace game ERR_MAPLOADERRORSUMMARY = 7, }; + struct directory_t + { + char path[256]; + char gamedir[256]; + }; + + struct searchpath_s + { + searchpath_s* next; + directory_t* dir; + int bLocalized; + int playersFolder; + int language; + int pad; + }; + struct scr_entref_t { unsigned short entnum; diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index efbab8d4..078941a1 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -129,6 +129,9 @@ namespace game int entnum, int offset)> GetEntityFieldValue{ 0xC09CC0 }; WEAK symbol G_MainMP_GetClientScore{ 0xB20550 }; + WEAK symbol G_GetWeaponForName { 0x733D40 }; + WEAK symbol G_GivePlayerWeapon{ 0x733D40 }; + WEAK symbol G_InitializeAmmo{ 0x733D40 }; WEAK symbol I_CleanStr{ 0xCFACC0 }; @@ -171,6 +174,8 @@ namespace game #define R_AddCmdDrawTextWithCursor(TXT, MC, F, UNK, X, Y, XS, YS, R, C, S, CP, CC) \ IW7_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, CP, CC, game::R_DrawSomething(S), 0, 0, 0, 0) + WEAK symbol Sys_Cwd{ 0xCFE5A0 }; + WEAK symbol Sys_Milliseconds{ 0xD58110 }; WEAK symbol Sys_CreateFile{ 0xCFDF50 }; @@ -178,6 +183,9 @@ namespace game WEAK symbol Sys_SendPacket{ 0xD57DE0 }; WEAK symbol Sys_GetPacket{ 0xD57D50 }; + WEAK symbol SEH_GetCurrentLanguageName{ 0xCBB090 }; + WEAK symbol SEH_GetLanguageName{ 0xCBB140 }; + WEAK symbol PMem_BeginAlloc{ 0xCF0E10 }; WEAK symbol PMem_EndAlloc{ 0xCF1070 }; WEAK symbol PMem_AllocFromSource_NoDebug{ 0xCF0A90 }; @@ -221,6 +229,7 @@ namespace game WEAK symbol SV_Loaded{ 0xC114C0 }; WEAK symbol SV_MapExists{ 0xCDB620 }; WEAK symbol SV_BotIsBot{ 0xC3BC90 }; + WEAK symbol SV_GetPlayerstateForClientNum{ 0xC123A0 }; WEAK symbol SND_StopSounds{ 0xCA06E0 }; WEAK symbol SND_SetMusicState{ 0xC9E110 }; @@ -273,9 +282,8 @@ namespace game WEAK symbol scr_VmPub{ 0x6B183B0 }; WEAK symbol scr_function_stack{ 0x6B22908 }; - WEAK game::symbol pmem_size{ 0x7686A28 }; - WEAK game::symbol pmem_buffer{ 0x7686A20 }; + WEAK symbol g_mem{ 0x7685560 }; + WEAK symbol g_scriptmem{ 0x7685FC0 }; - WEAK game::symbol g_mem{ 0x7685560 }; - WEAK game::symbol g_scriptmem{ 0x7685FC0 }; + WEAK symbol fs_searchpaths{ 0x756DEE0 }; }