Christmas Day Update

This commit is contained in:
6arelyFuture 2024-12-18 12:39:55 +01:00
parent 0090d894c4
commit 45907301ee
Signed by: Future
GPG Key ID: F2000F181A4F7C85
36 changed files with 724 additions and 184 deletions

2
deps/GSL vendored

@ -1 +1 @@
Subproject commit f1a494cfd2ce55fe88b5134eab985f5852667b8d
Subproject commit 272463043ef3e442f6c80a530d3dd38ee6781381

2
deps/WinToast vendored

@ -1 +1 @@
Subproject commit 821c4818ade1aa4da56ac753285c159ce26fd597
Subproject commit a78ce469b456c06103b3b30d4bd37e7bb80da30c

2
deps/asmjit vendored

@ -1 +1 @@
Subproject commit bfa0bf690c2e90cc0844f2f012efa41b916bde7e
Subproject commit cfc9f813cc6ccda63cad872edb32b38e0662bedb

2
deps/gsc-tool vendored

@ -1 +1 @@
Subproject commit b8e30e6334aa33ea731caf8d0700a9e6c7794c09
Subproject commit 9ff3b871ef59b14eb8b32d258bf18347e14a3e18

2
deps/libtomcrypt vendored

@ -1 +1 @@
Subproject commit 7e863d21429f94ed6a720e24499a12a3f852bb31
Subproject commit c900951dab1bb94bab803fc57688dac18e3b71f9

2
deps/libtommath vendored

@ -1 +1 @@
Subproject commit 8314bde5e5c8e5d9331460130a9d1066e324f091
Subproject commit 5809141a3a6ec1bf3443c927c02b955e19224016

2
deps/minhook vendored

@ -1 +1 @@
Subproject commit f5485b8454544c2f034c78f8f127c1d03dea3636
Subproject commit c1a7c3843bd1a5fe3eb779b64c0d823bca3dc339

2
deps/rapidjson vendored

@ -1 +1 @@
Subproject commit 6089180ecb704cb2b136777798fa1be303618975
Subproject commit d621dc9e9c77f81e5c8a35b8dcc16dcd63351321

2
deps/zlib vendored

@ -1 +1 @@
Subproject commit 9f0f2d4f9f1f28be7e16d8bf3b4e9d4ada70aa9f
Subproject commit ef24c4c7502169f016dcd2a26923dbaf3216748c

View File

@ -0,0 +1,35 @@
#include <std_include.hpp>
#include <loader/component_loader.hpp>
#include "game/game.hpp"
#include <utils/memory.hpp>
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<std::size_t>(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)

View File

@ -8,11 +8,13 @@
#include "console.hpp"
#include "network.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/smbios.hpp>
#include <utils/info_string.hpp>
#include <utils/cryptography.hpp>
#include <utils/hook.hpp>
#include <utils/info_string.hpp>
#include <utils/io.hpp>
#include <utils/properties.hpp>
#include <utils/smbios.hpp>
#include <utils/string.hpp>
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()

View File

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

View File

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

View File

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

View File

@ -1,12 +1,12 @@
#include <std_include.hpp>
#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 <utils/hook.hpp>
#include <utils/memory.hpp>
@ -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<void>(zone_name, zone_flags, is_base_map);
db_try_load_x_file_internal_hook.invoke<void>(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 <zone>\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<uint8_t>(0x1402FBF23, 0xEB); // DB_LoadXFile
utils::hook::nop(0x1402FC445, 2); // DB_SetFileLoadCompressor
command::add("materiallist", [](const command::params& params)
{

View File

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

View File

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

View File

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

View File

@ -37,9 +37,60 @@ namespace gsc
bool force_error_print = false;
std::optional<std::string> gsc_error_msg;
std::vector<devmap_entry> devmap_entries{};
std::optional<devmap_entry> 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<std::pair<std::uint16_t, std::uint16_t>> 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<std::pair<std::uint16_t, std::uint16_t>> best_line_info{};
std::uint32_t best_codepos = 0;
assert(codepos >= entry->bytecode);
const std::uint32_t codepos_offset = static_cast<std::uint32_t>(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<unsigned int>(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<const std::uint8_t*>(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<void>(0x1404E4D00, mark_pos);
game::LargeLocalResetToMark(mark_pos);
return;
}
console::warn("******* script runtime error ********\n");
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x144D57840);
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(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<void>(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<unsigned int>(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<dev_map_instruction> devmap{};
const auto* devmap_ptr = reinterpret_cast<const dev_map*>(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<game::BuiltinFunction>(SELECT_VALUE(0x14086F468, 0x1409E6CE8), scr_print);
utils::hook::set<game::BuiltinFunction>(SELECT_VALUE(0x14086F480, 0x1409E6D00), scr_print_ln);
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D353C, 0x14042E33C), 0x1000); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D353B + 1, 0x14042E33B + 1), 0x1000); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D3542 + 4, 0x14042E342 + 4), RVA(&func_table)); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403E0BDD + 3, 0x14043BBBE + 3), RVA(&func_table)); // VM_Execute_0
// utils::hook::set<std::uint32_t>(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<game::BuiltinFunction>(0x1409E6E38, assert_ex_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E6E50, assert_msg_cmd);
utils::hook::set<game::BuiltinFunction>(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<game::BuiltinFunction>(0x1409E6E38, assert_ex_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E6E50, assert_msg_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E6E20, assert_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E94D0, scr_cmd_is_dedicated_server);
}
};
}

View File

@ -1,9 +1,36 @@
#pragma once
#include <xsk/gsc/engine/iw6_pc.hpp>
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<dev_map_instruction> 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);

View File

@ -3,15 +3,16 @@
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/io.hpp>
#include <utils/hook.hpp>
#include <utils/memory.hpp>
#include <utils/compression.hpp>
#include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/memory.hpp>
#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<void>(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<int>(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<void>(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<int>(function_handle.second), 0);
game::RemoveRefToObject(thread);
}
utils::hook::invoke<void>(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<int>(function_handle.second), 0);
game::RemoveRefToObject(thread);
}
}
void scr_load_level_multiplayer_stub()
{
utils::hook::invoke<void>(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);
}
}
};
}

View File

@ -0,0 +1,157 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "mods.hpp"
#include <utils/hook.hpp>
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<game::Sys_File>(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<game::XZoneInfo> 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<unsigned int>(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<game::dvar_t**>(SELECT_VALUE(0x145856D38, 0x147876000));
// Remove DVAR_INIT from fs_game
utils::hook::set<std::uint32_t>(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)

View File

@ -0,0 +1,7 @@
#pragma once
namespace mods
{
bool is_using_mods();
bool db_mod_file_exists();
}

View File

@ -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();
}
});
}
};
}

View File

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