more progress

This commit is contained in:
quaK 2023-12-30 20:27:32 +02:00
parent 4d03122d67
commit 0e1c8f02de
10 changed files with 248 additions and 129 deletions

View File

@ -10,6 +10,7 @@
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/concurrency.hpp>
#include <utils/io.hpp>
//#define XFILE_DEBUG
@ -56,6 +57,28 @@ namespace fastfiles
return db_load_x_zone_hook.invoke<void>(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<char*>(&header.scriptfile->compressedLen), 4);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->len), 4);
buffer.append(reinterpret_cast<char*>(&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<game::XAssetHeader>(type, name, allow_create_default);
@ -65,6 +88,10 @@ namespace fastfiles
game::g_assetNames[static_cast<unsigned int>(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)

View File

@ -25,21 +25,44 @@ namespace filesystem
return search_paths;
}
void fs_display_path()
{
console::info("Current language: %s\n", game::SEH_GetLanguageName(*reinterpret_cast<int*>(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<void>(name);
fs_display_path();
console::info("----------------------\n");
}
std::vector<std::filesystem::path> 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_);
}
}

View File

@ -279,26 +279,6 @@ namespace gsc
scr_error(va("Parameter %u does not exist", index + 1));
return nullptr;
}
template <size_t rva>
void safe_func()
{
static utils::hook::detour hook;
static const auto stub = []()
{
__try
{
hook.invoke<void>();
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
game::Scr_ErrorInternal();
}
};
const auto ptr = rva + 0_b;
hook.create(reinterpret_cast<void*>(ptr), stub);
}
}
std::optional<std::pair<std::string, std::string>> 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();
}
};
}

View File

@ -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<uint32_t>(0xBFD16C_b, 0x1000); // change builtin func count
@ -476,4 +475,4 @@ namespace gsc
};
}
//REGISTER_COMPONENT(gsc::extension)
REGISTER_COMPONENT(gsc::extension)

View File

@ -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<game::ScriptFile*>(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<void>(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<std::uint8_t> script_data;
script_data.assign(file_buffer.begin(), file_buffer.end());
return {{}, script_data};
return { {}, script_data };
});
scr_begin_load_scripts_hook.invoke<void>(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<void>(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<void>();
scr_end_load_scripts_hook.invoke<void>(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<void>();
}
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<void>(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<uint32_t>(0xA75B5C_b + 1, 0x480000 + static_cast<std::uint32_t>(script_memory.size));
utils::hook::set<uint32_t>(0xA75BAA_b + 4, 0x480 + (static_cast<std::uint32_t>(script_memory.size) >> 12));
utils::hook::set<uint32_t>(0xA75BBE_b + 6, 0x480 + (static_cast<std::uint32_t>(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();
}
});
}
};
}

View File

@ -7,6 +7,8 @@
#include <utils/hook.hpp>
#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<void>(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

View File

@ -24,17 +24,11 @@ namespace scripting
std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
std::unordered_map<const char*, std::pair<std::string, std::string>> script_function_table_rev;
utils::concurrency::container<shared_table_t> 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<void>(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<void>();
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<void>(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<void>(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<void>(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<void>(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);
}
};
}

View File

@ -3,15 +3,11 @@
namespace scripting
{
using shared_table_t = std::unordered_map<std::string, std::string>;
extern std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
extern std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
extern std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
extern std::unordered_map<const char*, std::pair<std::string, std::string>> script_function_table_rev;
extern utils::concurrency::container<shared_table_t> shared_table;
extern std::string current_file;
void on_shutdown(const std::function<void(bool, bool)>& callback);

View File

@ -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;

View File

@ -129,6 +129,9 @@ namespace game
int entnum, int offset)> GetEntityFieldValue{ 0xC09CC0 };
WEAK symbol<int(int clientNum)> G_MainMP_GetClientScore{ 0xB20550 };
WEAK symbol<void* (void*, const char* name)> G_GetWeaponForName { 0x733D40 };
WEAK symbol<int (void*, const void* weapon, int dualWield, int startInAltMode, int usedBefore)> G_GivePlayerWeapon{ 0x733D40 };
WEAK symbol<int(void*, const void* weapon, const bool isAlternate, int hadWeapon)> G_InitializeAmmo{ 0x733D40 };
WEAK symbol<char* (char* string)> 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<char* ()> Sys_Cwd{ 0xCFE5A0 };
WEAK symbol<int()> Sys_Milliseconds{ 0xD58110 };
WEAK symbol<HANDLE(Sys_Folder folder, const char* baseFilename)> Sys_CreateFile{ 0xCFDF50 };
@ -178,6 +183,9 @@ namespace game
WEAK symbol<int(int length, void const* data, const netadr_s* to)> Sys_SendPacket{ 0xD57DE0 };
WEAK symbol<int(netadr_s* net_from, msg_t* net_message)> Sys_GetPacket{ 0xD57D50 };
WEAK symbol<char* ()> SEH_GetCurrentLanguageName{ 0xCBB090 };
WEAK symbol<char* (int code)> SEH_GetLanguageName{ 0xCBB140 };
WEAK symbol<void(const char* name, int allocDir)> PMem_BeginAlloc{ 0xCF0E10 };
WEAK symbol<void(const char* name, int allocDir)> PMem_EndAlloc{ 0xCF1070 };
WEAK symbol<char* (const size_t size, unsigned int alignment, int type, int source)> PMem_AllocFromSource_NoDebug{ 0xCF0A90 };
@ -221,6 +229,7 @@ namespace game
WEAK symbol<bool()> SV_Loaded{ 0xC114C0 };
WEAK symbol<bool(const char* name)> SV_MapExists{ 0xCDB620 };
WEAK symbol<bool(int clientNum)> SV_BotIsBot{ 0xC3BC90 };
WEAK symbol<void* (int num)> SV_GetPlayerstateForClientNum{ 0xC123A0 };
WEAK symbol<void(int)> SND_StopSounds{ 0xCA06E0 };
WEAK symbol<void(const char*)> SND_SetMusicState{ 0xC9E110 };
@ -273,9 +282,8 @@ namespace game
WEAK symbol<scrVmPub_t> scr_VmPub{ 0x6B183B0 };
WEAK symbol<function_stack_t> scr_function_stack{ 0x6B22908 };
WEAK game::symbol<unsigned __int64> pmem_size{ 0x7686A28 };
WEAK game::symbol<unsigned char*> pmem_buffer{ 0x7686A20 };
WEAK symbol<PhysicalMemory> g_mem{ 0x7685560 };
WEAK symbol<PhysicalMemory> g_scriptmem{ 0x7685FC0 };
WEAK game::symbol<PhysicalMemory> g_mem{ 0x7685560 };
WEAK game::symbol<PhysicalMemory> g_scriptmem{ 0x7685FC0 };
WEAK symbol<searchpath_s> fs_searchpaths{ 0x756DEE0 };
}