forked from alterware/iw6-mod
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b54b3f51af
|
||
|
|
02ba3b3663
|
||
|
|
043f8f07c0 | ||
|
|
582e8e05be
|
||
|
|
6ff635498d | ||
|
|
67c0b5ddfd | ||
|
|
1b78912b33 | ||
|
|
deafda9061
|
||
|
|
61f0374d10
|
||
|
|
9052daab5c
|
||
|
|
a299874dd2
|
||
|
|
27d5985574
|
||
|
|
b60692f0d7
|
||
|
|
6f2c293970
|
||
|
|
03b44731a8 | ||
|
|
ea8d2e483f
|
||
|
|
833e25700c
|
||
|
|
bc0372a249
|
||
|
|
3d77a21f3d
|
||
|
|
199206ee5a
|
||
|
|
45907301ee
|
@@ -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
Vendored
-1
Submodule deps/WinToast deleted from 821c4818ad
Vendored
+1
-1
Submodule deps/asmjit updated: bfa0bf690c...e8c8e2e48a
Vendored
+1
-1
Submodule deps/gsc-tool updated: b8e30e6334...c9bd8e5c6a
Vendored
+1
-1
Submodule deps/libtomcrypt updated: 7e863d2142...c421e570c6
Vendored
+1
-1
Submodule deps/libtommath updated: 8314bde5e5...839ae9ea66
Vendored
+1
-1
Submodule deps/minhook updated: f5485b8454...1e9ad1eb42
Vendored
-32
@@ -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)
|
||||
Vendored
+1
-1
Submodule deps/rapidjson updated: 6089180ecb...24b5e7a8b2
Vendored
+1
-1
Submodule deps/zlib updated: 9f0f2d4f9f...5a82f71ed1
+2
-2
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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([]()
|
||||
{
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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([]()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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", [&]()
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace mods
|
||||
{
|
||||
bool is_using_mods();
|
||||
bool db_mod_file_exists();
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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), ®ister_class_ex_a_stub);
|
||||
utils::hook::set<std::uint8_t>(SELECT_VALUE(0x14043BF09, 0x140500829), 0x90);
|
||||
utils::hook::call(0x140500824, ®ister_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");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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, ¶ms);
|
||||
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;
|
||||
};
|
||||
@@ -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 = ¶ms->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);
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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_;
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
#include <utils/nt.hpp>
|
||||
|
||||
#include "launcher/launcher.hpp"
|
||||
|
||||
class loader final
|
||||
{
|
||||
public:
|
||||
|
||||
+2
-15
@@ -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);
|
||||
|
||||
@@ -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 @@
|
||||
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
Reference in New Issue
Block a user