21 Commits
Author SHA1 Message Date
Future b54b3f51af fix: add back this patch cuz idk 2026-09-10 08:24:14 +02:00
Future 02ba3b3663 feat: admen abooze 2026-09-09 21:14:45 +02:00
Future 043f8f07c0 Merge branch 'master' of https://git.alterware.dev/alterware/iw6-mod 2026-09-09 17:38:19 +02:00
Future 582e8e05be feat: fart out new fixes (cc: Wanted) DO NOT TELL PYRO PETE 2026-09-09 17:37:47 +02:00
Future 6ff635498d maint: update Premake5 (beta8) 2026-01-28 11:03:18 +00:00
Future 67c0b5ddfd feat: slowmotion command 2026-01-19 15:34:46 +01:00
Future 1b78912b33 chore: update 2025-11-16 21:38:02 +01:00
Future deafda9061 chore: remove WinToast 2025-06-04 10:58:19 +02:00
Future 61f0374d10 chore: update 2025-05-21 18:47:56 +02:00
Future 9052daab5c maint: update Premake5 2025-04-11 11:10:05 +02:00
Future a299874dd2 fix: remove _s poison 2025-02-06 12:34:59 +01:00
Future 27d5985574 fix noob team 2025-02-03 20:02:50 +01:00
Future b60692f0d7 feat: more arxan patches 2025-01-30 13:14:21 +01:00
Future 6f2c293970 chore: update deps 2025-01-29 13:25:42 +01:00
Future 03b44731a8 fix(gsc): I got one big silly goose in me 2025-01-29 08:21:56 +00:00
Future ea8d2e483f fix(gameplay): fix gun game 2025-01-28 10:38:57 +01:00
Future 833e25700c feat(gsc): make bots join a server automatically 2025-01-28 08:57:47 +01:00
Future bc0372a249 feat(mods): add LUI menu 2025-01-27 22:28:55 +01:00
3d77a21f3d assets(weapons): cache weapons from code (Code quaK/H1-Mod probably took from Pluto xD)
Co-authored-by: H1-Mod Team <[email protected]>
Co-authored-by: HighTechRedNeck <[email protected]>
2025-01-24 13:59:57 +01:00
FutureandAnomaly 199206ee5a maint: January Update
Co-authored-by: Anomaly <[email protected]>
2025-01-11 10:20:05 +01:00
Future 45907301ee Christmas Day Update 2024-12-18 12:39:55 +01:00
107 changed files with 1856 additions and 2555 deletions
-3
View File
@@ -29,9 +29,6 @@
[submodule "deps/udis86"]
path = deps/udis86
url = https://github.com/vmt/udis86.git
[submodule "deps/WinToast"]
path = deps/WinToast
url = https://github.com/mohabouje/WinToast.git
[submodule "deps/zlib"]
path = deps/zlib
url = https://github.com/madler/zlib.git
Vendored
+1 -1
Submodule deps/GSL updated: f1a494cfd2...543d0dd3fe
-1
Submodule deps/WinToast deleted from 821c4818ad
Vendored
+1 -1
+1 -1
+1 -1
-32
View File
@@ -1,32 +0,0 @@
wintoast = {
source = path.join(dependencies.basePath, "WinToast"),
}
function wintoast.import()
links { "WinToast" }
wintoast.includes()
end
function wintoast.includes()
includedirs {
path.join(wintoast.source, "include"),
}
end
function wintoast.project()
project "WinToast"
language "C++"
wintoast.includes()
rapidjson.import();
files {
path.join(wintoast.source, "include/wintoastlib.h"),
path.join(wintoast.source, "src/wintoastlib.cpp"),
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, wintoast)
+1 -1
Vendored
+1 -1
Submodule deps/zlib updated: 9f0f2d4f9f...5a82f71ed1
+2 -2
View File
@@ -257,7 +257,7 @@ if os.getenv("CI") then
defines {"CI"}
end
flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"}
multiprocessorcompile "On"
filter "platforms:x64"
defines {"_WINDOWS", "WIN32"}
@@ -268,7 +268,7 @@ filter "configurations:Release"
buildoptions {"/GL"}
linkoptions {"/IGNORE:4702", "/LTCG"}
defines {"NDEBUG"}
flags {"FatalCompileWarnings"}
fatalwarnings {"All"}
filter {}
filter "configurations:Debug"
+1 -5
View File
@@ -103,9 +103,7 @@ namespace arxan
BOOL WINAPI set_thread_context_stub(const HANDLE thread, CONTEXT* context)
{
if (!game::environment::is_sp()
&& game::dwGetLogOnStatus(0) == game::DW_LIVE_CONNECTED
&& context->ContextFlags == CONTEXT_DEBUG_REGISTERS)
if (game::dwGetLogOnStatus(0) == game::DW_LIVE_CONNECTED && context->ContextFlags == CONTEXT_DEBUG_REGISTERS)
{
return TRUE;
}
@@ -142,8 +140,6 @@ namespace arxan
void post_unpack() override
{
if (game::environment::is_sp()) return;
utils::hook::jump(0x1404FE1E0, 0x1404FE2D0); // idk
utils::hook::jump(0x140558C20, 0x140558CB0); // dwNetPump
utils::hook::jump(0x140591850, 0x1405918E0); // dwLobbyPump
+35
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)
+59
View File
@@ -0,0 +1,59 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "component/console.hpp"
#include <utils/hook.hpp>
namespace weapons
{
namespace
{
void g_setup_level_weapon_def_stub()
{
game::G_SetupLevelWeaponDef();
// The count on most maps is well below 200
std::array<game::WeaponCompleteDef*, 256> weapons{};
const auto count = game::DB_GetAllXAssetOfType_FastFile(game::ASSET_TYPE_WEAPON, (void**)weapons.data(), static_cast<int>(weapons.max_size()));
std::sort(weapons.begin(), weapons.begin() + count, [](game::WeaponCompleteDef* weapon1, game::WeaponCompleteDef* weapon2)
{
assert(weapon1->szInternalName);
assert(weapon2->szInternalName);
return std::strcmp(weapon1->szInternalName, weapon2->szInternalName) < 0;
});
#ifdef _DEBUG
console::info("Found %i weapons to precache\n", count);
#endif
for (auto i = 0; i < count; ++i)
{
#ifdef _DEBUG
console::info("Precaching weapon \"%s\"\n", weapons[i]->szInternalName);
#endif
(void)game::G_GetWeaponForName(weapons[i]->szInternalName);
}
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
// Kill Scr_PrecacheItem (We are going to do this from code)
utils::hook::nop(0x1403BDB10, 4);
utils::hook::set<std::uint8_t>(0x1403BDB10, 0xC3);
// Load weapons from the DB
utils::hook::call(0x14039F382, g_setup_level_weapon_def_stub);
utils::hook::call(0x1403AE31A, g_setup_level_weapon_def_stub);
}
};
}
REGISTER_COMPONENT(weapons::component)
+93 -29
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,21 @@ 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))
{
// Migration
utils::io::remove_file(hw_profile_path);
}
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) };
return hw_profile_info;
}
std::string get_protected_data()
@@ -52,7 +62,7 @@ namespace auth
return {};
}
const auto size = std::min(data_out.cbData, 52ul);
const auto size = std::min<DWORD>(data_out.cbData, 52);
std::string result{ reinterpret_cast<char*>(data_out.pbData), size };
LocalFree(data_out.pbData);
@@ -76,9 +86,72 @@ namespace auth
return entropy;
}
utils::cryptography::ecc::key& get_key()
bool load_key(utils::cryptography::ecc::key& key)
{
static auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy());
std::string data{};
auto key_path = (utils::properties::get_appdata_path() / "iw6-private.key").generic_string();
if (!utils::io::read_file(key_path, &data))
{
return false;
}
key.deserialize(data);
if (!key.is_valid())
{
console::error("Loaded key is invalid!\n");
return false;
}
return true;
}
utils::cryptography::ecc::key generate_key()
{
auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy());
if (!key.is_valid())
{
throw std::runtime_error("Failed to generate cryptographic key!");
}
auto key_path = (utils::properties::get_appdata_path() / "iw6-private.key").generic_string();
if (!utils::io::write_file(key_path, key.serialize()))
{
console::error("Failed to write cryptographic key!\n");
}
console::info("Generated cryptographic key: %llX\n", key.get_hash());
return key;
}
utils::cryptography::ecc::key load_or_generate_key()
{
utils::cryptography::ecc::key key{};
if (load_key(key))
{
console::info("Loaded cryptographic key: %llX\n", key.get_hash());
return key;
}
return generate_key();
}
utils::cryptography::ecc::key get_key_internal()
{
auto key = load_or_generate_key();
auto key_path = (utils::properties::get_appdata_path() / "iw6-public.key").generic_string();
if (!utils::io::write_file(key_path, key.get_public_key()))
{
console::error("Failed to write public key!\n");
}
return key;
}
const utils::cryptography::ecc::key& get_key()
{
static auto key = get_key_internal();
return key;
}
@@ -86,7 +159,7 @@ namespace auth
{
std::string connect_string(format, len);
game::SV_Cmd_TokenizeString(connect_string.data());
const auto _ = gsl::finally([]()
const auto _0 = gsl::finally([]()
{
game::SV_Cmd_EndTokenizedString();
});
@@ -109,7 +182,7 @@ namespace auth
proto::network::connect_info info;
info.set_publickey(get_key().get_public_key());
info.set_signature(sign_message(get_key(), challenge));
info.set_signature(utils::cryptography::ecc::sign_message(get_key(), challenge));
info.set_infostring(connect_string);
network::send(*adr, "connect", info.SerializeAsString());
@@ -150,14 +223,14 @@ namespace auth
utils::cryptography::ecc::key key;
key.set(info.publickey());
const auto xuid = strtoull(steam_id.data(), nullptr, 16);
const auto xuid = std::strtoull(steam_id.data(), nullptr, 16);
if (xuid != key.get_hash())
{
network::send(*from, "error", "XUID doesn't match the certificate!", '\n');
return;
}
if (!key.is_valid() || !verify_message(key, challenge, info.signature()))
if (!key.is_valid() || !utils::cryptography::ecc::verify_message(key, challenge, info.signature()))
{
network::send(*from, "error", "Challenge signature was invalid!", '\n');
return;
@@ -199,25 +272,16 @@ namespace auth
void post_unpack() override
{
// Patch steam id bit check
if (game::environment::is_sp())
{
utils::hook::jump(0x1404BF3F0, 0x1404BF446);
utils::hook::jump(0x1404C02AF, 0x1404C02F0);
utils::hook::jump(0x1404C07F4, 0x1404C0842);
}
else
{
utils::hook::jump(0x140585410, 0x140585466);
utils::hook::jump(0x140142252, 0x140142293);
utils::hook::jump(0x140142334, 0x140142389);
utils::hook::jump(0x1405864EF, 0x140586530);
utils::hook::jump(0x140586A80, 0x140586AC6);
utils::hook::jump(0x140585410, 0x140585466);
utils::hook::jump(0x140142252, 0x140142293);
utils::hook::jump(0x140142334, 0x140142389);
utils::hook::jump(0x1405864EF, 0x140586530);
utils::hook::jump(0x140586A80, 0x140586AC6);
utils::hook::jump(0x140479636, get_direct_connect_stub(), true);
utils::hook::call(0x1402C4F8E, send_connect_data_stub);
}
utils::hook::jump(0x140479636, get_direct_connect_stub(), true);
utils::hook::call(0x1402C4F8E, send_connect_data_stub);
command::add("guid", []
command::add("guid", []() -> void
{
console::info("Your guid: %llX\n", steam::SteamUser()->GetSteamID().bits);
});
+5 -9
View File
@@ -25,9 +25,7 @@ namespace binding
if (value && value < 100)
{
const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
"bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
if (len < 0)
{
return bytes_used;
@@ -40,9 +38,7 @@ namespace binding
value -= 100;
if (static_cast<size_t>(value) < custom_binds.size() && !custom_binds[value].empty())
{
const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
"bind %s \"%s\"\n", key_button, custom_binds[value].data());
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, custom_binds[value].data());
if (len < 0)
{
return bytes_used;
@@ -119,13 +115,13 @@ namespace binding
}
// write all bindings to config file
utils::hook::call(SELECT_VALUE(0x14023DF2B, 0x1402C465B), key_write_bindings_to_buffer_stub);
utils::hook::call(0x1402C465B, key_write_bindings_to_buffer_stub);
// links a custom command to an index
utils::hook::jump(SELECT_VALUE(0x14023D6E0, 0x1402C3E50), key_get_binding_for_cmd_stub);
utils::hook::jump(0x1402C3E50, key_get_binding_for_cmd_stub);
// execute custom binds
cl_execute_key_hook.create(SELECT_VALUE(0x140239970, 0x1402BF0E0), &cl_execute_key_stub);
cl_execute_key_hook.create(0x1402BF0E0, &cl_execute_key_stub);
}
};
}
+2 -5
View File
@@ -13,6 +13,8 @@
#include <utils/hook.hpp>
#include <utils/string.hpp>
#define ALLOW_CUSTOM_BOT_NAMES
namespace bots
{
namespace
@@ -158,11 +160,6 @@ namespace bots
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
get_bot_name_hook.create(game::SV_BotGetRandomName, get_random_bot_name);
command::add("spawnBot", [](const command::params& params)
+2 -3
View File
@@ -40,9 +40,8 @@ namespace branding
localized_strings::override("LUA_MENU_LEGAL_COPYRIGHT", "iw6-mod: " VERSION " by AlterWare.\n");
utils::hook::call(SELECT_VALUE(0x1403BDABA, 0x140414424), dvar_set_string_stub);
ui_get_formatted_build_number_hook.create(
SELECT_VALUE(0x140415FD0, 0x1404D7C00), ui_get_formatted_build_number_stub);
utils::hook::call(0x140414424, dvar_set_string_stub);
ui_get_formatted_build_number_hook.create(0x1404D7C00, ui_get_formatted_build_number_stub);
scheduler::loop([]()
{
-5
View File
@@ -28,11 +28,6 @@ namespace bullet
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
dvars::bg_surfacePenetration = game::Dvar_RegisterFloat("bg_surfacePenetration", 0.0f,
0.0f, std::numeric_limits<float>::max(), game::DVAR_FLAG_SAVED,
"Set to a value greater than 0 to override the surface penetration depth");
+44 -20
View File
@@ -76,7 +76,16 @@ namespace colors
void com_clean_name_stub(const char* in, char* out, const int out_size)
{
strncpy_s(out, out_size, in, _TRUNCATE);
// Check that the name is at least 3 char without colors
char name[16]{};
game::I_strncpyz(out, in, std::min<int>(out_size, sizeof(name)));
utils::string::strip(out, name, std::strlen(out) + 1);
if (std::strlen(name) < 3)
{
game::I_strncpyz(out, "UnnamedPlayer", std::min<int>(out_size, sizeof(name)));
}
}
char* i_clean_str_stub(char* string)
@@ -86,29 +95,42 @@ namespace colors
return string;
}
size_t get_client_name_stub(const int local_client_num, const int index, char* buf, const int size,
const size_t unk, const size_t unk2)
int cl_get_client_name_and_clan_tag_stub(const int local_client_num, const int index, char* name_buf, const int name_size, char* clan_tag_buf, int clan_tag_size)
{
// CL_GetClientName (CL_GetClientNameAndClantag?)
const auto result = reinterpret_cast<size_t(*)(int, int, char*, int, size_t, size_t)>(0x1402CF790)(
local_client_num, index, buf, size, unk, unk2);
// CL_GetClientNameAndClanTag -> CL_GetClientNameAndClanTagColorize
const auto result = utils::hook::invoke<int>(0x1402CF790, local_client_num, index, name_buf, name_size, clan_tag_buf, clan_tag_size);
utils::string::strip(buf, buf, size);
utils::string::strip(name_buf, name_buf, name_size);
return result;
}
int com_sprintf_stub(char* dest, int size, const char* fmt, const char* name)
{
const auto len = game::Com_sprintf(dest, size, fmt, name);
if (len < 0)
{
game::I_strncpyz(dest, "UnnamedAgent", size);
return len;
}
// This should inherit the name of the owner (a player) which already passed the length check in Com_CleanName
utils::string::strip(dest, dest, len + 1);
return len;
}
void rb_lookup_color_stub(const char index, DWORD* color)
{
*color = RGB(255, 255, 255);
if (index == '8')
{
*color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x145FFD958, 0x1480E85BC));
*color = *reinterpret_cast<DWORD*>(0x1480E85BC);
}
else if (index == '9')
{
*color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x145FFD95C, 0x1480E85C0));
*color = *reinterpret_cast<DWORD*>(0x1480E85C0);
}
else if (index == ':')
{
@@ -116,7 +138,7 @@ namespace colors
}
else if (index == ';')
{
*color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x145FFD964, 0x1480E85C8));
*color = *reinterpret_cast<DWORD*>(0x1480E85C8);
}
else
{
@@ -135,20 +157,22 @@ namespace colors
return;
}
if (!game::environment::is_sp())
{
// allows colored name in-game
utils::hook::jump(0x1404F5FC0, com_clean_name_stub);
// allows colored name in-game
utils::hook::call(0x1403881CF, com_clean_name_stub);
utils::hook::call(0x140388224, com_clean_name_stub);
// don't apply colors to overhead names
utils::hook::call(0x14025CE79, get_client_name_stub);
// don't apply colors to overhead names
utils::hook::call(0x14025CE79, cl_get_client_name_and_clan_tag_stub);
// patch I_CleanStr
utils::hook::jump(0x1404F63C0, i_clean_str_stub);
}
// don't apply colors to overhead names of agents (like dogs or juggernauts)
// hook Com_sprintf in CL_GetAgentName
utils::hook::call(0x1402CF760, com_sprintf_stub);
// patch I_CleanStr
utils::hook::jump(0x1404F63C0, i_clean_str_stub);
// force new colors
utils::hook::jump(SELECT_VALUE(0x14055DCC0, 0x14062AE80), rb_lookup_color_stub);
utils::hook::jump(0x14062AE80, rb_lookup_color_stub);
// add colors
add(0, 0, 0); // 0 - Black
+28 -143
View File
@@ -2,6 +2,7 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "command.hpp"
#include "console.hpp"
@@ -232,7 +233,10 @@ namespace command
void execute(std::string command, const bool sync)
{
command += "\n";
if (!command.ends_with('\n'))
{
command += "\n";
}
if (sync)
{
@@ -249,16 +253,9 @@ namespace command
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
add_sp_commands();
}
else
{
utils::hook::call(0x14041213C, &parse_commandline_stub);
utils::hook::call(0x14041213C, &parse_commandline_stub);
add_mp_commands();
}
add_mp_commands();
add_commands_generic();
}
@@ -445,105 +442,6 @@ namespace command
execute(dvar->current.string);
});
}
void add_sp_commands()
{
add("god", []
{
if (!game::SV_Loaded())
{
return;
}
game::sp::g_entities[0].flags ^= game::FL_GODMODE;
game::CG_GameMessage(0, utils::string::va("godmode %s",
game::sp::g_entities[0].flags & game::FL_GODMODE
? "^2on"
: "^1off"));
});
add("notarget", []
{
if (!game::SV_Loaded())
{
return;
}
game::sp::g_entities[0].flags ^= game::FL_NOTARGET;
game::CG_GameMessage(0, utils::string::va("notarget %s",
game::sp::g_entities[0].flags & game::FL_NOTARGET
? "^2on"
: "^1off"));
});
add("noclip", []
{
if (!game::SV_Loaded())
{
return;
}
game::sp::g_entities[0].client->flags ^= 1;
game::CG_GameMessage(0, utils::string::va("noclip %s",
game::sp::g_entities[0].client->flags & 1
? "^2on"
: "^1off"));
});
add("ufo", []
{
if (!game::SV_Loaded())
{
return;
}
game::sp::g_entities[0].client->flags ^= 2;
game::CG_GameMessage(0, utils::string::va("ufo %s",
game::sp::g_entities[0].client->flags & 2
? "^2on"
: "^1off"));
});
add("give", [](const params& params)
{
if (!game::SV_Loaded())
{
return;
}
if (params.size() < 2)
{
game::CG_GameMessage(0, "You did not specify a weapon name");
return;
}
auto ps = game::SV_GetPlayerstateForClientNum(0);
auto wp = game::G_GetWeaponForName(params.get(1));
if (game::G_GivePlayerWeapon(ps, wp, 0, 0, 0))
{
game::G_InitializeAmmo(ps, wp, 0);
game::G_SelectWeapon(0, wp);
}
});
add("take", [](const params& params)
{
if (!game::SV_Loaded())
{
return;
}
if (params.size() < 2)
{
game::CG_GameMessage(0, "You did not specify a weapon name");
return;
}
auto ps = game::SV_GetPlayerstateForClientNum(0);
auto wp = game::G_GetWeaponForName(params.get(1));
game::G_TakePlayerWeapon(ps, wp);
});
}
static void add_mp_commands()
{
@@ -553,78 +451,65 @@ namespace command
{
if (!dvars::sv_cheats->current.enabled)
{
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
game::engine::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,
utils::string::va("f \"godmode %s\"",
game::mp::g_entities[client_num].flags & game::FL_GODMODE
? "^2on"
: "^1off"));
game::engine::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" : "^1off"));
});
add_sv("notarget", [](const int client_num, const params_sv&)
{
if (!dvars::sv_cheats->current.enabled)
{
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
game::engine::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,
utils::string::va("f \"notarget %s\"",
game::mp::g_entities[client_num].flags & game::FL_NOTARGET
? "^2on"
: "^1off"));
game::engine::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" : "^1off"));
});
add_sv("noclip", [](const int client_num, const params_sv&)
{
if (!dvars::sv_cheats->current.enabled)
{
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
game::engine::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,
utils::string::va("f \"noclip %s\"",
game::mp::g_entities[client_num].client->flags & 1
? "^2on"
: "^1off"));
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
utils::string::va("f \"noclip %s\"", game::mp::g_entities[client_num].client->flags & 1 ? "^2on" : "^1off"));
});
add_sv("ufo", [](const int client_num, const params_sv&)
{
if (!dvars::sv_cheats->current.enabled)
{
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
game::engine::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,
utils::string::va("f \"ufo %s\"",
game::mp::g_entities[client_num].client->flags & 2
? "^2on"
: "^1off"));
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
utils::string::va("f \"ufo %s\"", game::mp::g_entities[client_num].client->flags & 2 ? "^2on" : "^1off"));
});
add_sv("setviewpos", [](const int client_num, const params_sv& params)
{
if (!dvars::sv_cheats->current.enabled)
{
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
game::engine::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,
"f \"You did not specify the correct number of coordinates\"");
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
"f \"You did not specify the correct number of coordinates\"");
return;
}
@@ -637,14 +522,14 @@ namespace command
{
if (!dvars::sv_cheats->current.enabled)
{
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
game::engine::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,
"f \"You did not specify the correct number of coordinates\"");
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
"f \"You did not specify the correct number of coordinates\"");
return;
}
@@ -657,13 +542,13 @@ namespace command
{
if (!dvars::sv_cheats->current.enabled)
{
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
game::engine::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::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
return;
}
@@ -680,13 +565,13 @@ namespace command
{
if (!dvars::sv_cheats->current.enabled)
{
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
game::engine::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::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
return;
}
+8 -9
View File
@@ -84,15 +84,14 @@ namespace console
void print_stub(const char* fmt, ...)
{
char buffer[4096]{};
va_list ap;
va_start(ap, fmt);
char buffer[4096]{};
const auto res = vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
(void)res;
print_message(buffer);
[[maybe_unused]] const auto len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
print_message(buffer);
}
void append_text(const char* text)
@@ -126,7 +125,7 @@ namespace console
void post_unpack() override
{
// Redirect input (]command)
utils::hook::jump(SELECT_VALUE(0x14043DFA0, 0x140502A80), append_text);
utils::hook::jump(0x140502A80, append_text);
utils::hook::jump(printf, print_stub);
@@ -220,7 +219,7 @@ namespace console
HWND get_window()
{
return *reinterpret_cast<HWND*>((SELECT_VALUE(0x145A7B490, 0x147AD1DB0)));
return *reinterpret_cast<HWND*>(0x147AD1DB0);
}
void set_title(std::string title)
@@ -242,7 +241,7 @@ namespace console
SetWindowPos(get_window(), nullptr, rect.left, rect.top, width, height, 0);
auto* const logo_window = *reinterpret_cast<HWND*>(SELECT_VALUE(0x145A7B4A0, 0x147AD1DC0));
auto* const logo_window = *reinterpret_cast<HWND*>(0x147AD1DC0);
SetWindowPos(logo_window, nullptr, 5, 5, width - 25, 60, 0);
}
+1 -1
View File
@@ -49,7 +49,7 @@ namespace cursor
{
// This is the legacy cursor
// TODO: Find the new cursor drawing...
utils::hook::call(SELECT_VALUE(0x14040117E, 0x1404BB119), draw_cursor_stub);
utils::hook::call(0x1404BB119, draw_cursor_stub);
scheduler::loop([]()
{
-22
View File
@@ -198,25 +198,6 @@ namespace dedicated
return hwnd;
}
void sys_error_stub(const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
scheduler::once([]()
{
command::execute("map_rotate");
}, scheduler::pipeline::main, 3s);
game::Com_Error(game::ERR_DROP, "%s", buffer);
}
void add_commands()
{
command::add("map", [](const command::params& params)
@@ -364,9 +345,6 @@ namespace dedicated
utils::hook::nop(0x1404F8BE1, 2); // ^
utils::hook::set<uint8_t>(0x140328660, 0xC3); // Disable image pak file loading
// Stop crashing from sys_errors
utils::hook::jump(0x1404FF510, sys_error_stub);
// Reduce min required memory
utils::hook::set<uint64_t>(0x1404FA6BD, 0x80000000);
utils::hook::set<uint64_t>(0x1404FA76F, 0x80000000);
+7 -8
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;
@@ -56,8 +56,7 @@ namespace dedicated_info
std::string cleaned_hostname = sv_hostname->current.string;
utils::string::strip(sv_hostname->current.string, cleaned_hostname.data(),
cleaned_hostname.size() + 1);
utils::string::strip(sv_hostname->current.string, cleaned_hostname.data(), cleaned_hostname.size() + 1);
console::set_title(utils::string::va("%s on %s [%d/%d] (%d)", cleaned_hostname.data(),
mapname->current.string, client_count,
+2 -2
View File
@@ -222,7 +222,7 @@ namespace demonware
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
vsnprintf(buffer, sizeof(buffer), msg, ap);
printf("%s: %s\n", function, buffer);
va_end(ap);
@@ -442,7 +442,7 @@ namespace demonware
void post_unpack() override
{
utils::hook::jump(SELECT_VALUE(0x140602230, 0x1406F54D0), bd_logger_stub);
utils::hook::jump(0x1406F54D0, bd_logger_stub);
}
void pre_destroy() override
+1 -3
View File
@@ -37,7 +37,7 @@ namespace discord
if (!game::CL_IsCgameInitialized())
{
discord_presence.details = game::environment::is_sp() ? "Singleplayer" : "Multiplayer";
discord_presence.details = "Multiplayer";
discord_presence.state = "Main Menu";
discord_presence.partySize = 0;
@@ -47,8 +47,6 @@ namespace discord
}
else
{
if (game::environment::is_sp()) return;
const auto* gametype = game::UI_LocalizeGametype(game::Dvar_FindVar("ui_gametype")->current.string);
const auto* map = game::UI_LocalizeMapname(game::Dvar_FindVar("ui_mapname")->current.string);
+16 -17
View File
@@ -1,8 +1,8 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "console.hpp"
@@ -11,7 +11,7 @@
namespace dvar_cheats
{
void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::dvar_value* value)
void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::DvarValue* value)
{
if (dvar && dvar->name == "sv_cheats"s)
{
@@ -112,23 +112,24 @@ namespace dvar_cheats
void cg_set_client_dvar_from_server(const int local_client_num, game::mp::cg_s* cg, const char* dvar_id, const char* value)
{
if (dvar_id == "cg_fov"s || dvar_id == "com_maxfps"s)
{
return;
}
const auto* dvar = game::Dvar_FindVar(dvar_id);
if (dvar)
{
// If we send as string, it can't be set with source SERVERCMD because the game only allows that source on real server cmd dvars.
// Just use external instead as if it was being set by the console
game::Dvar_SetFromStringByNameFromSource(dvar_id, value, game::DvarSetSource::DVAR_SOURCE_EXTERNAL);
}
else
if (!dvar)
{
// Not a dvar name, assume it is an id and the game will handle normally
game::CG_SetClientDvarFromServer(local_client_num, cg, dvar_id, value);
return;
}
if (dvar && ((dvar->flags & game::DVAR_FLAG_SAVED) != 0))
{
console::info("Not allowing server to override archive dvar '%s'\n", dvar->name);
return;
}
// If we send as string, it can't be set with source SERVERCMD because the game only allows that source on real server cmd dvars.
// Just use external instead as if it was being set by the console
game::Dvar_SetFromStringByNameFromSource(dvar_id, value, game::DVAR_SOURCE_EXTERNAL);
}
void set_client_dvar_by_string(const int entity_num, const char* value)
@@ -136,7 +137,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::engine::SV_GameSendServerCommand(entity_num, game::SV_CMD_RELIABLE, command);
}
const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a)
@@ -170,8 +171,6 @@ namespace dvar_cheats
public:
void post_unpack() override
{
if (game::environment::is_sp()) return;
utils::hook::nop(0x1404F0D13, 4); // let our stub handle zero-source sets
utils::hook::jump(0x1404F0D1A, dvar_flag_checks_stub, true); // check extra dvar flags when setting values
+56 -29
View File
@@ -1,16 +1,16 @@
#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>
#include <utils/io.hpp>
#include <utils/memory.hpp>
#include <utils/string.hpp>
namespace fastfiles
{
@@ -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)
@@ -32,6 +32,12 @@ namespace fastfiles
return;
}
const auto out_name = std::format("gsc_dump/{}.gscbin", name);
if (utils::io::file_exists(out_name))
{
return;
}
std::string buffer;
buffer.append(header.scriptfile->name, std::strlen(header.scriptfile->name) + 1);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->compressedLen), 4);
@@ -40,12 +46,38 @@ namespace fastfiles
buffer.append(header.scriptfile->buffer, header.scriptfile->compressedLen);
buffer.append(reinterpret_cast<char*>(header.scriptfile->bytecode), header.scriptfile->bytecodeLen);
const auto out_name = std::format("gsc_dump/{}.gscbin", name);
utils::io::write_file(out_name, buffer);
console::info("Dumped %s\n", out_name.data());
console::info("Dumped %s\n", out_name.c_str());
}
void dump_csv_table(const std::string& name, game::XAssetHeader header)
{
if (!dvars::g_dump_string_tables->current.enabled)
{
return;
}
const auto out_name = std::format("csv_dump/{}.csv", name);
if (utils::io::file_exists(out_name))
{
return;
}
std::string buffer;
for (auto row = 0; row < header.stringTable->rowCount; row++)
{
for (auto column = 0; column < header.stringTable->columnCount; column++)
{
const auto* string = header.stringTable->values[(row * header.stringTable->columnCount) + column].string;
buffer.append(utils::string::va("%s%s", string ? string : "", (column == header.stringTable->columnCount - 1) ? "\n" : ","));
}
}
utils::io::write_file(out_name, buffer);
console::info("Dumped %s\n", out_name.c_str());
}
game::XAssetHeader db_find_x_asset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
{
@@ -58,6 +90,11 @@ namespace fastfiles
dump_gsc_script(name, result);
}
if (type == game::ASSET_TYPE_STRINGTABLE)
{
dump_csv_table(name, result);
}
if (diff > 100)
{
console::print(
@@ -106,30 +143,16 @@ namespace fastfiles
public:
void post_unpack() override
{
db_try_load_x_file_internal_hook.create(SELECT_VALUE(0x140275850, 0x1403237F0), &db_try_load_x_file_internal);
db_try_load_x_file_internal_hook.create(0x1403237F0, &db_try_load_x_file_internal);
db_find_x_asset_header_hook.create(game::DB_FindXAssetHeader, db_find_x_asset_header_stub);
dvars::g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts to binary format");
dvars::g_dump_string_tables = game::Dvar_RegisterBool("g_dumpStringTables", false, game::DVAR_FLAG_NONE, "Dump CSV files");
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);
});
utils::hook::call(0x140156350, p_mem_free_stub);
utils::hook::call(0x140324259, p_mem_free_stub);
// who asked for this this ?
command::add("materiallist", [](const command::params& params)
{
game::DB_EnumXAssets_FastFile(game::ASSET_TYPE_MATERIAL, [](const game::XAssetHeader header, void*)
@@ -141,10 +164,14 @@ namespace fastfiles
}, nullptr, false);
});
if (!game::environment::is_sp())
{
reallocate_asset_pool(game::ASSET_TYPE_WEAPON, 320);
}
reallocate_asset_pool(game::ASSET_TYPE_WEAPON, 320);
// Fixes ("Exceeded limit of 16 'font' assets.") new steam files use a 32 font pool (2026)
reallocate_asset_pool(game::ASSET_TYPE_FONT, 32);
// Allow loading of unsigned fastfiles
utils::hook::set<uint8_t>(0x1402FBF23, 0xEB); // DB_LoadXFile
utils::hook::nop(0x1402FC445, 2); // DB_SetFileLoadCompressor
}
};
}
+20 -24
View File
@@ -83,15 +83,22 @@ namespace filesystem
void startup()
{
register_path("iw6");
const auto base = std::filesystem::current_path();
register_path(base / "iw6");
register_path(get_binary_directory() + "\\data");
if (get_binary_directory() != base)
{
register_path(base / "data");
}
// game's search paths
register_path("devraw");
register_path("devraw_shared");
register_path("raw_shared");
register_path("raw");
register_path("main");
register_path(base / "devraw");
register_path(base / "devraw_shared");
register_path(base / "raw_shared");
register_path(base / "raw");
register_path(base / "main");
}
void check_for_startup()
@@ -268,29 +275,18 @@ namespace filesystem
void post_unpack() override
{
// Set fs_basegame
utils::hook::inject(SELECT_VALUE(0x14041C053, 0x1404DDA13), "iw6");
utils::hook::inject(0x1404DDA13, "iw6");
#ifndef INJECT_HOST_AS_LIB
utils::hook::jump(SELECT_VALUE(0x1404351B0, 0x1404F96C0), sys_default_install_path_stub);
utils::hook::jump(0x1404F96C0, sys_default_install_path_stub);
#endif
if (game::environment::is_sp())
{
utils::hook::call(0x14041B744, fs_startup_stub);
utils::hook::call(0x1404DD704, fs_startup_stub);
utils::hook::call(0x1404DDB43, fs_startup_stub);
utils::hook::call(0x14041CD00, register_custom_path_stub);
utils::hook::call(0x14041CD20, register_custom_path_stub);
utils::hook::call(0x14041CD5F, register_custom_path_stub);
}
else
{
utils::hook::call(0x1404DD704, fs_startup_stub);
utils::hook::call(0x1404DDB43, fs_startup_stub);
utils::hook::call(0x1404DE550, register_custom_path_stub);
utils::hook::call(0x1404DE570, register_custom_path_stub);
utils::hook::call(0x1404DE5AF, register_custom_path_stub);
}
utils::hook::call(0x1404DE550, register_custom_path_stub);
utils::hook::call(0x1404DE570, register_custom_path_stub);
utils::hook::call(0x1404DE5AF, register_custom_path_stub);
}
};
}
+3 -3
View File
@@ -85,7 +85,7 @@ namespace fps
perf_calc_fps(&cg_perf, cg_perf.frame_ms);
utils::hook::invoke<void>(SELECT_VALUE(0x1405806E0, 0x140658E30));
utils::hook::invoke<void>(0x140658E30);
}
void cg_draw_fps()
@@ -167,10 +167,10 @@ namespace fps
// fps setup
cg_perf.perf_start = std::chrono::high_resolution_clock::now();
utils::hook::call(SELECT_VALUE(0x140242C11, 0x1402CF457), &perf_update);
utils::hook::call(0x1402CF457, &perf_update);
// change cg_drawfps flags to saved
utils::hook::call(SELECT_VALUE(0x1401F400A, 0x140272B98), &cg_draw_fps_register_stub);
utils::hook::call(0x140272B98, &cg_draw_fps_register_stub);
cg_drawPing = game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Draw ping");
+10 -10
View File
@@ -63,7 +63,7 @@ namespace game_console
void clear()
{
strncpy_s(con.buffer, "", sizeof(con.buffer));
game::I_strncpyz(con.buffer, "", sizeof(con.buffer));
con.cursor = 0;
fixed_input = "";
@@ -249,7 +249,7 @@ namespace game_console
dvars::con_inputDvarInactiveValueColor->current.vector, offset);
}
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice));
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice));
con.globals.may_auto_complete = true;
}
else if (matches.size() > 1)
@@ -274,7 +274,7 @@ namespace game_console
}
}
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice));
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice));
con.globals.may_auto_complete = true;
}
}
@@ -364,11 +364,11 @@ namespace game_console
void print_internal(const char* fmt, ...)
{
char va_buffer[0x200] = { 0 };
char va_buffer[1024]{};
va_list ap;
va_start(ap, fmt);
vsprintf_s(va_buffer, fmt, ap);
vsnprintf(va_buffer, sizeof(va_buffer), fmt, ap);
va_end(ap);
const auto formatted = std::string(va_buffer);
@@ -424,7 +424,7 @@ namespace game_console
con.buffer[1] = '\0';
}
strncat_s(con.buffer, con.globals.auto_complete_choice, 64);
game::I_strncat(con.buffer, sizeof(con.buffer), con.globals.auto_complete_choice);
con.cursor = static_cast<int>(std::string(con.buffer).length());
if (con.cursor != 254)
@@ -549,7 +549,7 @@ namespace game_console
if (history_index != -1)
{
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
@@ -564,7 +564,7 @@ namespace game_console
if (history_index != -1)
{
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
@@ -721,7 +721,7 @@ namespace game_console
con.output_visible = false;
con.display_line_offset = 0;
con.line_count = 0;
strncpy_s(con.buffer, "", sizeof(con.buffer));
game::I_strncpyz(con.buffer, "", sizeof(con.buffer));
con.globals.x = 0.0f;
con.globals.y = 0.0f;
@@ -729,7 +729,7 @@ namespace game_console
con.globals.font_height = 0.0f;
con.globals.may_auto_complete = false;
con.globals.info_line_count = 0;
strncpy_s(con.globals.auto_complete_choice, "", sizeof(con.globals.auto_complete_choice));
game::I_strncpyz(con.globals.auto_complete_choice, "", sizeof(con.globals.auto_complete_choice));
// add clear command
command::add("clear", [&]()
+14 -23
View File
@@ -3,12 +3,10 @@
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "scheduler.hpp"
#include "scripting.hpp"
#include "console.hpp"
#include "game_log.hpp"
#include "gsc/script_extension.hpp"
#include "scheduler.hpp"
#include "scripting.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
@@ -23,7 +21,7 @@ namespace game_log
char buf[1024]{};
std::size_t out_chars = 0;
for (auto i = 0u; i < game::Scr_GetNumParam(); ++i)
for (std::uint32_t i = 0; i < game::Scr_GetNumParam(); ++i)
{
const auto* value = game::Scr_GetString(i);
const auto len = std::strlen(value);
@@ -34,7 +32,7 @@ namespace game_log
break;
}
strncat_s(buf, value, _TRUNCATE);
game::I_strncat(buf, sizeof(buf), value);
}
g_log_printf("%s", buf);
@@ -49,22 +47,15 @@ namespace game_log
return;
}
char buffer[0x400]{};
char buffer[1024]{};
va_list ap;
va_start(ap, fmt);
vsprintf_s(buffer, fmt, ap);
vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
const auto time = *game::level_time / 1000;
utils::io::write_file(log, utils::string::va("%3i:%i%i %s",
time / 60,
time % 60 / 10,
time % 60 % 10,
buffer
), true);
utils::io::write_file(log, utils::string::va("%3i:%i%i %s", time / 60, time % 60 / 10, time % 60 % 10, buffer), true);
}
class component final : public component_interface
@@ -72,11 +63,6 @@ namespace game_log
public:
void post_load() override
{
if (game::environment::is_sp())
{
return;
}
utils::hook::set<game::BuiltinFunction>(0x1409E8A20, gscr_log_print);
scheduler::once([]
@@ -102,9 +88,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");
+2 -34
View File
@@ -65,30 +65,6 @@ namespace gameplay
a.jmp(0x140383796);
});
const auto pm_bouncing_stub_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
const auto no_bounce = a.newLabel();
const auto loc_14046ED26 = a.newLabel();
a.push(rax);
a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&dvars::pm_bouncing)));
a.mov(al, byte_ptr(rax, 0x10));
a.cmp(ptr(rbp, -0x40), al);
a.pop(rax);
a.jz(no_bounce);
a.jmp(0x14046EC7E);
a.bind(no_bounce);
a.cmp(ptr(rbp, -0x80), r13d);
a.jnz(loc_14046ED26);
a.jmp(0x14046EC6C);
a.bind(loc_14046ED26);
a.jmp(0x14046ED26);
});
const auto pm_bouncing_stub_mp = utils::hook::assemble([](utils::hook::assembler& a)
{
const auto no_bounce = a.newLabel();
@@ -327,21 +303,13 @@ namespace gameplay
void post_unpack() override
{
// Implement bouncing dvar
if (game::environment::is_sp())
{
utils::hook::nop(0x14046EC5C, 16);
}
utils::hook::jump(
SELECT_VALUE(0x14046EC5C, 0x140228FFF), SELECT_VALUE(pm_bouncing_stub_sp, pm_bouncing_stub_mp), true);
utils::hook::jump(0x140228FFF, pm_bouncing_stub_mp, true);
dvars::pm_bouncing = game::Dvar_RegisterBool("pm_bouncing", false,
game::DVAR_FLAG_REPLICATED, "Enable bouncing");
dvars::player_sustainAmmo = game::Dvar_RegisterBool("player_sustainAmmo", false,
game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo.");
pm_weapon_use_ammo_hook.create(SELECT_VALUE(0x140479640, 0x140238A90), &pm_weapon_use_ammo_stub);
if (game::environment::is_sp()) return;
pm_weapon_use_ammo_hook.create(0x140238A90, &pm_weapon_use_ammo_stub);
// Implement player ejection dvar
dvars::g_playerEjection = game::Dvar_RegisterBool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED, "Flag whether player ejection is on or off");
+29 -11
View File
@@ -114,6 +114,25 @@ namespace gsc
return res;
}
bool player_cmd_set_client_dvar_is_valid_name(const char* dvar_name)
{
if (!game::Dvar_IsValidName(dvar_name))
{
scr_error(va("%s is an invalid dvar name", dvar_name));
}
return true;
}
void player_cmd_set_client_dvar_server_dvar()
{
scr_error("server dvar cannot be set as a client dvar");
}
void player_cmd_set_client_dvar_non_writable_dvar()
{
scr_error("non-writable dvar cannot be set as a client dvar");
}
}
unsigned int scr_get_object(unsigned int index)
@@ -284,11 +303,6 @@ namespace gsc
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
scr_emit_function_hook.create(0x14042E150, &scr_emit_function_stub);
utils::hook::call(0x14042E0E4, compile_error_stub); // LinkFile
@@ -296,16 +310,20 @@ namespace gsc
utils::hook::call(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::call(0x14038A4F7, player_cmd_set_client_dvar_is_valid_name);
utils::hook::call(0x14038A52F, player_cmd_set_client_dvar_server_dvar);
utils::hook::call(0x14038A53A, player_cmd_set_client_dvar_non_writable_dvar);
}
void pre_destroy() override
+113 -26
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,22 +171,31 @@ 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;
}
@@ -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,22 @@ 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());
}
void scr_bot_auto_connect_enabled()
{
if (game::environment::is_dedi() && dvars::sv_botsAutoJoin->current.enabled)
{
game::Scr_AddInt(1); // 2 seems to be unused (incomplete GSC/game mode)
return;
}
game::Scr_AddInt(game::BG_BotsConnectType());
}
const char* get_code_pos(const int index)
{
if (static_cast<unsigned int>(index) >= game::scr_VmPub->outparamcount)
@@ -248,6 +324,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;
@@ -268,30 +360,21 @@ namespace gsc
public:
void post_unpack() override
{
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<game::BuiltinFunction>(0x1409E6CE8, scr_print);
utils::hook::set<game::BuiltinFunction>(0x1409E6D00, scr_print_ln);
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D353C, 0x14042E33C), 0x1000); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(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::inject(SELECT_VALUE(0x1403D38E4 + 3, 0x14042E734 + 3), &func_table); // Scr_BeginLoadScripts
if (game::environment::is_sp())
{
return;
}
utils::hook::set<std::uint32_t>(0x14042E342 + 4, RVA(&func_table)); // Scr_RegisterFunction
// utils::hook::set<std::uint32_t>(0x14043BBBE + 3, RVA(&func_table)); // VM_Execute_0
utils::hook::inject(0x14042E734 + 3, &func_table); // Scr_BeginLoadScripts
utils::hook::nop(0x14043BBBE + 5, 2);
utils::hook::call(0x14043BBBE, vm_call_builtin_function);
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(0x14043CEB1, vm_error_stub);
add_function("replacefunc", []
{
@@ -309,10 +392,14 @@ namespace gsc
command::execute(cmd);
});
add_function("isdedicated", []
{
game::Scr_AddInt(game::environment::is_dedi());
});
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);
dvars::sv_botsAutoJoin = game::Dvar_RegisterBool("sv_botsAutoJoin", false, game::DVAR_FLAG_NONE, "");
utils::hook::set<game::BuiltinFunction>(0x1409E92F0, scr_bot_auto_connect_enabled);
}
};
}
@@ -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);
+91 -36
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,33 @@ 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");
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 +249,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 +291,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);
@@ -276,7 +336,7 @@ namespace gsc
build = static_cast<xsk::gsc::build>(static_cast<unsigned int>(build) | static_cast<unsigned int>(xsk::gsc::build::dev_blocks));
}
gsc_ctx->init(build, []([[maybe_unused]] auto const* ctx, const auto& included_path) -> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>>
gsc_ctx->init(build, []([[maybe_unused]] const auto* ctx, const auto& included_path) -> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>>
{
const auto script_name = std::filesystem::path(included_path).replace_extension().string();
@@ -298,14 +358,14 @@ namespace gsc
return {{}, script_data};
});
utils::hook::invoke<void>(SELECT_VALUE(0x1403D3860, 0x14042E6B0));
utils::hook::invoke<void>(0x14042E6B0);
}
void scr_end_load_scripts_stub()
{
gsc_ctx->cleanup();
utils::hook::invoke<void>(SELECT_VALUE(0x140603C00, 0x14042E7E0));
utils::hook::invoke<void>(0x14042E7E0);
}
}
@@ -332,44 +392,39 @@ namespace gsc
public:
loading()
{
gsc_ctx = std::make_unique<xsk::gsc::iw6_pc::context>();
gsc_ctx = std::make_unique<xsk::gsc::iw6_pc::context>(xsk::gsc::instance::server);
}
void post_unpack() override
{
dvars::com_developer = reinterpret_cast<game::dvar_t**>(SELECT_VALUE(0x141603850, 0x1419A9700));
dvars::com_developer = reinterpret_cast<game::dvar_t**>(0x1419A9700);
// Load our scripts with an uncompressed stack
utils::hook::call(SELECT_VALUE(0x1403DC8F0, 0x140437940), db_get_raw_buffer_stub);
utils::hook::call(0x140437940, db_get_raw_buffer_stub);
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
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments");
if (game::environment::is_sp())
{
return;
}
utils::hook::call(0x1403CCED9, scr_begin_load_scripts_stub); // GScr_LoadScripts
utils::hook::call(0x1403CD08D, scr_end_load_scripts_stub); // GScr_LoadScripts
// ProcessScript
utils::hook::call(0x1404378D7, find_script);
utils::hook::call(0x1404378E7, db_is_x_asset_default);
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments");
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void
{
if (clear_scripts && post_shutdown)
{
clear();
}
});
// 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)
{
clear();
}
});
utils::hook::call(0x14039F653, scr_load_level_multiplayer_stub);
}
};
}
+2 -2
View File
@@ -50,8 +50,8 @@ namespace input
return;
}
cl_char_event_hook.create(SELECT_VALUE(0x14023CE50, 0x1402C2AE0), cl_char_event_stub);
cl_key_event_hook.create(SELECT_VALUE(0x14023D070, 0x1402C2CE0), cl_key_event_stub);
cl_char_event_hook.create(0x1402C2AE0, cl_char_event_stub);
cl_key_event_hook.create(0x1402C2CE0, cl_key_event_stub);
}
};
}
+1 -1
View File
@@ -58,7 +58,7 @@ namespace localized_strings
void post_unpack() override
{
// Change some localized strings
seh_string_ed_get_string_hook.create(SELECT_VALUE(0x1403F42D0, 0x1404A5F60), &seh_string_ed_get_string);
seh_string_ed_get_string_hook.create(0x1404A5F60, &seh_string_ed_get_string);
}
};
}
+5 -40
View File
@@ -10,43 +10,15 @@ namespace logger
{
namespace
{
utils::hook::detour com_error_hook;
const game::dvar_t* logger_dev = nullptr;
void print_com_error(int, const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
console::error("%s", buffer);
}
void com_error_stub(const int error, const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
console::error("Error: %s\n", buffer);
com_error_hook.invoke<void>(error, "%s", buffer);
}
void print_warning(const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
vsnprintf(buffer, sizeof(buffer), msg, ap);
va_end(ap);
console::warn("%s", buffer);
@@ -58,7 +30,7 @@ namespace logger
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
vsnprintf(buffer, sizeof(buffer), msg, ap);
va_end(ap);
console::info("%s", buffer);
@@ -75,7 +47,7 @@ namespace logger
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
vsnprintf(buffer, sizeof(buffer), msg, ap);
va_end(ap);
console::info("%s", buffer);
@@ -141,17 +113,10 @@ namespace logger
sub_1401DAA40();
}
if (!game::environment::is_sp())
{
utils::hook::call(0x140501AE3, print_com_error);
}
com_error_hook.create(game::Com_Error, com_error_stub);
// Make havok script's print function actually print
utils::hook::jump(SELECT_VALUE(0x1406283A4, 0x140732184), print);
utils::hook::jump(0x140732184, print);
logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED, "Print dev stuff");
logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_NONE, "Print dev stuff");
}
};
}
+1 -1
View File
@@ -99,7 +99,7 @@ namespace map_rotation
console::error("%s: %s contains invalid data!\n", ex.what(), sv_map_rotation->name);
}
#ifdef _DEBUG
console::info("dedicated_rotation size after parsing is '%llu'", dedicated_rotation.get_entries_size());
console::info("dedicated_rotation size after parsing is '%llu'\n", dedicated_rotation.get_entries_size());
#endif
}
+229
View File
@@ -0,0 +1,229 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
#include "console.hpp"
#include "mods.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace mods
{
namespace
{
utils::hook::detour sys_create_file_hook;
void db_build_os_path_from_source(const char* zone_name, const game::FF_DIR source, const int size, char* filename)
{
char user_map[MAX_PATH]{};
switch (source)
{
case game::FFD_DEFAULT:
(void)game::Com_sprintf(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)game::Com_sprintf(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:
game::I_strncpyz(user_map, zone_name, sizeof(user_map));
(void)game::Com_sprintf(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(game::Sys_Folder folder, const char* base_filename)
{
auto result = sys_create_file_hook.invoke<game::Sys_File>(folder, base_filename);
if (result.handle != INVALID_HANDLE_VALUE)
{
return result;
}
if (!is_using_mods())
{
return result;
}
// .ff extension was added previously
if (!std::strcmp(base_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 fs_game_dir_domain_func(game::dvar_t* dvar, game::DvarValue new_value)
{
if (*new_value.string == '\0')
{
return true;
}
if (game::I_strnicmp(new_value.string, "mods", 4) != 0)
{
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
return false;
}
if (5 < std::strlen(new_value.string) && (new_value.string[4] == '\\' || new_value.string[4] == '/'))
{
const auto* s1 = std::strstr(new_value.string, "..");
const auto* s2 = std::strstr(new_value.string, "::");
if (s1 == nullptr && s2 == nullptr)
{
return true;
}
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
return false;
}
// Invalid path specified
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
return false;
}
}
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**>(0x147876000);
// Remove DVAR_INIT from fs_game
utils::hook::set<std::uint32_t>(0x1404DDA45 + 2, game::DVAR_FLAG_SERVERINFO);
utils::hook::inject(0x1404DDA57 + 3, &fs_game_dir_domain_func);
// 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
command::add("loadmod", [](const command::params& params) -> void
{
if (params.size() != 2)
{
console::info("USAGE: %s \"mods/<mod name>\"", params.get(0));
return;
}
std::string mod_name = utils::string::to_lower(params.get(1));
if (!mod_name.empty() && !mod_name.starts_with("mods/"))
{
mod_name = "mods/" + mod_name;
}
// change fs_game if needed
if (mod_name != (*dvars::fs_gameDirVar)->current.string)
{
game::Dvar_SetString((*dvars::fs_gameDirVar), mod_name.c_str());
command::execute("vid_restart\n");
}
});
command::add("unloadmod", [](const command::params& params) -> void
{
if (*dvars::fs_gameDirVar == nullptr || *(*dvars::fs_gameDirVar)->current.string == '\0')
{
return;
}
game::Dvar_SetString(*dvars::fs_gameDirVar, "");
command::execute("vid_restart\n");
});
// TODO: without a way to monitor all the ways fs_game can be changed there is no way to detect when we
// should unregister the path from the internal filesystem we use
// HINT: It could be done in fs_game_dir_domain_func, but I haven't tested if that's the best place to monitor for changes and register/unregister the mods folder
}
};
}
REGISTER_COMPONENT(mods::component)
+7
View File
@@ -0,0 +1,7 @@
#pragma once
namespace mods
{
bool is_using_mods();
bool db_mod_file_exists();
}
-5
View File
@@ -210,11 +210,6 @@ namespace network
void post_unpack() override
{
{
if (game::environment::is_sp())
{
return;
}
// redirect dw_sendto to raw socket
utils::hook::jump(0x140501A00, dw_send_to_stub);
+6 -13
View File
@@ -63,12 +63,10 @@ namespace notifies
const auto* guid = game::SV_GetGuid(client_num);
const auto* name = game::mp::svs_clients[client_num].name;
game_log::g_log_printf("%s;%s;%i;%s;%s\n",
params.get(0),
guid,
game_log::g_log_printf("%s;%s;%i;%s;%s\n", params.get(0), guid,
client_num,
name,
message.data()
message.c_str()
);
}, scheduler::pipeline::server);
@@ -174,22 +172,17 @@ namespace notifies
public:
void post_unpack() override
{
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)
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void
{
if (free_scripts)
if (clear_scripts && post_shutdown)
{
vm_execute_hooks.clear();
}
});
utils::hook::call(0x1404724DD, client_command_stub);
}
};
}
+80 -40
View File
@@ -2,6 +2,7 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "command.hpp"
#include "console.hpp"
@@ -39,11 +40,6 @@ namespace party
void connect_to_party(const game::netadr_s& target, const std::string& mapname, const std::string& gametype)
{
if (game::environment::is_sp())
{
return;
}
if (game::Live_SyncOnlineDataFlags(0))
{
scheduler::once([=]()
@@ -81,6 +77,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)
@@ -160,28 +176,26 @@ namespace party
{
for (auto i = 0; !name.empty() && i < *game::mp::svs_clientCount; ++i)
{
if (game::mp::g_entities[i].client)
if (!game::mp::g_entities[i].client)
{
char client_name[16] = {0};
strncpy_s(client_name, game::mp::g_entities[i].client->sess.cs.name, sizeof(client_name));
game::I_CleanStr(client_name);
continue;
}
if (client_name == name)
{
return i;
}
char client_name[16]{};
game::I_strncpyz(client_name, game::mp::g_entities[i].client->sess.cs.name, sizeof(client_name));
game::I_CleanStr(client_name);
if (client_name == name)
{
return i;
}
}
return -1;
}
void connect(const game::netadr_s& target)
{
if (game::environment::is_sp())
{
return;
}
command::execute("lui_open popup_acceptinginvite", false);
connect_state.host = target;
@@ -206,11 +220,6 @@ namespace party
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
// hook disconnect command function
utils::hook::jump(0x1402C6370, disconnect_stub);
@@ -360,7 +369,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::engine::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 +383,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::engine::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 +397,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::engine::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 +410,61 @@ namespace party
const auto message = params.join(1);
game::SV_GameSendServerCommand(-1, 0, utils::string::va("%c \"%s\"", 84, message.data()));
game::engine::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;
+35 -52
View File
@@ -2,6 +2,7 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "command.hpp"
#include "console.hpp"
@@ -32,12 +33,13 @@ namespace patches
{
return;
}
return sv_kick_client_num_hook.invoke<void>(clientNum, reason);
}
std::string get_login_username()
{
char username[UNLEN + 1];
char username[UNLEN + 1]{};
DWORD username_len = UNLEN + 1;
if (!GetUserNameA(username, &username_len))
{
@@ -54,7 +56,7 @@ namespace patches
if (game::environment::is_mp())
{
// Make name save
game::Dvar_RegisterString("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name.");
game::Dvar_RegisterString("name", get_login_username().c_str(), game::DVAR_FLAG_SAVED, "Player name.");
}
return com_register_dvars_hook.invoke<void>();
@@ -72,8 +74,10 @@ namespace patches
if (exec_params.size() == 2)
{
std::string file_name = exec_params.get(1);
if (file_name.find(".cfg") == std::string::npos)
if (!file_name.ends_with(".cfg"))
{
file_name.append(".cfg");
}
const auto file = filesystem::file(file_name);
if (file.exists())
@@ -103,23 +107,6 @@ namespace patches
a.jmp(0x1403F7574);
});
auto cmd_exec_stub_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
const auto success = a.newLabel();
a.pushad64();
a.call_aligned(cmd_exec_patch);
a.test(al, al);
a.popad64();
a.jz(success);
a.mov(edx, 0x18000);
a.jmp(0x1403B39C0);
a.bind(success);
a.jmp(0x1403B3A04);
});
int dvar_command_patch() // game makes this return an int and compares with eax instead of al -_-
{
const command::params args{};
@@ -163,11 +150,11 @@ namespace patches
game::AimAssist_AddToTargetList(a1, a2);
}
game::dvar_t* register_cg_fov_stub(const char* name, float value, float min, float /*max*/,
game::dvar_t* register_cg_fov_stub(const char* name, const float value, const float min, [[maybe_unused]] const float max,
const unsigned int flags,
const char* description)
{
return game::Dvar_RegisterFloat(name, value, min, 160, flags | 1, description);
return game::Dvar_RegisterFloat(name, value, min, 160, flags, description);
}
void bsp_sys_error_stub(const char* error, const char* arg1)
@@ -232,14 +219,10 @@ namespace patches
LoadLibraryA("PhysXUpdateLoader64.dll");
// Unlock fps in main menu
utils::hook::set<std::uint8_t>(SELECT_VALUE(0x140242DDB, 0x1402CF58B), 0xEB);
utils::hook::set<std::uint8_t>(0x1402CF58B, 0xEB);
// Unlock cg_fov
utils::hook::call(SELECT_VALUE(0x1401F3E96, 0x14027273C), register_cg_fov_stub);
if (game::environment::is_sp())
{
utils::hook::call(0x1401F3EC7, register_cg_fov_stub);
}
utils::hook::call(0x14027273C, register_cg_fov_stub);
// changed max value from 85 -> 1000
if (!game::environment::is_dedi())
@@ -247,11 +230,8 @@ namespace patches
dvars::override::register_int("com_maxfps", 85, 0, 1000, game::DVAR_FLAG_SAVED);
}
if (!game::environment::is_sp())
{
//increased max limit for sv_network_fps, the lower limit is the default one. Original range is from 20 to 200 times a second.
utils::hook::call(0x140476F4F, register_network_fps_stub);
}
// increased max limit for sv_network_fps, the lower limit is the default one. Original range is from 20 to 200 times a second.
utils::hook::call(0x140476F4F, register_network_fps_stub);
// register cg_gun_ dvars with new values and flags
// maybe _x can stay usable within a reasonable range? it can make scoped weapons DRASTICALLY better on high FOVs
@@ -263,33 +243,33 @@ namespace patches
dvars::override::register_float("cg_fovScale", 1.0f, 0.1f, 5.0f, game::DVAR_FLAG_SAVED);
// Patch Dvar_Command to print out values how CoD4 does it
utils::hook::jump(SELECT_VALUE(0x1403BFCB0, 0x140416A60), dvar_command_patch);
utils::hook::jump(0x140416A60, dvar_command_patch);
// Allow executing custom cfg files with the "exec" command
utils::hook::jump(SELECT_VALUE(0x1403B39BB, 0x1403F752B), SELECT_VALUE(0x1403B3A12, 0x1403F7582));
utils::hook::jump(0x1403F752B, 0x1403F7582);
//Use a relative jump to empty memory first
utils::hook::jump(SELECT_VALUE(0x1403B3A12, 0x1403F7582), SELECT_VALUE(cmd_exec_stub_sp, cmd_exec_stub_mp), true);
utils::hook::jump(0x1403F7582, cmd_exec_stub_mp, true);
// Use empty memory to go to our stub first (can't do close jump, so need space for 12 bytes)
// Fix mouse lag
utils::hook::nop(SELECT_VALUE(0x14043E6CB, 0x140504A2B), 6);
// nop call to SetThreadExecutionState for good luck (???)
utils::hook::nop(0x140504A2B, 6);
// They are lying, this cannot actually fix a black screen on startup :(
scheduler::loop([]()
{
SetThreadExecutionState(ES_DISPLAY_REQUIRED);
}, scheduler::pipeline::main);
if (game::environment::is_sp())
{
patch_sp();
}
else
{
patch_mp();
}
patch_mp();
}
static void patch_mp()
{
// Bypass Arxan function
utils::hook::nop(0x1404758C0, 16);
utils::hook::jump(0x1404758C0, game::engine::SV_GameSendServerCommand, true);
utils::hook::call(0x140477399, game::engine::SV_SendServerCommand);
// Register dvars
com_register_dvars_hook.create(0x140413A90, &com_register_dvars_stub);
@@ -326,6 +306,13 @@ namespace patches
dvars::override::register_int("igs_s1", 1, 0, 1, 0);
dvars::override::register_int("igs_crossgame", 1, 0, 1, 0);
// Required by UI scripts. Missing when joining a dedi and causes crashes
game::Dvar_RegisterInt("scr_gun_winlimit", 1, 0, 10, game::DVAR_FLAG_REPLICATED, "Win limit for Gun Game");
game::Dvar_RegisterInt("scr_gun_scorelimit", 18, 1, 1000, game::DVAR_FLAG_REPLICATED, "Score limit for Gun Game");
// Remove cheat protection from r_hudOutlinePostMode
utils::hook::set<uint8_t>(0x1405DDF23 + 2, 0x40);
// Patch game chat on resolutions higher than 1080p to use the right font
utils::hook::call(0x14025C825, get_chat_font_handle);
utils::hook::call(0x1402BC42F, get_chat_font_handle);
@@ -357,13 +344,9 @@ namespace patches
utils::hook::nop(0x1403A1A0F, 1);
// ^^
utils::hook::nop(0x1403A072F, 5); // LiveStorage_RecordMovementInMatchdata
}
static void patch_sp()
{
// SP doesn't initialize WSA
WSADATA wsa_data;
WSAStartup(MAKEWORD(2, 2), &wsa_data);
// Disable Com_Error in NET_SendPacket
utils::hook::nop(0x140501AE3, 5);
}
};
}
-5
View File
@@ -74,11 +74,6 @@ namespace ranked
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
if (game::environment::is_mp())
{
// This must be registered as 'true' to avoid crash when starting a private match
+6 -7
View File
@@ -181,16 +181,15 @@ namespace raw_mouse
return;
}
utils::hook::call(SELECT_VALUE(0x14043845F, 0x1404FC3FB), &in_mouse_move);
utils::hook::call(0x1404FC3FB, &in_mouse_move);
utils::hook::call(SELECT_VALUE(0x140519F71, 0x1405E69A1), &create_window_ex_a_stub);
utils::hook::set<std::uint8_t>(SELECT_VALUE(0x140519F76, 0x1405E69A6), 0x90);
utils::hook::call(0x1405E69A1, &create_window_ex_a_stub);
utils::hook::set<std::uint8_t>(0x1405E69A6, 0x90);
utils::hook::call(SELECT_VALUE(0x14043BF04, 0x140500824), &register_class_ex_a_stub);
utils::hook::set<std::uint8_t>(SELECT_VALUE(0x14043BF09, 0x140500829), 0x90);
utils::hook::call(0x140500824, &register_class_ex_a_stub);
utils::hook::set<std::uint8_t>(0x140500829, 0x90);
cl_rawInput = game::Dvar_RegisterBool("cl_rawInput", true,
game::DVAR_FLAG_SAVED, "Use Raw Input for mouse input. This fixes mouse acceleration issue");
cl_rawInput = game::Dvar_RegisterBool("cl_rawInput", true, game::DVAR_FLAG_SAVED, "Use Raw Input for mouse. This fixes mouse acceleration issue");
}
};
}
+17 -17
View File
@@ -53,23 +53,25 @@ namespace rcon
const auto client = &game::mp::svs_clients[i];
auto self = &game::mp::g_entities[i];
char clean_name[32]{};
strncpy_s(clean_name, self->client->sess.cs.name, sizeof(clean_name));
game::I_CleanStr(clean_name);
if (client->header.state > game::CS_FREE && self && self->client)
if (client->header.state == game::CS_FREE || !self || !self->client)
{
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
i,
self->client->sess.scores.score,
game::SV_BotIsBot(i) ? "Yes" : "No",
(client->header.state == game::CS_RECONNECTING) ? "CNCT" : (client->header.state == game::CS_ZOMBIE) ? "ZMBI" : utils::string::va("%4i", client->ping),
game::SV_GetGuid(i),
clean_name,
network::net_adr_to_string(client->header.netchan.remoteAddress),
client->header.netchan.remoteAddress.port)
);
continue;
}
char clean_name[32]{};
game::I_strncpyz(clean_name, self->client->sess.cs.name, sizeof(clean_name));
game::I_CleanStr(clean_name);
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
i,
self->client->sess.scores.score,
game::SV_BotIsBot(i) ? "Yes" : "No",
(client->header.state == game::CS_RECONNECTING) ? "CNCT" : (client->header.state == game::CS_ZOMBIE) ? "ZMBI" : utils::string::va("%4i", client->ping),
game::SV_GetGuid(i),
clean_name,
network::net_adr_to_string(client->header.netchan.remoteAddress),
client->header.netchan.remoteAddress.port)
);
}
return buffer;
@@ -121,8 +123,6 @@ namespace rcon
public:
void post_unpack() override
{
if (game::environment::is_sp()) return;
scheduler::once([]()
{
game::Dvar_RegisterString("rcon_password", "", game::DvarFlags::DVAR_FLAG_NONE,
+3 -4
View File
@@ -46,12 +46,11 @@ namespace renderer
return;
}
dvars::r_fullbright = game::Dvar_RegisterBool("r_fullbright", false, game::DvarFlags::DVAR_FLAG_SAVED,
dvars::r_fullbright = game::Dvar_RegisterBool("r_fullbright", false, game::DVAR_FLAG_CHEAT,
"Toggles rendering without lighting");
r_init_draw_method_hook.create(SELECT_VALUE(0x1404FF600, 0x1405CB470), &r_init_draw_method_stub);
r_update_front_end_dvar_options_hook.create(
SELECT_VALUE(0x140535FF0, 0x140603240), &r_update_front_end_dvar_options_stub);
r_init_draw_method_hook.create(0x1405CB470, &r_init_draw_method_stub);
r_update_front_end_dvar_options_hook.create(0x140603240, &r_update_front_end_dvar_options_stub);
}
};
}
+4 -4
View File
@@ -149,7 +149,7 @@ namespace scheduler
{
schedule([=]()
{
const auto dw_init = game::environment::is_sp() || game::Live_SyncOnlineDataFlags(0) == 0;
const auto dw_init = game::Live_SyncOnlineDataFlags(0) == 0;
if (dw_init && game::Sys_IsDatabaseReady2())
{
once(callback, type, delay);
@@ -177,10 +177,10 @@ namespace scheduler
void post_unpack() override
{
r_end_frame_hook.create(SELECT_VALUE(0x140534860, 0x140601AA0), scheduler::r_end_frame_stub);
r_end_frame_hook.create(0x140601AA0, scheduler::r_end_frame_stub);
utils::hook::call(SELECT_VALUE(0x1403BC922, 0x140413142), scheduler::main_frame_stub);
utils::hook::call(SELECT_VALUE(0x1403185FD, 0x1403A0AF9), scheduler::server_frame_stub);
utils::hook::call(0x140413142, scheduler::main_frame_stub);
utils::hook::call(0x1403A0AF9, scheduler::server_frame_stub);
}
void pre_destroy() override
+21 -20
View File
@@ -34,7 +34,7 @@ namespace scripting
std::unordered_map<unsigned int, std::string> canonical_string_table;
std::vector<std::function<void(int)>> shutdown_callbacks;
std::vector<std::function<void(int, int)>> shutdown_callbacks;
std::vector<std::function<void()>> 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<void>(free_scripts);
g_shutdown_game_hook.invoke<void>(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<void(int)>& callback)
void on_shutdown(const std::function<void(int, int)>& callback)
{
shutdown_callbacks.push_back(callback);
}
@@ -190,24 +195,20 @@ namespace scripting
public:
void post_unpack() override
{
// SP address is wrong, but should be ok
scr_load_level_hook.create(SELECT_VALUE(0x14013D5D0, 0x1403C4E60), &scr_load_level_stub);
g_shutdown_game_hook.create(SELECT_VALUE(0x140318C10, 0x1403A0DF0), &g_shutdown_game_stub);
scr_load_level_hook.create(0x1403C4E60, &scr_load_level_stub);
g_shutdown_game_hook.create(0x1403A0DF0, &g_shutdown_game_stub);
scr_set_thread_position_hook.create(SELECT_VALUE(0x1403D3560, 0x14042E360), &scr_set_thread_position_stub);
process_script_hook.create(SELECT_VALUE(0x1403DC870, 0x1404378C0), &process_script_stub);
scr_set_thread_position_hook.create(0x14042E360, &scr_set_thread_position_stub);
process_script_hook.create(0x1404378C0, &process_script_stub);
sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, &sl_get_canonical_string_stub);
if (!game::environment::is_sp())
{
// Make some room for pre_main hook
utils::hook::jump(0x1402084A0, has_config_string_index);
// Make some room for pre_main hook
utils::hook::jump(0x1402084A0, has_config_string_index);
// Allow precaching anytime
utils::hook::jump(0x1402084A5, is_pre_main_stub);
utils::hook::set<uint16_t>(0x1402084D0, 0xD3EB); // jump to 0x1402084A5
g_find_config_string_index.create(0x140161F90, &g_find_config_string_index_stub);
}
// Allow precaching anytime
utils::hook::jump(0x1402084A5, is_pre_main_stub);
utils::hook::set<uint16_t>(0x1402084D0, 0xD3EB); // jump to 0x1402084A5
g_find_config_string_index.create(0x140161F90, &g_find_config_string_index_stub);
}
};
}
+1 -1
View File
@@ -8,7 +8,7 @@ namespace scripting
extern std::string current_file;
void on_shutdown(const std::function<void(int)>& callback);
void on_shutdown(const std::function<void(int, int)>& callback);
void on_init(const std::function<void()>& callback);
std::optional<std::string> get_canonical_string(unsigned int id);
+75 -1
View File
@@ -10,6 +10,8 @@ namespace security
{
namespace
{
utils::hook::detour ui_replace_directive_hook;
void set_cached_playerdata_stub(const int localclient, const int index1, const int index2)
{
if (index1 >= 0 && index1 < 18 && index2 >= 0 && index2 < 42)
@@ -30,6 +32,71 @@ namespace security
utils::hook::invoke<void>(0x140472500, client, msg);
}
void ui_replace_directive_stub(const int local_client_num, const char* src_string, char* dst_string, const int dst_buffer_size)
{
assert(src_string);
if (!src_string)
{
return;
}
assert(dst_string);
if (!dst_string)
{
return;
}
assert(dst_buffer_size > 0);
if (dst_buffer_size <= 0)
{
return;
}
constexpr std::size_t MAX_HUDELEM_TEXT_LEN = 0x100;
if (std::strlen(src_string) > MAX_HUDELEM_TEXT_LEN)
{
return;
}
ui_replace_directive_hook.invoke<void>(local_client_num, src_string, dst_string, dst_buffer_size);
}
int hud_elem_set_enum_string_stub(char* string, const char* format, ...)
{
va_list ap;
va_start(ap, format);
const auto len = vsnprintf(string, 0x800, format, ap);
va_end(ap);
string[0x800 - 1] = '\0';
return len;
}
int sv_add_bot_stub(char* string, const char* format, ...)
{
va_list ap;
va_start(ap, format);
const auto len = vsnprintf(string, 0x400, format, ap);
va_end(ap);
string[0x400 - 1] = '\0';
return len;
}
int sv_add_test_client_stub(char* string, const char* format, ...)
{
va_list ap;
va_start(ap, format);
const auto len = vsnprintf(string, 0x400, format, ap);
va_end(ap);
string[0x400 - 1] = '\0';
return len;
}
}
class component final : public component_interface
@@ -37,13 +104,20 @@ namespace security
public:
void post_unpack() override
{
if (game::environment::is_sp()) return;
// sprinf
utils::hook::call(0x140399B0F, hud_elem_set_enum_string_stub);
// Patch vulnerability in PlayerCards_SetCachedPlayerData
utils::hook::call(0x140287C5C, set_cached_playerdata_stub);
// sprinf
utils::hook::call(0x140470A88, sv_add_bot_stub);
utils::hook::call(0x140470F68, sv_add_test_client_stub);
// It is possible to make the server hang if left unchecked
utils::hook::call(0x14047A29A, sv_execute_client_message_stub);
ui_replace_directive_hook.create(0x1404D8A00, ui_replace_directive_stub);
}
};
}
+28 -1
View File
@@ -2,6 +2,9 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "command.hpp"
#include "console.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
@@ -54,6 +57,25 @@ namespace slowmotion
client->nextSnapshotTime = *game::mp::serverTime - 1;
}
}
void set_code_time_scale(float timescale)
{
assert(timescale > 0.0f);
*game::com_codeTimeScale = timescale;
}
void com_timescale_f(const command::params& params)
{
if (params.size() != 2)
{
console::info("timescale <rate>\n");
return;
}
const auto timescale = static_cast<float>(std::atof(params.get(1)));
console::info("timescale set to %f\n", timescale);
set_code_time_scale(timescale);
}
}
class component final : public component_interface
@@ -61,7 +83,12 @@ namespace slowmotion
public:
void post_unpack() override
{
if (!game::environment::is_dedi()) return;
command::add("timescale", com_timescale_f);
if (!game::environment::is_dedi())
{
return;
}
utils::hook::jump(0x1403B4A10, scr_cmd_set_slow_motion);
+3 -3
View File
@@ -30,9 +30,9 @@ namespace splash
void post_unpack() override
{
// Disable native splash screen
utils::hook::nop(SELECT_VALUE(0x14043C15B, 0x140500ADB), 5);
utils::hook::jump(SELECT_VALUE(0x14043D5A0, 0x140502000), destroy_stub);
utils::hook::jump(SELECT_VALUE(0x14043D5E0, 0x140502040), destroy_stub);
utils::hook::nop(0x140500ADB, 5);
utils::hook::jump(0x140502000, destroy_stub);
utils::hook::jump(0x140502040, destroy_stub);
}
void pre_destroy() override
+34 -4
View File
@@ -26,6 +26,8 @@ namespace steam_proxy
ownership_state state_;
utils::binary_resource runner_file(RUNNER, "runner.exe");
bool is_disabled() { return true; }
}
class component final : public component_interface
@@ -33,7 +35,7 @@ namespace steam_proxy
public:
void post_load() override
{
if (game::environment::is_dedi())
if (game::environment::is_dedi() || is_disabled())
{
return;
}
@@ -44,16 +46,27 @@ namespace steam_proxy
#ifndef DEV_BUILD
try
{
const std::string game_type = game::environment::is_sp() ? "singleplayer" : "multiplayer";
state_ = this->start_mod("\xF0\x9F\x92\x8E" " iw6-mod: " + (game_type), game::environment::is_sp() ? 209160 : 209170);
const std::string game_type = "multiplayer";
state_ = this->start_mod("\xF0\x9F\x92\x8E" " iw6-mod: " + (game_type), 209170);
}
catch (const std::exception& ex)
{
state_ = ownership_state::error;
printf("Steam: %s\n", ex.what());
}
#ifdef STEAM_OWNERSHIP_CHECK
if (utils::nt::is_wine())
{
state_ = ownership_state::success;
return;
}
#endif
#ifdef STEAM_OWNERSHIP_CHECK
evaluate_ownership_state(state_);
#endif
#endif
(void)state_;
}
void pre_destroy() override
@@ -210,6 +223,23 @@ namespace steam_proxy
return scheduler::cond_end;
});
}
#ifdef STEAM_OWNERSHIP_CHECK
void evaluate_ownership_state(const ownership_state state)
{
switch (state)
{
case ownership_state::nosteam:
throw std::runtime_error("Steam must be running to play this game!");
case ownership_state::unowned:
throw std::runtime_error("You must own the game on Steam to play this mod!");
case ownership_state::error:
throw std::runtime_error("Failed to verify ownership of the game!");
case ownership_state::success:
break;
}
}
#endif
};
}
+2 -3
View File
@@ -51,13 +51,12 @@ namespace system_check
{
static std::unordered_map<std::string, std::string> mp_zone_hashes =
{
{"patch_common_mp.ff", "F1F08BFD03D0496199FAFD49CAA3E5786B70084A4C3C8841ACC4A7B7616D226C"},
{"patch_common_mp.ff", "1731093802719CFD4F35FE1D16373D9F97BBB4E75E72D1AE4A84996CF51C6870"},
};
static std::unordered_map<std::string, std::string> sp_zone_hashes =
{
{"patch_common.ff", "883DB33A1E386420EC6EF19F25C0D8081D01C1945BA2BCAD9FBD5460A201D6AA"},
{"patch_common_alien_mp.ff", "78B00BFF961F69F9A45446D40638A5A7F5C3462F1AF05A833066772C62FB5DB2"},
{"patch_common_alien_mp.ff", "22F0CE99DD26376F44AE896FF8307CB38E14B65C805548E00A6251465CB6666A"},
};
return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes));
+41 -22
View File
@@ -32,15 +32,13 @@ namespace ui_scripting
std::string root;
};
struct globals
struct
{
std::string in_require_script;
std::vector<script> loaded_scripts;
bool load_raw_script{};
std::string raw_script_name{};
};
globals globals;
} globals;
bool is_loaded_script(const std::string& name)
{
@@ -156,7 +154,7 @@ namespace ui_scripting
game_type["issingleplayer"] = [](const game&)
{
return ::game::environment::is_sp();
return false;
};
game_type["ismultiplayer"] = [](const game&)
@@ -195,6 +193,27 @@ namespace ui_scripting
setup_functions();
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(0x14017B120)); // hks::base_print
lua["directoryexists"] = [](const std::string& string)
{
return utils::io::directory_exists(string);
};
lua["listfiles"] = [](const std::string& string)
{
return utils::io::list_files(string);
};
lua["directoryisempty"] = [](const std::string& string)
{
return utils::io::directory_is_empty(string);
};
lua["fileexists"] = [](const std::string& string)
{
return utils::io::file_exists(string);
};
lua["table"]["unpack"] = lua["unpack"];
lua["luiglobals"] = lua;
@@ -276,7 +295,7 @@ namespace ui_scripting
void* lua_atpanic_stub(void* l, [[maybe_unused]] void* panicf)
{
return utils::hook::invoke<void*>(SELECT_VALUE(0x1401851F0, 0x1401A4730), l, SELECT_VALUE(0x1401789A0, 0x140197BC0));
return utils::hook::invoke<void*>(0x1401A4730, l, 0x140197BC0);
}
int luaopen_stub([[maybe_unused]] void* l)
@@ -341,31 +360,31 @@ namespace ui_scripting
void post_unpack() override
{
// Disable debug breakpoints in the assembly of hksDefaultPanic
utils::hook::set<std::uint8_t>(SELECT_VALUE(0x140178C5E, 0x140197E7E), 0x90);
utils::hook::set<std::uint8_t>(0x140197E7E, 0x90);
// Restore hksDefaultPanic for complete error messages
utils::hook::call(SELECT_VALUE(0x1401B0028, 0x1401CE6C8), lua_atpanic_stub);
utils::hook::call(0x1401CE6C8, lua_atpanic_stub);
// Do not allow the HKS vm to open LUA's libraries
utils::hook::jump(SELECT_VALUE(0x14015BD10, 0x14017E040), luaopen_stub); // io
utils::hook::jump(SELECT_VALUE(0x14015C3D0, 0x14017E700), luaopen_stub); // os
utils::hook::jump(SELECT_VALUE(0x14015D400, 0x14017F730), luaopen_stub); // serialize
utils::hook::jump(SELECT_VALUE(0x14015D3D0, 0x14017F700), luaopen_stub); // havokscript
utils::hook::jump(SELECT_VALUE(0x14015C930, 0x14017EC60), luaopen_stub); // debug
utils::hook::jump(0x14017E040, luaopen_stub); // io
utils::hook::jump(0x14017E700, luaopen_stub); // os
utils::hook::jump(0x14017F730, luaopen_stub); // serialize
utils::hook::jump(0x14017F700, luaopen_stub); // havokscript
utils::hook::jump(0x14017EC60, luaopen_stub); // debug
utils::hook::nop(SELECT_VALUE(0x14015B639, 0x14017D969), 5); // coroutine
utils::hook::nop(0x14017D969, 5); // coroutine
// Disable unsafe functions
utils::hook::jump(SELECT_VALUE(0x140158C20, 0x14017AF50), hks_base_stub); // base_loadfile
utils::hook::jump(SELECT_VALUE(0x140158B10, 0x14017AE40), hks_base_stub); // base_dofile
utils::hook::jump(SELECT_VALUE(0x14015D7E0, 0x14017FB10), hks_base_stub); // base_load
utils::hook::jump(0x14017AF50, hks_base_stub); // base_loadfile
utils::hook::jump(0x14017AE40, hks_base_stub); // base_dofile
utils::hook::jump(0x14017FB10, hks_base_stub); // base_load
utils::hook::jump(SELECT_VALUE(0x1401569D0, 0x140178CF0), hks_base_stub); // package_loadlib
utils::hook::jump(SELECT_VALUE(0x140163CD0, 0x140177E00), hks_base_stub); // package_c_loader
utils::hook::jump(SELECT_VALUE(0x140163DE0, 0x140177F10), hks_base_stub); // package_all_in_one_loader
utils::hook::jump(0x140178CF0, hks_base_stub); // package_loadlib
utils::hook::jump(0x140177E00, hks_base_stub); // package_c_loader
utils::hook::jump(0x140177F10, hks_base_stub); // package_all_in_one_loader
utils::hook::jump(SELECT_VALUE(0x140157DA0, 0x14017A0D0), hks_base_stub); // string_dump
utils::hook::jump(SELECT_VALUE(0x1401606A0, 0x1401747D0), hks_base_stub); // os_execute
utils::hook::jump(0x14017A0D0, hks_base_stub); // string_dump
utils::hook::jump(0x1401747D0, hks_base_stub); // os_execute
if (!game::environment::is_mp())
{
+18 -92
View File
@@ -59,19 +59,6 @@ namespace ultrawide
}
}
// general aspect ratio
auto ultrawide_spawn_window_stub_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.call_aligned(0x140519430); // og - setup hwnd struct
a.lea(rcx, ptr(rsp, 0x0A0));
a.call_aligned(ultrawide_patch);
a.mov(edi, ptr(rsp, 0x0B0)); // mov edi, [rsp+0B0h]
a.jmp(0x140519ED3);
});
auto ultrawide_spawn_window_stub_mp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.call_aligned(0x1405E5E70); // og - setup hwnd struct
@@ -84,27 +71,6 @@ namespace ultrawide
a.jmp(0x1405E6903);
});
// menu aspect ratio + left offset
auto ultrawide_menu_stub_01_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(eax);
a.mov(eax, dword_ptr(reinterpret_cast<int64_t>(&hud_aspect_ratio)));
a.movd(xmm5, eax);
a.divss(xmm0, xmm5); // divss xmm0, cs:FLOAT_1_77778 // -> custom aspect ratio
a.subss(xmm1, xmm0); // subss xmm1, xmm0
a.movaps(xmm0, xmm3); // movaps xmm0, xmm3
a.mov(eax, dword_ptr(reinterpret_cast<int64_t>(&hud_aspect_ratio_inv)));
a.movd(xmm5, eax);
a.mulss(xmm0, xmm5); // mulss xmm0, cs:dword_14083D47C // -1.77778 // -> left menu offset
a.pop(eax);
a.jmp(0x1401CD772);
});
auto ultrawide_menu_stub_01_mp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(eax);
@@ -124,22 +90,6 @@ namespace ultrawide
a.jmp(0x1402008D2);
});
// menu aspect ratio right offset
auto ultrawide_menu_stub_02_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(eax);
a.mov(eax, dword_ptr(reinterpret_cast<int64_t>(&hud_aspect_ratio)));
a.movd(xmm5, eax);
a.mulss(xmm0, xmm5); // mulss xmm0, cs:FLOAT_1_77778 // -> custom aspect ratio
a.mulss(xmm0, xmm1); // mulss xmm0, xmm1
a.pop(eax);
a.jmp(0x1401CD79F);
});
auto ultrawide_menu_stub_02_mp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(eax);
@@ -154,22 +104,6 @@ namespace ultrawide
a.jmp(0x1402008FF);
});
// fix loadscreen and in-game hud aspect ratio
auto ultrawide_hud_stub_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(eax);
a.mov(eax, dword_ptr(reinterpret_cast<int64_t>(&hud_aspect_ratio)));
a.movd(xmm5, eax);
a.divss(xmm6, xmm5); // divss xmm6, cs:FLOAT_1_77778
a.mulss(xmm6, xmm3); // mulss xmm6, xmm3
a.pop(eax);
a.jmp(0x14024D32F);
});
auto ultrawide_hud_stub_mp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(eax);
@@ -191,46 +125,38 @@ namespace ultrawide
void post_unpack() override
{
// ultrawide patches
utils::hook::call(SELECT_VALUE(0x140510D0E, 0x1405DCF58), dvar_register_aspect_ratio);
utils::hook::call(0x1405DCF58, dvar_register_aspect_ratio);
// register r_aspectRatioCustom
utils::hook::jump(
SELECT_VALUE(0x140519EC7, 0x1405E68F7),
SELECT_VALUE(ultrawide_spawn_window_stub_sp, ultrawide_spawn_window_stub_mp), true);
utils::hook::jump(0x1405E68F7, ultrawide_spawn_window_stub_mp, true);
// apply general aspect ratio
utils::hook::jump(
SELECT_VALUE(0x1401CD75B, 0x1402008BB),
SELECT_VALUE(ultrawide_menu_stub_01_sp, ultrawide_menu_stub_01_mp), true);
utils::hook::jump(0x1402008BB, ultrawide_menu_stub_01_mp, true);
// fix menu aspect ratio and left offset
utils::hook::jump(
SELECT_VALUE(0x1401CD793, 0x1402008F3),
SELECT_VALUE(ultrawide_menu_stub_02_sp, ultrawide_menu_stub_02_mp), true);
utils::hook::jump(0x1402008F3, ultrawide_menu_stub_02_mp, true);
// fix menu aspect ratio right offset
utils::hook::jump(
SELECT_VALUE(0x14024D323, 0x1402F7043), SELECT_VALUE(ultrawide_hud_stub_sp, ultrawide_hud_stub_mp),
true); // fix aspect ratio for loadscreen and general in-game hud
utils::hook::jump(0x1402F7043, ultrawide_hud_stub_mp, true); // fix aspect ratio for loadscreen and general in-game hud
// safeArea_adjusted dvars :: disable resets (in-game hud padding)
utils::hook::nop(SELECT_VALUE(0x14024AEF2, 0x1402D4FB2), 5); // safeArea_adjusted_horizontal
utils::hook::nop(SELECT_VALUE(0x14024D655, 0x1402F7375), 5); // safeArea_adjusted_horizontal
utils::hook::nop(0x1402D4FB2, 5); // safeArea_adjusted_horizontal
utils::hook::nop(0x1402F7375, 5); // safeArea_adjusted_horizontal
utils::hook::nop(SELECT_VALUE(0x14024AF19, 0x1402D4FD9), 5);
utils::hook::set<BYTE>(SELECT_VALUE(0x14024AF19, 0x1402D4FD9), 0xC3);
utils::hook::nop(0x1402D4FD9, 5);
utils::hook::set<uint8_t>(0x1402D4FD9, 0xC3);
// safeArea_adjusted_vertical :: return instead of jumping to Dvar_SetFloat
utils::hook::nop(SELECT_VALUE(0x14024D664, 0x1402F7384), 5); // safeArea_adjusted_vertical
utils::hook::nop(0x1402F7384, 5); // safeArea_adjusted_vertical
// safeArea_ dvars :: remove cheat protection + add saved flag
utils::hook::set<BYTE>(SELECT_VALUE(0x14024D19F + 4, 0x1402F6EBF + 4), 0x1);
utils::hook::set<uint8_t>(0x1402F6EBF + 4, 0x1);
// register safeArea_horizontal (mov dword ptr [rsp+20h], 4)
utils::hook::set<BYTE>(SELECT_VALUE(0x14024D1D7 + 4, 0x1402F6EF7 + 4), 0x1);
// register safeArea_vertical (mov dword ptr [rsp+20h], 4)
utils::hook::set<uint8_t>(0x1402F6EF7 + 4, 0x1);
// register safeArea_vertical (mov dword ptr [rsp+20h], 4)
utils::hook::set<BYTE>(SELECT_VALUE(0x14024D207 + 5, 0x1402F6F27 + 5), 0x0);
utils::hook::set<BYTE>(SELECT_VALUE(0x14024D207 + 4, 0x1402F6F27 + 4), 0x1);
utils::hook::set<uint8_t>(0x1402F6F27 + 5, 0x0);
utils::hook::set<uint8_t>(0x1402F6F27 + 4, 0x1);
// register safeArea_adjusted_horizontal (mov dword ptr [rsp+20h], 2000h)
utils::hook::set<BYTE>(SELECT_VALUE(0x14024D237 + 5, 0x1402F6F57 + 5), 0x0);
utils::hook::set<BYTE>(SELECT_VALUE(0x14024D237 + 4, 0x1402F6F57 + 4), 0x1);
// register safeArea_adjusted_vertical (mov dword ptr [rsp+20h], 2000h)
utils::hook::set<uint8_t>(0x1402F6F57 + 5, 0x0);
utils::hook::set<uint8_t>(0x1402F6F57 + 4, 0x1);
// register safeArea_adjusted_vertical (mov dword ptr [rsp+20h], 2000h)
}
};
}
+3 -3
View File
@@ -30,7 +30,7 @@ namespace demonware
uint64_t file_id;
uint32_t create_time;
uint32_t modified_time;
bool priv;
bool visibility;
uint64_t owner_id;
std::string filename;
uint32_t file_size;
@@ -41,7 +41,7 @@ namespace demonware
buffer->write_uint64(this->file_id);
buffer->write_uint32(this->create_time);
buffer->write_uint32(this->modified_time);
buffer->write_bool(this->priv);
buffer->write_bool(this->visibility);
buffer->write_uint64(this->owner_id);
buffer->write_string(this->filename);
}
@@ -52,7 +52,7 @@ namespace demonware
buffer->read_uint64(&this->file_id);
buffer->read_uint32(&this->create_time);
buffer->read_uint32(&this->modified_time);
buffer->read_bool(&this->priv);
buffer->read_bool(&this->visibility);
buffer->read_uint64(&this->owner_id);
buffer->read_string(&this->filename);
}
+20 -4
View File
@@ -10,19 +10,35 @@ namespace demonware
this->register_service(4, &bdGroup::get_groups);
}
void bdGroup::set_groups(i_server* server, byte_buffer* /*buffer*/) const
void bdGroup::set_groups(i_server* server, byte_buffer* buffer)
{
//uint32_t groupCount;
// TODO: Implement array reading
uint32_t entries_count{};
buffer->read_array_header(game::BD_BB_UNSIGNED_INTEGER32_TYPE, &entries_count);
auto reply = server->create_reply(this->get_sub_type());
buffer->set_use_data_types(false);
for (uint32_t i = 0; i < entries_count; ++i)
{
uint32_t group_id{};
buffer->read_uint32(&group_id);
if (group_id < ARRAYSIZE(this->groups))
{
this->groups[group_id] = 999;
}
}
buffer->set_use_data_types(true);
reply->send();
}
void bdGroup::get_groups(i_server* server, byte_buffer* buffer)
{
uint32_t group_count;
buffer->read_array_header(8, &group_count);
buffer->read_array_header(game::BD_BB_UNSIGNED_INTEGER32_TYPE, &group_count);
auto reply = server->create_reply(this->get_sub_type());
@@ -9,7 +9,7 @@ namespace demonware
bdGroup();
private:
void set_groups(i_server* server, byte_buffer* buffer) const;
void set_groups(i_server* server, byte_buffer* buffer);
void get_groups(i_server* server, byte_buffer* buffer);
uint32_t groups[512]{};
@@ -17,7 +17,7 @@ namespace demonware
{
std::string get_motd_text()
{
return "This is not a copy & pasted client";
return "Welcome to iw6-mod. Did you know aiming down sights increases accuracy? Online Interactions Not Rated by the ESRB.";
}
}
@@ -119,11 +119,11 @@ namespace demonware
void bdStorage::set_legacy_user_file(i_server* server, byte_buffer* buffer) const
{
bool priv;
bool visibility;
std::string filename, data;
buffer->read_string(&filename);
buffer->read_bool(&priv);
buffer->read_bool(&visibility);
buffer->read_blob(&data);
const auto id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
@@ -139,11 +139,11 @@ namespace demonware
info->file_id = id;
info->filename = filename;
info->create_time = uint32_t(time(nullptr));
info->create_time = static_cast<uint32_t>(std::time(nullptr));
info->modified_time = info->create_time;
info->file_size = uint32_t(data.size());
info->file_size = static_cast<uint32_t>(data.size());
info->owner_id = 0;
info->priv = priv;
info->visibility = visibility;
auto reply = server->create_reply(this->get_sub_type());
reply->add(info);
@@ -170,11 +170,11 @@ namespace demonware
info->file_id = id;
info->filename = "<>";
info->create_time = uint32_t(time(nullptr));
info->create_time = static_cast<uint32_t>(std::time(nullptr));
info->modified_time = info->create_time;
info->file_size = uint32_t(data.size());
info->file_size = static_cast<uint32_t>(data.size());
info->owner_id = 0;
info->priv = false;
info->visibility = false;
auto reply = server->create_reply(this->get_sub_type());
reply->add(info);
@@ -207,12 +207,12 @@ namespace demonware
void bdStorage::list_legacy_user_files(i_server* server, byte_buffer* buffer) const
{
uint64_t unk;
uint64_t owner;
uint32_t date;
uint16_t num_results, offset;
std::string filename, data;
buffer->read_uint64(&unk);
buffer->read_uint64(&owner);
buffer->read_uint32(&date);
buffer->read_uint16(&num_results);
buffer->read_uint16(&offset);
@@ -229,9 +229,9 @@ namespace demonware
info->filename = filename;
info->create_time = 0;
info->modified_time = info->create_time;
info->file_size = uint32_t(data.size());
info->owner_id = 0;
info->priv = false;
info->file_size = static_cast<uint32_t>(data.size());
info->owner_id = owner;
info->visibility = false;
reply->add(info);
}
@@ -260,9 +260,9 @@ namespace demonware
info->filename = filename;
info->create_time = 0;
info->modified_time = info->create_time;
info->file_size = uint32_t(data.size());
info->file_size = static_cast<uint32_t>(data.size());
info->owner_id = 0;
info->priv = false;
info->visibility = false;
reply->add(info);
}
@@ -312,13 +312,13 @@ namespace demonware
void bdStorage::set_user_file(i_server* server, byte_buffer* buffer) const
{
bool priv;
bool visibility;
uint64_t owner;
std::string game, filename, data;
buffer->read_string(&game);
buffer->read_string(&filename);
buffer->read_bool(&priv);
buffer->read_bool(&visibility);
buffer->read_blob(&data);
buffer->read_uint64(&owner);
@@ -329,11 +329,11 @@ namespace demonware
info->file_id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
info->filename = filename;
info->create_time = uint32_t(time(nullptr));
info->create_time = static_cast<uint32_t>(std::time(nullptr));
info->modified_time = info->create_time;
info->file_size = uint32_t(data.size());
info->file_size = static_cast<uint32_t>(data.size());
info->owner_id = owner;
info->priv = priv;
info->visibility = visibility;
auto reply = server->create_reply(this->get_sub_type());
reply->add(info);
+5 -1
View File
@@ -16,6 +16,7 @@ namespace dvars
game::dvar_t* con_inputCmdMatchColor = nullptr;
game::dvar_t* sv_cheats = nullptr;
game::dvar_t* sv_botsAutoJoin = nullptr;
game::dvar_t* g_playerEjection = nullptr;
game::dvar_t* g_playerCollision = nullptr;
@@ -24,6 +25,7 @@ namespace dvars
game::dvar_t* g_rocketPushbackScale = nullptr;
game::dvar_t* g_enableElevators = nullptr;
game::dvar_t* g_dump_scripts = nullptr;
game::dvar_t* g_dump_string_tables = nullptr;
game::dvar_t* g_log = nullptr;
game::dvar_t* bg_surfacePenetration = nullptr;
@@ -46,7 +48,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)
{
+4
View File
@@ -15,6 +15,7 @@ namespace dvars
extern game::dvar_t* con_inputCmdMatchColor;
extern game::dvar_t* sv_cheats;
extern game::dvar_t* sv_botsAutoJoin;
extern game::dvar_t* g_playerCollision;
extern game::dvar_t* g_playerEjection;
@@ -23,6 +24,7 @@ namespace dvars
extern game::dvar_t* g_rocketPushbackScale;
extern game::dvar_t* g_enableElevators;
extern game::dvar_t* g_dump_scripts;
extern game::dvar_t* g_dump_string_tables;
extern game::dvar_t* g_log;
extern game::dvar_t* bg_surfacePenetration;
@@ -47,6 +49,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);
}
+206
View File
@@ -0,0 +1,206 @@
#include <std_include.hpp>
#include <game/game.hpp>
#include "sv_game.hpp"
#include <component/console.hpp>
#include <utils/string.hpp>
namespace game::engine
{
char* SV_ExpandNewlines(char* in)
{
static char string[1024];
unsigned int l = 0;
while (*in && l < sizeof(string) - 3)
{
if (*in == '\n')
{
string[l++] = '\\';
string[l++] = 'n';
}
else
{
if (*in != '\x14' && *in != '\x15')
{
string[l++] = *in;
}
}
++in;
}
string[l] = '\0';
return string;
}
void SV_CullIgnorableServerCommands(mp::client_t* client)
{
int to = client->reliableSent + 1;
for (int from = to; from <= client->reliableSequence; ++from)
{
int from_index = from & 0x7F;
assert(client->netBuf.reliableCommandInfo[from_index].time >= 0);
if (client->netBuf.reliableCommandInfo[from_index].type)
{
int to_index = to & 0x7F;
if (to_index != from_index)
{
client->netBuf.reliableCommandInfo[to_index] = client->netBuf.reliableCommandInfo[from_index];
}
++to;
}
}
client->reliableSequence = to - 1;
}
void SV_DelayDropClient(mp::client_t* drop, const char* reason)
{
assert(drop);
assert(reason);
assert(drop->header.state != CS_FREE);
if (drop->header.state == CS_ZOMBIE)
{
#ifdef _DEBUG
console::info("(drop->dropReason) = %s", drop->dropReason);
#endif
}
else if (!drop->dropReason)
{
drop->dropReason = reason;
}
}
void SV_AddServerCommand(mp::client_t* client, svscmd_type type, const char* cmd)
{
static_assert(offsetof(mp::client_t, netBuf.reliableCommandInfo[0].cmd) == 0xC44);
if (client->testClient == TC_BOT)
{
return;
}
if (client->reliableSequence - client->reliableAcknowledge < 64 && client->header.state == CS_ACTIVE || (SV_CullIgnorableServerCommands(client), type))
{
int len = static_cast<int>(std::strlen(cmd)) + 1;
int to = SV_CanReplaceServerCommand(client, reinterpret_cast<const unsigned char*>(cmd), len);
if (to < 0)
{
++client->reliableSequence;
}
else
{
int from = to + 1;
while (from <= client->reliableSequence)
{
client->netBuf.reliableCommandInfo[to & 0x7F] = client->netBuf.reliableCommandInfo[from & 0x7F];
++from;
++to;
}
}
if (client->reliableSequence - client->reliableAcknowledge == 129)
{
#ifdef _DEBUG
console::info("===== pending server commands =====\n");
int i = 0;
for (i = client->reliableAcknowledge + 1; i <= client->reliableSequence; ++i)
{
console::info("cmd %5d: %8d: %s\n", i, client->netBuf.reliableCommandInfo[i & 0x7F].time, client->netBuf.reliableCommandInfo[i & 0x7F].cmd);
}
console::info("cmd %5d: %8d: %s\n", i, *game::mp::serverTime, cmd);
#endif
NET_OutOfBandPrint(NS_SERVER, &client->header.netchan.remoteAddress, "disconnect");
SV_DelayDropClient(client, "EXE_SERVERCOMMANDOVERFLOW");
type = SV_CMD_RELIABLE;
cmd = utils::string::va("%c \"EXE_SERVERCOMMANDOVERFLOW\"", 'r');
}
int index = client->reliableSequence & 0x7F;
MSG_WriteReliableCommandToBuffer(cmd, client->netBuf.reliableCommandInfo[index].cmd, sizeof(client->netBuf.reliableCommandInfo[index].cmd));
client->netBuf.reliableCommandInfo[index].time = *game::mp::serverTime;
client->netBuf.reliableCommandInfo[index].type = type;
}
}
void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...)
{
mp::client_t* client;
int j, len;
va_list va;
const auto server_command_buf_large = std::make_unique<char[]>(0x20000);
va_start(va, fmt);
len = vsnprintf(server_command_buf_large.get(), 0x20000, fmt, va);
va_end(va);
assert(len >= 0);
if (cl)
{
SV_AddServerCommand(cl, type, server_command_buf_large.get());
return;
}
if (environment::is_dedi() && !std::strncmp(server_command_buf_large.get(), "print", 5))
{
console::info("broadcast: %s\n", SV_ExpandNewlines(server_command_buf_large.get()));
}
const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
for (j = 0, client = mp::svs_clients; j < sv_maxclients->current.integer; j++, client++)
{
if (client->header.state < CS_CLIENTLOADING)
{
continue;
}
SV_AddServerCommand(client, type, server_command_buf_large.get());
}
}
void SV_GameSendServerCommand(int clientNum, svscmd_type type, const char* text)
{
[[maybe_unused]] const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
if (clientNum == -1)
{
SV_SendServerCommand(nullptr, type, "%s", text);
return;
}
assert(sv_maxclients->current.integer >= 1 && sv_maxclients->current.integer <= 18);
assert(static_cast<unsigned>(clientNum) < sv_maxclients->current.unsignedInt);
SV_SendServerCommand(&mp::svs_clients[clientNum], type, "%s", text);
}
void SV_ReconnectClients(int savepersist)
{
const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
for (int i = 0; i < sv_maxclients->current.integer; ++i)
{
mp::client_t* client = &mp::svs_clients[i];
if (client->header.state < CS_CONNECTED)
{
continue;
}
SV_AddServerCommand(client, SV_CMD_RELIABLE, utils::string::va("%c", savepersist != 0 ? 107 : 118));
const char* denied = ClientConnect(i, client->scriptId);
if (denied)
{
SV_DropClient(client, denied, true);
console::info("SV_MapRestart_f: dropped client %i - denied!\n", i);
}
else if (client->header.state == CS_ACTIVE)
{
SV_ClientEnterWorld(client, &client->lastUsercmd);
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
namespace game::engine
{
void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...);
void SV_GameSendServerCommand(int clientNum, svscmd_type type, const char* text);
void SV_ReconnectClients(int savepersist);
}
+7 -8
View File
@@ -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;
@@ -49,11 +56,6 @@ namespace game
return get_mode() == launcher::mode::multiplayer;
}
bool is_sp()
{
return get_mode() == launcher::mode::singleplayer;
}
void set_mode(const launcher::mode _mode)
{
mode = _mode;
@@ -70,9 +72,6 @@ namespace game
case launcher::mode::multiplayer:
return "Multiplayer";
case launcher::mode::singleplayer:
return "Multiplayer";
case launcher::mode::none:
return "None";
+11 -8
View File
@@ -1,9 +1,16 @@
#pragma once
#include "structs.hpp"
#include "launcher/launcher.hpp"
#define SELECT_VALUE(sp, mp) (game::environment::is_sp() ? (sp) : (mp))
namespace launcher
{
enum class mode
{
none,
multiplayer,
server,
};
}
#define SERVER_CD_KEY "iw6-mod-CD-Key"
@@ -14,7 +21,6 @@ namespace game
launcher::mode get_mode();
bool is_mp();
bool is_sp();
bool is_dedi();
void set_mode(launcher::mode mode);
@@ -34,11 +40,6 @@ namespace game
T* get() const
{
if (environment::is_sp())
{
return sp_object_;
}
return mp_object_;
}
@@ -63,6 +64,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");
+1 -1
View File
@@ -70,7 +70,7 @@ namespace scripting
script_function get_function_by_index(const std::uint32_t index)
{
static const auto function_table = &gsc::func_table;
static const auto method_table = SELECT_VALUE(0x144E1F9E0, 0x1446B8A90);
static const auto method_table = 0x1446B8A90;
if (index <= 0x1000)
{
+142 -10
View File
@@ -10,6 +10,14 @@ namespace game
typedef vec_t vec3_t[3];
typedef vec_t vec4_t[4];
enum ControllerIndex_t
{
INVALID_CONTROLLER_PORT = -1,
CONTROLLER_INDEX_0 = 0x0,
CONTROLLER_INDEX_FIRST = 0x0,
CONTROLLER_INDEX_COUNT = 0x1,
};
enum
{
FL_GODMODE = 0x1,
@@ -47,6 +55,12 @@ namespace game
PMF_UNK3 = 0x4000,
};
enum svscmd_type
{
SV_CMD_CAN_IGNORE,
SV_CMD_RELIABLE,
};
enum XAssetType
{
ASSET_TYPE_PHYSPRESET = 0x0,
@@ -127,6 +141,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,
@@ -622,6 +649,33 @@ namespace game
BD_MAX_ERROR_CODE = 0x27E2,
};
enum bdBitBufferDataType
{
BD_BB_NO_TYPE = 0x0,
BD_BB_BOOL_TYPE = 0x1,
BD_BB_SIGNED_CHAR8_TYPE = 0x2,
BD_BB_UNSIGNED_CHAR8_TYPE = 0x3,
BD_BB_WCHAR16_TYPE = 0x4,
BD_BB_SIGNED_INTEGER16_TYPE = 0x5,
BD_BB_UNSIGNED_INTEGER16_TYPE = 0x6,
BD_BB_SIGNED_INTEGER32_TYPE = 0x7,
BD_BB_UNSIGNED_INTEGER32_TYPE = 0x8,
BD_BB_SIGNED_INTEGER64_TYPE = 0x9,
BD_BB_UNSIGNED_INTEGER64_TYPE = 0xA,
BD_BB_RANGED_SIGNED_INTEGER32_TYPE = 0xB,
BD_BB_RANGED_UNSIGNED_INTEGER32_TYPE = 0xC,
BD_BB_FLOAT32_TYPE = 0xD,
BD_BB_FLOAT64_TYPE = 0xE,
BD_BB_RANGED_FLOAT32_TYPE = 0xF,
BD_BB_SIGNED_CHAR8_STRING_TYPE = 0x10,
BD_BB_UNSIGNED_CHAR8_STRING_TYPE = 0x11,
BD_BB_MBSTRING_TYPE = 0x12,
BD_BB_BLOB_TYPE = 0x13,
BD_BB_NAN_TYPE = 0x14,
BD_BB_FULL_TYPE = 0x15,
BD_BB_MAX_TYPE = 0x20,
};
enum bdNATType : uint8_t
{
BD_NAT_UNKNOWN = 0x0,
@@ -936,6 +990,27 @@ namespace game
LOOKUP_ERROR_COUNT = 0x5,
};
struct ComStreamSyncModel
{
unsigned __int16 modelIndex;
unsigned __int8 alternateIndex;
};
static_assert(sizeof(ComStreamSyncModel) == 4);
struct ComStreamedSyncModelList
{
unsigned int modelCount;
ComStreamSyncModel models[18];
};
static_assert(sizeof(ComStreamedSyncModelList) == 0x4C);
struct ComStreamedSyncSwapBuffer
{
ComStreamedSyncModelList modelLists[2];
};
struct StructuredDataEnumEntry
{
unsigned int name;
@@ -1106,6 +1181,10 @@ namespace game
DVAR_FLAG_LATCHED = 0x2,
DVAR_FLAG_CHEAT = 0x4,
DVAR_FLAG_REPLICATED = 0x8,
DVAR_FLAG_SCRIPTINFO = 0x10,
DVAR_FLAG_INTERNAL = 0x80,
DVAR_FLAG_EXTERNAL = 0x100,
DVAR_FLAG_SERVERINFO = 0x400,
DVAR_FLAG_WRITE = 0x800,
DVAR_FLAG_READ = 0x2000,
};
@@ -1124,7 +1203,7 @@ namespace game
rgb = 9 // Color without alpha
};
union dvar_value
union DvarValue
{
bool enabled;
int integer;
@@ -1167,9 +1246,9 @@ namespace game
unsigned int flags; //08
dvar_type type; //0C
bool modified; //0D
dvar_value current; //10
dvar_value latched;
dvar_value reset;
DvarValue current; //10
DvarValue latched;
DvarValue reset;
dvar_limits domain;
};
@@ -1553,6 +1632,13 @@ namespace game
VAR_TOTAL_COUNT = 0x1B,
};
struct ScriptFunctions
{
int maxSize;
int count;
int* address;
};
struct VariableStackBuffer
{
const char* pos;
@@ -1909,6 +1995,12 @@ namespace game
const char* buffer;
};
struct WeaponCompleteDef
{
const char* szInternalName;
WeaponDef* weapDef;
};
union XAssetHeader
{
void* data;
@@ -1944,16 +2036,16 @@ namespace game
menuDef_t *menu;
AnimationClass *animClass;
LocalizeEntry *localize;
WeaponAttachment *attachment;
WeaponCompleteDef *weapon;
SndDriverGlobals *sndDriverGlobals;
WeaponAttachment *attachment;*/
WeaponCompleteDef* weapon;
/*SndDriverGlobals *sndDriverGlobals;
FxEffectDef *fx;
FxImpactTable *impactFx;
SurfaceFxTable *surfaceFx;*/
RawFile* rawfile;
ScriptFile* scriptfile;
/*StringTable *stringTable;
LeaderboardDef *leaderboardDef;
StringTable* stringTable;
/*LeaderboardDef *leaderboardDef;
StructuredDataDefSet *structuredDataDefSet;
TracerDef *tracerDef;
VehicleDef *vehDef;
@@ -2015,6 +2107,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...
@@ -2368,6 +2472,24 @@ namespace game
int isInKillcam;
};
struct svscmd_info_t
{
int time;
int type;
char cmd[1024];
};
static_assert(sizeof(svscmd_info_t) == 0x408);
struct client_net_buffers_t
{
svscmd_info_t reliableCommandInfo[128];
char netchanOutgoingBuffer[131072];
char netchanIncomingBuffer[2048];
};
static_assert(sizeof(client_net_buffers_t) == 0x40C00);
struct client_t
{
clientHeader_t header;
@@ -2377,7 +2499,17 @@ namespace game
int reliableAcknowledge;
int reliableSent;
int messageAcknowledge;
char _0xC30[0x41238];
int largeCommandSequence;
int gamestateMessageNum;
int challenge;
client_net_buffers_t netBuf;
int cumulThinkTime;
int beginCmdIndex;
int currCmdIndex;
usercmd_s lastUsercmd;
usercmd_s cmds[8];
int lastClientCommand;
char lastClientCommandString[1024];
gentity_s* gentity;
char name[16];
int lastPacketTime;
+48 -17
View File
@@ -8,17 +8,19 @@ namespace game
* Functions
**************************************************************/
WEAK symbol<void(unsigned int id)> AddRefToObject{0, 0x1404326D0};
WEAK symbol<void(unsigned int id)> AddRefToObject{0x1403D7A10, 0x1404326D0};
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x1403D7740, 0x1404326E0};
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0, 0x1404329B0};
WEAK symbol<ObjectVariableValue*(unsigned int* index)> AllocVariable{0x1403D7A70, 0x140432A10};
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x1403D90F0, 0x1404340C0};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0, 0x140433FB0};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x1403D8FE0, 0x140433FB0};
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x1403D91C0, 0x140434190};
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x140139D80};
WEAK symbol<void(unsigned int weapon, bool isAlternate, char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0, 0x140239370};
WEAK symbol<void()> BG_ClearWeaponDef{0x0, 0x140238D20};
WEAK symbol<bool()> BG_BotsConnectType{0x0, 0x140217080};
WEAK symbol<void()> Com_Frame_Try_Block_Function{0x1403BC980, 0x1404131A0};
WEAK symbol<const char*(char const**)> Com_Parse{0x1404313E0, 0x1404F50E0};
@@ -28,6 +30,8 @@ namespace game
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1404158C0};
WEAK symbol<void(const char* text_in)> Com_TokenizeString{0x1403B4150, 0x1403F7CC0};
WEAK symbol<void()> Com_EndTokenizeString{0x1403B37C0, 0x1403F7330};
WEAK symbol<void()> Com_StreamSync_UpdateLaunchData{0x0, 0x140411B50};
WEAK symbol<int(char* dest, int size, const char* fmt, ...)> Com_sprintf{0x140432310, 0x1404F6260};
WEAK symbol<void(const char* message)> Conbuf_AppendText{0x14043DDE0, 0x1405028C0};
@@ -54,11 +58,12 @@ namespace game
WEAK symbol<XAssetHeader(XAssetType type, const char* name, int allowCreateDefault)> DB_FindXAssetHeader{0x140272300, 0x14031F3A0};
WEAK symbol<int(XAssetType type, const char* name)> DB_XAssetExists{0x140276200, 0x1403245E0};
WEAK symbol<int(XAssetType type, const char* name)> DB_IsXAssetDefault{0x140273480 , 0x1403204D0};
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x0140272E80, 0x14031FF80};
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x140272E80, 0x14031FF80};
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x140272D50, 0x14031FE50};
WEAK symbol<int(const char* zoneName)> DB_IsLocalized{0x140273210, 0x140320360};
WEAK symbol<int(XAssetType type, void** assets, int maxCount)> DB_GetAllXAssetOfType_FastFile{0x0, 0x14031FC00};
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, PMem_Source source)> PMem_AllocFromSource_NoDebug{0x0140430B80, 0x001404F46C0};
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, PMem_Source source)> PMem_AllocFromSource_NoDebug{0x140430B80, 0x1404F46C0};
WEAK symbol<void(const char* name, PMem_Direction allocDir)> PMem_Free{0x140430EC0 , 0x1404F4A30};
WEAK symbol<void*(unsigned int size)> Hunk_AllocateTempMemoryHighInternal{0x140423C70, 0x1404E4E20};
@@ -85,7 +90,8 @@ namespace game
WEAK symbol<void(const dvar_t* dvar, const char* string)> Dvar_SetString{0x14042D6E0, 0x1404F08E0};
WEAK symbol<void(const char*, const char*, DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x14042D000, 0x1404F00B0};
WEAK symbol<void()> Dvar_Sort{0x14042DEF0, 0x1404F1210};
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x14042E710, 0x1404F1A30};
WEAK symbol<const char*(dvar_t* dvar, DvarValue value)> Dvar_ValueToString{0x14042E710, 0x1404F1A30};
WEAK symbol<bool(const char* dvarName)> Dvar_IsValidName{0x0, 0x1404ED0E0};
WEAK symbol<long long (const char* qpath, char** buffer)> FS_ReadFile{0x14041D0B0, 0x1404DE900};
WEAK symbol<void(void* buffer)> FS_FreeFile{0x14041D0A0, 0x1404DE8F0};
@@ -96,6 +102,7 @@ namespace game
WEAK symbol<Weapon(const char* pickupName, int model)> G_FindItem{0x140462490, 0x14021B7E0};
WEAK symbol<int(playerState_s* ps, Weapon weapon, int dualWield, int startInAltMode, int usedBefore)> G_GivePlayerWeapon{0x140359E20, 0x1403DA5E0};
WEAK symbol<Weapon(const char* name)> G_GetWeaponForName{0x140359890, 0x1403DA060};
WEAK symbol<void()> G_SetupLevelWeaponDef{0x0, 0x1403DA910};
WEAK symbol<void()> G_Glass_Update{0x14030E680, 0x140397450};
WEAK symbol<void (playerState_s* ps, Weapon weapon, int hadWeapon)> G_InitializeAmmo{0x140311F00, 0x14039AEA0};
WEAK symbol<void(int clientNum, Weapon weapon)> G_SelectWeapon{0x14035A200, 0x1403DA880};
@@ -103,18 +110,21 @@ namespace game
WEAK symbol<unsigned int (const char* name, /*ConfigString*/ unsigned int start, unsigned int max, int create,
const char* errormsg)> G_FindConfigstringIndex{0x0, 0x140161F90};
WEAK symbol<int(int server_time)> G_RunFrame{0x0, 0x1403A05E0};
WEAK symbol<int(int clientNun)> G_GetClientScore{0x0, 0x14039EF60};
WEAK symbol<game_hudelem_s*(int clientNum, int teamNum)> HudElem_Alloc{0x0, 0x1403997E0};
WEAK symbol<char*(char* string)> I_CleanStr{0x140432460, 0x1404F63C0};
WEAK symbol<void(char* dest, const char* src, int destsize)> I_strncpyz{0x140432810, 0x1404F67A0};
WEAK symbol<void(char* dest, int size, const char* src)> I_strncat{0x140432740, 0x1404F66D0};
WEAK symbol<char*(GfxImage* image, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipCount,
uint32_t imageFlags, DXGI_FORMAT imageFormat, const char* name, const void* initData)>
Image_Setup{0x140517910, 0x1405E4380};
uint32_t imageFlags, DXGI_FORMAT imageFormat, const char* name, const void* initData)> Image_Setup{0x140517910, 0x1405E4380};
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x14023D9A0, 0x1402C40E0};
WEAK symbol<unsigned int (int)> Live_SyncOnlineDataFlags{0, 0x1405ABF70};
WEAK symbol<void(int localControllerIndex)> LiveStorage_StatsWriteNotNeeded{0x1403BA420, 0x140409120};
WEAK symbol<bool(int controllerIndex, unsigned int name, int value, StatsGroup statsGroup)> LiveStorage_PlayerDataSetIntByName{0x1403B8C20, 0x140404730};
WEAK symbol<bool(std::uint8_t* persistentData, const char* lookupString, int value, std::uint8_t* modifiedFlags, StatsGroup statsGroup)> LiveStorage_PlayerDataSetReservedInt{0x1403B8D00, 0x140404820};
@@ -132,8 +142,9 @@ namespace game
WEAK symbol<StructuredDataDef*(const char* filename, unsigned int maxSize)>StructuredDataDef_GetAsset{0, 0x1404E6560};
WEAK symbol<StringTable*(const char* fileName, const StringTable** tablePtr)>StringTable_GetAsset{0, 0x1404E6170};
WEAK symbol<const char*(const StringTable* table, const int row, const int column)> StringTable_GetColumnValueForRow{0, 0x1404E61A0};
WEAK symbol<int(const StringTable* table, const int comparisonColumn, const char* value)> StringTable_LookupRowNumForValue{0, 0x1404E6260};
WEAK symbol<const char*(const StringTable* table, int row, int column)> StringTable_GetColumnValueForRow{0, 0x1404E61A0};
WEAK symbol<int(const StringTable* table, int comparisonColumn, const char* value)> StringTable_LookupRowNumForValue{0, 0x1404E6260};
WEAK symbol<int(const char* string)> StringTable_HashString{0x1404259A0, 0x1404E6320};
WEAK symbol<void(int localClientNum, const char* menuName, int isPopup, int isModal, unsigned int isExclusive)> LUI_OpenMenu{0x1403FD460, 0x1404B3610};
// Made up name, replaced by ScopedCriticalSection on Black Ops 3
@@ -163,13 +174,17 @@ namespace game
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1403D84F0, 0x1404334A0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0, 0x1404333A0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int id)> GetVariableName{0x1403D8E90, 0x140433E60};
WEAK symbol<void (VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x1403DC810, 0x140437860};
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x1403DC810, 0x140437860};
WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x1403D9AF0, 0x140434A10};
WEAK symbol<const char*(unsigned int index)> Scr_GetString{0, 0x140439160};
WEAK symbol<const char*(unsigned int index)> Scr_GetString{0x1403DE200, 0x140439160};
WEAK symbol<unsigned int(unsigned int index)> Scr_GetConstString{0x1403DDAC0, 0x140438AD0};
WEAK symbol<unsigned int(unsigned int index)> Scr_GetConstIString{0x1403DD8A0, 0x1404388B0};
WEAK symbol<void(int value)> Scr_AddInt{0x0, 0x140437E70};
WEAK symbol<void(const char* value)> Scr_AddString{0x0, 0x1404381D0};
WEAK symbol<int(unsigned int index)> Scr_GetInt{0x0, 0x140438E10};
WEAK symbol<float(unsigned int index)> Scr_GetFloat{0, 0x140438D60};
WEAK symbol<int(unsigned int index)> Scr_GetInt{0x1403DDEB0, 0x140438E10};
WEAK symbol<float(unsigned int index)> Scr_GetFloat{0x1403DDD50, 0x140438D60};
WEAK symbol<void(unsigned int index, float* vectorValue)> Scr_GetVector{0x1403DE470, 0x1404393D0};
WEAK symbol<unsigned int(unsigned int index)> Scr_GetObject{0x1403DDF70, 0x140438ED0};
WEAK symbol<unsigned int()> Scr_GetNumParam{0x1403DDF60, 0x140438EC0};
WEAK symbol<void()> Scr_ClearOutParams{0x1403DD500, 0x140438600};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x1403DBDC0, 0x140436E10};
@@ -178,15 +193,16 @@ namespace game
WEAK symbol<void(unsigned int id, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyId{0x1403DE730, 0x140439700};
WEAK symbol<void(unsigned int stringValue, unsigned int paramcount)> Scr_NotifyLevel{0x0, 0x1404397D0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> Scr_GetEntityId{0x0, 0x140436D60};
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x0, 0x140434AC0};
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x1403D9BA0, 0x140434AC0};
WEAK symbol<unsigned __int16(int handle, unsigned int paramcount)> Scr_ExecThread{0x1403DD600, 0x1404386E0};
WEAK symbol<unsigned int(const char* name)> Scr_LoadScript{0x1403D3C50, 0x14042EAA0};
WEAK symbol<unsigned int(const char* script, unsigned int name)> Scr_GetFunctionHandle{0x1403D3AD0 , 0x14042E920};
WEAK symbol<unsigned int(void* func, int type, unsigned int name)> Scr_RegisterFunction{0x1403D3530, 0x14042E330};
WEAK symbol<void()> Scr_ErrorInternal{0x0, 0x140438660};
WEAK symbol<unsigned int(const char** pName, int* type)> Scr_GetFunction{0x14034A950, 0x1403CD9F0};
WEAK symbol<void()> Scr_ErrorInternal{0x1403DD580 , 0x140438660};
WEAK symbol<int(unsigned int)> GetObjectType{0x0, 0x140433CF0};
WEAK symbol<int(unsigned int)> GetObjectType{0x1403D8D30, 0x140433CF0};
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0, 0x14043A280};
@@ -204,10 +220,13 @@ namespace game
WEAK symbol<void()> SV_MatchEnd{0x0, 0x14047A090};
WEAK symbol<void(netadr_s* from)> SV_DirectConnect{0, 0x140471390};
WEAK symbol<void(int, int, const char*)> SV_GameSendServerCommand{0x140490F40, 0x1404758C0};
WEAK symbol<bool()> SV_Loaded{0x140491820, 0x1404770C0};
WEAK symbol<void(int localClientNum, const char* map, bool mapIsPreloaded)> SV_StartMap{0, 0x140470170};
WEAK symbol<void(int localClientNum, const char* map, bool mapIsPreloaded, bool migrate)> SV_StartMapForParty{0, 0x1404702F0};
WEAK symbol<void(mp::client_t* cl, int isReconnectingClient)> SV_StreamSync_ClientConnect{0x0, 0x140488080};
WEAK symbol<const char*(int clientNum, unsigned __int16 scriptPersId)> ClientConnect{0x0, 0x140387630};
WEAK symbol<void(mp::client_t* client, usercmd_s* cmd)> SV_ClientEnterWorld{0x0, 0x1404710F0};
WEAK symbol<void(mp::client_t* drop, const char* reason, bool tellThem)> SV_DropClient{0x0, 0x140472110};
WEAK symbol<mp::gentity_s*(const char*, unsigned int, unsigned int, unsigned int)> SV_AddBot{0, 0x140470920};
WEAK symbol<bool(int clientNum)> SV_BotIsBot{0, 0x140461340};
@@ -220,6 +239,7 @@ namespace game
WEAK symbol<const char*(int clientNum)> SV_GetGuid{0, 0x140475990};
WEAK symbol<void(int clientNum, const char* reason)> SV_KickClientNum{0, 0x14046F730};
WEAK symbol<void(int index, const char* string)> SV_SetConfigstring{0, 0x140477450};
WEAK symbol<int(mp::client_t* client, const unsigned char* cmd, int cmdSize)> SV_CanReplaceServerCommand{0x0, 0x140478F00};
WEAK symbol<void(const char* error, ...)> Sys_Error{0x14043AC20, 0x1404FF510};
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x1403C2D40, 0x140423920};
@@ -236,6 +256,7 @@ namespace game
WEAK symbol<const char*(const char*)> UI_LocalizeMapname{0, 0x1404B96D0};
WEAK symbol<const char*(const char*)> UI_LocalizeGametype{0, 0x1404B90F0};
WEAK symbol<void(int localClientNum, const char* srcString, char* dstString, int dstBufferSize)> UI_ReplaceDirective{0x0, 0x1404D8A00};
WEAK symbol<DWOnlineStatus(int)> dwGetLogOnStatus{0, 0x140589490};
@@ -246,6 +267,12 @@ namespace game
WEAK symbol<void(void* ps)> Jump_ClearState{0x0, 0x140213120};
WEAK symbol<void(const char* pszCommand, char* pszBuffer, int iBufferSize)> MSG_WriteReliableCommandToBuffer{0x0, 0x1404232B0};
WEAK symbol<void(unsigned __int64 markPos)> LargeLocalResetToMark{0x140423B50, 0x1404E4D00};
WEAK symbol<int(const char* s0, const char* s1, int n)> I_strnicmp{0x140432840, 0x1404F67D0};
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14062E030, 0x140738060};
WEAK symbol<int (jmp_buf* Buf)> _setjmp{0x14062F030, 0x140739060};
@@ -281,6 +308,8 @@ namespace game
WEAK symbol<DB_LoadData> g_load{0x1418CF4A0, 0x141EEFBA0};
WEAK symbol<float> com_codeTimeScale{0x0, 0x1445CEF84};
WEAK symbol<WinVars_t> g_wv{0x145A7BAD0, 0x147AD2630};
WEAK symbol<WinMouseVars_t> s_wmv{0x145A73750, 0x147AC9D78};
@@ -324,6 +353,8 @@ namespace game
WEAK symbol<int> serverTime{0, 0x14647B280};
WEAK symbol<XZone> g_zones_0{0, 0x143A46498};
WEAK symbol<int> s_launchDataAvailable{0x0, 0x1445CE354};
}
namespace hks
@@ -1,115 +0,0 @@
#include <std_include.hpp>
#include "html_frame.hpp"
doc_host_ui_handler::doc_host_ui_handler(html_frame* frame): frame_(frame)
{
}
HRESULT doc_host_ui_handler::QueryInterface(REFIID riid, LPVOID* ppvObj)
{
auto client_site = this->frame_->get_client_site();
if (client_site)
{
return client_site->QueryInterface(riid, ppvObj);
}
return E_NOINTERFACE;
}
ULONG doc_host_ui_handler::AddRef()
{
return 1;
}
ULONG doc_host_ui_handler::Release()
{
return 1;
}
HRESULT doc_host_ui_handler::ShowContextMenu(DWORD /*dwID*/, POINT* /*ppt*/, IUnknown* /*pcmdtReserved*/,
IDispatch* /*pdispReserved*/)
{
return S_OK;
}
HRESULT doc_host_ui_handler::ShowUI(DWORD /*dwID*/, IOleInPlaceActiveObject* /*pActiveObject*/,
IOleCommandTarget* /*pCommandTarget*/,
IOleInPlaceFrame* /*pFrame*/, IOleInPlaceUIWindow* /*pDoc*/)
{
return S_OK;
}
HRESULT doc_host_ui_handler::HideUI()
{
return S_OK;
}
HRESULT doc_host_ui_handler::UpdateUI()
{
return S_OK;
}
HRESULT doc_host_ui_handler::EnableModeless(BOOL /*fEnable*/)
{
return S_OK;
}
HRESULT doc_host_ui_handler::OnDocWindowActivate(BOOL /*fActivate*/)
{
return S_OK;
}
HRESULT doc_host_ui_handler::OnFrameWindowActivate(BOOL /*fActivate*/)
{
return S_OK;
}
HRESULT doc_host_ui_handler::ResizeBorder(LPCRECT /*prcBorder*/, IOleInPlaceUIWindow* /*pUIWindow*/,
BOOL /*fRameWindow*/)
{
return S_OK;
}
HRESULT doc_host_ui_handler::TranslateAcceleratorA(LPMSG /*lpMsg*/, const GUID* pguidCmdGroup, DWORD /*nCmdID*/)
{
pguidCmdGroup = nullptr;
return S_FALSE;
}
HRESULT doc_host_ui_handler::GetOptionKeyPath(LPOLESTR* /*pchKey*/, DWORD /*dw*/)
{
return S_FALSE;
}
HRESULT doc_host_ui_handler::GetDropTarget(IDropTarget* /*pDropTarget*/, IDropTarget** /*ppDropTarget*/)
{
return S_FALSE;
}
HRESULT doc_host_ui_handler::GetExternal(IDispatch** ppDispatch)
{
*ppDispatch = this->frame_->get_html_dispatch();
return (*ppDispatch) ? S_OK : S_FALSE;
}
HRESULT doc_host_ui_handler::FilterDataObject(IDataObject* /*pDO*/, IDataObject** ppDORet)
{
*ppDORet = nullptr;
return S_FALSE;
}
HRESULT STDMETHODCALLTYPE doc_host_ui_handler::TranslateUrl(DWORD /*dwTranslate*/, OLECHAR __RPC_FAR* /*pchURLIn*/,
OLECHAR __RPC_FAR* __RPC_FAR* ppchURLOut)
{
*ppchURLOut = nullptr;
return S_FALSE;
}
HRESULT doc_host_ui_handler::GetHostInfo(DOCHOSTUIINFO __RPC_FAR * pInfo)
{
pInfo->cbSize = sizeof(DOCHOSTUIINFO);
pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_DPI_AWARE /*| DOCHOSTUIFLAG_SCROLL_NO*/;
pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
return S_OK;
}
@@ -1,47 +0,0 @@
#pragma once
class html_frame;
class doc_host_ui_handler final : public IDocHostUIHandler
{
public:
doc_host_ui_handler(html_frame* frame);
virtual ~doc_host_ui_handler() = default;
private:
html_frame* frame_;
public: // IDocHostUIHandler interface
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObj) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE ShowContextMenu(
DWORD dwID,
POINT __RPC_FAR * ppt,
IUnknown __RPC_FAR * pcmdtReserved,
IDispatch __RPC_FAR * pdispReserved) override;
HRESULT STDMETHODCALLTYPE ShowUI(
DWORD dwID,
IOleInPlaceActiveObject __RPC_FAR * pActiveObject,
IOleCommandTarget __RPC_FAR * pCommandTarget,
IOleInPlaceFrame __RPC_FAR * pFrame,
IOleInPlaceUIWindow __RPC_FAR * pDoc) override;
HRESULT STDMETHODCALLTYPE GetHostInfo(DOCHOSTUIINFO __RPC_FAR * pInfo) override;
HRESULT STDMETHODCALLTYPE HideUI() override;
HRESULT STDMETHODCALLTYPE UpdateUI() override;
HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable) override;
HRESULT STDMETHODCALLTYPE OnDocWindowActivate(BOOL fActivate) override;
HRESULT STDMETHODCALLTYPE OnFrameWindowActivate(BOOL fActivate) override;
HRESULT STDMETHODCALLTYPE ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR * pUIWindow,
BOOL fRameWindow) override;
HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpMsg, const GUID __RPC_FAR * pguidCmdGroup, DWORD nCmdID)
override;
HRESULT STDMETHODCALLTYPE GetOptionKeyPath(LPOLESTR __RPC_FAR * pchKey, DWORD dw) override;
HRESULT STDMETHODCALLTYPE GetDropTarget(IDropTarget __RPC_FAR * pDropTarget,
IDropTarget __RPC_FAR *__RPC_FAR * ppDropTarget) override;
HRESULT STDMETHODCALLTYPE GetExternal(IDispatch __RPC_FAR *__RPC_FAR * ppDispatch) override;
HRESULT STDMETHODCALLTYPE TranslateUrl(DWORD dwTranslate, OLECHAR __RPC_FAR * pchURLIn,
OLECHAR __RPC_FAR *__RPC_FAR * ppchURLOut) override;
HRESULT STDMETHODCALLTYPE FilterDataObject(IDataObject __RPC_FAR * pDO, IDataObject __RPC_FAR *__RPC_FAR * ppDORet)
override;
};
@@ -1,48 +0,0 @@
#include <std_include.hpp>
#include "html_argument.hpp"
html_argument::html_argument(VARIANT* val) : value_(val)
{
}
bool html_argument::is_empty() const
{
return this->value_ == nullptr || this->value_->vt == VT_EMPTY;
}
bool html_argument::is_string() const
{
if (this->is_empty()) return false;
return this->value_->vt == VT_BSTR;
}
bool html_argument::is_number() const
{
if (this->is_empty()) return false;
return this->value_->vt == VT_I4;
}
bool html_argument::is_bool() const
{
if (this->is_empty()) return false;
return this->value_->vt == VT_BOOL;
}
std::string html_argument::get_string() const
{
if (!this->is_string()) return {};
std::wstring wide_string(this->value_->bstrVal);
return std::string(wide_string.begin(), wide_string.end());
}
int html_argument::get_number() const
{
if (!this->is_number()) return 0;
return this->value_->intVal;
}
bool html_argument::get_bool() const
{
if (!this->is_bool()) return false;
return this->value_->boolVal != FALSE;
}
@@ -1,20 +0,0 @@
#pragma once
class html_argument final
{
public:
html_argument(VARIANT* val);
bool is_empty() const;
bool is_string() const;
bool is_number() const;
bool is_bool() const;
std::string get_string() const;
int get_number() const;
bool get_bool() const;
private:
VARIANT* value_;
};
@@ -1,61 +0,0 @@
#include <std_include.hpp>
#include "html_frame.hpp"
html_dispatch::html_dispatch(html_frame* frame) : frame_(frame)
{
}
HRESULT html_dispatch::QueryInterface(const IID& riid, LPVOID* ppvObj)
{
if (!memcmp(&riid, &IID_IUnknown, sizeof(GUID)) ||
!memcmp(&riid, &IID_IDispatch, sizeof(GUID)))
{
*ppvObj = this;
this->AddRef();
return S_OK;
}
*ppvObj = nullptr;
return E_NOINTERFACE;
}
ULONG html_dispatch::AddRef()
{
return 1;
}
ULONG html_dispatch::Release()
{
return 1;
}
HRESULT html_dispatch::GetTypeInfoCount(UINT* pctinfo)
{
return S_FALSE;
}
HRESULT html_dispatch::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
{
return S_FALSE;
}
HRESULT html_dispatch::GetIDsOfNames(const IID& riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
{
for (unsigned int i = 0; i < cNames; ++i)
{
std::wstring wide_name(rgszNames[i]);
std::string name(wide_name.begin(), wide_name.end());
rgDispId[i] = this->frame_->get_callback_id(name);
}
return S_OK;
}
HRESULT html_dispatch::Invoke(DISPID dispIdMember, const IID& riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
html_frame::callback_params params(pDispParams, pVarResult);
this->frame_->invoke_callback(dispIdMember, &params);
return S_OK;
}
@@ -1,24 +0,0 @@
#pragma once
class html_frame;
class html_dispatch final : public IDispatch
{
public:
html_dispatch(html_frame* frame);
virtual ~html_dispatch() = default;
private:
html_frame* frame_;
public: // IDispatch interface
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT* pctinfo) override;
HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) override;
HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
override;
HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) override;
};
-320
View File
@@ -1,320 +0,0 @@
#include <std_include.hpp>
#include "html_frame.hpp"
#include <utils/nt.hpp>
#include <utils/hook.hpp>
std::atomic<int> html_frame::frame_count_ = 0;
namespace
{
void* original_func{};
GUID browser_emulation_guid{0xac969931, 0x3566, 0x4b50, {0xae, 0x48, 0x71, 0xb9, 0x6a, 0x75, 0xc8, 0x79}};
int WINAPI co_internet_feature_value_internal_stub(const GUID* guid, uint32_t* result)
{
const auto res = static_cast<decltype(co_internet_feature_value_internal_stub)*>(original_func)(guid, result);
if (IsEqualGUID(*guid, browser_emulation_guid))
{
*result = 11000;
return 0;
}
return res;
}
void setup_ie_hook()
{
static const auto _ = []
{
const auto urlmon = utils::nt::library::load("urlmon.dll"s);
const auto target = urlmon.get_iat_entry("iertutil.dll", MAKEINTRESOURCEA(700));
original_func = *target;
utils::hook::set(target, co_internet_feature_value_internal_stub);
return 0;
}();
(void)_;
}
}
html_frame::callback_params::callback_params(DISPPARAMS* params, VARIANT* res) : result(res)
{
for (auto i = params->cArgs; i > 0; --i)
{
auto param = &params->rgvarg[i - 1];
this->arguments.emplace_back(param);
}
}
html_frame::html_frame() : in_place_frame_(this), in_place_site_(this), ui_handler_(this), client_site_(this),
html_dispatch_(this)
{
setup_ie_hook();
if (frame_count_++ == 0 && OleInitialize(nullptr) != S_OK)
{
throw std::runtime_error("Unable to initialize the OLE library");
}
set_browser_feature("FEATURE_BROWSER_EMULATION", 11000);
set_browser_feature("FEATURE_GPU_RENDERING", 1);
}
html_frame::~html_frame()
{
if (--frame_count_ <= 0)
{
frame_count_ = 0;
OleUninitialize();
}
}
void html_frame::object_deleter(IUnknown* object)
{
if (object)
{
object->Release();
}
}
HWND html_frame::get_window() const
{
return this->window_;
}
std::shared_ptr<IOleObject> html_frame::get_browser_object() const
{
return this->browser_object_;
}
ole_in_place_frame* html_frame::get_in_place_frame()
{
return &this->in_place_frame_;
}
ole_in_place_site* html_frame::get_in_place_site()
{
return &this->in_place_site_;
}
doc_host_ui_handler* html_frame::get_ui_handler()
{
return &this->ui_handler_;
}
ole_client_site* html_frame::get_client_site()
{
return &this->client_site_;
}
html_dispatch* html_frame::get_html_dispatch()
{
return &this->html_dispatch_;
}
std::shared_ptr<IWebBrowser2> html_frame::get_web_browser() const
{
if (!this->browser_object_) return {};
IWebBrowser2* web_browser = nullptr;
if (FAILED(this->browser_object_->QueryInterface(IID_IWebBrowser2, reinterpret_cast<void**>(&web_browser)))
|| !web_browser)
return {};
return std::shared_ptr<IWebBrowser2>(web_browser, object_deleter);
}
std::shared_ptr<IDispatch> html_frame::get_dispatch() const
{
const auto web_browser = this->get_web_browser();
if (!web_browser) return {};
IDispatch* dispatch = nullptr;
if (FAILED(web_browser->get_Document(&dispatch)) || !dispatch) return {};
return std::shared_ptr<IDispatch>(dispatch, object_deleter);
}
std::shared_ptr<IHTMLDocument2> html_frame::get_document() const
{
const auto dispatch = this->get_dispatch();
if (!dispatch) return {};
IHTMLDocument2* document = nullptr;
if (FAILED(dispatch->QueryInterface(IID_IHTMLDocument2, reinterpret_cast<void**>(&document)))
|| !document)
return {};
return std::shared_ptr<IHTMLDocument2>(document, object_deleter);
}
void html_frame::initialize(const HWND window)
{
if (this->window_) return;
this->window_ = window;
this->create_browser();
this->initialize_browser();
}
void html_frame::create_browser()
{
LPCLASSFACTORY class_factory = nullptr;
if (FAILED(
CoGetClassObject(CLSID_WebBrowser, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, nullptr, IID_IClassFactory,
reinterpret_cast<void **>(&class_factory))) || !class_factory)
{
throw std::runtime_error("Unable to get the class factory");
}
IOleObject* browser_object = nullptr;
class_factory->CreateInstance(nullptr, IID_IOleObject, reinterpret_cast<void**>(&browser_object));
class_factory->Release();
if (!browser_object)
{
throw std::runtime_error("Unable to create browser object");
}
this->browser_object_ = std::shared_ptr<IOleObject>(browser_object, [](IOleObject* browser_object)
{
if (browser_object)
{
browser_object->Close(OLECLOSE_NOSAVE);
object_deleter(browser_object);
}
});
}
void html_frame::initialize_browser()
{
this->browser_object_->SetClientSite(this->get_client_site());
this->browser_object_->SetHostNames(L"Hostname", nullptr);
RECT rect;
GetClientRect(this->get_window(), &rect);
OleSetContainedObject(this->browser_object_.get(), TRUE);
this->browser_object_->DoVerb(OLEIVERB_SHOW, nullptr, this->get_client_site(), -1, this->get_window(), &rect);
this->resize(rect.right, rect.bottom);
}
bool html_frame::set_browser_feature(const std::string& feature, DWORD value)
{
const auto registry_path = R"(SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\)" + feature;
HKEY key = nullptr;
if (RegCreateKeyA(HKEY_CURRENT_USER, registry_path.data(), &key) == ERROR_SUCCESS)
{
RegCloseKey(key);
}
key = nullptr;
if (RegOpenKeyExA(
HKEY_CURRENT_USER, registry_path.data(), 0,
KEY_ALL_ACCESS, &key) != ERROR_SUCCESS)
{
return false; // Error :(
}
const utils::nt::library self;
const auto name = self.get_name();
DWORD type{};
auto is_new = true;
if (RegQueryValueExA(key, name.data(), nullptr, &type, nullptr, nullptr) == ERROR_SUCCESS)
{
is_new = false;
}
RegSetValueExA(key, name.data(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(key);
return is_new;
}
void html_frame::resize(const DWORD width, const DWORD height) const
{
auto web_browser = this->get_web_browser();
if (web_browser)
{
web_browser->put_Left(0);
web_browser->put_Top(0);
web_browser->put_Width(width);
web_browser->put_Height(height);
}
}
bool html_frame::load_url(const std::string& url) const
{
auto web_browser = this->get_web_browser();
if (!web_browser) return false;
std::wstring wide_url(url.begin(), url.end());
VARIANT my_url;
VariantInit(&my_url);
my_url.vt = VT_BSTR;
my_url.bstrVal = SysAllocString(wide_url.data());
const auto _ = gsl::finally([&my_url]() { VariantClear(&my_url); });
if (!my_url.bstrVal) return false;
return SUCCEEDED(web_browser->Navigate2(&my_url, nullptr, nullptr, nullptr, nullptr));
}
bool html_frame::load_html(const std::string& html) const
{
if (!this->load_url("about:blank")) return false;
const auto document = this->get_document();
if (!document) return false;
SAFEARRAYBOUND safe_array_bound = {1, 0};
auto safe_array = SafeArrayCreate(VT_VARIANT, 1, &safe_array_bound);
if (!safe_array) return false;
const auto _ = gsl::finally([safe_array]() { SafeArrayDestroy(safe_array); });
VARIANT* variant = nullptr;
if (FAILED(SafeArrayAccessData(safe_array, reinterpret_cast<void**>(&variant))) || !variant) return false;
std::wstring wide_html(html.begin(), html.end());
variant->vt = VT_BSTR;
variant->bstrVal = SysAllocString(wide_html.data());
if (!variant->bstrVal) return false;
document->write(safe_array);
document->close();
return true;
}
int html_frame::get_callback_id(const std::string& name)
{
for (auto i = 0u; i < this->callbacks_.size(); ++i)
{
if (this->callbacks_[i].first == name)
{
return i;
}
}
return -1;
}
void html_frame::invoke_callback(const int id, callback_params* params)
{
if (id >= 0 && static_cast<unsigned int>(id) < this->callbacks_.size())
{
this->callbacks_[id].second(params);
}
}
void html_frame::register_callback(const std::string& name, const std::function<void(callback_params*)>& callback)
{
this->callbacks_.emplace_back(name, callback);
}
-67
View File
@@ -1,67 +0,0 @@
#pragma once
#include "ole_in_place_frame.hpp"
#include "ole_in_place_site.hpp"
#include "doc_host_ui_handler.hpp"
#include "ole_client_site.hpp"
#include "html_dispatch.hpp"
#include "html_argument.hpp"
class html_frame
{
public:
class callback_params final
{
public:
callback_params(DISPPARAMS* params, VARIANT* res);
std::vector<html_argument> arguments;
html_argument result;
};
html_frame();
virtual ~html_frame();
void initialize(HWND window);
void resize(DWORD width, DWORD height) const;
bool load_url(const std::string& url) const;
bool load_html(const std::string& html) const;
HWND get_window() const;
std::shared_ptr<IOleObject> get_browser_object() const;
std::shared_ptr<IWebBrowser2> get_web_browser() const;
std::shared_ptr<IDispatch> get_dispatch() const;
std::shared_ptr<IHTMLDocument2> get_document() const;
ole_in_place_frame* get_in_place_frame();
ole_in_place_site* get_in_place_site();
doc_host_ui_handler* get_ui_handler();
ole_client_site* get_client_site();
html_dispatch* get_html_dispatch();
int get_callback_id(const std::string& name);
void invoke_callback(int id, callback_params* params);
void register_callback(const std::string& name, const std::function<void(callback_params*)>& callback);
private:
HWND window_ = nullptr;
std::shared_ptr<IOleObject> browser_object_;
ole_in_place_frame in_place_frame_;
ole_in_place_site in_place_site_;
doc_host_ui_handler ui_handler_;
ole_client_site client_site_;
html_dispatch html_dispatch_;
std::vector<std::pair<std::string, std::function<void(callback_params*)>>> callbacks_;
void create_browser();
void initialize_browser();
static bool set_browser_feature(const std::string& feature, DWORD value);
static void object_deleter(IUnknown* object);
static std::atomic<int> frame_count_;
};
-29
View File
@@ -1,29 +0,0 @@
#include <std_include.hpp>
#include "html_window.hpp"
window* html_window::get_window()
{
return this;
}
html_frame* html_window::get_html_frame()
{
return this;
}
LRESULT html_window::processor(const UINT message, const WPARAM w_param, const LPARAM l_param)
{
if (message == WM_SIZE)
{
this->resize(LOWORD(l_param), HIWORD(l_param));
return 0;
}
if (message == WM_CREATE)
{
this->initialize(*this);
return 0;
}
return window::processor(message, w_param, l_param);
}
-15
View File
@@ -1,15 +0,0 @@
#pragma once
#include "../window.hpp"
#include "html_frame.hpp"
class html_window final : public window, public html_frame
{
public:
~html_window() = default;
window* get_window();
html_frame* get_html_frame();
private:
LRESULT processor(UINT message, WPARAM w_param, LPARAM l_param) override;
};
@@ -1,77 +0,0 @@
#include <std_include.hpp>
#include "html_frame.hpp"
ole_client_site::ole_client_site(html_frame* frame): frame_(frame)
{
}
HRESULT ole_client_site::QueryInterface(REFIID riid, LPVOID* ppvObject)
{
if (!memcmp(&riid, &IID_IUnknown, sizeof(GUID)) ||
!memcmp(&riid, &IID_IOleClientSite, sizeof(GUID)))
{
*ppvObject = this;
this->AddRef();
return S_OK;
}
if (!memcmp(&riid, &IID_IOleInPlaceSite, sizeof(GUID)))
{
auto in_place_site = this->frame_->get_in_place_site();
in_place_site->AddRef();
*ppvObject = in_place_site;
return S_OK;
}
if (!memcmp(&riid, &IID_IDocHostUIHandler, sizeof(GUID)))
{
auto ui_handler = this->frame_->get_ui_handler();
ui_handler->AddRef();
*ppvObject = ui_handler;
return S_OK;
}
*ppvObject = nullptr;
return E_NOINTERFACE;
}
ULONG ole_client_site::AddRef()
{
return 1;
}
ULONG ole_client_site::Release()
{
return 1;
}
HRESULT ole_client_site::SaveObject()
{
return E_NOTIMPL;
}
HRESULT ole_client_site::GetMoniker(DWORD /*dwAssign*/, DWORD /*dwWhichMoniker*/, IMoniker** /*ppmk*/)
{
return E_NOTIMPL;
}
HRESULT ole_client_site::GetContainer(LPOLECONTAINER* ppContainer)
{
*ppContainer = nullptr;
return E_NOINTERFACE;
}
HRESULT ole_client_site::ShowObject()
{
return NOERROR;
}
HRESULT ole_client_site::OnShowWindow(BOOL /*fShow*/)
{
return E_NOTIMPL;
}
HRESULT ole_client_site::RequestNewObjectLayout()
{
return E_NOTIMPL;
}
@@ -1,24 +0,0 @@
#pragma once
class html_frame;
class ole_client_site final : public IOleClientSite
{
public:
ole_client_site(html_frame* frame);
virtual ~ole_client_site() = default;
private:
html_frame* frame_;
public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObject) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE SaveObject() override;
HRESULT STDMETHODCALLTYPE GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk) override;
HRESULT STDMETHODCALLTYPE GetContainer(LPOLECONTAINER FAR* ppContainer) override;
HRESULT STDMETHODCALLTYPE ShowObject() override;
HRESULT STDMETHODCALLTYPE OnShowWindow(BOOL fShow) override;
HRESULT STDMETHODCALLTYPE RequestNewObjectLayout() override;
};
@@ -1,82 +0,0 @@
#include <std_include.hpp>
#include "html_frame.hpp"
ole_in_place_frame::ole_in_place_frame(html_frame* frame): frame_(frame)
{
}
HRESULT ole_in_place_frame::QueryInterface(REFIID /*riid*/, LPVOID* /*ppvObj*/)
{
return E_NOTIMPL;
}
ULONG ole_in_place_frame::AddRef()
{
return 1;
}
ULONG ole_in_place_frame::Release()
{
return 1;
}
HRESULT ole_in_place_frame::GetWindow(HWND* lphwnd)
{
*lphwnd = this->frame_->get_window();
return S_OK;
}
HRESULT ole_in_place_frame::ContextSensitiveHelp(BOOL /*fEnterMode*/)
{
return E_NOTIMPL;
}
HRESULT ole_in_place_frame::GetBorder(LPRECT /*lprectBorder*/)
{
return E_NOTIMPL;
}
HRESULT ole_in_place_frame::RequestBorderSpace(LPCBORDERWIDTHS /*pborderwidths*/)
{
return E_NOTIMPL;
}
HRESULT ole_in_place_frame::SetBorderSpace(LPCBORDERWIDTHS /*pborderwidths*/)
{
return E_NOTIMPL;
}
HRESULT ole_in_place_frame::SetActiveObject(IOleInPlaceActiveObject* /*pActiveObject*/, LPCOLESTR /*pszObjName*/)
{
return S_OK;
}
HRESULT ole_in_place_frame::InsertMenus(HMENU /*hmenuShared*/, LPOLEMENUGROUPWIDTHS /*lpMenuWidths*/)
{
return E_NOTIMPL;
}
HRESULT ole_in_place_frame::SetMenu(HMENU /*hmenuShared*/, HOLEMENU /*holemenu*/, HWND /*hwndActiveObject*/)
{
return S_OK;
}
HRESULT ole_in_place_frame::RemoveMenus(HMENU /*hmenuShared*/)
{
return E_NOTIMPL;
}
HRESULT ole_in_place_frame::SetStatusText(LPCOLESTR /*pszStatusText*/)
{
return S_OK;
}
HRESULT ole_in_place_frame::EnableModeless(BOOL /*fEnable*/)
{
return S_OK;
}
HRESULT ole_in_place_frame::TranslateAcceleratorA(LPMSG /*lpmsg*/, WORD /*wID*/)
{
return E_NOTIMPL;
}
@@ -1,30 +0,0 @@
#pragma once
class html_frame;
class ole_in_place_frame final : public IOleInPlaceFrame
{
public:
ole_in_place_frame(html_frame* frame);
virtual ~ole_in_place_frame() = default;
private:
html_frame* frame_;
public: // IOleInPlaceFrame interface
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE GetWindow(HWND FAR* lphwnd) override;
HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode) override;
HRESULT STDMETHODCALLTYPE GetBorder(LPRECT lprectBorder) override;
HRESULT STDMETHODCALLTYPE RequestBorderSpace(LPCBORDERWIDTHS pborderwidths) override;
HRESULT STDMETHODCALLTYPE SetBorderSpace(LPCBORDERWIDTHS pborderwidths) override;
HRESULT STDMETHODCALLTYPE SetActiveObject(IOleInPlaceActiveObject* pActiveObject, LPCOLESTR pszObjName) override;
HRESULT STDMETHODCALLTYPE InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths) override;
HRESULT STDMETHODCALLTYPE SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject) override;
HRESULT STDMETHODCALLTYPE RemoveMenus(HMENU hmenuShared) override;
HRESULT STDMETHODCALLTYPE SetStatusText(LPCOLESTR pszStatusText) override;
HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable) override;
HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpmsg, WORD wID) override;
};
@@ -1,105 +0,0 @@
#include <std_include.hpp>
#include "html_frame.hpp"
ole_in_place_site::ole_in_place_site(html_frame* frame) : frame_(frame)
{
}
HRESULT ole_in_place_site::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
auto client_site = this->frame_->get_client_site();
if (client_site)
{
return client_site->QueryInterface(riid, ppvObj);
}
return E_NOINTERFACE;
}
ULONG ole_in_place_site::AddRef()
{
return 1;
}
ULONG ole_in_place_site::Release()
{
return 1;
}
HRESULT ole_in_place_site::GetWindow(HWND* lphwnd)
{
*lphwnd = this->frame_->get_window();
return S_OK;
}
HRESULT ole_in_place_site::ContextSensitiveHelp(BOOL /*fEnterMode*/)
{
return E_NOTIMPL;
}
HRESULT ole_in_place_site::CanInPlaceActivate()
{
return S_OK;
}
HRESULT ole_in_place_site::OnInPlaceActivate()
{
return S_OK;
}
HRESULT ole_in_place_site::OnUIActivate()
{
return S_OK;
}
HRESULT ole_in_place_site::GetWindowContext(LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc,
LPRECT /*lprcPosRect*/, LPRECT /*lprcClipRect*/,
LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
*lplpFrame = this->frame_->get_in_place_frame();
*lplpDoc = nullptr;
lpFrameInfo->fMDIApp = FALSE;
lpFrameInfo->hwndFrame = this->frame_->get_window();
lpFrameInfo->haccel = nullptr;
lpFrameInfo->cAccelEntries = 0;
return S_OK;
}
HRESULT ole_in_place_site::Scroll(SIZE /*scrollExtent*/)
{
return E_NOTIMPL;
}
HRESULT ole_in_place_site::OnUIDeactivate(BOOL /*fUndoable*/)
{
return S_OK;
}
HRESULT ole_in_place_site::OnInPlaceDeactivate()
{
return S_OK;
}
HRESULT ole_in_place_site::DiscardUndoState()
{
return E_NOTIMPL;
}
HRESULT ole_in_place_site::DeactivateAndUndo()
{
return E_NOTIMPL;
}
HRESULT ole_in_place_site::OnPosRectChange(LPCRECT lprcPosRect)
{
IOleInPlaceObject* in_place = nullptr;
if (!this->frame_->get_browser_object()->QueryInterface(IID_IOleInPlaceObject, reinterpret_cast<void**>(&in_place)))
{
in_place->SetObjectRects(lprcPosRect, lprcPosRect);
in_place->Release();
}
return S_OK;
}
@@ -1,32 +0,0 @@
#pragma once
class html_frame;
class ole_in_place_site final : public IOleInPlaceSite
{
public:
ole_in_place_site(html_frame* frame);
virtual ~ole_in_place_site() = default;
private:
html_frame* frame_;
public: // IOleInPlaceSite interface
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE GetWindow(HWND FAR* lphwnd) override;
HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode) override;
HRESULT STDMETHODCALLTYPE CanInPlaceActivate() override;
HRESULT STDMETHODCALLTYPE OnInPlaceActivate() override;
HRESULT STDMETHODCALLTYPE OnUIActivate() override;
HRESULT STDMETHODCALLTYPE GetWindowContext(LPOLEINPLACEFRAME FAR* lplpFrame, LPOLEINPLACEUIWINDOW FAR* lplpDoc,
LPRECT lprcPosRect, LPRECT lprcClipRect,
LPOLEINPLACEFRAMEINFO lpFrameInfo) override;
HRESULT STDMETHODCALLTYPE Scroll(SIZE scrollExtent) override;
HRESULT STDMETHODCALLTYPE OnUIDeactivate(BOOL fUndoable) override;
HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate() override;
HRESULT STDMETHODCALLTYPE DiscardUndoState() override;
HRESULT STDMETHODCALLTYPE DeactivateAndUndo() override;
HRESULT STDMETHODCALLTYPE OnPosRectChange(LPCRECT lprcPosRect) override;
};
-69
View File
@@ -1,69 +0,0 @@
#include <std_include.hpp>
#include "launcher.hpp"
#include <utils/nt.hpp>
launcher::launcher()
{
this->create_main_menu();
}
void launcher::create_main_menu()
{
this->main_window_.register_callback("openUrl", [](html_frame::callback_params* params)
{
if (params->arguments.empty()) return;
const auto param = params->arguments[0];
if (!param.is_string()) return;
const auto url = param.get_string();
ShellExecuteA(nullptr, "open", url.data(), nullptr, nullptr, SW_SHOWNORMAL);
});
this->main_window_.register_callback("selectMode", [this](html_frame::callback_params* params)
{
if (params->arguments.empty()) return;
const auto param = params->arguments[0];
if (!param.is_number()) return;
const auto number = static_cast<mode>(param.get_number());
if (number == mode::singleplayer || number == mode::multiplayer)
{
this->select_mode(number);
}
});
this->main_window_.set_callback(
[](window* window, const UINT message, const WPARAM w_param, const LPARAM l_param) -> LRESULT
{
if (message == WM_CLOSE)
{
window::close_all();
}
return DefWindowProcA(*window, message, w_param, l_param);
});
this->main_window_.create("iw6-mod", 750, 420);
this->main_window_.load_html(load_content(MENU_MAIN));
this->main_window_.show();
}
launcher::mode launcher::run() const
{
window::run();
return this->mode_;
}
void launcher::select_mode(const mode mode)
{
this->mode_ = mode;
this->main_window_.close();
}
std::string launcher::load_content(const int res)
{
return utils::nt::load_resource(res);
}
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#include "html/html_window.hpp"
class launcher final
{
public:
enum class mode
{
none,
singleplayer,
multiplayer,
server,
};
launcher();
mode run() const;
private:
mode mode_ = mode::none;
html_window main_window_;
void select_mode(mode mode);
void create_main_menu();
static std::string load_content(int res);
};
-208
View File
@@ -1,208 +0,0 @@
#include <std_include.hpp>
#include "window.hpp"
#include <utils/nt.hpp>
std::mutex window::mutex_;
std::vector<window*> window::windows_;
window::window()
{
ZeroMemory(&this->wc_, sizeof(this->wc_));
this->classname_ = "window-base-" + std::to_string(time(nullptr));
this->wc_.cbSize = sizeof(this->wc_);
this->wc_.style = CS_HREDRAW | CS_VREDRAW;
this->wc_.lpfnWndProc = static_processor;
this->wc_.hInstance = GetModuleHandle(nullptr);
this->wc_.hCursor = LoadCursor(nullptr, IDC_ARROW);
this->wc_.hIcon = LoadIcon(this->wc_.hInstance, MAKEINTRESOURCE(102));
this->wc_.hIconSm = this->wc_.hIcon;
this->wc_.hbrBackground = HBRUSH(COLOR_WINDOW);
this->wc_.lpszClassName = this->classname_.data();
RegisterClassEx(&this->wc_);
}
void window::create(const std::string& title, const int width, const int height, const long flags)
{
{
std::lock_guard _(mutex_);
windows_.push_back(this);
}
const auto x = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;
const auto y = (GetSystemMetrics(SM_CYSCREEN) - height) / 2;
this->handle_ = CreateWindowExA(NULL, this->wc_.lpszClassName, title.data(), flags, x, y, width, height, nullptr,
nullptr, this->wc_.hInstance, this);
SendMessageA(this->handle_, WM_DPICHANGED, 0, 0);
}
window::~window()
{
this->close();
UnregisterClass(this->wc_.lpszClassName, this->wc_.hInstance);
}
void window::close()
{
if (!this->handle_) return;
SendMessageA(this->handle_, WM_KILL_WINDOW, NULL, NULL);
this->handle_ = nullptr;
}
void window::run()
{
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void window::close_all()
{
std::unique_lock lock(mutex_);
auto window_list = windows_;
lock.unlock();
const auto current_thread_id = GetCurrentThreadId();
for (auto& window : window_list)
{
const auto thread_id = GetWindowThreadProcessId(*window, nullptr);
if (thread_id == current_thread_id)
{
window->close();
}
}
}
void window::remove_window(const window* window)
{
std::lock_guard _(mutex_);
for (auto i = windows_.begin(); i != windows_.end(); ++i)
{
if (*i == window)
{
windows_.erase(i);
break;
}
}
}
int window::get_window_count()
{
std::lock_guard _(mutex_);
auto count = 0;
const auto current_thread_id = GetCurrentThreadId();
for (const auto& window : windows_)
{
const auto thread_id = GetWindowThreadProcessId(*window, nullptr);
if (thread_id == current_thread_id)
{
++count;
}
}
return count;
}
void window::show() const
{
ShowWindow(this->handle_, SW_SHOW);
UpdateWindow(this->handle_);
}
void window::hide() const
{
ShowWindow(this->handle_, SW_HIDE);
UpdateWindow(this->handle_);
}
void window::set_callback(const std::function<LRESULT(window*, UINT, WPARAM, LPARAM)>& callback)
{
this->callback_ = callback;
}
LRESULT window::processor(const UINT message, const WPARAM w_param, const LPARAM l_param)
{
if (message == WM_DPICHANGED)
{
const utils::nt::library user32{"user32.dll"};
const auto get_dpi = user32 ? user32.get_proc<UINT(WINAPI *)(HWND)>("GetDpiForWindow") : nullptr;
if (get_dpi)
{
const auto dpi = get_dpi(*this);
if (dpi != this->last_dpi_)
{
RECT rect;
GetWindowRect(*this, &rect);
const auto scale = dpi * 1.0 / this->last_dpi_;
this->last_dpi_ = dpi;
const auto width = rect.right - rect.left;
const auto height = rect.bottom - rect.top;
MoveWindow(*this, rect.left, rect.top, int(width * scale), int(height * scale), TRUE);
}
}
}
if (message == WM_DESTROY)
{
remove_window(this);
if (get_window_count() == 0)
{
PostQuitMessage(0);
}
return TRUE;
}
if (message == WM_KILL_WINDOW)
{
DestroyWindow(*this);
return 0;
}
if (this->callback_)
{
return this->callback_(this, message, w_param, l_param);
}
return DefWindowProc(*this, message, w_param, l_param);
}
LRESULT CALLBACK window::static_processor(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param)
{
if (message == WM_CREATE)
{
auto data = reinterpret_cast<LPCREATESTRUCT>(l_param);
SetWindowLongPtrA(hwnd, GWLP_USERDATA, LONG_PTR(data->lpCreateParams));
reinterpret_cast<window*>(data->lpCreateParams)->handle_ = hwnd;
}
const auto self = reinterpret_cast<window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (self) return self->processor(message, w_param, l_param);
return DefWindowProc(hwnd, message, w_param, l_param);
}
window::operator HWND() const
{
return this->handle_;
}
-44
View File
@@ -1,44 +0,0 @@
#pragma once
#define WM_KILL_WINDOW (WM_USER+0)
class window
{
public:
window();
virtual ~window();
void create(const std::string& title, int width, int height,
long flags = (WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX)));
void close();
void show() const;
void hide() const;
void set_callback(const std::function<LRESULT(window*, UINT, WPARAM, LPARAM)>& callback);
operator HWND() const;
static void run();
static void close_all();
protected:
virtual LRESULT processor(UINT message, WPARAM w_param, LPARAM l_param);
private:
uint32_t last_dpi_ = 96;
WNDCLASSEX wc_{};
HWND handle_ = nullptr;
std::string classname_;
std::function<LRESULT(window*, UINT, WPARAM, LPARAM)> callback_;
static LRESULT CALLBACK static_processor(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param);
static std::mutex mutex_;
static std::vector<window*> windows_;
static void remove_window(const window* window);
static int get_window_count();
};
-2
View File
@@ -1,8 +1,6 @@
#pragma once
#include <utils/nt.hpp>
#include "launcher/launcher.hpp"
class loader final
{
public:
+2 -15
View File
@@ -1,5 +1,4 @@
#include <std_include.hpp>
#include "launcher/launcher.hpp"
#include "loader/loader.hpp"
#include "loader/component_loader.hpp"
#include "game/game.hpp"
@@ -64,9 +63,7 @@ LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info)
&exception_information, nullptr, nullptr))
{
char buf[4096]{};
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n",
exception_info->ExceptionRecord->ExceptionCode,
exception_info->ExceptionRecord->ExceptionAddress);
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n", exception_info->ExceptionRecord->ExceptionCode, exception_info->ExceptionRecord->ExceptionAddress);
game::show_error(buf);
}
@@ -111,11 +108,6 @@ launcher::mode detect_mode_from_arguments()
return launcher::mode::multiplayer;
}
if (utils::flags::has_flag("singleplayer"))
{
return launcher::mode::singleplayer;
}
return launcher::mode::none;
}
@@ -156,9 +148,6 @@ FARPROC load_binary(const launcher::mode mode)
case launcher::mode::multiplayer:
binary = "iw6mp64_ship.exe";
break;
case launcher::mode::singleplayer:
binary = "iw6sp64_ship.exe";
break;
case launcher::mode::none:
default:
throw std::runtime_error("Invalid game mode!");
@@ -279,9 +268,7 @@ int main()
auto mode = detect_mode_from_arguments();
if (mode == launcher::mode::none)
{
const launcher launcher;
mode = launcher.run();
if (mode == launcher::mode::none) return 0;
mode = launcher::mode::multiplayer;
}
game::environment::set_mode(mode);
+1 -1
View File
@@ -74,7 +74,7 @@ BEGIN
VALUE "FileDescription", "iw6-mod"
VALUE "FileVersion", VERSION_FILE
VALUE "InternalName", "iw6-mod"
VALUE "LegalCopyright", "Copyright (C) 2023 AlterWare. All rights reserved."
VALUE "LegalCopyright", "Copyright (C) 2026 AlterWare. All rights reserved."
VALUE "Licence", "GPLv3"
VALUE "Info", "https://alterware.dev"
VALUE "OriginalFilename", "iw6-mod.exe"
+1 -1
View File
@@ -1 +1 @@
Welcome to iw6-mod. This is not a copy & pasted project.
Welcome to iw6-mod. Did you know aiming down sights increases accuracy? Online Interactions Not Rated by the ESRB.

Some files were not shown because too many files have changed in this diff Show More