init
This commit is contained in:
49
src/client/component/asset_restrict.cpp
Normal file
49
src/client/component/asset_restrict.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/memory.hpp>
|
||||
|
||||
namespace asset_restrict {
|
||||
namespace {
|
||||
game::XAssetEntry entry_pool[789312];
|
||||
|
||||
game::XAssetHeader reallocate_asset_pool(game::XAssetType type,
|
||||
const int size) {
|
||||
const auto entry_size =
|
||||
reinterpret_cast<int (*)()>(game::DB_GetXAssetSizeHandlers[type])();
|
||||
const game::XAssetHeader pool_entry = {
|
||||
.data = utils::memory::allocate(entry_size * size)};
|
||||
game::DB_XAssetPool[type] = pool_entry.data;
|
||||
game::g_poolSize[type] = size;
|
||||
|
||||
return pool_entry;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
patch_entry_pool_sp();
|
||||
reallocate_asset_pool(game::ASSET_TYPE_GAMEWORLD_MP, 1);
|
||||
}
|
||||
|
||||
static void patch_entry_pool_sp() {
|
||||
// Apply new size
|
||||
utils::hook::set<std::uint32_t>(0x581740, sizeof(entry_pool) /
|
||||
sizeof(game::XAssetEntry));
|
||||
utils::hook::set<game::XAssetEntry*>(0x581721, entry_pool + 1);
|
||||
utils::hook::set<game::XAssetEntry*>(0x581732, entry_pool + 1);
|
||||
|
||||
utils::hook::signature signature(0x411000, 0x200000);
|
||||
|
||||
signature.add({"\x60\xB3\xB2\x00", "xxxx", [](char* address) {
|
||||
utils::hook::set<game::XAssetEntry*>(address, entry_pool);
|
||||
}});
|
||||
|
||||
signature.process();
|
||||
}
|
||||
};
|
||||
} // namespace asset_restrict
|
||||
|
||||
REGISTER_COMPONENT(asset_restrict::component)
|
64
src/client/component/assets/assets.cpp
Normal file
64
src/client/component/assets/assets.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "localize_entry.hpp"
|
||||
#include "map_ents.hpp"
|
||||
#include "raw_file.hpp"
|
||||
#include "string_table.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace assets {
|
||||
namespace {
|
||||
void load_asset(game::XAssetType type, game::XAssetHeader* header) {
|
||||
if (header) {
|
||||
switch (type) {
|
||||
case game::ASSET_TYPE_LOCALIZE_ENTRY:
|
||||
process_localize_entry(header);
|
||||
break;
|
||||
case game::ASSET_TYPE_MAP_ENTS:
|
||||
process_map_ents(header);
|
||||
break;
|
||||
case game::ASSET_TYPE_RAWFILE:
|
||||
process_raw_file(header);
|
||||
break;
|
||||
case game::ASSET_TYPE_STRINGTABLE:
|
||||
process_string_table(header);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __declspec(naked) db_add_x_asset_stub() {
|
||||
__asm {
|
||||
pushad;
|
||||
|
||||
push [esp + 0x20 + 0x8];
|
||||
push [esp + 0x20 + 0x8];
|
||||
call load_asset;
|
||||
add esp, 0x8;
|
||||
|
||||
popad;
|
||||
|
||||
sub esp, 0x14;
|
||||
mov eax, dword ptr [esp + 0x1C];
|
||||
|
||||
push 0x581EE7;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class asset final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
// We may modify assets to some extents here or just dump them
|
||||
utils::hook(0x581EE0, db_add_x_asset_stub, HOOK_JUMP).install()->quick();
|
||||
utils::hook::nop(0x581EE5, 2);
|
||||
}
|
||||
};
|
||||
} // namespace assets
|
||||
|
||||
REGISTER_COMPONENT(assets::asset)
|
26
src/client/component/assets/localize_entry.cpp
Normal file
26
src/client/component/assets/localize_entry.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "localize_entry.hpp"
|
||||
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/flags.hpp>
|
||||
|
||||
namespace assets {
|
||||
namespace {
|
||||
bool is_enabled() { IS_FLAG_ENABLED(dump_localize_entry); }
|
||||
} // namespace
|
||||
|
||||
void process_localize_entry(game::XAssetHeader* header) {
|
||||
if (!is_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* localize = header->localize;
|
||||
const auto filename =
|
||||
utils::string::va("raw/localizedstrings/{0}", localize->name);
|
||||
|
||||
// Simple format, should be fine for now
|
||||
utils::io::write_file(filename, localize->value);
|
||||
}
|
||||
} // namespace assets
|
5
src/client/component/assets/localize_entry.hpp
Normal file
5
src/client/component/assets/localize_entry.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace assets {
|
||||
void process_localize_entry(game::XAssetHeader* header);
|
||||
}
|
30
src/client/component/assets/map_ents.cpp
Normal file
30
src/client/component/assets/map_ents.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "component/filesystem.hpp"
|
||||
|
||||
#include "map_ents.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace assets {
|
||||
namespace {
|
||||
std::string map_entities;
|
||||
|
||||
void load_map_entities(game::MapEnts* entry) {
|
||||
const auto file_name = utils::string::va("{0}.ents", entry->name);
|
||||
const filesystem::file ent_file(file_name, game::FS_THREAD_DATABASE);
|
||||
|
||||
// Load ent file from raw if it exists
|
||||
if (ent_file.exists()) {
|
||||
map_entities = ent_file.get_buffer();
|
||||
entry->entityString = map_entities.data();
|
||||
entry->numEntityChars = static_cast<int>(map_entities.size()) + 1;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void process_map_ents(game::XAssetHeader* header) {
|
||||
auto* map_ents = header->mapEnts;
|
||||
load_map_entities(map_ents);
|
||||
}
|
||||
} // namespace assets
|
5
src/client/component/assets/map_ents.hpp
Normal file
5
src/client/component/assets/map_ents.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace assets {
|
||||
void process_map_ents(game::XAssetHeader* header);
|
||||
}
|
181
src/client/component/assets/raw_file.cpp
Normal file
181
src/client/component/assets/raw_file.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "raw_file.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/flags.hpp>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
namespace assets {
|
||||
namespace {
|
||||
utils::hook::detour db_read_raw_file_hook;
|
||||
utils::hook::detour com_load_info_string_hook;
|
||||
|
||||
char* db_read_raw_file_stub(const char* filename, char* buf, int size) {
|
||||
auto file_handle = 0;
|
||||
const auto file_size = game::FS_FOpenFileRead(filename, &file_handle);
|
||||
|
||||
if (file_handle) {
|
||||
if ((file_size + 1) <= size) {
|
||||
game::FS_Read(buf, file_size, file_handle);
|
||||
buf[file_size] = '\0';
|
||||
game::FS_FCloseFile(file_handle);
|
||||
return buf;
|
||||
}
|
||||
|
||||
game::FS_FCloseFile(file_handle);
|
||||
game::Com_PrintError(
|
||||
game::CON_CHANNEL_ERROR,
|
||||
"Ignoring raw file '%s' as it exceeds buffer size %i > %i\n", filename,
|
||||
file_size, size);
|
||||
}
|
||||
|
||||
auto* rawfile =
|
||||
game::DB_FindXAssetHeader(game::ASSET_TYPE_RAWFILE, filename).rawfile;
|
||||
if (game::DB_IsXAssetDefault(game::ASSET_TYPE_RAWFILE, filename)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
game::DB_GetRawBuffer(rawfile, buf, size);
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char* com_load_info_string_fast_file(const char* file_name,
|
||||
const char* file_desc,
|
||||
const char* ident,
|
||||
char* load_buffer) {
|
||||
const static DWORD Com_LoadInfoString_FastFile_t = 0x602FA0;
|
||||
const char* result{};
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
|
||||
mov ebx, load_buffer;
|
||||
mov edi, file_name;
|
||||
push ident;
|
||||
push file_desc;
|
||||
call Com_LoadInfoString_FastFile_t;
|
||||
add esp, 0x8;
|
||||
mov result, eax;
|
||||
|
||||
popad;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* com_load_info_string_load_obj(const char* file_name,
|
||||
const char* file_desc,
|
||||
const char* ident,
|
||||
char* load_buffer) {
|
||||
int file_handle;
|
||||
|
||||
const auto file_len =
|
||||
game::FS_FOpenFileByMode(file_name, &file_handle, game::FS_READ);
|
||||
if (file_len < 0) {
|
||||
game::Com_DPrintf(game::CON_CHANNEL_SYSTEM,
|
||||
"Could not load %s [%s] as rawfile\n", file_desc,
|
||||
file_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto ident_len = static_cast<int>(std::strlen(ident));
|
||||
game::FS_Read(load_buffer, ident_len, file_handle);
|
||||
load_buffer[ident_len] = '\0';
|
||||
|
||||
if (std::strncmp(load_buffer, ident, ident_len) != 0) {
|
||||
game::Com_Error(game::ERR_DROP,
|
||||
"\x15"
|
||||
"File [%s] is not a %s\n",
|
||||
file_name, file_desc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((file_len - ident_len) >= 0x4000) {
|
||||
game::Com_Error(game::ERR_DROP,
|
||||
"\x15"
|
||||
"File [%s] is too long of a %s to parse\n",
|
||||
file_name, file_desc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
game::FS_Read(load_buffer, file_len - ident_len, file_handle);
|
||||
load_buffer[file_len - ident_len] = '\0';
|
||||
game::FS_FCloseFile(file_handle);
|
||||
|
||||
return load_buffer;
|
||||
}
|
||||
|
||||
const char* com_load_info_string_stub(const char* file_name,
|
||||
const char* file_desc, const char* ident,
|
||||
char* load_buffer) {
|
||||
const auto* buffer =
|
||||
com_load_info_string_load_obj(file_name, file_desc, ident, load_buffer);
|
||||
if (!buffer) {
|
||||
buffer = com_load_info_string_fast_file(file_name, file_desc, ident,
|
||||
load_buffer);
|
||||
}
|
||||
|
||||
if (!game::Info_Validate(buffer)) {
|
||||
game::Com_Error(game::ERR_DROP,
|
||||
"\x15"
|
||||
"File [%s] is not a valid %s\n",
|
||||
file_name, file_desc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool is_enabled() { IS_FLAG_ENABLED(dump_raw_file); }
|
||||
} // namespace
|
||||
|
||||
void process_raw_file(game::XAssetHeader* header) {
|
||||
if (!is_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* raw_file = header->rawfile;
|
||||
const auto filename = utils::string::va("raw/{0}", raw_file->name);
|
||||
|
||||
if (raw_file->compressedLen > 0) {
|
||||
std::vector<std::uint8_t> uncompressed;
|
||||
uncompressed.resize(raw_file->len);
|
||||
|
||||
if (uncompress(uncompressed.data(), (uLongf*)&raw_file->len,
|
||||
reinterpret_cast<const Bytef*>(raw_file->buffer),
|
||||
raw_file->compressedLen) == Z_OK) {
|
||||
std::string data;
|
||||
data.assign(uncompressed.begin(), uncompressed.end());
|
||||
|
||||
utils::io::write_file(filename, data);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If uncompressed just dump it
|
||||
utils::io::write_file(filename, raw_file->buffer);
|
||||
}
|
||||
|
||||
class raw_file final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
// Remove fs_game check for moddable raw files
|
||||
// allows non-fs_game to modify raw files
|
||||
utils::hook::nop(0x612932, 2);
|
||||
|
||||
db_read_raw_file_hook.create(0x46DA60, &db_read_raw_file_stub);
|
||||
|
||||
com_load_info_string_hook.create(0x42DB20, &com_load_info_string_stub);
|
||||
}
|
||||
|
||||
void pre_destroy() override { db_read_raw_file_hook.clear(); }
|
||||
};
|
||||
} // namespace assets
|
||||
|
||||
REGISTER_COMPONENT(assets::raw_file)
|
5
src/client/component/assets/raw_file.hpp
Normal file
5
src/client/component/assets/raw_file.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace assets {
|
||||
void process_raw_file(game::XAssetHeader* header);
|
||||
}
|
46
src/client/component/assets/string_table.cpp
Normal file
46
src/client/component/assets/string_table.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "string_table.hpp"
|
||||
|
||||
#include <utils/flags.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace assets {
|
||||
namespace {
|
||||
void dump_string_table(game::XAssetHeader* header) {
|
||||
const auto* string_table = header->stringTable;
|
||||
const auto filename = utils::string::va("raw/{0}", string_table->name);
|
||||
|
||||
std::string csv;
|
||||
|
||||
const auto rows = string_table->rowCount;
|
||||
const auto columns = string_table->columnCount;
|
||||
|
||||
for (auto x = 0; x < rows; ++x) {
|
||||
for (auto y = 0; y < columns; ++y) {
|
||||
const char* cell = string_table->values[(x * columns) + y].string;
|
||||
csv += cell;
|
||||
|
||||
if (y + 1 < columns) {
|
||||
csv += ",";
|
||||
}
|
||||
}
|
||||
|
||||
if (x + 1 < rows) {
|
||||
csv += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
utils::io::write_file(filename, csv);
|
||||
}
|
||||
|
||||
bool is_enabled() { IS_FLAG_ENABLED(dump_string_table); }
|
||||
} // namespace
|
||||
|
||||
void process_string_table(game::XAssetHeader* header) {
|
||||
if (is_enabled()) {
|
||||
dump_string_table(header);
|
||||
}
|
||||
}
|
||||
} // namespace assets
|
5
src/client/component/assets/string_table.hpp
Normal file
5
src/client/component/assets/string_table.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace assets {
|
||||
void process_string_table(game::XAssetHeader* header);
|
||||
}
|
84
src/client/component/auth.cpp
Normal file
84
src/client/component/auth.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/cryptography.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/smbios.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "auth.hpp"
|
||||
|
||||
namespace auth {
|
||||
namespace {
|
||||
std::string get_hw_profile_guid() {
|
||||
HW_PROFILE_INFO info;
|
||||
if (!GetCurrentHwProfileA(&info)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::string{info.szHwProfileGuid, sizeof(info.szHwProfileGuid)};
|
||||
}
|
||||
|
||||
std::string get_protected_data() {
|
||||
std::string input = "Alter-Ware-IW4-SP-Auth";
|
||||
|
||||
DATA_BLOB data_in{}, data_out{};
|
||||
data_in.pbData = reinterpret_cast<std::uint8_t*>(input.data());
|
||||
data_in.cbData = static_cast<DWORD>(input.size());
|
||||
if (CryptProtectData(&data_in, nullptr, nullptr, nullptr, nullptr,
|
||||
CRYPTPROTECT_LOCAL_MACHINE, &data_out) != TRUE) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto size = std::min<std::uint32_t>(data_out.cbData, 52);
|
||||
std::string result(reinterpret_cast<char*>(data_out.pbData), size);
|
||||
LocalFree(data_out.pbData);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string get_hdd_serial() {
|
||||
DWORD serial{};
|
||||
if (!GetVolumeInformationA("C:\\", nullptr, 0, &serial, nullptr, nullptr,
|
||||
nullptr, 0)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return utils::string::va("{0:08X}", serial);
|
||||
}
|
||||
|
||||
std::string get_key_entropy() {
|
||||
std::string entropy{};
|
||||
entropy.append(utils::smbios::get_uuid());
|
||||
entropy.append(get_hw_profile_guid());
|
||||
entropy.append(get_protected_data());
|
||||
entropy.append(get_hdd_serial());
|
||||
|
||||
if (entropy.empty()) {
|
||||
entropy.resize(32);
|
||||
utils::cryptography::random::get_data(entropy.data(), entropy.size());
|
||||
}
|
||||
|
||||
return entropy;
|
||||
}
|
||||
|
||||
utils::cryptography::ecc::key& get_key() {
|
||||
static auto key =
|
||||
utils::cryptography::ecc::generate_key(512, get_key_entropy());
|
||||
return key;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::uint64_t get_guid() { return get_key().get_hash(); }
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
// Patch Steam_GetClientIDAsXUID
|
||||
utils::hook::set<std::uint32_t>(0x4911B0, 0xC301B0);
|
||||
}
|
||||
};
|
||||
} // namespace auth
|
||||
|
||||
REGISTER_COMPONENT(auth::component)
|
5
src/client/component/auth.hpp
Normal file
5
src/client/component/auth.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace auth {
|
||||
std::uint64_t get_guid();
|
||||
}
|
254
src/client/component/botlib/l_precomp.cpp
Normal file
254
src/client/component/botlib/l_precomp.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "l_precomp.hpp"
|
||||
#include "l_script.hpp"
|
||||
|
||||
namespace pc {
|
||||
constexpr auto DEFINEHASHSIZE = 1024;
|
||||
|
||||
constexpr auto MAX_SOURCEFILES = 64;
|
||||
|
||||
game::define_s* globaldefines;
|
||||
|
||||
void free_token(game::token_s* token) {
|
||||
game::FreeMemory(token);
|
||||
--*game::numtokens;
|
||||
}
|
||||
|
||||
game::define_s* define_from_string(const char* string) {
|
||||
game::source_s src;
|
||||
|
||||
auto* script = load_script_memory(
|
||||
string, static_cast<int>(std::strlen(string)), "*extern");
|
||||
// create a new source
|
||||
std::memset(&src, 0, sizeof(game::source_s));
|
||||
strncpy_s(src.filename, "*extern", _TRUNCATE);
|
||||
|
||||
src.scriptstack = script;
|
||||
src.definehash = static_cast<game::define_s**>(
|
||||
game::GetClearedMemory(DEFINEHASHSIZE * sizeof(game::define_s*)));
|
||||
|
||||
// create a define from the source
|
||||
auto res = game::PC_Directive_define(&src);
|
||||
|
||||
// free any tokens if left
|
||||
for (auto* t = src.tokens; t; t = src.tokens) {
|
||||
src.tokens = src.tokens->next;
|
||||
free_token(t);
|
||||
}
|
||||
|
||||
game::define_s* def = nullptr;
|
||||
for (auto i = 0; i < DEFINEHASHSIZE; ++i) {
|
||||
if (src.definehash[i]) {
|
||||
def = src.definehash[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
game::FreeMemory(src.definehash);
|
||||
free_script(script);
|
||||
|
||||
// if the define was created successfully
|
||||
if (res > 0) {
|
||||
return def;
|
||||
}
|
||||
|
||||
// free the define is created
|
||||
if (src.defines) {
|
||||
game::PC_FreeDefine(def);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int name_hash(char* name) {
|
||||
auto hash = 0;
|
||||
for (auto i = 0; name[i] != '\0'; ++i) {
|
||||
hash += name[i] * (119 + i);
|
||||
}
|
||||
|
||||
hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE - 1);
|
||||
return hash;
|
||||
}
|
||||
|
||||
void add_define_to_hash(game::define_s* define, game::define_s** definehash) {
|
||||
auto hash = name_hash(define->name);
|
||||
define->hashnext = definehash[hash];
|
||||
definehash[hash] = define;
|
||||
}
|
||||
|
||||
int add_define(game::source_s* source, const char* string) {
|
||||
|
||||
auto* define = define_from_string(string);
|
||||
if (!define) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
add_define_to_hash(define, source->definehash);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
game::define_s* copy_define([[maybe_unused]] game::source_s* source,
|
||||
game::define_s* define) {
|
||||
game::token_s *token, *newtoken, *lasttoken;
|
||||
|
||||
auto* newdefine = static_cast<game::define_s*>(
|
||||
game::GetMemory(sizeof(game::define_s) + std::strlen(define->name) + 1));
|
||||
|
||||
// copy the define name
|
||||
newdefine->name = (char*)newdefine + sizeof(game::define_s);
|
||||
std::memcpy(newdefine->name, define->name, std::strlen(define->name) + 1);
|
||||
|
||||
newdefine->flags = define->flags;
|
||||
newdefine->builtin = define->builtin;
|
||||
newdefine->numparms = define->numparms;
|
||||
|
||||
// the define is not linked
|
||||
newdefine->next = nullptr;
|
||||
newdefine->hashnext = nullptr;
|
||||
|
||||
// copy the define tokens
|
||||
newdefine->tokens = nullptr;
|
||||
for (lasttoken = nullptr, token = define->tokens; token;
|
||||
token = token->next) {
|
||||
newtoken = game::PC_CopyToken(token);
|
||||
newtoken->next = nullptr;
|
||||
if (lasttoken) {
|
||||
lasttoken->next = newtoken;
|
||||
} else {
|
||||
newdefine->tokens = newtoken;
|
||||
}
|
||||
|
||||
lasttoken = newtoken;
|
||||
}
|
||||
|
||||
// copy the define parameters
|
||||
newdefine->parms = nullptr;
|
||||
for (lasttoken = nullptr, token = define->parms; token; token = token->next) {
|
||||
newtoken = game::PC_CopyToken(token);
|
||||
newtoken->next = nullptr;
|
||||
if (lasttoken) {
|
||||
lasttoken->next = newtoken;
|
||||
} else {
|
||||
newdefine->parms = newtoken;
|
||||
}
|
||||
|
||||
lasttoken = newtoken;
|
||||
}
|
||||
|
||||
return newdefine;
|
||||
}
|
||||
|
||||
void add_global_defines_to_source(game::source_s* source) {
|
||||
for (auto* define = globaldefines; define; define = define->next) {
|
||||
auto* newdefine = copy_define(source, define);
|
||||
|
||||
add_define_to_hash(newdefine, source->definehash);
|
||||
}
|
||||
}
|
||||
|
||||
game::source_s* load_source_file(const char* filename) {
|
||||
auto* script = load_script_file(filename);
|
||||
if (!script) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
script->next = nullptr;
|
||||
|
||||
auto* source =
|
||||
static_cast<game::source_s*>(game::GetMemory(sizeof(game::source_s)));
|
||||
std::memset(source, 0, sizeof(game::source_s));
|
||||
|
||||
strncpy_s(source->filename, filename, _TRUNCATE);
|
||||
source->scriptstack = script;
|
||||
source->tokens = nullptr;
|
||||
source->defines = nullptr;
|
||||
source->indentstack = nullptr;
|
||||
source->skip = 0;
|
||||
|
||||
source->definehash = static_cast<game::define_s**>(
|
||||
game::GetClearedMemory(DEFINEHASHSIZE * sizeof(game::define_s*)));
|
||||
|
||||
add_global_defines_to_source(source);
|
||||
return source;
|
||||
}
|
||||
|
||||
int load_source_handle(const char* filename, const char** builtin_defines) {
|
||||
int i;
|
||||
|
||||
for (i = 1; i < MAX_SOURCEFILES; ++i) {
|
||||
if (!game::sourceFiles[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= MAX_SOURCEFILES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto* source = load_source_file(filename);
|
||||
if (!source) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (builtin_defines) {
|
||||
for (auto define_iter = 0; builtin_defines[define_iter]; ++define_iter) {
|
||||
add_define(source, builtin_defines[define_iter]);
|
||||
}
|
||||
}
|
||||
|
||||
game::sourceFiles[i] = source;
|
||||
return i;
|
||||
}
|
||||
|
||||
void free_source(game::source_s* source) {
|
||||
// free all the scripts
|
||||
while (source->scriptstack) {
|
||||
auto* script = source->scriptstack;
|
||||
source->scriptstack = source->scriptstack->next;
|
||||
free_script(script);
|
||||
}
|
||||
|
||||
// free all the tokens
|
||||
while (source->tokens) {
|
||||
auto* token = source->tokens;
|
||||
source->tokens = source->tokens->next;
|
||||
free_token(token);
|
||||
}
|
||||
|
||||
for (auto i = 0; i < DEFINEHASHSIZE; ++i) {
|
||||
while (source->definehash[i]) {
|
||||
auto* define = source->definehash[i];
|
||||
source->definehash[i] = source->definehash[i]->hashnext;
|
||||
game::PC_FreeDefine(define);
|
||||
}
|
||||
}
|
||||
|
||||
// free all indents
|
||||
while (source->indentstack) {
|
||||
auto* indent = source->indentstack;
|
||||
source->indentstack = source->indentstack->next;
|
||||
game::FreeMemory(indent);
|
||||
}
|
||||
|
||||
if (source->definehash) {
|
||||
game::FreeMemory(source->definehash);
|
||||
}
|
||||
|
||||
// free the source itself
|
||||
game::FreeMemory(source);
|
||||
}
|
||||
|
||||
int free_source_handle(int handle) {
|
||||
if (handle < 1 || handle >= MAX_SOURCEFILES)
|
||||
return 0;
|
||||
|
||||
if (!game::sourceFiles[handle])
|
||||
return 0;
|
||||
|
||||
free_source(game::sourceFiles[handle]);
|
||||
game::sourceFiles[handle] = nullptr;
|
||||
return 1;
|
||||
}
|
||||
} // namespace pc
|
6
src/client/component/botlib/l_precomp.hpp
Normal file
6
src/client/component/botlib/l_precomp.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace pc {
|
||||
int load_source_handle(const char* filename, const char** builtin_defines);
|
||||
int free_source_handle(int handle);
|
||||
} // namespace pc
|
204
src/client/component/botlib/l_script.cpp
Normal file
204
src/client/component/botlib/l_script.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
#include <std_include.hpp>
|
||||
#include "l_script.hpp"
|
||||
|
||||
namespace pc {
|
||||
// clang-format off
|
||||
game::punctuation_s default_punctuations[] =
|
||||
{
|
||||
{ ">>=", P_RSHIFT_ASSIGN, nullptr },
|
||||
{ "<<=", P_LSHIFT_ASSIGN, nullptr },
|
||||
|
||||
{ "...", P_PARMS, nullptr },
|
||||
// define merge operator
|
||||
{ "##", P_PRECOMPMERGE, nullptr },
|
||||
// logic operators
|
||||
{ "&&", P_LOGIC_AND, nullptr },
|
||||
{ "||", P_LOGIC_OR, nullptr },
|
||||
{ ">=", P_LOGIC_GEQ, nullptr },
|
||||
{ "<=", P_LOGIC_LEQ, nullptr },
|
||||
{ "==", P_LOGIC_EQ, nullptr },
|
||||
{ "!=", P_LOGIC_UNEQ, nullptr },
|
||||
// arithmatic operators
|
||||
{ "*=", P_MUL_ASSIGN, nullptr },
|
||||
{ "/=",P_DIV_ASSIGN, nullptr },
|
||||
{ "%=", P_MOD_ASSIGN, nullptr },
|
||||
{ "+=", P_ADD_ASSIGN, nullptr },
|
||||
{ "-=", P_SUB_ASSIGN,nullptr },
|
||||
{ "++", P_INC, nullptr },
|
||||
{ "--", P_DEC, nullptr },
|
||||
// binary operators
|
||||
{ "&=", P_BIN_AND_ASSIGN, nullptr },
|
||||
{ "|=", P_BIN_OR_ASSIGN, nullptr },
|
||||
{ "^=", P_BIN_XOR_ASSIGN, nullptr },
|
||||
{ ">>", P_RSHIFT, nullptr },
|
||||
{ "<<", P_LSHIFT, nullptr },
|
||||
// reference operators
|
||||
{ "->", P_POINTERREF, nullptr },
|
||||
// C++
|
||||
{ "::", P_CPP1, nullptr },
|
||||
{ ".*", P_CPP2, nullptr },
|
||||
// arithmatic operators
|
||||
{ "*", P_MUL, nullptr },
|
||||
{ "/", P_DIV,nullptr },
|
||||
{ "%", P_MOD, nullptr },
|
||||
{ "+", P_ADD, nullptr },
|
||||
{ "-", P_SUB, nullptr },
|
||||
{ "=", P_ASSIGN, nullptr },
|
||||
// binary operators
|
||||
{ "&", P_BIN_AND, nullptr },
|
||||
{ "|", P_BIN_OR, nullptr },
|
||||
{ "^", P_BIN_XOR, nullptr },
|
||||
{ "~", P_BIN_NOT, nullptr },
|
||||
// logic operators
|
||||
{ "!", P_LOGIC_NOT, nullptr },
|
||||
{ ">", P_LOGIC_GREATER, nullptr },
|
||||
{ "<", P_LOGIC_LESS, nullptr },
|
||||
// reference operator
|
||||
{ ".", P_REF, nullptr },
|
||||
// seperators
|
||||
{ ",", P_COMMA, nullptr },
|
||||
{ ";", P_SEMICOLON, nullptr },
|
||||
// label indication
|
||||
{ ":", P_COLON, nullptr },
|
||||
// if statement
|
||||
{ "?", P_QUESTIONMARK, nullptr },
|
||||
// embracements
|
||||
{ "(", P_PARENTHESESOPEN, nullptr },
|
||||
{ ")", P_PARENTHESESCLOSE, nullptr },
|
||||
{ "{", P_BRACEOPEN, nullptr },
|
||||
{ "}", P_BRACECLOSE, nullptr },
|
||||
{ "[", P_SQBRACKETOPEN, nullptr },
|
||||
{ "]", P_SQBRACKETCLOSE, nullptr },
|
||||
//
|
||||
{ "\\", P_BACKSLASH, nullptr },
|
||||
// precompiler operator
|
||||
{ "#", P_PRECOMP, nullptr },
|
||||
{ "$", P_DOLLAR, nullptr },
|
||||
{ nullptr, 0, nullptr },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
void create_punctuation_table(game::script_s* script,
|
||||
game::punctuation_s* punctuations) {
|
||||
game::punctuation_s* p;
|
||||
|
||||
// get memory for the table
|
||||
if (!script->punctuationtable) {
|
||||
script->punctuationtable = static_cast<game::punctuation_s**>(
|
||||
game::GetMemory(256 * sizeof(game::punctuation_s*)));
|
||||
}
|
||||
|
||||
std::memset(script->punctuationtable, 0, 256 * sizeof(game::punctuation_s*));
|
||||
|
||||
// add the punctuations in the list to the punctuation table
|
||||
for (auto i = 0; punctuations[i].p; ++i) {
|
||||
auto* newp = &punctuations[i];
|
||||
game::punctuation_s* lastp = nullptr;
|
||||
|
||||
// sort the punctuations in this table entry on length (longer punctuations
|
||||
// first)
|
||||
for (p = script->punctuationtable[(unsigned int)newp->p[0]]; p;
|
||||
p = p->next) {
|
||||
if (std::strlen(p->p) < std::strlen(newp->p)) {
|
||||
newp->next = p;
|
||||
if (lastp) {
|
||||
lastp->next = newp;
|
||||
} else {
|
||||
script->punctuationtable[(unsigned int)newp->p[0]] = newp;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
lastp = p;
|
||||
}
|
||||
|
||||
if (!p) {
|
||||
newp->next = nullptr;
|
||||
if (lastp) {
|
||||
lastp->next = newp;
|
||||
} else {
|
||||
script->punctuationtable[(unsigned int)newp->p[0]] = newp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_script_punctuations(game::script_s* script) {
|
||||
create_punctuation_table(script, default_punctuations);
|
||||
script->punctuations = default_punctuations;
|
||||
}
|
||||
|
||||
game::script_s* load_script_file(const char* filename) {
|
||||
int fp;
|
||||
char pathname[game::MAX_QPATH];
|
||||
|
||||
sprintf_s(pathname, "%s", filename);
|
||||
const auto length = game::FS_FOpenFileRead(pathname, &fp);
|
||||
if (!fp) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* buffer = game::GetClearedMemory(sizeof(game::script_s) + length + 1);
|
||||
auto* script = static_cast<game::script_s*>(buffer);
|
||||
strncpy_s(script->filename, filename, _TRUNCATE);
|
||||
|
||||
script->buffer = static_cast<char*>(buffer) + sizeof(game::script_s);
|
||||
script->buffer[length] = '\0';
|
||||
script->length = length;
|
||||
// pointer in script buffer
|
||||
script->script_p = script->buffer;
|
||||
// pointer in script buffer before reading token
|
||||
script->lastscript_p = script->buffer;
|
||||
// pointer to end of script buffer
|
||||
script->end_p = &script->buffer[length];
|
||||
// set if there's a token available in script->token
|
||||
script->tokenavailable = false;
|
||||
|
||||
script->line = 1;
|
||||
script->lastline = 1;
|
||||
|
||||
set_script_punctuations(script);
|
||||
|
||||
game::FS_Read(script->buffer, length, fp);
|
||||
game::FS_FCloseFile(fp);
|
||||
|
||||
script->length = game::Com_Compress(script->buffer);
|
||||
return script;
|
||||
}
|
||||
|
||||
game::script_s* load_script_memory(const char* ptr, int length,
|
||||
const char* name) {
|
||||
auto* buffer = game::GetClearedMemory(sizeof(game::script_s) + length + 1);
|
||||
auto* script = static_cast<game::script_s*>(buffer);
|
||||
|
||||
strncpy_s(script->filename, name, _TRUNCATE);
|
||||
script->buffer = static_cast<char*>(buffer) + sizeof(game::script_s);
|
||||
script->buffer[length] = '\0';
|
||||
script->length = length;
|
||||
// pointer in script buffer
|
||||
script->script_p = script->buffer;
|
||||
// pointer in script buffer before reading token
|
||||
script->lastscript_p = script->buffer;
|
||||
// pointer to end of script buffer
|
||||
script->end_p = &script->buffer[length];
|
||||
// set if there's a token available in script->token
|
||||
script->tokenavailable = false;
|
||||
|
||||
script->line = 1;
|
||||
script->lastline = 1;
|
||||
|
||||
set_script_punctuations(script);
|
||||
|
||||
std::memcpy(script->buffer, ptr, length);
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
void free_script(game::script_s* script) {
|
||||
if (script->punctuationtable)
|
||||
game::FreeMemory(script->punctuationtable);
|
||||
|
||||
game::FreeMemory(script);
|
||||
}
|
||||
} // namespace pc
|
70
src/client/component/botlib/l_script.hpp
Normal file
70
src/client/component/botlib/l_script.hpp
Normal file
@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#define P_RSHIFT_ASSIGN 1
|
||||
#define P_LSHIFT_ASSIGN 2
|
||||
#define P_PARMS 3
|
||||
#define P_PRECOMPMERGE 4
|
||||
|
||||
#define P_LOGIC_AND 5
|
||||
#define P_LOGIC_OR 6
|
||||
#define P_LOGIC_GEQ 7
|
||||
#define P_LOGIC_LEQ 8
|
||||
#define P_LOGIC_EQ 9
|
||||
#define P_LOGIC_UNEQ 10
|
||||
|
||||
#define P_MUL_ASSIGN 11
|
||||
#define P_DIV_ASSIGN 12
|
||||
#define P_MOD_ASSIGN 13
|
||||
#define P_ADD_ASSIGN 14
|
||||
#define P_SUB_ASSIGN 15
|
||||
#define P_INC 16
|
||||
#define P_DEC 17
|
||||
|
||||
#define P_BIN_AND_ASSIGN 18
|
||||
#define P_BIN_OR_ASSIGN 19
|
||||
#define P_BIN_XOR_ASSIGN 20
|
||||
#define P_RSHIFT 21
|
||||
#define P_LSHIFT 22
|
||||
|
||||
#define P_POINTERREF 23
|
||||
#define P_CPP1 24
|
||||
#define P_CPP2 25
|
||||
#define P_MUL 26
|
||||
#define P_DIV 27
|
||||
#define P_MOD 28
|
||||
#define P_ADD 29
|
||||
#define P_SUB 30
|
||||
#define P_ASSIGN 31
|
||||
|
||||
#define P_BIN_AND 32
|
||||
#define P_BIN_OR 33
|
||||
#define P_BIN_XOR 34
|
||||
#define P_BIN_NOT 35
|
||||
|
||||
#define P_LOGIC_NOT 36
|
||||
#define P_LOGIC_GREATER 37
|
||||
#define P_LOGIC_LESS 38
|
||||
|
||||
#define P_REF 39
|
||||
#define P_COMMA 40
|
||||
#define P_SEMICOLON 41
|
||||
#define P_COLON 42
|
||||
#define P_QUESTIONMARK 43
|
||||
|
||||
#define P_PARENTHESESOPEN 44
|
||||
#define P_PARENTHESESCLOSE 45
|
||||
#define P_BRACEOPEN 46
|
||||
#define P_BRACECLOSE 47
|
||||
#define P_SQBRACKETOPEN 48
|
||||
#define P_SQBRACKETCLOSE 49
|
||||
#define P_BACKSLASH 50
|
||||
|
||||
#define P_PRECOMP 51
|
||||
#define P_DOLLAR 52
|
||||
|
||||
namespace pc {
|
||||
game::script_s* load_script_file(const char* filename);
|
||||
game::script_s* load_script_memory(const char* ptr, int length,
|
||||
const char* name);
|
||||
void free_script(game::script_s* script);
|
||||
} // namespace pc
|
123
src/client/component/branding.cpp
Normal file
123
src/client/component/branding.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include <version.h>
|
||||
|
||||
namespace branding {
|
||||
namespace {
|
||||
#ifdef _DEBUG
|
||||
constexpr auto* BUILD_TYPE = "IW4_DEV SP";
|
||||
#else
|
||||
constexpr auto* BUILD_TYPE = "IW4 SP";
|
||||
#endif
|
||||
|
||||
constexpr const char* get_build_number() {
|
||||
return SHORTVERSION " latest " __DATE__ " " __TIME__;
|
||||
}
|
||||
|
||||
const char* get_version_string() {
|
||||
const auto* result = utils::string::va(
|
||||
"{0} {1} build {2} {3}", BUILD_TYPE, "(Alpha)", get_build_number(),
|
||||
reinterpret_cast<const char*>(0x6A1574));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void dvar_set_version_string(const game::dvar_t* dvar, const char* /*value*/) {
|
||||
const auto* result = get_version_string();
|
||||
utils::hook::invoke<void>(0x480E70, dvar, result);
|
||||
}
|
||||
|
||||
void cg_draw_version() {
|
||||
assert(game::ScrPlace_IsFullScreenActive());
|
||||
|
||||
// Default values
|
||||
constexpr auto font_scale = 0.25f;
|
||||
constexpr auto max_chars = std::numeric_limits<int>::max();
|
||||
// Default colours
|
||||
constexpr float shadow_color[] = {0.0f, 0.0f, 0.0f, 0.69f};
|
||||
constexpr float color[] = {0.4f, 0.69f, 1.0f, 0.69f};
|
||||
|
||||
auto* const placement = game::ScrPlace_GetUnsafeFullPlacement();
|
||||
auto* const font = game::UI_GetFontHandle(placement, 0, 0.583f);
|
||||
|
||||
const auto width = game::UI_TextWidth((*dvars::version)->current.string, 0,
|
||||
font, font_scale);
|
||||
const auto height = game::UI_TextHeight(font, font_scale);
|
||||
|
||||
// clang-format off
|
||||
game::UI_DrawText(placement, (*dvars::version)->current.string, max_chars,
|
||||
font,
|
||||
1.0f - (dvars::cg_drawVersionX->current.value +
|
||||
static_cast<float>(width)),
|
||||
1.0f - (dvars::cg_drawVersionY->current.value +
|
||||
static_cast<float>(height)),
|
||||
3, 3, font_scale, shadow_color, 0);
|
||||
game::UI_DrawText(placement, (*dvars::version)->current.string, max_chars,
|
||||
font,
|
||||
(0.0f - static_cast<float>(width)) -
|
||||
dvars::cg_drawVersionX->current.value,
|
||||
(0.0f - static_cast<float>(height)) -
|
||||
dvars::cg_drawVersionY->current.value,
|
||||
3, 3, font_scale, color, 0);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void cg_draw_full_screen_debug_overlays_stub(int local_client_num) {
|
||||
assert(game::ScrPlace_IsFullScreenActive());
|
||||
|
||||
if (dvars::cg_drawVersion->current.enabled) {
|
||||
cg_draw_version();
|
||||
}
|
||||
|
||||
utils::hook::invoke<void>(0x44BD00, local_client_num);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
utils::hook(0x46F570, get_build_number, HOOK_JUMP).install()->quick();
|
||||
utils::hook(0x60429A, dvar_set_version_string, HOOK_CALL)
|
||||
.install()
|
||||
->quick();
|
||||
|
||||
utils::hook::set<const char*>(0x446A48, "iw4-sp: Console");
|
||||
utils::hook::set<const char*>(0x50C110, "iw4-sp: Game");
|
||||
|
||||
utils::hook::set<const char*>(0x579364, "iw4-sp: " SHORTVERSION "> ");
|
||||
|
||||
// Com_Init_Try_Block_Function
|
||||
utils::hook::set<const char*>(0x604004, BUILD_TYPE);
|
||||
utils::hook::set<const char*>(0x603FFF, SHORTVERSION);
|
||||
utils::hook::set<const char*>(0x603FF5, __DATE__);
|
||||
|
||||
register_branding_dvars();
|
||||
utils::hook(0x57DAFF, cg_draw_full_screen_debug_overlays_stub, HOOK_CALL)
|
||||
.install() // hook*
|
||||
->quick();
|
||||
}
|
||||
|
||||
static void register_branding_dvars() {
|
||||
#ifdef _DEBUG
|
||||
constexpr auto value = true;
|
||||
#else
|
||||
constexpr auto value = false;
|
||||
#endif
|
||||
dvars::cg_drawVersion = game::Dvar_RegisterBool(
|
||||
"cg_drawVersion", value, game::DVAR_NONE, "Draw the game version");
|
||||
dvars::cg_drawVersionX = game::Dvar_RegisterFloat(
|
||||
"cg_drawVersionX", 50.0f, 0.0f, 512.0f, game::DVAR_NONE,
|
||||
"X offset for the version string");
|
||||
dvars::cg_drawVersionY = game::Dvar_RegisterFloat(
|
||||
"cg_drawVersionY", 18.0f, 0.0f, 512.0f, game::DVAR_NONE,
|
||||
"Y offset for the version string");
|
||||
}
|
||||
};
|
||||
} // namespace branding
|
||||
|
||||
REGISTER_COMPONENT(branding::component)
|
39
src/client/component/ceg.cpp
Normal file
39
src/client/component/ceg.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace ceg {
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
// Some unnecessary CEG functions.
|
||||
// Important functions are patched in the exe already
|
||||
|
||||
utils::hook::set<std::uint32_t>(0x44AD80, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x476A20, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x4E3B90, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x4E3B90, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x411160, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x41E390, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x47C2E0, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x4EEA90, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x40E380, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x4E45C0, 0xC301B0);
|
||||
|
||||
// Killer caller
|
||||
utils::hook::set<std::uint8_t>(0x43F320, 0xC3);
|
||||
utils::hook::set<std::uint8_t>(0x458ED0, 0xC3);
|
||||
utils::hook::set<std::uint8_t>(0x47A140, 0xC3);
|
||||
|
||||
utils::hook::nop(0x411166, 9);
|
||||
|
||||
// Remove 'Steam Start' check
|
||||
utils::hook::nop(0x43FAD5, 12);
|
||||
// Shutdown
|
||||
utils::hook::set<std::uint8_t>(0x4619B0, 0xC3);
|
||||
}
|
||||
};
|
||||
} // namespace ceg
|
||||
|
||||
REGISTER_COMPONENT(ceg::component)
|
152
src/client/component/command.cpp
Normal file
152
src/client/component/command.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/memory.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
|
||||
namespace command {
|
||||
namespace {
|
||||
utils::hook::detour client_command_hook;
|
||||
|
||||
std::unordered_map<std::string, std::function<void(params&)>> handlers;
|
||||
std::unordered_map<std::string,
|
||||
std::function<void(game::gentity_s*, params_sv&)>>
|
||||
handlers_sv;
|
||||
|
||||
void main_handler() {
|
||||
params params;
|
||||
|
||||
const auto command = utils::string::to_lower(params[0]);
|
||||
if (handlers.contains(command)) {
|
||||
handlers[command](params);
|
||||
}
|
||||
}
|
||||
|
||||
void client_command_stub(int client_num, const char* s) {
|
||||
|
||||
auto* entity = &game::g_entities[client_num];
|
||||
|
||||
params_sv params;
|
||||
|
||||
const auto command = utils::string::to_lower(params[0]);
|
||||
const auto got = handlers_sv.find(command);
|
||||
|
||||
if (got != handlers_sv.end()) {
|
||||
got->second(entity, params);
|
||||
return;
|
||||
}
|
||||
|
||||
client_command_hook.invoke<void>(client_num, s);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
params::params() : nesting_(game::cmd_args->nesting) {
|
||||
assert(game::cmd_args->nesting < game::CMD_MAX_NESTING);
|
||||
}
|
||||
|
||||
int params::size() const { return game::cmd_args->argc[this->nesting_]; }
|
||||
|
||||
const char* params::get(const int index) const {
|
||||
if (index >= this->size()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return game::cmd_args->argv[this->nesting_][index];
|
||||
}
|
||||
|
||||
std::string params::join(const int index) const {
|
||||
std::string result;
|
||||
|
||||
for (auto i = index; i < this->size(); i++) {
|
||||
if (i > index)
|
||||
result.append(" ");
|
||||
result.append(this->get(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
params_sv::params_sv() : nesting_(game::sv_cmd_args->nesting) {
|
||||
assert(game::sv_cmd_args->nesting < game::CMD_MAX_NESTING);
|
||||
}
|
||||
|
||||
int params_sv::size() const { return game::sv_cmd_args->argc[this->nesting_]; }
|
||||
|
||||
const char* params_sv::get(const int index) const {
|
||||
if (index >= this->size()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return game::sv_cmd_args->argv[this->nesting_][index];
|
||||
}
|
||||
|
||||
std::string params_sv::join(const int index) const {
|
||||
std::string result;
|
||||
|
||||
for (auto i = index; i < this->size(); i++) {
|
||||
if (i > index)
|
||||
result.append(" ");
|
||||
result.append(this->get(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void add_raw(const char* name, void (*callback)(), const int is_key) {
|
||||
assert(is_key == 0 || is_key == 1);
|
||||
|
||||
game::Cmd_AddCommand(
|
||||
name, callback,
|
||||
utils::memory::get_allocator()->allocate<game::cmd_function_s>(), is_key);
|
||||
}
|
||||
|
||||
void add(const char* name, const std::function<void(const params&)>& callback) {
|
||||
const auto command = utils::string::to_lower(name);
|
||||
|
||||
if (!handlers.contains(command)) {
|
||||
add_raw(name, main_handler);
|
||||
}
|
||||
|
||||
handlers[command] = callback;
|
||||
}
|
||||
|
||||
void add(const char* name, const std::function<void()>& callback) {
|
||||
add(name, [callback](const params&) { callback(); });
|
||||
}
|
||||
|
||||
void add_sv(const char* name,
|
||||
std::function<void(game::gentity_s*, const params_sv&)> callback) {
|
||||
const auto command = utils::string::to_lower(name);
|
||||
|
||||
if (!handlers_sv.contains(command)) {
|
||||
handlers_sv[command] = callback;
|
||||
}
|
||||
}
|
||||
|
||||
void execute(std::string command, const bool sync) {
|
||||
command += "\n";
|
||||
|
||||
if (sync) {
|
||||
game::Cmd_ExecuteSingleCommand(0, 0, command.data());
|
||||
} else {
|
||||
game::Cbuf_AddText(0, command.data());
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
static_assert(sizeof(game::cmd_function_s) == 0x18);
|
||||
static_assert(offsetof(game::gentity_s, client) == 0x108);
|
||||
|
||||
void post_load() override {
|
||||
client_command_hook.create(0x4DF4B0, client_command_stub);
|
||||
}
|
||||
|
||||
void pre_destroy() override { client_command_hook.clear(); }
|
||||
};
|
||||
} // namespace command
|
||||
|
||||
REGISTER_COMPONENT(command::component)
|
40
src/client/component/command.hpp
Normal file
40
src/client/component/command.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
namespace command {
|
||||
class params {
|
||||
public:
|
||||
params();
|
||||
|
||||
[[nodiscard]] int size() const;
|
||||
[[nodiscard]] const char* get(int index) const;
|
||||
[[nodiscard]] std::string join(int index) const;
|
||||
|
||||
const char* operator[](const int index) const { return this->get(index); }
|
||||
|
||||
private:
|
||||
int nesting_;
|
||||
};
|
||||
|
||||
class params_sv {
|
||||
public:
|
||||
params_sv();
|
||||
|
||||
[[nodiscard]] int size() const;
|
||||
[[nodiscard]] const char* get(int index) const;
|
||||
[[nodiscard]] std::string join(int index) const;
|
||||
|
||||
const char* operator[](const int index) const { return this->get(index); }
|
||||
|
||||
private:
|
||||
int nesting_;
|
||||
};
|
||||
|
||||
void add_raw(const char* name, void (*callback)(), int is_key = 0);
|
||||
void add(const char* name, const std::function<void(const params&)>& callback);
|
||||
void add(const char* name, const std::function<void()>& callback);
|
||||
|
||||
void add_sv(const char* name,
|
||||
std::function<void(game::gentity_s*, const params_sv&)> callback);
|
||||
|
||||
void execute(std::string command, bool sync = false);
|
||||
} // namespace command
|
71
src/client/component/console.cpp
Normal file
71
src/client/component/console.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "dvar.hpp"
|
||||
|
||||
namespace console {
|
||||
namespace {
|
||||
bool is_command;
|
||||
|
||||
void con_toggle_console() {
|
||||
game::Field_Clear(game::g_consoleField);
|
||||
|
||||
if (game::conDrawInputGlob->matchIndex >= 0 &&
|
||||
game::conDrawInputGlob->autoCompleteChoice[0] != '\0') {
|
||||
game::conDrawInputGlob->matchIndex = -1;
|
||||
game::conDrawInputGlob->autoCompleteChoice[0] = '\0';
|
||||
}
|
||||
|
||||
game::g_consoleField->widthInPixels = *game::g_console_field_width;
|
||||
game::g_consoleField->charHeight = *game::g_console_char_height;
|
||||
game::g_consoleField->fixedSize = 1;
|
||||
game::con->outputVisible = false;
|
||||
|
||||
// clientUIActives[0].keyCatchers
|
||||
utils::hook::set<std::uint32_t>(0x929140,
|
||||
*reinterpret_cast<std::uint32_t*>(0x929140) ^
|
||||
game::KEYCATCH_CONSOLE);
|
||||
}
|
||||
|
||||
bool con_is_dvar_command_stub(const char* cmd) {
|
||||
is_command = game::Con_IsDvarCommand(cmd);
|
||||
return is_command;
|
||||
}
|
||||
|
||||
void cmd_for_each_stub(void (*callback)(const char* str)) {
|
||||
if (!is_command) {
|
||||
utils::hook::invoke<void>(0x4B7000, callback);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
static_assert(sizeof(game::field_t) == 0x118);
|
||||
static_assert(sizeof(game::ConDrawInputGlob) == 0x64);
|
||||
|
||||
void post_start() override {
|
||||
// Prevents console from opening
|
||||
dvar::override::register_bool("monkeytoy", false, game::DVAR_NONE);
|
||||
}
|
||||
|
||||
void post_load() override {
|
||||
utils::hook(0x44317E, con_toggle_console, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // CL_KeyEvent
|
||||
utils::hook(0x442E8E, con_toggle_console, HOOK_JUMP)
|
||||
.install()
|
||||
->quick(); // CL_KeyEvent
|
||||
|
||||
// Con_DrawInput
|
||||
utils::hook(0x57946D, con_is_dvar_command_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick();
|
||||
utils::hook(0x57951C, cmd_for_each_stub, HOOK_CALL).install()->quick();
|
||||
}
|
||||
};
|
||||
} // namespace console
|
||||
|
||||
REGISTER_COMPONENT(console::component)
|
129
src/client/component/debug.cpp
Normal file
129
src/client/component/debug.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
#include "game/engine/scoped_critical_section.hpp"
|
||||
#include "game/engine/large_local.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
namespace debug {
|
||||
namespace {
|
||||
void com_assert_f() { assert("a" && false); }
|
||||
|
||||
void com_bug_f(const command::params& params) {
|
||||
char new_file_name[0x105]{};
|
||||
char to_ospath[MAX_PATH]{};
|
||||
char from_ospath[MAX_PATH]{};
|
||||
const char* bug;
|
||||
|
||||
if (!*game::logfile) {
|
||||
game::Com_PrintError(game::CON_CHANNEL_ERROR,
|
||||
"CopyFile failed: logfile wasn't opened\n");
|
||||
}
|
||||
|
||||
if (params.size() == 2) {
|
||||
bug = params.get(1);
|
||||
} else {
|
||||
assert(dvars::bug_name);
|
||||
bug = dvars::bug_name->current.string;
|
||||
}
|
||||
|
||||
sprintf_s(new_file_name, "%s_%s.log", bug, game::Live_GetLocalClientName(0));
|
||||
|
||||
game::engine::scoped_critical_section _(game::CRITSECT_CONSOLE,
|
||||
game::SCOPED_CRITSECT_NORMAL);
|
||||
|
||||
if (*game::logfile) {
|
||||
game::FS_FCloseFile(*game::logfile);
|
||||
*game::logfile = 0;
|
||||
}
|
||||
|
||||
game::FS_BuildOSPath(std::filesystem::current_path().string().data(), "",
|
||||
"console.log", from_ospath);
|
||||
game::FS_BuildOSPath(std::filesystem::current_path().string().data(), "",
|
||||
new_file_name, to_ospath);
|
||||
const auto result = CopyFileA(from_ospath, to_ospath, 0);
|
||||
game::Com_OpenLogFile();
|
||||
|
||||
if (!result) {
|
||||
game::Com_PrintError(game::CON_CHANNEL_ERROR, "CopyFile failed(%d) %s %s\n",
|
||||
GetLastError(), "console.log", new_file_name);
|
||||
}
|
||||
}
|
||||
|
||||
void com_bug_name_inc_f() {
|
||||
char buf[260]{};
|
||||
|
||||
if (std::strlen(dvars::bug_name->current.string) < 4) {
|
||||
game::Dvar_SetString(dvars::bug_name, "bug0");
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::strncmp(dvars::bug_name->current.string, "bug", 3) != 0) {
|
||||
game::Dvar_SetString(dvars::bug_name, "bug0");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto n = std::strtol(dvars::bug_name->current.string + 3, nullptr, 10);
|
||||
sprintf_s(buf, "bug%d", n + 1);
|
||||
game::Dvar_SetString(dvars::bug_name, buf);
|
||||
}
|
||||
|
||||
void g_print_fast_file_errors(const char* fastfile) {
|
||||
assert(fastfile);
|
||||
|
||||
game::engine::large_local rawfile_buf_large_local(0x18000);
|
||||
auto* rawfile_buf = static_cast<char*>(rawfile_buf_large_local.get_buf());
|
||||
|
||||
auto* text = game::DB_ReadRawFile(fastfile, rawfile_buf, 0x18000);
|
||||
|
||||
assert(text);
|
||||
|
||||
if (*text) {
|
||||
game::Com_PrintError(game::CON_CHANNEL_ERROR,
|
||||
"There were errors when building fast file '%s'\n",
|
||||
fastfile);
|
||||
game::Com_PrintError(game::CON_CHANNEL_ERROR, "%s", text);
|
||||
}
|
||||
}
|
||||
|
||||
void g_init_game_stub() {
|
||||
utils::hook::invoke<void>(0x4D6410);
|
||||
|
||||
g_print_fast_file_errors("code_post_gfx");
|
||||
g_print_fast_file_errors("common");
|
||||
g_print_fast_file_errors((*dvars::sv_mapname)->current.string);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
static_assert(sizeof(RTL_CRITICAL_SECTION) == 0x18);
|
||||
|
||||
void post_load() override {
|
||||
utils::hook::set<void (*)()>(0x604203, com_assert_f);
|
||||
|
||||
scheduler::once(
|
||||
[] {
|
||||
dvars::bug_name = game::Dvar_RegisterString(
|
||||
"bug_name", "bug0", game::DVAR_CHEAT | game::DVAR_CODINFO,
|
||||
"Name appended to the copied console log");
|
||||
},
|
||||
scheduler::pipeline::main);
|
||||
|
||||
command::add("bug", com_bug_f);
|
||||
command::add("bug_name_inc", com_bug_name_inc_f);
|
||||
|
||||
#ifdef _DEBUG
|
||||
utils::hook(0x4C79DF, g_init_game_stub, HOOK_CALL)
|
||||
.install() // hook*
|
||||
->quick(); // Scr_FreeEntityList
|
||||
#endif
|
||||
}
|
||||
};
|
||||
} // namespace debug
|
||||
|
||||
REGISTER_COMPONENT(debug::component)
|
76
src/client/component/discord.cpp
Normal file
76
src/client/component/discord.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <discord_rpc.h>
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace discord {
|
||||
namespace {
|
||||
DiscordRichPresence discord_presence;
|
||||
|
||||
void update_discord() {
|
||||
Discord_RunCallbacks();
|
||||
|
||||
if (!discord_presence.startTimestamp) {
|
||||
discord_presence.startTimestamp =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
Discord_UpdatePresence(&discord_presence);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
DiscordEventHandlers handlers;
|
||||
ZeroMemory(&handlers, sizeof(handlers));
|
||||
handlers.ready = ready;
|
||||
handlers.errored = errored;
|
||||
handlers.disconnected = errored;
|
||||
handlers.joinGame = nullptr;
|
||||
handlers.spectateGame = nullptr;
|
||||
handlers.joinRequest = nullptr;
|
||||
|
||||
Discord_Initialize("978049907585863710", &handlers, 1, nullptr);
|
||||
|
||||
scheduler::once(
|
||||
[] {
|
||||
scheduler::once(update_discord, scheduler::pipeline::async);
|
||||
scheduler::loop(update_discord, scheduler::pipeline::async, 15s);
|
||||
},
|
||||
scheduler::pipeline::main);
|
||||
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void pre_destroy() override {
|
||||
if (initialized_) {
|
||||
Discord_Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool initialized_ = false;
|
||||
|
||||
static void ready(const DiscordUser* request) {
|
||||
ZeroMemory(&discord_presence, sizeof(discord_presence));
|
||||
|
||||
discord_presence.instance = 1;
|
||||
discord_presence.startTimestamp = 0;
|
||||
printf("Discord: Ready\n");
|
||||
Discord_UpdatePresence(&discord_presence);
|
||||
}
|
||||
|
||||
static void errored(const int error_code, const char* message) {
|
||||
printf("Discord: (%i) %s\n", error_code, message);
|
||||
}
|
||||
};
|
||||
} // namespace discord
|
||||
|
||||
REGISTER_COMPONENT(discord::component)
|
172
src/client/component/dvar.cpp
Normal file
172
src/client/component/dvar.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "dvar.hpp"
|
||||
|
||||
namespace dvar {
|
||||
struct dvar_base {
|
||||
std::uint16_t flags{};
|
||||
};
|
||||
|
||||
struct dvar_bool : dvar_base {
|
||||
bool value{};
|
||||
};
|
||||
|
||||
struct dvar_float : dvar_base {
|
||||
float value{};
|
||||
float min{};
|
||||
float max{};
|
||||
};
|
||||
|
||||
struct dvar_int : dvar_base {
|
||||
int value{};
|
||||
int min{};
|
||||
int max{};
|
||||
};
|
||||
|
||||
struct dvar_string : dvar_base {
|
||||
std::string value{};
|
||||
};
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
T* find_dvar(std::unordered_map<std::string, T>& map, const std::string& name) {
|
||||
auto i = map.find(name);
|
||||
if (i != map.end()) {
|
||||
return &i->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace override {
|
||||
static std::unordered_map<std::string, dvar_bool> register_bool_overrides;
|
||||
static std::unordered_map<std::string, dvar_float> register_float_overrides;
|
||||
static std::unordered_map<std::string, dvar_int> register_int_overrides;
|
||||
static std::unordered_map<std::string, dvar_string> register_string_overrides;
|
||||
|
||||
void register_bool(const std::string& name, const bool value,
|
||||
const std::uint16_t flags) {
|
||||
dvar_bool values;
|
||||
values.value = value;
|
||||
values.flags = flags;
|
||||
register_bool_overrides[name] = values;
|
||||
}
|
||||
|
||||
void register_float(const std::string& name, const float value, const float min,
|
||||
const float max, const std::uint16_t flags) {
|
||||
dvar_float values;
|
||||
values.value = value;
|
||||
values.min = min;
|
||||
values.max = max;
|
||||
values.flags = flags;
|
||||
register_float_overrides[name] = values;
|
||||
}
|
||||
|
||||
void register_int(const std::string& name, const int value, const int min,
|
||||
const int max, const std::uint16_t flags) {
|
||||
dvar_int values;
|
||||
values.value = value;
|
||||
values.min = min;
|
||||
values.max = max;
|
||||
values.flags = flags;
|
||||
register_int_overrides[name] = values;
|
||||
}
|
||||
|
||||
void register_string(const std::string& name, const std::string& value,
|
||||
const std::uint16_t flags) {
|
||||
dvar_string values;
|
||||
values.value = value;
|
||||
values.flags = flags;
|
||||
register_string_overrides[name] = values;
|
||||
}
|
||||
} // namespace override
|
||||
|
||||
namespace {
|
||||
utils::hook::detour dvar_register_bool_hook;
|
||||
utils::hook::detour dvar_register_float_hook;
|
||||
utils::hook::detour dvar_register_int_hook;
|
||||
utils::hook::detour dvar_register_string_hook;
|
||||
|
||||
const game::dvar_t* dvar_register_bool_stub(const char* name, bool value,
|
||||
std::uint16_t flags,
|
||||
const char* description) {
|
||||
auto* var = find_dvar(override::register_bool_overrides, name);
|
||||
if (var) {
|
||||
value = var->value;
|
||||
flags = var->flags;
|
||||
}
|
||||
|
||||
return dvar_register_bool_hook.invoke<const game::dvar_t*>(name, value, flags,
|
||||
description);
|
||||
}
|
||||
|
||||
const game::dvar_t* dvar_register_float_stub(const char* name, float value,
|
||||
float min, float max,
|
||||
std::uint16_t flags,
|
||||
const char* description) {
|
||||
auto* var = find_dvar(override::register_float_overrides, name);
|
||||
if (var) {
|
||||
value = var->value;
|
||||
min = var->min;
|
||||
max = var->max;
|
||||
flags = var->flags;
|
||||
}
|
||||
|
||||
return dvar_register_float_hook.invoke<const game::dvar_t*>(
|
||||
name, value, min, max, flags, description);
|
||||
}
|
||||
|
||||
const game::dvar_t* dvar_register_int_stub(const char* name, int value, int min,
|
||||
int max, std::uint16_t flags,
|
||||
const char* description) {
|
||||
auto* var = find_dvar(override::register_int_overrides, name);
|
||||
if (var) {
|
||||
value = var->value;
|
||||
min = var->min;
|
||||
max = var->max;
|
||||
flags = var->flags;
|
||||
}
|
||||
|
||||
return dvar_register_int_hook.invoke<const game::dvar_t*>(
|
||||
name, value, min, max, flags, description);
|
||||
}
|
||||
|
||||
const game::dvar_t* dvar_register_string_stub(const char* name,
|
||||
const char* value,
|
||||
std::uint16_t flags,
|
||||
const char* description) {
|
||||
auto* var = find_dvar(override::register_string_overrides, name);
|
||||
if (var) {
|
||||
value = var->value.data();
|
||||
flags = var->flags;
|
||||
}
|
||||
|
||||
return dvar_register_string_hook.invoke<const game::dvar_t*>(
|
||||
name, value, flags, description);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
dvar_register_bool_hook.create(0x429390, &dvar_register_bool_stub);
|
||||
dvar_register_float_hook.create(0x4051D0, &dvar_register_float_stub);
|
||||
dvar_register_int_hook.create(0x4E9490, &dvar_register_int_stub);
|
||||
dvar_register_string_hook.create(0x49E0B0, &dvar_register_string_stub);
|
||||
}
|
||||
|
||||
void pre_destroy() override {
|
||||
dvar_register_bool_hook.clear();
|
||||
dvar_register_float_hook.clear();
|
||||
dvar_register_int_hook.clear();
|
||||
dvar_register_string_hook.clear();
|
||||
}
|
||||
};
|
||||
} // namespace dvar
|
||||
|
||||
REGISTER_COMPONENT(dvar::component)
|
13
src/client/component/dvar.hpp
Normal file
13
src/client/component/dvar.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace dvar {
|
||||
namespace override {
|
||||
void register_bool(const std::string& name, bool value, std::uint16_t flags);
|
||||
void register_float(const std::string& name, float value, float min, float max,
|
||||
std::uint16_t flags);
|
||||
void register_int(const std::string& name, int value, int min, int max,
|
||||
std::uint16_t flags);
|
||||
void register_string(const std::string& name, const std::string& value,
|
||||
std::uint16_t flags);
|
||||
} // namespace override
|
||||
} // namespace dvar
|
68
src/client/component/dvar_patches.cpp
Normal file
68
src/client/component/dvar_patches.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "scheduler.hpp"
|
||||
#include "dvar.hpp"
|
||||
|
||||
namespace dvar_patches {
|
||||
namespace {
|
||||
const game::dvar_t* dvar_register_name(const char* dvar_name, const char* value,
|
||||
unsigned __int16 flags,
|
||||
const char* description) {
|
||||
return game::Dvar_RegisterString(
|
||||
dvar_name, value, game::DVAR_ARCHIVE | game::DVAR_USERINFO, description);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_start() override {
|
||||
dvar::override::register_bool("intro", false, game::DVAR_ARCHIVE);
|
||||
dvar::override::register_float("cg_fov", 65.0f, 65.0f, 160.0f,
|
||||
game::DVAR_ARCHIVE | game::DVAR_SAVED);
|
||||
dvar::override::register_string("fs_basegame", BASEGAME, game::DVAR_INIT);
|
||||
|
||||
#ifdef _DEBUG
|
||||
dvar::override::register_bool("sv_cheats", true, game::DVAR_NONE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void post_load() override {
|
||||
utils::hook(0x475156, dvar_register_name, HOOK_CALL).install()->quick();
|
||||
patch_sp();
|
||||
}
|
||||
|
||||
private:
|
||||
static void patch_sp() {
|
||||
utils::hook::set<std::uint8_t>(0x635841, 0xEB); // Read only
|
||||
utils::hook::set<std::uint8_t>(0x635913, 0xEB); // Cheat protected
|
||||
utils::hook::set<std::uint8_t>(0x6358A5, 0xEB); // Write protected
|
||||
utils::hook::set<std::uint8_t>(0x635974, 0xEB); // Latched
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Nop Dvar_RegisterVariant for sv_cheats in Dvar_Init
|
||||
utils::hook::nop(0x471522, 5);
|
||||
#endif
|
||||
|
||||
// Skip dvar output
|
||||
utils::hook::set<std::uint8_t>(0x4CD2B7, 0xEB);
|
||||
|
||||
// Ignore server dvar change for clients
|
||||
utils::hook::set<std::uint8_t>(0x63580B, 0xEB);
|
||||
|
||||
scheduler::once(
|
||||
[] {
|
||||
game::Dvar_RegisterBool("scr_damageFeedback", true, game::DVAR_NONE,
|
||||
"Show marker when hitting enemies");
|
||||
game::Dvar_RegisterString("connect_ip", "127.0.0.1:28960",
|
||||
game::DVAR_NONE,
|
||||
"Temporary dvar used to connect");
|
||||
},
|
||||
scheduler::pipeline::main);
|
||||
}
|
||||
};
|
||||
} // namespace dvar_patches
|
||||
|
||||
REGISTER_COMPONENT(dvar_patches::component)
|
82
src/client/component/filesystem.cpp
Normal file
82
src/client/component/filesystem.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "game_module.hpp"
|
||||
#include "filesystem.hpp"
|
||||
|
||||
namespace filesystem {
|
||||
namespace {
|
||||
const char* sys_default_install_path_stub() {
|
||||
static auto current_path = std::filesystem::current_path().string();
|
||||
return current_path.data();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::vector<std::string> vectored_file_list(const std::string& path,
|
||||
const std::string& extension) {
|
||||
std::vector<std::string> file_list;
|
||||
|
||||
auto num_files = 0;
|
||||
const auto** files = game::FS_ListFiles(path.data(), extension.data(),
|
||||
game::FS_LIST_ALL, &num_files, 10);
|
||||
|
||||
if (files) {
|
||||
for (auto i = 0; i < num_files; ++i) {
|
||||
if (files[i]) {
|
||||
file_list.emplace_back(files[i]);
|
||||
}
|
||||
}
|
||||
|
||||
game::FS_FreeFileList(files, 10);
|
||||
}
|
||||
|
||||
return file_list;
|
||||
}
|
||||
|
||||
std::string get_binary_directory() {
|
||||
const auto dir = game_module::get_host_module().get_folder();
|
||||
return utils::string::replace(dir, "/", "\\");
|
||||
}
|
||||
|
||||
file::file(std::string name, game::FsThread thread) : name_(std::move(name)) {
|
||||
assert(!this->name_.empty());
|
||||
|
||||
auto handle = 0;
|
||||
const auto len =
|
||||
game::FS_FOpenFileReadForThread(name_.data(), &handle, thread);
|
||||
|
||||
if (!handle) {
|
||||
this->valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto* buf = static_cast<char*>(game::Hunk_AllocateTempMemory(len + 1));
|
||||
game::FS_Read(buf, len, handle);
|
||||
buf[len] = '\0';
|
||||
|
||||
game::FS_FCloseFile(handle);
|
||||
|
||||
this->valid_ = true;
|
||||
this->buffer_.append(buf, len);
|
||||
}
|
||||
|
||||
bool file::exists() const { return this->valid_; }
|
||||
|
||||
const std::string& file::get_buffer() const { return this->buffer_; }
|
||||
|
||||
const std::string& file::get_name() const { return this->name_; }
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
utils::hook(0x465B90, sys_default_install_path_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // Sys_CreateFile
|
||||
}
|
||||
};
|
||||
} // namespace filesystem
|
||||
|
||||
REGISTER_COMPONENT(filesystem::component)
|
21
src/client/component/filesystem.hpp
Normal file
21
src/client/component/filesystem.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
namespace filesystem {
|
||||
std::vector<std::string> vectored_file_list(const std::string& path,
|
||||
const std::string& extension);
|
||||
std::string get_binary_directory();
|
||||
|
||||
class file {
|
||||
public:
|
||||
file(std::string name, game::FsThread thread);
|
||||
|
||||
[[nodiscard]] bool exists() const;
|
||||
[[nodiscard]] const std::string& get_buffer() const;
|
||||
[[nodiscard]] const std::string& get_name() const;
|
||||
|
||||
private:
|
||||
bool valid_ = false;
|
||||
std::string name_;
|
||||
std::string buffer_;
|
||||
};
|
||||
} // namespace filesystem
|
115
src/client/component/game_log.cpp
Normal file
115
src/client/component/game_log.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "gsc/extension.hpp"
|
||||
|
||||
#include "game_log.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
namespace game_log {
|
||||
namespace {
|
||||
int log_file = 0;
|
||||
|
||||
void g_scr_log_print() {
|
||||
char string[1024]{};
|
||||
std::size_t i_string_len = 0;
|
||||
|
||||
const auto i_num_parms = game::Scr_GetNumParam();
|
||||
for (std::size_t i = 0; i < i_num_parms; ++i) {
|
||||
const auto* psz_token = game::Scr_GetString(i);
|
||||
const auto i_token_len = std::strlen(psz_token);
|
||||
|
||||
i_string_len += i_token_len;
|
||||
if (i_string_len >= sizeof(string)) {
|
||||
// Do not overflow the buffer
|
||||
break;
|
||||
}
|
||||
|
||||
strncat_s(string, psz_token, _TRUNCATE);
|
||||
}
|
||||
|
||||
log_printf("%s", string);
|
||||
}
|
||||
|
||||
void g_init_game_stub() {
|
||||
game::Com_Printf(game::CON_CHANNEL_SERVER,
|
||||
"------- Game Initialization -------\n");
|
||||
game::Com_Printf(game::CON_CHANNEL_SERVER, "gamename: iw4-sp\n");
|
||||
game::Com_Printf(game::CON_CHANNEL_SERVER, "gamedate: %s\n", __DATE__);
|
||||
|
||||
if (*dvars::g_log->current.string == '\0') {
|
||||
game::Com_Printf(game::CON_CHANNEL_SERVER, "Not logging to disk.\n");
|
||||
} else {
|
||||
game::FS_FOpenFileByMode(dvars::g_log->current.string, &log_file,
|
||||
game::FS_APPEND_SYNC);
|
||||
|
||||
if (!log_file) {
|
||||
game::Com_PrintWarning(game::CON_CHANNEL_SERVER,
|
||||
"WARNING: Couldn't open logfile: %s\n");
|
||||
} else {
|
||||
log_printf(
|
||||
"------------------------------------------------------------\n");
|
||||
log_printf("InitGame\n");
|
||||
}
|
||||
}
|
||||
|
||||
utils::hook::invoke<void>(0x4FA880); // Vehicle_ClearServerDefs
|
||||
}
|
||||
|
||||
void g_shutdown_game_stub() {
|
||||
if (log_file) {
|
||||
log_printf("ShutdownGame:\n");
|
||||
log_printf(
|
||||
"------------------------------------------------------------\n");
|
||||
|
||||
game::FS_FCloseFile(log_file);
|
||||
log_file = 0;
|
||||
}
|
||||
|
||||
utils::hook::invoke<void>(0x455F90); // Actor_ClearThreatBiasGroups
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void log_printf(const char* fmt, ...) {
|
||||
char string[1024]{};
|
||||
char string2[1024]{};
|
||||
va_list ap;
|
||||
|
||||
if (!log_file) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf_s(string2, _TRUNCATE, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
const auto time = game::level->time / 1000;
|
||||
const auto len = sprintf_s(string, "%3i:%i%i %s", time / 60, time % 60 / 10,
|
||||
time % 60 % 10, string2);
|
||||
game::FS_Write(string, len, log_file);
|
||||
}
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
static_assert(offsetof(game::level_locals_t, time) == 0x34);
|
||||
|
||||
void post_load() override {
|
||||
utils::hook(0x4C75E0, g_init_game_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(0x418B5A, g_shutdown_game_stub, HOOK_CALL).install()->quick();
|
||||
|
||||
scheduler::once(
|
||||
[] {
|
||||
dvars::g_log = game::Dvar_RegisterString(
|
||||
"g_log", "games_sp.log", game::DVAR_NONE, "Log file name");
|
||||
},
|
||||
scheduler::pipeline::main);
|
||||
|
||||
gsc::add_function("logprint", g_scr_log_print);
|
||||
}
|
||||
};
|
||||
} // namespace game_log
|
||||
|
||||
REGISTER_COMPONENT(game_log::component)
|
5
src/client/component/game_log.hpp
Normal file
5
src/client/component/game_log.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace game_log {
|
||||
void log_printf(const char* fmt, ...);
|
||||
}
|
110
src/client/component/game_module.cpp
Normal file
110
src/client/component/game_module.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#ifdef INJECT_HOST_AS_LIB
|
||||
#include <utils/hook.hpp>
|
||||
#endif
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
|
||||
#include "game_module.hpp"
|
||||
|
||||
namespace game_module {
|
||||
#ifdef INJECT_HOST_AS_LIB
|
||||
namespace {
|
||||
utils::hook::detour handle_a_hook;
|
||||
utils::hook::detour handle_w_hook;
|
||||
utils::hook::detour handle_ex_a_hook;
|
||||
utils::hook::detour handle_ex_w_hook;
|
||||
utils::hook::detour file_name_a_hook;
|
||||
utils::hook::detour file_name_w_hook;
|
||||
|
||||
HMODULE WINAPI get_module_handle_a(const LPCSTR module_name) {
|
||||
if (!module_name) {
|
||||
return get_game_module();
|
||||
}
|
||||
|
||||
return handle_a_hook.invoke<HMODULE>(module_name);
|
||||
}
|
||||
|
||||
HMODULE WINAPI get_module_handle_w(const LPWSTR module_name) {
|
||||
if (!module_name) {
|
||||
return get_game_module();
|
||||
}
|
||||
|
||||
return handle_w_hook.invoke<HMODULE>(module_name);
|
||||
}
|
||||
|
||||
BOOL WINAPI get_module_handle_ex_a(const DWORD flags, const LPCSTR module_name,
|
||||
HMODULE* hmodule) {
|
||||
if (!module_name) {
|
||||
*hmodule = get_game_module();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return handle_ex_a_hook.invoke<BOOL>(flags, module_name, hmodule);
|
||||
}
|
||||
|
||||
BOOL WINAPI get_module_handle_ex_w(const DWORD flags, const LPCWSTR module_name,
|
||||
HMODULE* hmodule) {
|
||||
if (!module_name) {
|
||||
*hmodule = get_game_module();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return handle_ex_w_hook.invoke<BOOL>(flags, module_name, hmodule);
|
||||
}
|
||||
|
||||
DWORD WINAPI get_module_file_name_a(HMODULE hmodule, const LPSTR filename,
|
||||
const DWORD size) {
|
||||
if (!hmodule) {
|
||||
hmodule = get_game_module();
|
||||
}
|
||||
|
||||
return file_name_a_hook.invoke<DWORD>(hmodule, filename, size);
|
||||
}
|
||||
|
||||
DWORD WINAPI get_module_file_name_w(HMODULE hmodule, const LPWSTR filename,
|
||||
const DWORD size) {
|
||||
if (!hmodule || utils::nt::library(hmodule) == get_game_module()) {
|
||||
hmodule = get_host_module();
|
||||
}
|
||||
|
||||
return file_name_w_hook.invoke<DWORD>(hmodule, filename, size);
|
||||
}
|
||||
|
||||
void hook_module_resolving() {
|
||||
handle_a_hook.create(&GetModuleHandleA, &get_module_handle_a);
|
||||
handle_w_hook.create(&GetModuleHandleW, &get_module_handle_w);
|
||||
handle_ex_w_hook.create(&GetModuleHandleExA, &get_module_handle_ex_a);
|
||||
handle_ex_w_hook.create(&GetModuleHandleExW, &get_module_handle_ex_w);
|
||||
file_name_a_hook.create(&GetModuleFileNameA, &get_module_file_name_a);
|
||||
file_name_w_hook.create(&GetModuleFileNameW, &get_module_file_name_w);
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
utils::nt::library get_game_module() {
|
||||
static utils::nt::library game{reinterpret_cast<HMODULE>(0x400000)};
|
||||
return game;
|
||||
}
|
||||
|
||||
utils::nt::library get_host_module() {
|
||||
static utils::nt::library host{};
|
||||
return host;
|
||||
}
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_start() override { get_host_module(); }
|
||||
|
||||
void post_load() override {
|
||||
#ifdef INJECT_HOST_AS_LIB
|
||||
hook_module_resolving();
|
||||
#else
|
||||
assert(get_host_module() == get_game_module());
|
||||
#endif
|
||||
}
|
||||
};
|
||||
} // namespace game_module
|
||||
|
||||
REGISTER_COMPONENT(game_module::component)
|
6
src/client/component/game_module.hpp
Normal file
6
src/client/component/game_module.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace game_module {
|
||||
utils::nt::library get_game_module();
|
||||
utils::nt::library get_host_module();
|
||||
} // namespace game_module
|
1206
src/client/component/gsc/error.cpp
Normal file
1206
src/client/component/gsc/error.cpp
Normal file
File diff suppressed because it is too large
Load Diff
126
src/client/component/gsc/extension.cpp
Normal file
126
src/client/component/gsc/extension.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "extension.hpp"
|
||||
|
||||
namespace gsc {
|
||||
#define GSC_DEBUG_FUNCTIONS
|
||||
|
||||
namespace {
|
||||
struct script_function_def {
|
||||
game::BuiltinFunction actionFunc;
|
||||
bool type;
|
||||
};
|
||||
|
||||
struct script_method_def {
|
||||
game::BuiltinMethod actionFunc;
|
||||
bool type;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, script_function_def> custom_scr_funcs;
|
||||
std::unordered_map<std::string, script_method_def> custom_scr_meths;
|
||||
|
||||
game::BuiltinFunction built_in_get_function_stub(const char** p_name,
|
||||
int* type) {
|
||||
if (p_name) {
|
||||
const auto itr = custom_scr_funcs.find(utils::string::to_lower(*p_name));
|
||||
if (itr != custom_scr_funcs.end()) {
|
||||
*type = itr->second.type;
|
||||
return itr->second.actionFunc;
|
||||
}
|
||||
} else {
|
||||
for (const auto& [name, func] : custom_scr_funcs) {
|
||||
game::Scr_RegisterFunction(reinterpret_cast<int>(func.actionFunc),
|
||||
name.data());
|
||||
}
|
||||
}
|
||||
|
||||
// If no function was found let's call BuiltIn_GetFunction
|
||||
return utils::hook::invoke<game::BuiltinFunction>(0x4DD160, p_name, type);
|
||||
}
|
||||
|
||||
game::BuiltinMethod built_in_get_method_stub(const char** p_name, int* type) {
|
||||
if (p_name) {
|
||||
const auto itr = custom_scr_meths.find(utils::string::to_lower(*p_name));
|
||||
if (itr != custom_scr_meths.end()) {
|
||||
*type = itr->second.type;
|
||||
return itr->second.actionFunc;
|
||||
}
|
||||
} else {
|
||||
for (const auto& [name, meth] : custom_scr_meths) {
|
||||
game::Scr_RegisterFunction(reinterpret_cast<int>(meth.actionFunc),
|
||||
name.data());
|
||||
}
|
||||
}
|
||||
|
||||
// If no method was found let's call BuiltIn_GetMethod
|
||||
return utils::hook::invoke<game::BuiltinMethod>(0x5DB850, p_name, type);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void add_function(const char* name, game::BuiltinFunction func, bool type) {
|
||||
script_function_def def;
|
||||
def.actionFunc = func;
|
||||
def.type = type;
|
||||
|
||||
custom_scr_funcs.emplace(utils::string::to_lower(name), def);
|
||||
}
|
||||
|
||||
void add_method(const char* name, game::BuiltinMethod func, bool type) {
|
||||
script_method_def def;
|
||||
def.actionFunc = func;
|
||||
def.type = type;
|
||||
|
||||
custom_scr_meths.emplace(utils::string::to_lower(name), def);
|
||||
}
|
||||
|
||||
class extension final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
// Fetch custom functions
|
||||
utils::hook(0x4ADF9C, built_in_get_function_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // Scr_GetFunction
|
||||
|
||||
utils::hook(0x444827, built_in_get_method_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // Scr_GetMethod
|
||||
|
||||
add_functions();
|
||||
#ifdef GSC_DEBUG_FUNCTIONS
|
||||
add_debug_functions();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void add_functions() {
|
||||
add_function("Float", [] {
|
||||
switch (game::Scr_GetType(0)) {
|
||||
case game::VAR_STRING:
|
||||
game::Scr_AddFloat(
|
||||
static_cast<float>(std::atof(game::Scr_GetString(0))));
|
||||
break;
|
||||
case game::VAR_FLOAT:
|
||||
game::Scr_AddFloat(game::Scr_GetFloat(0));
|
||||
break;
|
||||
case game::VAR_INTEGER:
|
||||
game::Scr_AddFloat(static_cast<float>(game::Scr_GetInt(0)));
|
||||
break;
|
||||
default:
|
||||
game::Scr_ParamError(0, utils::string::va("cannot cast {0} to float",
|
||||
game::Scr_GetTypeName(0)));
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void add_debug_functions() {
|
||||
add_function("AddDebugCommand",
|
||||
[] { game::Cbuf_AddText(0, game::Scr_GetString(0)); });
|
||||
}
|
||||
};
|
||||
} // namespace gsc
|
||||
|
||||
REGISTER_COMPONENT(gsc::extension)
|
7
src/client/component/gsc/extension.hpp
Normal file
7
src/client/component/gsc/extension.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace gsc {
|
||||
void add_function(const char* name, game::BuiltinFunction func,
|
||||
bool type = false);
|
||||
void add_method(const char* name, game::BuiltinMethod func, bool type = false);
|
||||
} // namespace gsc
|
88
src/client/component/gsc/loading.cpp
Normal file
88
src/client/component/gsc/loading.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace gsc {
|
||||
namespace {
|
||||
std::vector<int> main_handles;
|
||||
std::vector<int> init_handles;
|
||||
|
||||
// Do not use C++ objects because Scr_LoadScript may longjmp
|
||||
void g_scr_load_scripts_stub() {
|
||||
// Clear handles (from previous GSC loading session)
|
||||
main_handles.clear();
|
||||
init_handles.clear();
|
||||
|
||||
char path[MAX_PATH]{};
|
||||
|
||||
auto num_files = 0;
|
||||
const auto** files =
|
||||
game::FS_ListFiles("scripts/", "gsc", game::FS_LIST_ALL, &num_files, 10);
|
||||
|
||||
for (auto i = 0; i < num_files; ++i) {
|
||||
const auto* script_file = files[i];
|
||||
game::Com_Printf(game::CON_CHANNEL_SERVER, "Loading script %s...\n",
|
||||
script_file);
|
||||
|
||||
const auto len = sprintf_s(path, "%s/%s", "scripts", script_file);
|
||||
if (len == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scr_LoadScriptInternal will add the '.gsc' suffix so we remove it
|
||||
path[len - 4] = '\0';
|
||||
|
||||
if (!game::Scr_LoadScript(path)) {
|
||||
game::Com_Printf(game::CON_CHANNEL_SERVER,
|
||||
"Script %s encountered an error while loading\n", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
game::Com_Printf(game::CON_CHANNEL_SERVER,
|
||||
"Script %s.gsc loaded successfully\n", path);
|
||||
|
||||
const auto main_handle = game::Scr_GetFunctionHandle(path, "main");
|
||||
if (main_handle) {
|
||||
main_handles.push_back(main_handle);
|
||||
}
|
||||
|
||||
const auto init_handle = game::Scr_GetFunctionHandle(path, "init");
|
||||
if (init_handle) {
|
||||
init_handles.push_back(init_handle);
|
||||
}
|
||||
// Allow scripts with no handles
|
||||
}
|
||||
|
||||
game::FS_FreeFileList(files, 10);
|
||||
}
|
||||
|
||||
void scr_load_level_stub() {
|
||||
for (const auto& handle : main_handles) {
|
||||
const auto id = game::Scr_ExecThread(handle, 0);
|
||||
game::Scr_FreeThread(static_cast<std::uint16_t>(id));
|
||||
}
|
||||
|
||||
utils::hook::invoke<void>(0x470860); // Scr_LoadLevel
|
||||
|
||||
for (const auto& handle : init_handles) {
|
||||
const auto id = game::Scr_ExecThread(handle, 0);
|
||||
game::Scr_FreeThread(static_cast<std::uint16_t>(id));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class loading final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
utils::hook(0x4054C2, g_scr_load_scripts_stub, HOOK_JUMP)
|
||||
.install()
|
||||
->quick();
|
||||
|
||||
utils::hook(0x4C1E34, scr_load_level_stub, HOOK_CALL).install()->quick();
|
||||
}
|
||||
};
|
||||
} // namespace gsc
|
||||
|
||||
REGISTER_COMPONENT(gsc::loading)
|
83
src/client/component/gsc/string.cpp
Normal file
83
src/client/component/gsc/string.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "extension.hpp"
|
||||
|
||||
namespace gsc {
|
||||
namespace {
|
||||
void scr_str_i_cmp() {
|
||||
const auto* string1 = game::SL_ConvertToString(game::Scr_GetConstString(0));
|
||||
const auto* string2 = game::SL_ConvertToString(game::Scr_GetConstString(1));
|
||||
|
||||
game::Scr_AddInt(game::I_stricmp(string1, string2));
|
||||
}
|
||||
|
||||
void scr_is_end_str() {
|
||||
const auto* str = game::Scr_GetString(0);
|
||||
const auto* suffix = game::Scr_GetString(1);
|
||||
|
||||
const auto str_len = std::strlen(str);
|
||||
const auto suffix_len = std::strlen(suffix);
|
||||
|
||||
if (suffix_len > str_len) {
|
||||
game::Scr_AddInt(0);
|
||||
return;
|
||||
}
|
||||
|
||||
game::Scr_AddInt(
|
||||
std::memcmp(str + str_len - suffix_len, suffix, suffix_len) == 0);
|
||||
}
|
||||
|
||||
void scr_to_upper() {
|
||||
const auto script_value = game::Scr_GetConstString(0);
|
||||
const auto* string = game::SL_ConvertToString(script_value);
|
||||
|
||||
char out[1024]{};
|
||||
bool changed = false;
|
||||
|
||||
std::size_t i = 0;
|
||||
while (i < sizeof(out)) {
|
||||
const auto value = *string;
|
||||
const auto result =
|
||||
static_cast<char>(std::toupper(static_cast<unsigned char>(value)));
|
||||
|
||||
out[i] = result;
|
||||
|
||||
if (value != result) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (result == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
++string;
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i >= sizeof(out)) {
|
||||
game::Scr_Error("string too long");
|
||||
return;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
game::Scr_AddString(out);
|
||||
} else {
|
||||
game::SL_AddRefToString(script_value);
|
||||
game::Scr_AddConstString(script_value);
|
||||
game::SL_RemoveRefToString(script_value);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class string final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
add_function("StrICmp", scr_str_i_cmp);
|
||||
add_function("IsEndStr", scr_is_end_str);
|
||||
add_function("ToUpper", scr_to_upper);
|
||||
}
|
||||
};
|
||||
} // namespace gsc
|
||||
|
||||
REGISTER_COMPONENT(gsc::string)
|
16
src/client/component/images.cpp
Normal file
16
src/client/component/images.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace images {
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
// Skip version check
|
||||
utils::hook::set<std::uint8_t>(0x544746, 0xEB);
|
||||
}
|
||||
};
|
||||
} // namespace images
|
||||
|
||||
REGISTER_COMPONENT(images::component)
|
73
src/client/component/lean.cpp
Normal file
73
src/client/component/lean.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
|
||||
namespace lean {
|
||||
namespace {
|
||||
game::kbutton_t in_lean_left;
|
||||
game::kbutton_t in_lean_right;
|
||||
|
||||
void in_lean_left_down() { game::IN_KeyDown(&in_lean_left); }
|
||||
|
||||
void in_lean_left_up() { game::IN_KeyUp(&in_lean_left); }
|
||||
|
||||
void in_lean_right_down() { game::IN_KeyDown(&in_lean_right); }
|
||||
|
||||
void in_lean_right_up() { game::IN_KeyUp(&in_lean_right); }
|
||||
|
||||
void set_lean_flags(game::usercmd_s* cmd) {
|
||||
if (in_lean_left.active || in_lean_left.wasPressed) {
|
||||
cmd->buttons |= game::CMD_BUTTON_LEAN_LEFT;
|
||||
}
|
||||
|
||||
if (in_lean_right.active || in_lean_right.wasPressed) {
|
||||
cmd->buttons |= game::CMD_BUTTON_LEAN_RIGHT;
|
||||
}
|
||||
|
||||
in_lean_left.wasPressed = false;
|
||||
in_lean_right.wasPressed = false;
|
||||
}
|
||||
|
||||
void __declspec(naked) cl_cmd_buttons_stub() {
|
||||
__asm {
|
||||
pushad;
|
||||
|
||||
push esi;
|
||||
call set_lean_flags;
|
||||
add esp, 0x4;
|
||||
|
||||
popad;
|
||||
|
||||
// code skipped by our hook
|
||||
mov eax, ecx;
|
||||
imul eax, eax, 0x21C;
|
||||
|
||||
// CL_CmdButton
|
||||
push 0x57B2B8;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
static_assert(sizeof(game::kbutton_t) == 0x14);
|
||||
static_assert(sizeof(game::usercmd_s) == 0x40);
|
||||
|
||||
void post_load() override {
|
||||
command::add_raw("+leanleft", in_lean_left_down, TRUE);
|
||||
command::add_raw("-leanleft", in_lean_left_up, TRUE);
|
||||
|
||||
command::add_raw("+leanright", in_lean_right_down, TRUE);
|
||||
command::add_raw("-leanright", in_lean_right_up, TRUE);
|
||||
|
||||
utils::hook(0x57B2B0, cl_cmd_buttons_stub, HOOK_JUMP).install()->quick();
|
||||
utils::hook::nop(0x57B2B0 + 5, 3);
|
||||
}
|
||||
};
|
||||
} // namespace lean
|
||||
|
||||
REGISTER_COMPONENT(lean::component)
|
83
src/client/component/network.cpp
Normal file
83
src/client/component/network.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
#include "network.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace network {
|
||||
namespace {
|
||||
void cl_co_op_connect(const command::params& params) {
|
||||
// Valid format is IP Port
|
||||
if (params.size() < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto input = std::strtol(params.get(2), nullptr, 10);
|
||||
const auto port = ::htons(static_cast<std::uint16_t>(input));
|
||||
|
||||
command::execute(
|
||||
utils::string::va("connect_coop {0} {1}", params.get(1), port));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override { patch_sp(); }
|
||||
|
||||
private:
|
||||
static void patch_sp() {
|
||||
// Ignore protocol mismatch
|
||||
utils::hook::set<std::uint8_t>(0x65D0C2, 0xEB);
|
||||
utils::hook::set<std::uint8_t>(0x65D0ED, 0xEB);
|
||||
|
||||
// Disable matchmaking stuff
|
||||
utils::hook::set<std::uint8_t>(0x43BAE0, 0xEB);
|
||||
|
||||
// Ignore 'MAX_PACKET_USERCMDS'
|
||||
utils::hook::set<std::uint8_t>(0x4B1436, 0xEB);
|
||||
|
||||
// Disable IWNet stuff
|
||||
utils::hook::set<std::uint8_t>(0x44E824, 0xEB);
|
||||
utils::hook::set<std::uint8_t>(0x44E808, 0xEB);
|
||||
|
||||
// Disable MP packet handler
|
||||
utils::hook::set<std::uint8_t>(0x65E717, 0xEB);
|
||||
|
||||
// Disable LSP packet handler
|
||||
utils::hook::set<std::uint8_t>(0x65E3A4, 0xEB);
|
||||
|
||||
// Avoid spam
|
||||
utils::hook(0x65E786, game::Com_DPrintf, HOOK_CALL).install()->quick();
|
||||
utils::hook(0x65D659, game::Com_DPrintf, HOOK_CALL).install()->quick();
|
||||
|
||||
// Disable BigShort in NET_AdrToString
|
||||
utils::hook(0x4BF4D1, game::ShortNoSwap, HOOK_CALL).install()->quick();
|
||||
utils::hook(0x4BF50C, game::ShortNoSwap, HOOK_CALL).install()->quick();
|
||||
|
||||
// Fix NET_StringToAdr (Only known workaround that actually works)
|
||||
utils::hook::set<const char*>(0x475417, "connect_coop");
|
||||
command::add("connect", cl_co_op_connect);
|
||||
|
||||
// Disable BigShort in GetLocalAddressForEncryptedConnection
|
||||
utils::hook(0x4CF42D, game::ShortNoSwap, HOOK_CALL).install()->quick();
|
||||
|
||||
// Parse port as unsigned short in Net_AddrToString
|
||||
utils::hook::set<const char*>(0x4BF4F3, "%u.%u.%u.%u:%hu");
|
||||
|
||||
// Force Win socket initialization
|
||||
utils::hook::nop(0x42B649, 2);
|
||||
|
||||
// Kill LSP
|
||||
utils::hook::set<std::uint8_t>(0x4553F0, 0xC3); // Hello
|
||||
utils::hook::set<std::uint8_t>(0x428D00, 0xC3); // LSP_SendLogRequest
|
||||
utils::hook::set<std::uint8_t>(0x4D13C0, 0xC3); // LSP_ParsePacket
|
||||
utils::hook::set<std::uint8_t>(0x66D440, 0xC3); // LSP_AddKeepAlive
|
||||
utils::hook::nop(0x494E68, 5); // Don't create LSP socket
|
||||
}
|
||||
};
|
||||
} // namespace network
|
||||
|
||||
REGISTER_COMPONENT(network::component)
|
5
src/client/component/network.hpp
Normal file
5
src/client/component/network.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace network {
|
||||
using callback = std::function<void(const game::netadr_t&, const std::string&)>;
|
||||
}
|
43
src/client/component/no_border.cpp
Normal file
43
src/client/component/no_border.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace no_border {
|
||||
namespace {
|
||||
void __declspec(naked) window_style_stub() {
|
||||
__asm {
|
||||
push eax;
|
||||
mov eax, dvars::r_noBorder;
|
||||
cmp byte ptr [eax + 0x10], 1;
|
||||
pop eax;
|
||||
|
||||
je remove_border;
|
||||
|
||||
mov ebp, WS_VISIBLE | WS_SYSMENU | WS_CAPTION;
|
||||
jmp safe_return;
|
||||
|
||||
remove_border:
|
||||
mov ebp, WS_VISIBLE | WS_POPUP;
|
||||
|
||||
safe_return:
|
||||
push 0x50C0B8;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
utils::hook(0x50C0B3, window_style_stub, HOOK_JUMP).install()->quick();
|
||||
|
||||
dvars::r_noBorder =
|
||||
game::Dvar_RegisterBool("r_noBorder", false, game::DVAR_ARCHIVE,
|
||||
"Do not use a border in windowed mode");
|
||||
}
|
||||
};
|
||||
} // namespace no_border
|
||||
|
||||
REGISTER_COMPONENT(no_border::component)
|
100
src/client/component/patches.cpp
Normal file
100
src/client/component/patches.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
#include "filesystem.hpp"
|
||||
|
||||
namespace patches {
|
||||
namespace {
|
||||
void sys_init_stub() {
|
||||
game::Cmd_SetAutoComplete("exec", "", "cfg");
|
||||
utils::hook::invoke<void>(0x4EEB20);
|
||||
}
|
||||
|
||||
void cl_play_unskippable_cinematic_f() {
|
||||
// CL_PlayCinematic_f
|
||||
utils::hook::invoke<void>(0x4CC950);
|
||||
*game::cin_skippable = true;
|
||||
}
|
||||
|
||||
void cl_start_multiplayer_f() {
|
||||
utils::nt::update_dll_search_path(filesystem::get_binary_directory());
|
||||
utils::nt::launch_process("iw4mp.exe", "-multiplayer");
|
||||
command::execute("quit", false);
|
||||
}
|
||||
|
||||
const char* live_get_local_client_name_stub() {
|
||||
return game::Dvar_FindVar("name")->current.string;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
utils::hook(0x6042A2, sys_init_stub, HOOK_CALL).install()->quick();
|
||||
|
||||
utils::hook(0x492EF0, live_get_local_client_name_stub, HOOK_JUMP)
|
||||
.install()
|
||||
->quick();
|
||||
|
||||
patch_sp();
|
||||
}
|
||||
|
||||
private:
|
||||
static void patch_sp() {
|
||||
// Force external console
|
||||
utils::hook::nop(0x604071, 21);
|
||||
|
||||
// Remove limit on IWD file loading
|
||||
utils::hook::set<std::uint8_t>(0x630FF3, 0xEB);
|
||||
|
||||
// Ignore XUID match
|
||||
utils::hook::set<std::uint8_t>(0x65D1AF, 0xEB);
|
||||
|
||||
// Remove this particular string from the log file
|
||||
utils::hook::nop(0x6030A6, 5);
|
||||
|
||||
// Config related
|
||||
utils::hook::set<std::uint8_t>(0x4D3FD3, 0xEB);
|
||||
|
||||
// Improper quit popup
|
||||
utils::hook::nop(0x4F5B3A, 2);
|
||||
|
||||
// Remove fs_game check
|
||||
utils::hook::nop(0x612932, 2);
|
||||
|
||||
// Build os path stuff
|
||||
utils::hook::set<std::uint8_t>(0x6300BF, 0xEB);
|
||||
|
||||
// raw -> main
|
||||
utils::hook::set<std::uint32_t>(0x50A0B2, 0x723390);
|
||||
|
||||
// Disable 'replay' output
|
||||
utils::hook::nop(0x65F14A, 5);
|
||||
|
||||
// Disable heartbeat output
|
||||
utils::hook::nop(0x57EF87, 5);
|
||||
|
||||
// Allow intro to be skipped
|
||||
utils::hook::set<void (*)()>(0x47529F, cl_play_unskippable_cinematic_f);
|
||||
|
||||
// Start IW4 MP
|
||||
utils::hook::set<void (*)()>(0x475327, cl_start_multiplayer_f);
|
||||
|
||||
// Enable commandline arguments
|
||||
utils::hook::set<std::uint8_t>(0x453B24, 0xEB);
|
||||
|
||||
// Rename config
|
||||
utils::hook::set<const char*>(0x6040F2, CLIENT_CONFIG);
|
||||
utils::hook::set<const char*>(0x602D38, CLIENT_CONFIG);
|
||||
utils::hook::set<const char*>(0x6037C9, CLIENT_CONFIG);
|
||||
utils::hook::set<const char*>(0x4D3FDB, CLIENT_CONFIG);
|
||||
utils::hook::set<const char*>(0x469DB6, CLIENT_CONFIG);
|
||||
}
|
||||
};
|
||||
} // namespace patches
|
||||
|
||||
REGISTER_COMPONENT(patches::component)
|
245
src/client/component/player_movement.cpp
Normal file
245
src/client/component/player_movement.cpp
Normal file
@ -0,0 +1,245 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "gsc/extension.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace player_movement {
|
||||
namespace {
|
||||
void __declspec(naked) pm_step_slide_move_stub() {
|
||||
__asm {
|
||||
push eax;
|
||||
mov eax, dvars::pm_bounce;
|
||||
cmp byte ptr [eax + 0x10], 1;
|
||||
pop eax;
|
||||
|
||||
je bounce;
|
||||
|
||||
cmp dword ptr [esp + 0x24], 0;
|
||||
jnz no_bounce;
|
||||
|
||||
bounce:
|
||||
push 0x4E905B;
|
||||
ret;
|
||||
|
||||
no_bounce:
|
||||
push 0x4E906F;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
void pm_project_velocity_stub(const float* vel_in, const float* normal,
|
||||
float* vel_out) {
|
||||
const auto length_squared_2d = vel_in[0] * vel_in[0] + vel_in[1] * vel_in[1];
|
||||
|
||||
if (std::fabsf(normal[2]) < 0.001f || length_squared_2d == 0.0f) {
|
||||
vel_out[0] = vel_in[0];
|
||||
vel_out[1] = vel_in[1];
|
||||
vel_out[2] = vel_in[2];
|
||||
return;
|
||||
}
|
||||
|
||||
auto new_z = vel_in[0] * normal[0] + vel_in[1] * normal[1];
|
||||
new_z = -new_z / normal[2];
|
||||
|
||||
const auto length_scale =
|
||||
std::sqrtf((vel_in[2] * vel_in[2] + length_squared_2d) /
|
||||
(new_z * new_z + length_squared_2d));
|
||||
|
||||
if (dvars::pm_bouncingAllAngles->current.enabled ||
|
||||
(length_scale < 1.f || new_z < 0.f || vel_in[2] > 0.f)) {
|
||||
vel_out[0] = vel_in[0] * length_scale;
|
||||
vel_out[1] = vel_in[1] * length_scale;
|
||||
vel_out[2] = new_z * length_scale;
|
||||
}
|
||||
}
|
||||
|
||||
game::gentity_s* weapon_rocket_launcher_fire_stub(
|
||||
game::gentity_s* ent, unsigned int weapon_index, float spread,
|
||||
game::weaponParms* wp, const float* gun_vel,
|
||||
game::lockonFireParms* lock_parms, bool magic_bullet) {
|
||||
auto* result = utils::hook::invoke<game::gentity_s*>(
|
||||
0x443F20, ent, weapon_index, spread, wp, gun_vel, lock_parms,
|
||||
magic_bullet);
|
||||
|
||||
if (ent->client && dvars::pm_rocketJump->current.enabled) {
|
||||
const auto scale = dvars::pm_rocketJumpScale->current.value;
|
||||
ent->client->ps.velocity[0] += (0.0f - wp->forward[0]) * scale;
|
||||
ent->client->ps.velocity[1] += (0.0f - wp->forward[1]) * scale;
|
||||
ent->client->ps.velocity[2] += (0.0f - wp->forward[2]) * scale;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void cm_transformed_capsule_trace_stub(game::trace_t* results,
|
||||
const float* start, const float* end,
|
||||
const game::Bounds* bounds,
|
||||
const game::Bounds* capsule,
|
||||
int contents, const float* origin,
|
||||
const float* angles) {
|
||||
if (dvars::pm_playerCollision->current.enabled) {
|
||||
utils::hook::invoke<void>(0x47C7D0, results, start, end, bounds, capsule,
|
||||
contents, origin, angles);
|
||||
}
|
||||
}
|
||||
|
||||
void pm_trace_stub(game::pmove_t* pm, game::trace_t* results,
|
||||
const float* start, const float* end,
|
||||
const game::Bounds* bounds, int pass_entity_num,
|
||||
int content_mask) {
|
||||
game::PM_trace(pm, results, start, end, bounds, pass_entity_num,
|
||||
content_mask);
|
||||
// Allow the player to stand even when there is no headroom
|
||||
if (dvars::pm_elevators->current.enabled) {
|
||||
results->allsolid = false;
|
||||
}
|
||||
}
|
||||
|
||||
void pm_player_trace_stub(game::pmove_t* pm, game::trace_t* results,
|
||||
const float* start, const float* end,
|
||||
const game::Bounds* bounds, int pass_entity_num,
|
||||
int content_mask) {
|
||||
game::PM_playerTrace(pm, results, start, end, bounds, pass_entity_num,
|
||||
content_mask);
|
||||
|
||||
if (dvars::pm_elevators->current.enabled) {
|
||||
results->startsolid = false;
|
||||
}
|
||||
}
|
||||
|
||||
void pm_crash_land_stub(const float* v, float scale, const float* result) {
|
||||
if (!dvars::pm_disableLandingSlowdown->current.enabled) {
|
||||
game::Vec3Scale(v, scale, result);
|
||||
}
|
||||
}
|
||||
|
||||
void __declspec(naked) jump_check_stub() {
|
||||
__asm {
|
||||
push eax;
|
||||
mov eax, dvars::pm_bunnyHop;
|
||||
cmp byte ptr [eax + 0x10], 1;
|
||||
pop eax;
|
||||
|
||||
je auto_hop;
|
||||
|
||||
// Game's code
|
||||
test dword ptr [ebx + 0x48], 0x400;
|
||||
push 0x4D25EF;
|
||||
ret;
|
||||
|
||||
auto_hop:
|
||||
push 0x4D25FE;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
void __declspec(naked) p_move_single_stub() {
|
||||
__asm {
|
||||
push eax;
|
||||
mov eax, dvars::pm_snapVector;
|
||||
cmp byte ptr [eax + 0x10], 1;
|
||||
pop eax;
|
||||
|
||||
jnz skip;
|
||||
|
||||
lea ebp, [ebp + 0x28]; // velocity
|
||||
push ebp;
|
||||
call game::Sys_SnapVector;
|
||||
add esp, 0x4;
|
||||
|
||||
skip:
|
||||
|
||||
// Game's code
|
||||
pop edi;
|
||||
pop esi;
|
||||
pop ebp;
|
||||
pop ebx;
|
||||
add esp, 0x90;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
void g_scr_is_sprinting(const game::scr_entref_t entref) {
|
||||
const auto* client = game::GetEntity(entref)->client;
|
||||
if (!client) {
|
||||
game::Scr_Error("IsSprinting can only be called on a player");
|
||||
return;
|
||||
}
|
||||
|
||||
game::Scr_AddInt(game::PM_IsSprinting(&client->ps));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
static_assert(sizeof(game::weaponParms) == 0x48);
|
||||
static_assert(sizeof(game::lockonFireParms) == 0x18);
|
||||
|
||||
void post_load() override {
|
||||
utils::hook(0x4E9054, pm_step_slide_move_stub, HOOK_JUMP)
|
||||
.install()
|
||||
->quick(); // PM_StepSlideMove
|
||||
utils::hook(0x4E90BE, pm_project_velocity_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // PM_StepSlideMove
|
||||
|
||||
utils::hook(0x4FA809, weapon_rocket_launcher_fire_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // FireWeapon
|
||||
|
||||
utils::hook(0x4B6FC0, cm_transformed_capsule_trace_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // SV_ClipMoveToEntity
|
||||
utils::hook(0x57635F, cm_transformed_capsule_trace_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // CG_ClipMoveToEntity
|
||||
|
||||
utils::hook(0x64F439, pm_trace_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // PM_CheckDuck
|
||||
utils::hook(0x651A60, pm_player_trace_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // PM_CorrectAllSolid
|
||||
utils::hook(0x651AF2, pm_player_trace_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // PM_CorrectAllSolid
|
||||
|
||||
utils::hook(0x64E571, pm_crash_land_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // Vec3Scale
|
||||
utils::hook(0x4D25E8, jump_check_stub, HOOK_JUMP).install()->quick();
|
||||
|
||||
utils::hook(0x6530C3, p_move_single_stub, HOOK_JUMP).install()->quick();
|
||||
|
||||
gsc::add_method("IsSprinting", g_scr_is_sprinting);
|
||||
register_dvars();
|
||||
}
|
||||
|
||||
static void register_dvars() {
|
||||
// clang-format off
|
||||
dvars::pm_bounce = game::Dvar_RegisterBool(
|
||||
"pm_bounce", false, game::DVAR_NONE, "CoD4 Bounces");
|
||||
dvars::pm_bouncingAllAngles = game::Dvar_RegisterBool(
|
||||
"pm_bouncingAllAngles", false, game::DVAR_NONE, "Enable bouncing from all angles");
|
||||
dvars::pm_rocketJump = game::Dvar_RegisterBool(
|
||||
"pm_rocketJump", true, game::DVAR_NONE, "CoD4 rocket jumps");
|
||||
dvars::pm_rocketJumpScale = game::Dvar_RegisterFloat(
|
||||
"pm_rocketJumpScale", 64.0f, 0.0f, 1024.0f, game::DVAR_NONE, "");
|
||||
dvars::pm_playerCollision = game::Dvar_RegisterBool(
|
||||
"pm_playerCollision", true, game::DVAR_NONE, "Push intersecting players away from each other");
|
||||
dvars::pm_elevators = game::Dvar_RegisterBool(
|
||||
"pm_elevators", false, game::DVAR_NONE, "CoD4 elevators");
|
||||
dvars::pm_disableLandingSlowdown = game::Dvar_RegisterBool("pm_disableLandingSlowdown",
|
||||
false, game::DVAR_NONE, "Toggle landing slowdown");
|
||||
dvars::pm_bunnyHop = game::Dvar_RegisterBool("pm_bunnyHop",
|
||||
false, game::DVAR_NONE, "Constantly jump when holding space");
|
||||
dvars::pm_snapVector = game::Dvar_RegisterBool("pm_snapVector", false, game::DVAR_NONE, "Snap velocity");
|
||||
// clang-format on
|
||||
}
|
||||
};
|
||||
} // namespace player_movement
|
||||
|
||||
REGISTER_COMPONENT(player_movement::component)
|
163
src/client/component/scheduler.cpp
Normal file
163
src/client/component/scheduler.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/concurrency.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/thread.hpp>
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
namespace scheduler {
|
||||
namespace {
|
||||
struct task {
|
||||
std::function<bool()> handler{};
|
||||
std::chrono::milliseconds interval{};
|
||||
std::chrono::high_resolution_clock::time_point last_call{};
|
||||
};
|
||||
|
||||
using task_list = std::vector<task>;
|
||||
|
||||
class task_pipeline {
|
||||
public:
|
||||
void add(task&& task) {
|
||||
new_callbacks_.access([&task, this](task_list& tasks) {
|
||||
tasks.emplace_back(std::move(task));
|
||||
});
|
||||
}
|
||||
|
||||
void clear() {
|
||||
callbacks_.access([&](task_list& tasks) {
|
||||
this->merge_callbacks();
|
||||
tasks.clear();
|
||||
});
|
||||
}
|
||||
|
||||
void execute() {
|
||||
callbacks_.access([&](task_list& tasks) {
|
||||
this->merge_callbacks();
|
||||
|
||||
for (auto i = tasks.begin(); i != tasks.end();) {
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
const auto diff = now - i->last_call;
|
||||
|
||||
if (diff < i->interval) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
i->last_call = now;
|
||||
|
||||
const auto res = i->handler();
|
||||
if (res == cond_end) {
|
||||
i = tasks.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
utils::concurrency::container<task_list> new_callbacks_;
|
||||
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
||||
|
||||
void merge_callbacks() {
|
||||
callbacks_.access([&](task_list& tasks) {
|
||||
new_callbacks_.access([&](task_list& new_tasks) {
|
||||
tasks.insert(tasks.end(),
|
||||
std::move_iterator<task_list::iterator>(new_tasks.begin()),
|
||||
std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||
new_tasks = {};
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
volatile bool kill = false;
|
||||
std::thread thread;
|
||||
task_pipeline pipelines[pipeline::count];
|
||||
|
||||
void execute(const pipeline type) {
|
||||
assert(type >= 0 && type < pipeline::count);
|
||||
pipelines[type].execute();
|
||||
}
|
||||
|
||||
void g_glass_update_stub() {
|
||||
utils::hook::invoke<void>(0x43D7E0);
|
||||
execute(pipeline::server);
|
||||
}
|
||||
|
||||
void r_end_frame_stub() {
|
||||
utils::hook::invoke<void>(0x50F540);
|
||||
execute(pipeline::renderer);
|
||||
}
|
||||
|
||||
void main_frame_stub() {
|
||||
utils::hook::invoke<void>(0x4D3FC0);
|
||||
execute(pipeline::main);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void clear_tasks(const pipeline type) { return pipelines[type].clear(); }
|
||||
|
||||
void schedule(const std::function<bool()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay) {
|
||||
assert(type >= 0 && type < pipeline::count);
|
||||
|
||||
task task;
|
||||
task.handler = callback;
|
||||
task.interval = delay;
|
||||
task.last_call = std::chrono::high_resolution_clock::now();
|
||||
|
||||
pipelines[type].add(std::move(task));
|
||||
}
|
||||
|
||||
void loop(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay) {
|
||||
schedule(
|
||||
[callback]() {
|
||||
callback();
|
||||
return cond_continue;
|
||||
},
|
||||
type, delay);
|
||||
}
|
||||
|
||||
void once(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay) {
|
||||
schedule(
|
||||
[callback]() {
|
||||
callback();
|
||||
return cond_end;
|
||||
},
|
||||
type, delay);
|
||||
}
|
||||
|
||||
unsigned int thread_id;
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_start() override {
|
||||
thread = utils::thread::create_named_thread("Async Scheduler", [] {
|
||||
while (!kill) {
|
||||
execute(pipeline::async);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void post_load() override {
|
||||
utils::hook(0x4EDBDD, g_glass_update_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(0x49C3AF, main_frame_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(0x57DB13, r_end_frame_stub, HOOK_CALL).install()->quick();
|
||||
}
|
||||
|
||||
void pre_destroy() override {
|
||||
kill = true;
|
||||
if (thread.joinable()) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace scheduler
|
||||
|
||||
REGISTER_COMPONENT(scheduler::component)
|
33
src/client/component/scheduler.hpp
Normal file
33
src/client/component/scheduler.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace scheduler {
|
||||
enum pipeline {
|
||||
// Asynchronuous pipeline, disconnected from the game
|
||||
async = 0,
|
||||
|
||||
// The game's rendering pipeline
|
||||
renderer,
|
||||
|
||||
// The game's server thread
|
||||
server,
|
||||
|
||||
// The game's main thread
|
||||
main,
|
||||
|
||||
count,
|
||||
};
|
||||
|
||||
static constexpr auto cond_continue = false;
|
||||
static constexpr auto cond_end = true;
|
||||
|
||||
void clear_tasks(pipeline type);
|
||||
|
||||
void schedule(const std::function<bool()>& callback, pipeline type,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
void loop(const std::function<void()>& callback, pipeline type,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
void once(const std::function<void()>& callback, pipeline type,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
} // namespace scheduler
|
47
src/client/component/steam_patches.cpp
Normal file
47
src/client/component/steam_patches.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace steam_patches {
|
||||
namespace {
|
||||
void __declspec(naked) steam_init() {
|
||||
const static DWORD Steam_IsClientSignedInLocally_t = 0x4293F0;
|
||||
|
||||
__asm {
|
||||
call Steam_IsClientSignedInLocally_t;
|
||||
test al, al;
|
||||
jz return_safe;
|
||||
|
||||
push 0x43FAF9;
|
||||
ret;
|
||||
|
||||
return_safe:
|
||||
mov al, 1;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
patch_sp();
|
||||
utils::hook(0x43FAF0, steam_init, HOOK_JUMP).install()->quick();
|
||||
}
|
||||
|
||||
static void patch_sp() {
|
||||
// Prevent stat loading from steam
|
||||
utils::hook::set<std::uint8_t>(0x43FB33, 0xC3);
|
||||
|
||||
// Steam must be running
|
||||
utils::hook::nop(0x6040A3, 30);
|
||||
|
||||
// No-Steam
|
||||
utils::hook::nop(0x4E9458, 7);
|
||||
utils::hook::nop(0x4E9470, 7);
|
||||
}
|
||||
};
|
||||
} // namespace steam_patches
|
||||
|
||||
REGISTER_COMPONENT(steam_patches::component)
|
167
src/client/component/steam_proxy.cpp
Normal file
167
src/client/component/steam_proxy.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/binary_resource.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include <resource.hpp>
|
||||
|
||||
#include "steam/steam.hpp"
|
||||
#include "steam/interface.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
namespace {
|
||||
utils::binary_resource runner_file(RUNNER, "iw4sp-runner.exe");
|
||||
} // namespace
|
||||
|
||||
class steam_proxy final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
this->load_client();
|
||||
this->clean_up_on_error();
|
||||
|
||||
try {
|
||||
this->start_mod("iw4-sp singleplayer", 10180);
|
||||
} catch (const std::exception& ex) {
|
||||
printf("Steam: %s\n", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
void pre_destroy() override {
|
||||
if (this->steam_client_module_) {
|
||||
if (this->steam_pipe_) {
|
||||
if (this->global_user_) {
|
||||
this->steam_client_module_.invoke<void>(
|
||||
"Steam_ReleaseUser", this->steam_pipe_, this->global_user_);
|
||||
}
|
||||
|
||||
this->steam_client_module_.invoke<bool>("Steam_BReleaseSteamPipe",
|
||||
this->steam_pipe_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
utils::nt::library steam_client_module_{};
|
||||
|
||||
steam::interface client_engine_{};
|
||||
steam::interface client_user_{};
|
||||
steam::interface client_utils_{};
|
||||
|
||||
void* steam_pipe_ = nullptr;
|
||||
void* global_user_ = nullptr;
|
||||
|
||||
[[nodiscard]] void* load_client_engine() const {
|
||||
if (!this->steam_client_module_)
|
||||
return nullptr;
|
||||
|
||||
for (auto i = 1; i < 1000; ++i) {
|
||||
const auto* name =
|
||||
utils::string::va("CLIENTENGINE_INTERFACE_VERSION{0:03}", i);
|
||||
|
||||
auto* const client_engine = this->steam_client_module_.invoke<void*>(
|
||||
"CreateInterface", name, nullptr);
|
||||
if (client_engine)
|
||||
return client_engine;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void load_client() {
|
||||
const auto steam_path = steam::get_steam_install_directory();
|
||||
if (steam_path.empty())
|
||||
return;
|
||||
|
||||
utils::nt::library::load(steam_path / "tier0_s.dll");
|
||||
utils::nt::library::load(steam_path / "vstdlib_s.dll");
|
||||
this->steam_client_module_ =
|
||||
utils::nt::library::load(steam_path / "steamclient.dll");
|
||||
if (!this->steam_client_module_)
|
||||
return;
|
||||
|
||||
this->client_engine_ = load_client_engine();
|
||||
if (!this->client_engine_)
|
||||
return;
|
||||
|
||||
this->steam_pipe_ =
|
||||
this->steam_client_module_.invoke<void*>("Steam_CreateSteamPipe");
|
||||
this->global_user_ = this->steam_client_module_.invoke<void*>(
|
||||
"Steam_ConnectToGlobalUser", this->steam_pipe_);
|
||||
this->client_user_ = this->client_engine_.invoke<void*>(
|
||||
8, this->steam_pipe_, this->global_user_); // GetIClientUser
|
||||
this->client_utils_ = this->client_engine_.invoke<void*>(
|
||||
14, this->steam_pipe_); // GetIClientUtils
|
||||
}
|
||||
|
||||
void start_mod(const std::string& title, const std::size_t app_id) {
|
||||
__try {
|
||||
this->start_mod_unsafe(title, app_id);
|
||||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
this->do_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void start_mod_unsafe(const std::string& title, std::size_t app_id) {
|
||||
if (!this->client_utils_ || !this->client_user_)
|
||||
return;
|
||||
|
||||
if (!this->client_user_.invoke<bool>("BIsSubscribedApp", app_id)) {
|
||||
app_id = 480; // Spacewar
|
||||
}
|
||||
|
||||
this->client_utils_.invoke<void>("SetAppIDForCurrentPipe", app_id, false);
|
||||
|
||||
char our_directory[MAX_PATH]{};
|
||||
GetCurrentDirectoryA(sizeof(our_directory), our_directory);
|
||||
|
||||
const auto path = runner_file.get_extracted_file();
|
||||
const auto* cmd_line =
|
||||
utils::string::va("\"{0}\" -proc {1}", path, GetCurrentProcessId());
|
||||
|
||||
game_id game_id;
|
||||
game_id.raw.type = 1; // k_EGameIDTypeGameMod
|
||||
game_id.raw.app_id = app_id & 0xFFFFFF;
|
||||
|
||||
const auto* mod_id = "IW4";
|
||||
game_id.raw.mod_id =
|
||||
*reinterpret_cast<const unsigned int*>(mod_id) | 0x80000000;
|
||||
|
||||
this->client_user_.invoke<bool>("SpawnProcess", path.data(), cmd_line,
|
||||
our_directory, game_id.bits, title.data(),
|
||||
app_id, 0, 0, 0);
|
||||
}
|
||||
|
||||
void do_cleanup() {
|
||||
this->client_engine_ = nullptr;
|
||||
this->client_user_ = nullptr;
|
||||
this->client_utils_ = nullptr;
|
||||
|
||||
this->steam_pipe_ = nullptr;
|
||||
this->global_user_ = nullptr;
|
||||
|
||||
this->steam_client_module_ = utils::nt::library{nullptr};
|
||||
}
|
||||
|
||||
void clean_up_on_error() {
|
||||
scheduler::schedule(
|
||||
[this] {
|
||||
if (this->steam_client_module_ && this->steam_pipe_ &&
|
||||
this->global_user_ &&
|
||||
this->steam_client_module_.invoke<bool>(
|
||||
"Steam_BConnected", this->global_user_, this->steam_pipe_) &&
|
||||
this->steam_client_module_.invoke<bool>(
|
||||
"Steam_BLoggedOn", this->global_user_, this->steam_pipe_)) {
|
||||
return scheduler::cond_continue;
|
||||
}
|
||||
|
||||
this->do_cleanup();
|
||||
return scheduler::cond_end;
|
||||
},
|
||||
scheduler::pipeline::async);
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT(steam_proxy)
|
629
src/client/component/ui.cpp
Normal file
629
src/client/component/ui.cpp
Normal file
@ -0,0 +1,629 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "botlib/l_precomp.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace ui {
|
||||
namespace {
|
||||
#define ITEM_TYPE_LISTBOX 6 // scrollable list
|
||||
#define ITEM_TYPE_MULTI 12 // multiple list setting, enumerated
|
||||
|
||||
#define PARSE_FLOAT_TOKEN(name) \
|
||||
if (!game::I_stricmp(token.string, #name)) { \
|
||||
if (!game::PC_Float_Parse(handle, &g_load.loadAssets.##name##)) { \
|
||||
return false; \
|
||||
} \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define PARSE_INT_TOKEN(name) \
|
||||
if (!game::I_stricmp(token.string, #name)) { \
|
||||
if (!game::PC_Int_Parse(handle, &g_load.loadAssets.##name##)) { \
|
||||
return false; \
|
||||
} \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define FREE_STATEMENT(statement) \
|
||||
{ \
|
||||
if ((statement)) { \
|
||||
menu_free_expression_supporting_data((statement)->supportingData); \
|
||||
} \
|
||||
game::free_expression((statement)); \
|
||||
}
|
||||
|
||||
template <typename T, int N, int M> struct KeywordHashEntry {
|
||||
const char* keyword;
|
||||
int (*func)(T*, int);
|
||||
};
|
||||
|
||||
KeywordHashEntry<game::menuDef_t, 128, 3523>** menu_parse_keyword_hash;
|
||||
|
||||
std::vector<game::menuDef_t*> loaded_menus_list;
|
||||
|
||||
struct {
|
||||
game::loadAssets_t loadAssets;
|
||||
game::MenuList menuList;
|
||||
game::itemDef_s* items[512];
|
||||
game::menuDef_t* menus[512];
|
||||
} g_load;
|
||||
|
||||
char menu_buf[0x8000];
|
||||
|
||||
int asset_parse(int handle) {
|
||||
game::pc_token_s token{};
|
||||
|
||||
if (!game::PC_ReadTokenHandle(handle, &token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (game::I_stricmp(token.string, "{") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (!game::PC_ReadTokenHandle(handle, &token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!game::I_stricmp(token.string, ";")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!game::I_stricmp(token.string, "}")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PARSE_FLOAT_TOKEN(fadeClamp);
|
||||
PARSE_INT_TOKEN(fadeCycle);
|
||||
PARSE_FLOAT_TOKEN(fadeAmount);
|
||||
PARSE_FLOAT_TOKEN(fadeInAmount);
|
||||
|
||||
game::PC_SourceError(
|
||||
handle,
|
||||
"Unknown token %s in assetGlobalDef. Valid commands are 'fadeClamp', "
|
||||
"'fadeCycle', 'fadeAmount', and 'fadeInAmount'\n",
|
||||
token.string);
|
||||
}
|
||||
}
|
||||
|
||||
void* alloc(int size, int alignment) {
|
||||
return game::Hunk_AllocAlignInternal(size, alignment);
|
||||
}
|
||||
|
||||
template <int HASH_COUNT, int HASH_SEED>
|
||||
int keyword_hash_key(const char* keyword) {
|
||||
auto hash = 0;
|
||||
for (auto i = 0; keyword[i]; ++i) {
|
||||
hash +=
|
||||
(i + HASH_SEED) * std::tolower(static_cast<unsigned char>(keyword[i]));
|
||||
}
|
||||
return (hash + (hash >> 8)) & (128 - 1);
|
||||
}
|
||||
|
||||
template <typename T, int N, int M>
|
||||
KeywordHashEntry<T, N, M>* keyword_hash_find(KeywordHashEntry<T, N, M>** table,
|
||||
const char* keyword) {
|
||||
auto hash = keyword_hash_key<N, M>(keyword);
|
||||
KeywordHashEntry<T, N, M>* key = table[hash];
|
||||
if (key && !game::I_stricmp(key->keyword, keyword)) {
|
||||
return key;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int menu_parse(int handle, game::menuDef_t* menu) {
|
||||
game::pc_token_s token{};
|
||||
|
||||
if (!game::PC_ReadTokenHandle(handle, &token))
|
||||
return false;
|
||||
|
||||
if (*token.string != '{')
|
||||
return false;
|
||||
|
||||
while (true) {
|
||||
std::memset(&token, 0, sizeof(game::pc_token_s));
|
||||
|
||||
if (!game::PC_ReadTokenHandle(handle, &token)) {
|
||||
game::PC_SourceError(handle, "end of file inside menu\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*token.string == '}') {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* key = keyword_hash_find(menu_parse_keyword_hash, token.string);
|
||||
if (!key) {
|
||||
game::PC_SourceError(handle, "unknown menu keyword %s", token.string);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!key->func(menu, handle)) {
|
||||
game::PC_SourceError(handle, "couldn't parse menu keyword %s",
|
||||
token.string);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void item_set_screen_coords([[maybe_unused]] int context_index,
|
||||
game::itemDef_s* item, float x, float y,
|
||||
int horz_align, int vert_align) {
|
||||
assert(item);
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->window.border) {
|
||||
x += item->window.borderSize;
|
||||
y += item->window.borderSize;
|
||||
}
|
||||
|
||||
item->window.rect = item->window.rectClient;
|
||||
item->window.rect.x = item->window.rect.x + x;
|
||||
item->window.rect.y = item->window.rect.y + y;
|
||||
|
||||
if (!item->window.rect.horzAlign && !item->window.rect.vertAlign) {
|
||||
item->window.rect.horzAlign = static_cast<char>(horz_align);
|
||||
item->window.rect.vertAlign = static_cast<char>(vert_align);
|
||||
}
|
||||
}
|
||||
|
||||
game::rectDef_s* window_get_rect(game::windowDef_t* w) {
|
||||
assert(w);
|
||||
return &w->rect;
|
||||
}
|
||||
|
||||
void menu_update_position(int context_index, game::menuDef_t* menu) {
|
||||
if (menu == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* rect = window_get_rect(&menu->window);
|
||||
float x = rect->x;
|
||||
float y = rect->y;
|
||||
if (menu->window.border) {
|
||||
x += menu->window.borderSize;
|
||||
y += menu->window.borderSize;
|
||||
}
|
||||
|
||||
for (int i = 0; i < menu->itemCount; ++i) {
|
||||
item_set_screen_coords(context_index, menu->items[i], x, y, rect->horzAlign,
|
||||
rect->vertAlign);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_post_parse(game::menuDef_t* menu) {
|
||||
assert(menu);
|
||||
|
||||
const auto item_count = 4 * menu->itemCount;
|
||||
menu->items = static_cast<game::itemDef_s**>(alloc(item_count, 4));
|
||||
std::memcpy(menu->items, g_load.items, item_count);
|
||||
|
||||
if (menu->fullScreen) {
|
||||
menu->window.rect.x = 0.0f;
|
||||
menu->window.rect.y = 0.0f;
|
||||
menu->window.rect.w = 640.0f;
|
||||
menu->window.rect.h = 480.0f;
|
||||
}
|
||||
|
||||
menu_update_position(0, menu);
|
||||
}
|
||||
|
||||
void menu_set_cursor_item(int context_index, game::menuDef_t* menu,
|
||||
int cursor_item) {
|
||||
assert(context_index < game::MAX_POSSIBLE_LOCAL_CLIENTS);
|
||||
assert(menu);
|
||||
menu->cursorItem[context_index] = cursor_item;
|
||||
}
|
||||
|
||||
void window_init(game::windowDef_t* w) {
|
||||
std::memset(w, 0, sizeof(game::windowDef_t));
|
||||
w->borderSize = 1.0f;
|
||||
w->foreColor[0] = w->foreColor[1] = w->foreColor[2] = w->foreColor[3] = 1.0f;
|
||||
}
|
||||
|
||||
void menu_init(game::menuDef_t* menu) {
|
||||
std::memset(menu, 0, sizeof(game::menuDef_t));
|
||||
menu_set_cursor_item(0, menu, -1);
|
||||
|
||||
menu->fadeAmount = g_load.loadAssets.fadeAmount;
|
||||
menu->fadeInAmount = g_load.loadAssets.fadeInAmount;
|
||||
menu->fadeClamp = g_load.loadAssets.fadeClamp;
|
||||
menu->fadeCycle = g_load.loadAssets.fadeCycle;
|
||||
menu->items = g_load.items;
|
||||
|
||||
window_init(&menu->window);
|
||||
}
|
||||
|
||||
void menu_free_expression_supporting_data(
|
||||
game::ExpressionSupportingData* data) {
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < data->uifunctions.totalFunctions; ++i) {
|
||||
auto* function = data->uifunctions.functions[i];
|
||||
FREE_STATEMENT(function);
|
||||
}
|
||||
|
||||
data->uifunctions.totalFunctions = 0;
|
||||
}
|
||||
|
||||
void menu_free_event_handler(game::MenuEventHandlerSet* event_handler) {
|
||||
if (!event_handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < event_handler->eventHandlerCount; ++i) {
|
||||
auto* event = event_handler->eventHandlers[i];
|
||||
|
||||
game::ConditionalScript* conditional_script;
|
||||
game::MenuEventHandlerSet* else_script;
|
||||
game::SetLocalVarData* local_var;
|
||||
|
||||
switch (event->eventType) {
|
||||
case game::EVENT_IF:
|
||||
conditional_script = event->eventData.conditionalScript;
|
||||
menu_free_event_handler(conditional_script->eventHandlerSet);
|
||||
FREE_STATEMENT(conditional_script->eventExpression);
|
||||
break;
|
||||
case game::EVENT_ELSE:
|
||||
else_script = event->eventData.elseScript;
|
||||
menu_free_event_handler(else_script);
|
||||
break;
|
||||
case game::EVENT_SET_LOCAL_VAR_BOOL:
|
||||
case game::EVENT_SET_LOCAL_VAR_INT:
|
||||
case game::EVENT_SET_LOCAL_VAR_FLOAT:
|
||||
case game::EVENT_SET_LOCAL_VAR_STRING:
|
||||
local_var = event->eventData.setLocalVarData;
|
||||
FREE_STATEMENT(local_var->expression);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event_handler->eventHandlerCount = 0;
|
||||
}
|
||||
|
||||
void menu_free_item_key_handler(const game::ItemKeyHandler* key_handler) {
|
||||
while (key_handler) {
|
||||
menu_free_event_handler(key_handler->action);
|
||||
key_handler = key_handler->next;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_free_memory(game::menuDef_t* menu) {
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < menu->itemCount; ++i) {
|
||||
auto* item = menu->items[i];
|
||||
|
||||
FREE_STATEMENT(item->visibleExp);
|
||||
FREE_STATEMENT(item->disabledExp);
|
||||
FREE_STATEMENT(item->textExp);
|
||||
FREE_STATEMENT(item->materialExp);
|
||||
|
||||
menu_free_event_handler(item->mouseEnterText);
|
||||
menu_free_event_handler(item->mouseExitText);
|
||||
menu_free_event_handler(item->mouseEnter);
|
||||
menu_free_event_handler(item->mouseExit);
|
||||
menu_free_event_handler(item->action);
|
||||
menu_free_event_handler(item->accept);
|
||||
menu_free_event_handler(item->onFocus);
|
||||
menu_free_event_handler(item->leaveFocus);
|
||||
|
||||
menu_free_item_key_handler(item->onKey);
|
||||
|
||||
// free ItemFloatExpression*
|
||||
game::Menu_FreeItemMemory(item);
|
||||
|
||||
if (item->dataType == ITEM_TYPE_LISTBOX) {
|
||||
menu_free_event_handler(item->typeData.listBox->onDoubleClick);
|
||||
}
|
||||
}
|
||||
|
||||
menu->itemCount = 0;
|
||||
|
||||
menu_free_event_handler(menu->onOpen);
|
||||
menu_free_event_handler(menu->onCloseRequest);
|
||||
menu_free_event_handler(menu->onClose);
|
||||
menu_free_event_handler(menu->onESC);
|
||||
|
||||
menu_free_item_key_handler(menu->onKey);
|
||||
|
||||
FREE_STATEMENT(menu->visibleExp);
|
||||
FREE_STATEMENT(menu->rectXExp);
|
||||
FREE_STATEMENT(menu->rectYExp);
|
||||
FREE_STATEMENT(menu->rectWExp);
|
||||
FREE_STATEMENT(menu->rectHExp);
|
||||
FREE_STATEMENT(menu->openSoundExp);
|
||||
FREE_STATEMENT(menu->closeSoundExp);
|
||||
|
||||
// Our menu compiler code does not support parsing 'ui functions'
|
||||
if (!menu->expressionData) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < menu->expressionData->uifunctions.totalFunctions; ++i) {
|
||||
auto* function_statement = menu->expressionData->uifunctions.functions[i];
|
||||
FREE_STATEMENT(function_statement);
|
||||
}
|
||||
|
||||
menu->expressionData->uifunctions.totalFunctions = 0;
|
||||
}
|
||||
|
||||
void menus_free_all_memory(game::UiContext* dc) {
|
||||
for (auto menu = 0; menu < dc->menuCount; ++menu) {
|
||||
menu_free_memory(dc->Menus[menu]);
|
||||
}
|
||||
}
|
||||
|
||||
bool menu_new(int handle) {
|
||||
auto* menu = static_cast<game::menuDef_t*>(alloc(sizeof(game::menuDef_t), 4));
|
||||
menu_init(menu);
|
||||
|
||||
if (!menu_parse(handle, menu)) {
|
||||
menu_free_memory(menu);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!menu->window.name) {
|
||||
game::PC_SourceError(handle, "menu has no name");
|
||||
menu_free_memory(menu);
|
||||
return false;
|
||||
}
|
||||
|
||||
menu_post_parse(menu);
|
||||
if (static_cast<std::size_t>(g_load.menuList.menuCount) >=
|
||||
std::extent_v<decltype(g_load.menus)>) {
|
||||
game::Com_Error(game::ERR_DROP, "\x15"
|
||||
"Menu_New: "
|
||||
"\x14"
|
||||
"EXE_ERR_OUT_OF_MEMORY");
|
||||
}
|
||||
|
||||
g_load.menuList.menus[g_load.menuList.menuCount++] = menu;
|
||||
|
||||
loaded_menus_list.emplace_back(menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_menu_internal(const char* menu_file) {
|
||||
const char* builtin_defines[2];
|
||||
game::pc_token_s token;
|
||||
|
||||
builtin_defines[0] = "PC";
|
||||
builtin_defines[1] = nullptr;
|
||||
|
||||
game::Com_Printf(game::CON_CHANNEL_UI, "\tLoading '%s'...\n", menu_file);
|
||||
|
||||
const auto handle = pc::load_source_handle(menu_file, builtin_defines);
|
||||
if (!handle) {
|
||||
game::Com_PrintError(game::CON_CHANNEL_UI, "Couldn't find menu file '%s'\n",
|
||||
menu_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
while (game::PC_ReadTokenHandle(handle, &token)) {
|
||||
if (!game::I_stricmp(token.string, "}") ||
|
||||
!game::I_stricmp(token.string, "{")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!game::I_stricmp(token.string, "assetGlobalDef")) {
|
||||
asset_parse(handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!game::I_stricmp(token.string, "menudef")) {
|
||||
menu_new(handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
game::PC_SourceError(handle,
|
||||
"Unknown token %s in menu file. Expected \"menudef\" "
|
||||
"or \"assetglobaldef\".\n",
|
||||
token.string);
|
||||
}
|
||||
|
||||
pc::free_source_handle(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
int load_menu(const char** p) {
|
||||
if (*game::Com_Parse(p) != '{') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
const auto* token = game::Com_Parse(p);
|
||||
if (!game::I_stricmp(token, "}")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!token || !*token) {
|
||||
break;
|
||||
}
|
||||
|
||||
parse_menu_internal(token);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
game::MenuList* load_menus_load_obj(const char* menu_file) {
|
||||
std::memset(&g_load, 0, sizeof(g_load));
|
||||
g_load.menuList.menus = g_load.menus;
|
||||
|
||||
auto f = 0;
|
||||
auto len = game::FS_FOpenFileRead(menu_file, &f);
|
||||
|
||||
if (!f) {
|
||||
game::Com_PrintWarning(game::CON_CHANNEL_UI,
|
||||
"WARNING: menu file not found: %s\n", menu_file);
|
||||
#ifdef UI_LOAD_DEFAULT_MENU
|
||||
len = game::FS_FOpenFileByMode("ui/default.menu", &f, game::FS_READ);
|
||||
if (!f) {
|
||||
game::Com_Error(game::ERR_SERVERDISCONNECT,
|
||||
"\x15"
|
||||
"default.menu file not found. This is a default menu "
|
||||
"that you should have.\n");
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (static_cast<std::size_t>(len) >= sizeof(menu_buf)) {
|
||||
game::FS_FCloseFile(f);
|
||||
game::Com_Error(game::ERR_SERVERDISCONNECT,
|
||||
"\x15^1menu file too large: %s is %i, max allowed is %i",
|
||||
menu_file, len, sizeof(menu_buf));
|
||||
}
|
||||
|
||||
game::FS_Read(menu_buf, len, f);
|
||||
menu_buf[len] = '\0';
|
||||
game::FS_FCloseFile(f);
|
||||
|
||||
game::Com_Compress(menu_buf);
|
||||
|
||||
const auto* p = menu_buf;
|
||||
game::Com_BeginParseSession(menu_file);
|
||||
|
||||
for (auto* token = game::Com_Parse(&p); token; token = game::Com_Parse(&p)) {
|
||||
if (!*token || *token == '}' || !game::I_stricmp(token, "}") ||
|
||||
!game::I_stricmp(token, "loadmenu") && !load_menu(&p)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
game::Com_EndParseSession();
|
||||
return &g_load.menuList;
|
||||
}
|
||||
|
||||
game::MenuList* load_menu_load_obj(const char* menu_file) {
|
||||
std::memset(&g_load, 0, sizeof(g_load));
|
||||
g_load.menuList.menus = g_load.menus;
|
||||
|
||||
if (!parse_menu_internal(menu_file)) {
|
||||
game::Com_PrintWarning(game::CON_CHANNEL_UI,
|
||||
"WARNING: menu file not found: %s\n", menu_file);
|
||||
|
||||
#ifdef UI_LOAD_DEFAULT_MENU
|
||||
if (!parse_menu_internal("ui/default.menu")) {
|
||||
game::Com_Error(game::ERR_SERVERDISCONNECT,
|
||||
"\x15"
|
||||
"default.menu file not found. This is a default menu "
|
||||
"that you should have.\n");
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
return &g_load.menuList;
|
||||
}
|
||||
|
||||
game::MenuList* load_menus_fast_file(const char* menu_file) {
|
||||
return game::DB_FindXAssetHeader(game::ASSET_TYPE_MENULIST, menu_file)
|
||||
.menuList;
|
||||
}
|
||||
|
||||
game::MenuList* load_menus_stub(const char* menu_file) {
|
||||
auto* menu_list = load_menus_load_obj(menu_file);
|
||||
if (!menu_list) {
|
||||
menu_list = load_menus_fast_file(menu_file);
|
||||
}
|
||||
|
||||
return menu_list;
|
||||
}
|
||||
|
||||
game::MenuList* load_menu_fast_file(const char* menu_file) {
|
||||
return game::DB_FindXAssetHeader(game::ASSET_TYPE_MENULIST, menu_file)
|
||||
.menuList;
|
||||
}
|
||||
|
||||
game::MenuList* load_menu_stub(const char* menu_file) {
|
||||
auto* menu_list = load_menu_load_obj(menu_file);
|
||||
if (!menu_list) {
|
||||
menu_list = load_menu_fast_file(menu_file);
|
||||
}
|
||||
|
||||
return menu_list;
|
||||
}
|
||||
|
||||
void snd_fade_all_sounds_stub(float volume, int fade_time) {
|
||||
utils::hook::invoke<void>(0x4206A0, volume, fade_time);
|
||||
|
||||
for (auto& menu : loaded_menus_list) {
|
||||
menu_free_memory(menu);
|
||||
}
|
||||
|
||||
loaded_menus_list.clear();
|
||||
}
|
||||
|
||||
void ui_shutdown_stub() {
|
||||
for (auto& menu : loaded_menus_list) {
|
||||
menu_free_memory(menu);
|
||||
}
|
||||
|
||||
loaded_menus_list.clear();
|
||||
|
||||
utils::hook::invoke<void>(0x4CBD70);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
static_assert(offsetof(game::UiContext, menuCount) == 0xA38);
|
||||
static_assert(offsetof(game::UiContext, Menus) == 0x38);
|
||||
|
||||
static_assert(offsetof(game::menuDef_t, itemCount) == 0xAC);
|
||||
|
||||
static_assert(offsetof(game::itemDef_s, dataType) == 0xBC);
|
||||
static_assert(offsetof(game::itemDef_s, typeData) == 0x134);
|
||||
static_assert(offsetof(game::itemDef_s, floatExpressionCount) == 0x13C);
|
||||
static_assert(offsetof(game::itemDef_s, floatExpressions) == 0x140);
|
||||
|
||||
void post_load() override {
|
||||
menu_parse_keyword_hash =
|
||||
reinterpret_cast<KeywordHashEntry<game::menuDef_t, 128, 3523>**>(
|
||||
0x1933DB0);
|
||||
|
||||
patch_sp();
|
||||
}
|
||||
|
||||
void pre_destroy() override { assert(loaded_menus_list.empty()); }
|
||||
|
||||
private:
|
||||
static void patch_sp() {
|
||||
utils::hook(0x62DDD0, load_menus_stub, HOOK_JUMP).install()->quick();
|
||||
utils::hook::nop(0x62DDD0 + 5, 3);
|
||||
|
||||
utils::hook(0x621194, load_menu_stub, HOOK_CALL).install()->quick();
|
||||
|
||||
// Remove the menus from memory (CG_Shutdown)
|
||||
utils::hook(0x447905, snd_fade_all_sounds_stub, HOOK_CALL)
|
||||
.install() // hook*
|
||||
->quick();
|
||||
// Remove the menus from memory (UI_Shutdown)
|
||||
utils::hook(0x4F4C4F, ui_shutdown_stub, HOOK_CALL)
|
||||
.install() // hook*
|
||||
->quick();
|
||||
}
|
||||
};
|
||||
} // namespace ui
|
||||
|
||||
REGISTER_COMPONENT(ui::component)
|
74
src/client/component/ultra_wide.cpp
Normal file
74
src/client/component/ultra_wide.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace ultra_wide {
|
||||
const game::dvar_t* r_customAspectRatio;
|
||||
|
||||
const game::dvar_t* dvar_register_aspect_ratio(const char* dvar_name,
|
||||
const char** /*value_list*/,
|
||||
int default_index,
|
||||
unsigned __int16 flags,
|
||||
const char* description) {
|
||||
static const char* values[] = {"auto", "standard", "wide 16:10",
|
||||
"wide 16:9", "custom", nullptr};
|
||||
|
||||
// register enumeration dvar
|
||||
return game::Dvar_RegisterEnum(dvar_name, values, default_index, flags,
|
||||
description);
|
||||
}
|
||||
|
||||
void set_aspect_ratio() {
|
||||
*reinterpret_cast<float*>(0x1C91A78) = r_customAspectRatio->current.value;
|
||||
}
|
||||
|
||||
__declspec(naked) void set_aspect_ratio_stub() {
|
||||
__asm {
|
||||
mov eax, [eax + 0x10];
|
||||
cmp eax, 4;
|
||||
|
||||
mov dword ptr ds:0x1C91A68, edx;
|
||||
mov dword ptr ds:0x1C91A6C, esi;
|
||||
mov dword ptr ds:0x1C91A74, ecx;
|
||||
|
||||
ja default_case;
|
||||
je custom_ratio;
|
||||
|
||||
push 0x50AE6C;
|
||||
ret;
|
||||
|
||||
default_case:
|
||||
push 0x50AF6C;
|
||||
ret;
|
||||
|
||||
custom_ratio:
|
||||
pushad;
|
||||
call set_aspect_ratio;
|
||||
popad;
|
||||
|
||||
mov eax, 1; // set widescreen to 1
|
||||
|
||||
push 0x50AF05;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
utils::hook(0x51E80B, &dvar_register_aspect_ratio, HOOK_CALL)
|
||||
.install()
|
||||
->quick();
|
||||
|
||||
utils::hook(0x50AE4E, &set_aspect_ratio_stub, HOOK_JUMP).install()->quick();
|
||||
utils::hook::nop(0x50AE4E + 5, 1);
|
||||
|
||||
r_customAspectRatio = game::Dvar_RegisterFloat(
|
||||
"r_customAspectRatio", 16.0f / 9.0f, 4.0f / 3.0f, 63.0f / 9.0f,
|
||||
game::DVAR_ARCHIVE, "Screen aspect ratio");
|
||||
}
|
||||
};
|
||||
} // namespace ultra_wide
|
||||
|
||||
REGISTER_COMPONENT(ultra_wide::component)
|
48
src/client/component/weapons.cpp
Normal file
48
src/client/component/weapons.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace weapons {
|
||||
namespace {
|
||||
game::WeaponCompleteDef*
|
||||
bg_load_weapon_variant_def_fast_file_stub(const char* name) {
|
||||
if (auto* raw_weapon_file =
|
||||
game::BG_LoadWeaponVariantDefInternal("sp", name)) {
|
||||
return raw_weapon_file;
|
||||
}
|
||||
|
||||
auto* zone_weapon_file =
|
||||
game::DB_FindXAssetHeader(game::ASSET_TYPE_WEAPON, name).weapon;
|
||||
return game::DB_IsXAssetDefault(game::ASSET_TYPE_WEAPON, name)
|
||||
? nullptr
|
||||
: zone_weapon_file;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override { patch_sp(); }
|
||||
|
||||
private:
|
||||
static void patch_sp() {
|
||||
// Weapon asset existence check
|
||||
utils::hook::nop(0x43E1D8, 5);
|
||||
// Is asset default
|
||||
utils::hook::nop(0x43E1E0, 5);
|
||||
// Do not jump
|
||||
utils::hook::nop(0x43E1EA, 2);
|
||||
|
||||
// Ignore missing default weapon
|
||||
utils::hook::set<std::uint8_t>(0x659DBE, 0xEB);
|
||||
|
||||
utils::hook(0x659E00, bg_load_weapon_variant_def_fast_file_stub, HOOK_JUMP)
|
||||
.install()
|
||||
->quick();
|
||||
// Disable warning if raw weapon file cannot be found
|
||||
utils::hook::nop(0x659730, 5);
|
||||
}
|
||||
};
|
||||
} // namespace weapons
|
||||
|
||||
REGISTER_COMPONENT(weapons::component)
|
25
src/client/component/zone.cpp
Normal file
25
src/client/component/zone.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace zone {
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
static_assert(sizeof(game::XZoneInfo) == 0xC);
|
||||
|
||||
void post_load() override { patch_sp(); }
|
||||
|
||||
private:
|
||||
static void patch_sp() {
|
||||
// Ignore zone version mismatch
|
||||
utils::hook::set<std::uint8_t>(0x4256D8, 0xEB);
|
||||
|
||||
// Disc read error
|
||||
utils::hook::nop(0x4B7335, 2);
|
||||
utils::hook::set<std::uint8_t>(0x4256B9, 0xEB);
|
||||
}
|
||||
};
|
||||
} // namespace zone
|
||||
|
||||
REGISTER_COMPONENT(zone::component)
|
38
src/client/game/dvars.cpp
Normal file
38
src/client/game/dvars.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
namespace dvars {
|
||||
const game::dvar_t* r_noBorder = nullptr;
|
||||
|
||||
const game::dvar_t* pm_bounce = nullptr;
|
||||
const game::dvar_t* pm_bouncingAllAngles = nullptr;
|
||||
const game::dvar_t* pm_rocketJump = nullptr;
|
||||
const game::dvar_t* pm_rocketJumpScale = nullptr;
|
||||
const game::dvar_t* pm_playerCollision = nullptr;
|
||||
const game::dvar_t* pm_elevators = nullptr;
|
||||
const game::dvar_t* pm_disableLandingSlowdown = nullptr;
|
||||
const game::dvar_t* pm_bunnyHop = nullptr;
|
||||
const game::dvar_t* pm_snapVector = nullptr;
|
||||
|
||||
const game::dvar_t* cg_drawVersion = nullptr;
|
||||
const game::dvar_t* cg_drawVersionX = nullptr;
|
||||
const game::dvar_t* cg_drawVersionY = nullptr;
|
||||
|
||||
const game::dvar_t* bug_name = nullptr;
|
||||
|
||||
const game::dvar_t* g_log = nullptr;
|
||||
|
||||
// Game dvars
|
||||
const game::dvar_t** g_specialops =
|
||||
reinterpret_cast<const game::dvar_t**>(0x1B2E1E8);
|
||||
|
||||
const game::dvar_t** sv_mapname =
|
||||
reinterpret_cast<const game::dvar_t**>(0x1B2E1E4);
|
||||
|
||||
const game::dvar_t** version =
|
||||
reinterpret_cast<const game::dvar_t**>(0x145D690);
|
||||
|
||||
const game::dvar_t** com_developer =
|
||||
reinterpret_cast<const game::dvar_t**>(0x145D648);
|
||||
const game::dvar_t** com_developer_script =
|
||||
reinterpret_cast<const game::dvar_t**>(0x145EC58);
|
||||
} // namespace dvars
|
33
src/client/game/dvars.hpp
Normal file
33
src/client/game/dvars.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
namespace dvars {
|
||||
extern const game::dvar_t* r_noBorder;
|
||||
|
||||
extern const game::dvar_t* pm_bounce;
|
||||
extern const game::dvar_t* pm_bouncingAllAngles;
|
||||
extern const game::dvar_t* pm_rocketJump;
|
||||
extern const game::dvar_t* pm_rocketJumpScale;
|
||||
extern const game::dvar_t* pm_playerCollision;
|
||||
extern const game::dvar_t* pm_elevators;
|
||||
extern const game::dvar_t* pm_disableLandingSlowdown;
|
||||
extern const game::dvar_t* pm_bunnyHop;
|
||||
extern const game::dvar_t* pm_snapVector;
|
||||
|
||||
extern const game::dvar_t* cg_drawVersion;
|
||||
extern const game::dvar_t* cg_drawVersionX;
|
||||
extern const game::dvar_t* cg_drawVersionY;
|
||||
|
||||
extern const game::dvar_t* bug_name;
|
||||
|
||||
extern const game::dvar_t* g_log;
|
||||
|
||||
// Game dvars
|
||||
extern const game::dvar_t** g_specialops;
|
||||
|
||||
extern const game::dvar_t** sv_mapname;
|
||||
|
||||
extern const game::dvar_t** version;
|
||||
|
||||
extern const game::dvar_t** com_developer;
|
||||
extern const game::dvar_t** com_developer_script;
|
||||
} // namespace dvars
|
35
src/client/game/engine/fast_critical_section.cpp
Normal file
35
src/client/game/engine/fast_critical_section.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include <std_include.hpp>
|
||||
#include <game/game.hpp>
|
||||
|
||||
#include "fast_critical_section.hpp"
|
||||
|
||||
namespace game::engine {
|
||||
fast_critical_section_scope_read::fast_critical_section_scope_read(
|
||||
FastCriticalSection* cs) noexcept
|
||||
: cs_(cs) {
|
||||
if (this->cs_) {
|
||||
Sys_LockRead(this->cs_);
|
||||
}
|
||||
}
|
||||
|
||||
fast_critical_section_scope_read::~fast_critical_section_scope_read() noexcept {
|
||||
if (this->cs_) {
|
||||
Sys_UnlockRead(this->cs_);
|
||||
}
|
||||
}
|
||||
|
||||
fast_critical_section_scope_write::fast_critical_section_scope_write(
|
||||
FastCriticalSection* cs) noexcept
|
||||
: cs_(cs) {
|
||||
if (this->cs_) {
|
||||
Sys_LockWrite(this->cs_);
|
||||
}
|
||||
}
|
||||
|
||||
fast_critical_section_scope_write::
|
||||
~fast_critical_section_scope_write() noexcept {
|
||||
if (this->cs_) {
|
||||
Sys_UnlockWrite(this->cs_);
|
||||
}
|
||||
}
|
||||
} // namespace game::engine
|
21
src/client/game/engine/fast_critical_section.hpp
Normal file
21
src/client/game/engine/fast_critical_section.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
namespace game::engine {
|
||||
class fast_critical_section_scope_read {
|
||||
public:
|
||||
fast_critical_section_scope_read(FastCriticalSection* cs) noexcept;
|
||||
~fast_critical_section_scope_read() noexcept;
|
||||
|
||||
private:
|
||||
FastCriticalSection* cs_;
|
||||
};
|
||||
|
||||
class fast_critical_section_scope_write {
|
||||
public:
|
||||
fast_critical_section_scope_write(FastCriticalSection* cs) noexcept;
|
||||
~fast_critical_section_scope_write() noexcept;
|
||||
|
||||
private:
|
||||
FastCriticalSection* cs_;
|
||||
};
|
||||
} // namespace game::engine
|
87
src/client/game/engine/large_local.cpp
Normal file
87
src/client/game/engine/large_local.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "large_local.hpp"
|
||||
|
||||
namespace game::engine {
|
||||
namespace {
|
||||
constexpr auto PAGE_SIZE = 4096;
|
||||
|
||||
int can_use_server_large_local() { return Sys_IsServerThread(); }
|
||||
|
||||
void large_local_end(int start_pos) {
|
||||
assert(Sys_IsMainThread());
|
||||
assert(g_largeLocalBuf);
|
||||
|
||||
*g_largeLocalPos = start_pos;
|
||||
|
||||
assert(((*g_maxLargeLocalPos + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1)) <=
|
||||
(*g_minLargeLocalRightPos & ~(PAGE_SIZE - 1)));
|
||||
}
|
||||
|
||||
void large_local_end_right(int start_pos) {
|
||||
assert(can_use_server_large_local());
|
||||
assert(g_largeLocalBuf);
|
||||
|
||||
*g_largeLocalRightPos = start_pos;
|
||||
|
||||
assert(((*g_maxLargeLocalPos + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1)) <=
|
||||
(*g_minLargeLocalRightPos & ~(PAGE_SIZE - 1)));
|
||||
}
|
||||
|
||||
void* large_local_get_buf(int start_pos, int size) {
|
||||
assert(Sys_IsMainThread() || can_use_server_large_local());
|
||||
assert(g_largeLocalBuf);
|
||||
assert(!(size & 127));
|
||||
|
||||
if (Sys_IsMainThread()) {
|
||||
return &g_largeLocalBuf[start_pos];
|
||||
}
|
||||
|
||||
const auto start_index = start_pos - size;
|
||||
assert(start_index >= 0);
|
||||
|
||||
return &g_largeLocalBuf[start_index];
|
||||
}
|
||||
} // namespace
|
||||
|
||||
large_local::large_local(int size_param) {
|
||||
assert(size_param);
|
||||
assert(Sys_IsMainThread() || can_use_server_large_local());
|
||||
|
||||
size_param = ((size_param + (128 - 1)) & ~(128 - 1));
|
||||
|
||||
if (Sys_IsMainThread()) {
|
||||
this->start_pos_ = LargeLocalBegin(size_param);
|
||||
} else {
|
||||
this->start_pos_ = LargeLocalBeginRight(size_param);
|
||||
}
|
||||
|
||||
this->size_ = size_param;
|
||||
}
|
||||
|
||||
large_local::~large_local() {
|
||||
if (this->size_) {
|
||||
this->pop_buf();
|
||||
}
|
||||
}
|
||||
|
||||
void large_local::pop_buf() {
|
||||
assert(this->size_);
|
||||
assert(Sys_IsMainThread() || can_use_server_large_local());
|
||||
|
||||
if (Sys_IsMainThread()) {
|
||||
large_local_end(this->start_pos_);
|
||||
} else {
|
||||
large_local_end_right(this->start_pos_);
|
||||
}
|
||||
|
||||
this->size_ = 0;
|
||||
}
|
||||
|
||||
void* large_local::get_buf() const {
|
||||
assert(this->size_);
|
||||
assert(Sys_IsMainThread() || can_use_server_large_local());
|
||||
|
||||
return large_local_get_buf(this->start_pos_, this->size_);
|
||||
}
|
||||
} // namespace game::engine
|
22
src/client/game/engine/large_local.hpp
Normal file
22
src/client/game/engine/large_local.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
namespace game::engine {
|
||||
class large_local {
|
||||
public:
|
||||
explicit large_local(int size_param);
|
||||
~large_local();
|
||||
|
||||
large_local(large_local&&) = delete;
|
||||
large_local(const large_local&) = delete;
|
||||
large_local& operator=(large_local&&) = delete;
|
||||
large_local& operator=(const large_local&) = delete;
|
||||
|
||||
[[nodiscard]] void* get_buf() const;
|
||||
|
||||
private:
|
||||
void pop_buf();
|
||||
|
||||
int start_pos_;
|
||||
int size_;
|
||||
};
|
||||
} // namespace game::engine
|
65
src/client/game/engine/scoped_critical_section.cpp
Normal file
65
src/client/game/engine/scoped_critical_section.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "scoped_critical_section.hpp"
|
||||
|
||||
namespace game::engine {
|
||||
scoped_critical_section::scoped_critical_section(
|
||||
const CriticalSection s, const ScopedCriticalSectionType type) noexcept
|
||||
: s_(s), is_scoped_release_(false) {
|
||||
if (type == SCOPED_CRITSECT_NORMAL) {
|
||||
Sys_EnterCriticalSection(this->s_);
|
||||
this->has_ownership_ = true;
|
||||
} else {
|
||||
if (type == SCOPED_CRITSECT_TRY) {
|
||||
this->has_ownership_ = Sys_TryEnterCriticalSection(this->s_);
|
||||
} else {
|
||||
if (type == SCOPED_CRITSECT_RELEASE) {
|
||||
Sys_LeaveCriticalSection(this->s_);
|
||||
this->is_scoped_release_ = true;
|
||||
}
|
||||
|
||||
this->has_ownership_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scoped_critical_section::~scoped_critical_section() noexcept {
|
||||
if (!this->has_ownership_ || this->is_scoped_release_) {
|
||||
if (!this->has_ownership_ && this->is_scoped_release_) {
|
||||
Sys_EnterCriticalSection(this->s_);
|
||||
}
|
||||
} else {
|
||||
Sys_LeaveCriticalSection(this->s_);
|
||||
}
|
||||
}
|
||||
|
||||
void scoped_critical_section::enter_crit_sect() noexcept {
|
||||
assert(!this->has_ownership_);
|
||||
|
||||
this->has_ownership_ = true;
|
||||
Sys_EnterCriticalSection(this->s_);
|
||||
}
|
||||
|
||||
void scoped_critical_section::leave_crit_sect() noexcept {
|
||||
assert(this->has_ownership_);
|
||||
|
||||
this->has_ownership_ = false;
|
||||
Sys_LeaveCriticalSection(this->s_);
|
||||
}
|
||||
|
||||
bool scoped_critical_section::try_enter_crit_sect() noexcept {
|
||||
assert(!this->has_ownership_);
|
||||
|
||||
const auto result = Sys_TryEnterCriticalSection(this->s_);
|
||||
this->has_ownership_ = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool scoped_critical_section::has_ownership() const noexcept {
|
||||
return this->has_ownership_;
|
||||
}
|
||||
|
||||
bool scoped_critical_section::is_scoped_release() const noexcept {
|
||||
return this->is_scoped_release_;
|
||||
}
|
||||
} // namespace game::engine
|
22
src/client/game/engine/scoped_critical_section.hpp
Normal file
22
src/client/game/engine/scoped_critical_section.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
namespace game::engine {
|
||||
class scoped_critical_section {
|
||||
public:
|
||||
scoped_critical_section(CriticalSection s,
|
||||
ScopedCriticalSectionType type) noexcept;
|
||||
~scoped_critical_section() noexcept;
|
||||
|
||||
void enter_crit_sect() noexcept;
|
||||
void leave_crit_sect() noexcept;
|
||||
bool try_enter_crit_sect() noexcept;
|
||||
|
||||
[[nodiscard]] bool has_ownership() const noexcept;
|
||||
[[nodiscard]] bool is_scoped_release() const noexcept;
|
||||
|
||||
private:
|
||||
CriticalSection s_;
|
||||
bool has_ownership_;
|
||||
bool is_scoped_release_;
|
||||
};
|
||||
} // namespace game::engine
|
139
src/client/game/game.cpp
Normal file
139
src/client/game/game.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
namespace game {
|
||||
int FS_FOpenFileReadForThread(const char* filename, int* file,
|
||||
FsThread thread) {
|
||||
const static DWORD FS_FOpenFileReadForThread_t = 0x630380;
|
||||
int result{};
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
|
||||
mov eax, file;
|
||||
push thread;
|
||||
push filename;
|
||||
call FS_FOpenFileReadForThread_t;
|
||||
add esp, 0x8;
|
||||
mov result, eax;
|
||||
|
||||
popad;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void IN_KeyDown(kbutton_t* b) {
|
||||
const static DWORD IN_KeyDown_t = 0x57A350;
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
|
||||
mov esi, b;
|
||||
call IN_KeyDown_t;
|
||||
|
||||
popad;
|
||||
}
|
||||
}
|
||||
|
||||
void IN_KeyUp(kbutton_t* b) {
|
||||
const static DWORD IN_KeyUp_t = 0x57A3F0;
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
|
||||
mov esi, b;
|
||||
call IN_KeyUp_t;
|
||||
|
||||
popad;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScrPlace_IsFullScreenActive() {
|
||||
return *activeScreenPlacementMode == SCRMODE_FULL;
|
||||
}
|
||||
|
||||
ScreenPlacement* ScrPlace_GetUnsafeFullPlacement() {
|
||||
return scrPlaceFullUnsafe;
|
||||
}
|
||||
|
||||
bool Sys_TryEnterCriticalSection(CriticalSection critSect) {
|
||||
assert(static_cast<std::uint32_t>(critSect) <
|
||||
static_cast<std::uint32_t>(CRITSECT_COUNT));
|
||||
|
||||
return TryEnterCriticalSection(&s_criticalSection[critSect]) != FALSE;
|
||||
}
|
||||
|
||||
void Sys_LockRead(FastCriticalSection* critSect) {
|
||||
InterlockedIncrement(&critSect->readCount);
|
||||
while (critSect->writeCount) {
|
||||
Sys_Sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
void Sys_UnlockRead(FastCriticalSection* critSect) {
|
||||
assert(critSect->readCount > 0);
|
||||
InterlockedDecrement(&critSect->readCount);
|
||||
}
|
||||
|
||||
void Sys_UnlockWrite(FastCriticalSection* critSect) {
|
||||
assert(critSect->writeCount > 0);
|
||||
InterlockedDecrement(&critSect->writeCount);
|
||||
Sys_TempPriorityEnd(&critSect->tempPriority);
|
||||
}
|
||||
|
||||
void Sys_SnapVector(float* v) {
|
||||
v[0] = std::floorf(v[0] + 0.5f);
|
||||
v[1] = std::floorf(v[1] + 0.5f);
|
||||
v[2] = std::floorf(v[2] + 0.5f);
|
||||
}
|
||||
|
||||
int PC_Int_Parse(int handle, int* i) {
|
||||
const static DWORD PC_Int_Parse_t = 0x62DF10;
|
||||
int result{};
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
|
||||
mov eax, handle;
|
||||
mov esi, i;
|
||||
call PC_Int_Parse_t;
|
||||
mov result, eax;
|
||||
|
||||
popad;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int PC_Float_Parse(int handle, float* f) {
|
||||
const static DWORD PC_Float_Parse_t = 0x62DE40;
|
||||
int result{};
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
|
||||
mov eax, handle;
|
||||
mov esi, f;
|
||||
call PC_Float_Parse_t;
|
||||
mov result, eax;
|
||||
|
||||
popad;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Menu_FreeItemMemory(itemDef_s* item) {
|
||||
const static DWORD Menu_FreeItemMemory_t = 0x62B7E0;
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
|
||||
mov edi, item;
|
||||
call Menu_FreeItemMemory_t;
|
||||
|
||||
popad;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace game
|
63
src/client/game/game.hpp
Normal file
63
src/client/game/game.hpp
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
namespace game {
|
||||
template <typename T> struct base_symbol {
|
||||
base_symbol(const std::size_t address) : address_(address) {}
|
||||
|
||||
[[nodiscard]] T* get() const { return reinterpret_cast<T*>(this->address_); }
|
||||
|
||||
operator T*() const { return this->get(); }
|
||||
|
||||
T* operator->() const { return this->get(); }
|
||||
|
||||
private:
|
||||
std::size_t address_{};
|
||||
};
|
||||
|
||||
template <typename T> struct symbol : base_symbol<T> {
|
||||
using base_symbol<T>::base_symbol;
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct symbol<T(Args...)> : base_symbol<T(Args...)> {
|
||||
using func_type = T(Args...);
|
||||
using base_symbol<func_type>::base_symbol;
|
||||
};
|
||||
|
||||
// Functions that require asm stubs to be called
|
||||
// or have been inlined
|
||||
|
||||
int FS_FOpenFileReadForThread(const char* filename, int* file, FsThread thread);
|
||||
|
||||
void IN_KeyDown(kbutton_t* b);
|
||||
void IN_KeyUp(kbutton_t* b);
|
||||
|
||||
bool ScrPlace_IsFullScreenActive();
|
||||
ScreenPlacement* ScrPlace_GetUnsafeFullPlacement();
|
||||
|
||||
bool Sys_TryEnterCriticalSection(CriticalSection critSect);
|
||||
void Sys_LockRead(FastCriticalSection* critSect);
|
||||
void Sys_UnlockRead(FastCriticalSection* critSect);
|
||||
void Sys_UnlockWrite(FastCriticalSection* critSect);
|
||||
void Sys_SnapVector(float* v);
|
||||
|
||||
int PC_Int_Parse(int handle, int* i);
|
||||
int PC_Float_Parse(int handle, float* f);
|
||||
|
||||
void Menu_FreeItemMemory(itemDef_s* item);
|
||||
|
||||
// Global definitions
|
||||
constexpr auto CMD_MAX_NESTING = 8;
|
||||
|
||||
constexpr auto MAX_POSSIBLE_LOCAL_CLIENTS = 1;
|
||||
|
||||
constexpr std::size_t MAX_LOCAL_CLIENTS = 1;
|
||||
|
||||
constexpr auto MAX_QPATH = 64;
|
||||
|
||||
constexpr auto MAX_OPCODE_LOOKUP_SIZE = 0x1000000;
|
||||
constexpr auto MAX_SOURCEPOS_LOOKUP_SIZE = 0x800000;
|
||||
constexpr auto MAX_SOURCEBUF_LOOKUP_SIZE = 0x40000;
|
||||
} // namespace game
|
||||
|
||||
#include "symbols.hpp"
|
1690
src/client/game/structs.hpp
Normal file
1690
src/client/game/structs.hpp
Normal file
File diff suppressed because it is too large
Load Diff
321
src/client/game/symbols.hpp
Normal file
321
src/client/game/symbols.hpp
Normal file
@ -0,0 +1,321 @@
|
||||
#pragma once
|
||||
|
||||
#define WEAK __declspec(selectany)
|
||||
|
||||
namespace game {
|
||||
// Com
|
||||
WEAK symbol<void(int channel, const char* fmt, ...)> Com_Printf{0x41BD20};
|
||||
WEAK symbol<void(int channel, const char* fmt, ...)> Com_PrintWarning{0x406320};
|
||||
WEAK symbol<void(int channel, const char* fmt, ...)> Com_PrintError{0x4C6980};
|
||||
WEAK symbol<void(int channel, const char* fmt, ...)> Com_DPrintf{0x42B1F0};
|
||||
WEAK symbol<void(int channel, const char* msg, int error)> Com_PrintMessage{
|
||||
0x456B70};
|
||||
WEAK symbol<void(errorParm_t code, const char* fmt, ...)> Com_Error{0x43DD90};
|
||||
WEAK symbol<void()> Com_OpenLogFile{0x603030};
|
||||
WEAK symbol<int(char* data_p)> Com_Compress{0x4316A0};
|
||||
WEAK symbol<void()> Com_EventLoop{0x4987C0};
|
||||
WEAK symbol<void()> Com_ServerPacketEvent{0x47FD30};
|
||||
WEAK symbol<void(const char* filename)> Com_BeginParseSession{0x4A5C90};
|
||||
WEAK symbol<void()> Com_EndParseSession{0x4D12C0};
|
||||
WEAK symbol<const char*(const char** data_p)> Com_Parse{0x486600};
|
||||
|
||||
WEAK symbol<const char*(const char* fmt, ...)> va{0x4869F0};
|
||||
|
||||
// Con
|
||||
WEAK symbol<bool(const char* cmd)> Con_IsDvarCommand{0x4B6610};
|
||||
|
||||
// Sys
|
||||
WEAK symbol<void(const char* exeName)> Sys_QuitAndStartProcess{0x4D69A0};
|
||||
WEAK symbol<void(CriticalSection critSect)> Sys_EnterCriticalSection{0x4A4CD0};
|
||||
WEAK symbol<void(CriticalSection critSect)> Sys_LeaveCriticalSection{0x4F78E0};
|
||||
WEAK symbol<void(TempPriority*)> Sys_TempPriorityEnd{0x4FB800};
|
||||
WEAK symbol<void(FastCriticalSection* critSect)> Sys_LockWrite{0x43C1D0};
|
||||
WEAK symbol<int()> Sys_Milliseconds{0x44E130};
|
||||
WEAK symbol<bool()> Sys_IsMainThread{0x42FA00};
|
||||
WEAK symbol<bool()> Sys_IsServerThread{0x4590E0};
|
||||
WEAK symbol<bool()> Sys_IsDatabaseThread{0x4C9380};
|
||||
WEAK symbol<void(int valueIndex, void* data)> Sys_SetValue{0x483310};
|
||||
WEAK symbol<void(int msec)> Sys_Sleep{0x4CFBE0};
|
||||
WEAK symbol<void(const char* error, ...)> Sys_Error{0x40BFF0};
|
||||
|
||||
WEAK symbol<short(short l)> BigShort{0x40E7E0};
|
||||
WEAK symbol<short(short l)> ShortNoSwap{0x4261A0};
|
||||
|
||||
WEAK symbol<int(int size)> LargeLocalBegin{0x43fA50};
|
||||
WEAK symbol<int(int size)> LargeLocalBeginRight{0x6317D0};
|
||||
|
||||
// CL
|
||||
WEAK symbol<int(int localClientNum)> CL_IsCgameInitialized{0x4EEA50};
|
||||
WEAK symbol<void()> CL_CoOpConnect{0x57D240};
|
||||
|
||||
// BG
|
||||
WEAK symbol<WeaponCompleteDef*(const char* name, const char* folder)>
|
||||
BG_LoadWeaponVariantDefInternal{0x4F5AF0};
|
||||
|
||||
// Game
|
||||
WEAK symbol<int()> G_GetTime{0x4E94E0};
|
||||
|
||||
WEAK symbol<void(int, const char* text)> Cbuf_AddText{0x4A1090};
|
||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)>
|
||||
Cmd_ExecuteSingleCommand{0x46AFD0};
|
||||
WEAK symbol<void(const char* cmdName, void(*function),
|
||||
cmd_function_s* allocedCmd, int isKey)>
|
||||
Cmd_AddCommand{0x4478A0};
|
||||
WEAK symbol<void(const char* cmdName, const char* dir, const char* ext)>
|
||||
Cmd_SetAutoComplete{0x48A880};
|
||||
|
||||
// Dvars
|
||||
WEAK symbol<dvar_t*(const char* dvarName)> Dvar_FindVar{0x4B29D0};
|
||||
WEAK symbol<const dvar_t*(const char* dvarName, const char* value,
|
||||
unsigned __int16 flags, const char* description)>
|
||||
Dvar_RegisterString{0x49E0B0};
|
||||
WEAK symbol<const dvar_t*(const char* dvarName, bool value,
|
||||
unsigned __int16 flags, const char* description)>
|
||||
Dvar_RegisterBool{0x429390};
|
||||
WEAK symbol<const dvar_t*(const char* dvarName, const char** valueList,
|
||||
int defaultIndex, unsigned __int16 flags,
|
||||
const char* description)>
|
||||
Dvar_RegisterEnum{0x4CB7C0};
|
||||
WEAK symbol<const dvar_t*(const char* dvarName, float value, float min,
|
||||
float max, unsigned __int16 flags,
|
||||
const char* description)>
|
||||
Dvar_RegisterFloat{0x4051D0};
|
||||
WEAK symbol<const dvar_t*(const char* dvarName, int value, int min, int max,
|
||||
unsigned __int16 flags, const char* description)>
|
||||
Dvar_RegisterInt{0x4E9490};
|
||||
|
||||
WEAK symbol<void(const char* dvarName, double value)> Dvar_SetFloatByName{
|
||||
0x497250};
|
||||
WEAK symbol<void(const char* dvarName, const char* value)> Dvar_SetStringByName{
|
||||
0x440C60};
|
||||
WEAK symbol<void(const dvar_t* dvar, int value)> Dvar_SetInt{0x4FA540};
|
||||
WEAK symbol<void(const dvar_t* dvar, bool value)> Dvar_SetBool{0x4E57E0};
|
||||
WEAK symbol<void(const dvar_t* dvar, const char* value)> Dvar_SetString{
|
||||
0x480E70};
|
||||
WEAK symbol<const char*(const char* dvarName)> Dvar_GetString{0x411F50};
|
||||
WEAK symbol<bool(const char* dvarName)> Dvar_GetBool{0x481010};
|
||||
|
||||
// Script
|
||||
WEAK symbol<void(const char* error)> Scr_Error{0x4E9C50};
|
||||
WEAK symbol<void(const char* error)> Scr_ObjectError{0x470600};
|
||||
WEAK symbol<void(unsigned int paramIndex, const char* error)> Scr_ParamError{
|
||||
0x42C880};
|
||||
WEAK symbol<void()> Scr_ShutdownAllocNode{0x486BC0};
|
||||
WEAK symbol<unsigned int(const char* filename)> Scr_CreateCanonicalFilename{
|
||||
0x43A5E0};
|
||||
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)>
|
||||
FindVariable{0x4B78B0};
|
||||
WEAK symbol<void(unsigned int parentId, unsigned int unsignedValue)>
|
||||
RemoveVariable{0x4C2DD0};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int id)> FindObject{
|
||||
0x49A980};
|
||||
WEAK symbol<gentity_s*(scr_entref_t entref)> GetEntity{0x4678C0};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)>
|
||||
GetVariable{0x482290};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)>
|
||||
GetNewVariable{0x4F1990};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int id)> GetObject{
|
||||
0x4370B0};
|
||||
|
||||
WEAK symbol<unsigned int()> Scr_GetNumParam{0x4443F0};
|
||||
WEAK symbol<void()> Scr_ClearOutParams{0x4A3A00};
|
||||
WEAK symbol<const char*(unsigned int index)> Scr_GetTypeName{0x4CE240};
|
||||
WEAK symbol<const char*(unsigned int index)> Scr_GetString{0x4D39C0};
|
||||
WEAK symbol<void(const char* value)> Scr_AddString{0x4CD670};
|
||||
WEAK symbol<unsigned int(unsigned int index)> Scr_GetConstString{0x4AF1B0};
|
||||
WEAK symbol<void(unsigned int value)> Scr_AddConstString{0x404CF0};
|
||||
WEAK symbol<int(unsigned int index)> Scr_GetInt{0x454520};
|
||||
WEAK symbol<void(int value)> Scr_AddInt{0x4865B0};
|
||||
WEAK symbol<float(unsigned int index)> Scr_GetFloat{0x4AE590};
|
||||
WEAK symbol<void(float value)> Scr_AddFloat{0x4986E0};
|
||||
WEAK symbol<int(unsigned int index)> Scr_GetType{0x464EE0};
|
||||
WEAK symbol<void(int func, const char* name)> Scr_RegisterFunction{0x4F59C0};
|
||||
WEAK symbol<unsigned int(unsigned int index)> Scr_GetFunc{0x438E10};
|
||||
WEAK symbol<int(const char* pos)> Scr_IsInOpcodeMemory{0x47D1D0};
|
||||
|
||||
WEAK symbol<char*(const char* filename, const char* extFilename,
|
||||
const char* codePos, bool archive)>
|
||||
Scr_AddSourceBuffer{0x4173C0};
|
||||
WEAK symbol<unsigned int(const char* filename)> Scr_LoadScript{0x46CD90};
|
||||
WEAK symbol<int(const char* filename, const char* name)> Scr_GetFunctionHandle{
|
||||
0x462750};
|
||||
WEAK symbol<int(int handle, unsigned int paramcount)> Scr_ExecThread{0x41A2C0};
|
||||
WEAK symbol<void(unsigned __int16 handle)> Scr_FreeThread{0x4C44A0};
|
||||
|
||||
WEAK symbol<void(sval_u* parseData, unsigned char user)> ScriptParse{0x4956B0};
|
||||
WEAK symbol<void(sval_u* val, unsigned int filePosId, unsigned int fileCountId,
|
||||
unsigned int scriptId, PrecacheEntry* entries,
|
||||
int entriesCount)>
|
||||
ScriptCompile{0x4FFDA0};
|
||||
|
||||
WEAK symbol<char*(int len)> TempMalloc{0x4EA7C0};
|
||||
|
||||
// SL
|
||||
WEAK symbol<const char*(unsigned int stringValue)> SL_ConvertToString{0x40E990};
|
||||
WEAK symbol<void(unsigned int stringValue)> SL_AddRefToString{0x4C4BD0};
|
||||
WEAK symbol<void(unsigned int stringValue)> SL_RemoveRefToString{0x4698E0};
|
||||
|
||||
// NET
|
||||
WEAK symbol<const char*(netadr_t a)> NET_AdrToString{0x4BF490};
|
||||
WEAK symbol<const char*()> NET_ErrorString{0x430390};
|
||||
|
||||
// Memory
|
||||
WEAK symbol<void*(int size)> Hunk_AllocateTempMemory{0x492DF0};
|
||||
WEAK symbol<void*(int size, int alignment)> Hunk_AllocAlignInternal{0x486C40};
|
||||
WEAK symbol<void*(int size)> Hunk_AllocateTempMemoryHigh{0x403B40};
|
||||
WEAK symbol<HunkUser*(int maxSize, const char* name, bool fixed, int type)>
|
||||
Hunk_UserCreate{0x4F1A10};
|
||||
WEAK symbol<void*(HunkUser* user, int size, int alignment)> Hunk_UserAlloc{
|
||||
0x469410};
|
||||
|
||||
WEAK symbol<void(Statement_s* statement)> free_expression{0x436260};
|
||||
|
||||
WEAK symbol<void(void*)> _free{0x674BC5};
|
||||
|
||||
// Zone
|
||||
WEAK symbol<void*(int size)> Z_VirtualAllocInternal{0x4D9CF0};
|
||||
WEAK symbol<void*(int size, const char* name)> Z_TryVirtualAllocInternal{
|
||||
0x4D9590};
|
||||
WEAK symbol<void(void* ptr)> Z_VirtualFreeInternal{0x4FE260};
|
||||
|
||||
// DB
|
||||
WEAK symbol<XAssetHeader(XAssetType type, const char* name)>
|
||||
DB_FindXAssetHeader{0x40B200};
|
||||
WEAK symbol<int(XAssetType type, const char* name)> DB_IsXAssetDefault{
|
||||
0x41AB70};
|
||||
WEAK symbol<void(RawFile* rawfile, char* buffer, int size)> DB_GetRawBuffer{
|
||||
0x4345E0};
|
||||
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount,
|
||||
unsigned int syncMode)>
|
||||
DB_LoadXAssets{0x4CFC90};
|
||||
WEAK symbol<char*(const char* filename, char* buf, int size)> DB_ReadRawFile{
|
||||
0x46DA60};
|
||||
WEAK symbol<int(RawFile* rawfile)> DB_GetRawFileLen{0x4D2E60};
|
||||
|
||||
// FS
|
||||
WEAK symbol<int(const char* qpath, void** buffer)> _FS_ReadFile{0x4A5480};
|
||||
WEAK symbol<unsigned int(void* buffer, int len, int h)> FS_Read{0x42EDC0};
|
||||
WEAK symbol<int(const void* buffer, int len, int h)> FS_Write{0x449FA0};
|
||||
WEAK symbol<void(int h)> FS_FCloseFile{0x44E0A0};
|
||||
WEAK symbol<int(const char* qpath, int* f, fsMode_t mode)> FS_FOpenFileByMode{
|
||||
0x41DF70};
|
||||
WEAK symbol<int(const char* filename, int* file)> FS_FOpenFileRead{0x48DD10};
|
||||
WEAK symbol<const char**(const char* path, const char* extension,
|
||||
FsListBehavior_e behavior, int* numfiles,
|
||||
int allocTrackType)>
|
||||
FS_ListFiles{0x4448F0};
|
||||
WEAK symbol<void(const char** list, int allocTrackType)> FS_FreeFileList{
|
||||
0x41C7A0};
|
||||
WEAK symbol<void(const char* base, const char* game, const char* qpath,
|
||||
char* ospath)>
|
||||
FS_BuildOSPath{0x4E48F0};
|
||||
WEAK symbol<void(const char* gameName)> FS_Startup{0x47AF20};
|
||||
|
||||
// UI
|
||||
WEAK symbol<Font_s*(const ScreenPlacement* scrPlace, int fontEnum, float scale)>
|
||||
UI_GetFontHandle{0x4C2600};
|
||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)>
|
||||
UI_TextWidth{0x4F5070};
|
||||
WEAK symbol<int(Font_s* font, float scale)> UI_TextHeight{0x407710};
|
||||
WEAK symbol<void(const ScreenPlacement* scrPlace, const char* text,
|
||||
int maxChars, Font_s* font, float x, float y, int horzAlign,
|
||||
int vertAlign, float scale, const float* color, int style)>
|
||||
UI_DrawText{0x40FC70};
|
||||
|
||||
// PC
|
||||
WEAK symbol<int(source_s* source)> PC_Directive_define{0x4F8CF0};
|
||||
WEAK symbol<void(define_s* define)> PC_FreeDefine{0x464F40};
|
||||
WEAK symbol<token_s*(token_s* token)> PC_CopyToken{0x4D3670};
|
||||
WEAK symbol<int(int handle, pc_token_s* pc_token)> PC_ReadTokenHandle{0x46C3B0};
|
||||
WEAK symbol<void(int handle, const char* format, ...)> PC_SourceError{0x43A6D0};
|
||||
|
||||
WEAK symbol<void*(unsigned int size)> GetMemory{0x441880};
|
||||
WEAK symbol<void*(unsigned int size)> GetClearedMemory{0x41BCD0};
|
||||
WEAK symbol<void(void* ptr)> FreeMemory{0x4A7D20};
|
||||
|
||||
// PM
|
||||
WEAK symbol<void(pmove_t* pm, trace_t* results, const float* start,
|
||||
const float* end, const Bounds* bounds, int passEntityNum,
|
||||
int contentMask)>
|
||||
PM_trace{0x4B7A20};
|
||||
WEAK symbol<void(pmove_t* pm, trace_t* results, const float* start,
|
||||
const float* end, const Bounds* bounds, int passEntityNum,
|
||||
int contentMask)>
|
||||
PM_playerTrace{0x447B90};
|
||||
WEAK symbol<bool(const playerState_s* ps)> PM_IsSprinting{0x47CF70};
|
||||
|
||||
// Live
|
||||
WEAK symbol<const char*(int controllerIndex)> Live_GetLocalClientName{0x492EF0};
|
||||
|
||||
// Info
|
||||
WEAK symbol<int(const char* s)> Info_Validate{0x425530};
|
||||
|
||||
// IW functions, could use Microsoft specific functions but who cares
|
||||
WEAK symbol<int(const char* s0, const char* s1)> I_stricmp{0x409B80};
|
||||
WEAK symbol<int(const char* s0, const char* s1, int n)> I_strnicmp{0x491E60};
|
||||
|
||||
WEAK symbol<void(field_t* edit)> Field_Clear{0x45C350};
|
||||
|
||||
// String
|
||||
WEAK symbol<int(const char* string)> StringTable_HashString{0x498080};
|
||||
|
||||
// Vec3
|
||||
WEAK symbol<void(const float* v, float scale, const float* result)> Vec3Scale{
|
||||
0x429220};
|
||||
|
||||
// Variables
|
||||
WEAK symbol<CmdArgs> cmd_args{0x144FED0};
|
||||
WEAK symbol<CmdArgs> sv_cmd_args{0x145ABA0};
|
||||
WEAK symbol<gentity_s> g_entities{0xEAAC38};
|
||||
WEAK symbol<gclient_s> g_clients{0x10911E8};
|
||||
|
||||
WEAK symbol<scrVmPub_t> scrVmPub{0x190DDF0};
|
||||
WEAK symbol<scrVarPub_t> scrVarPub{0x18E7508};
|
||||
WEAK symbol<scrCompilePub_t> scrCompilePub{0x156BF88};
|
||||
WEAK symbol<scrCompileGlob_t> scrCompileGlob{0x158CFC8};
|
||||
WEAK symbol<scrAnimPub_t> scrAnimPub{0x156BB68};
|
||||
|
||||
WEAK symbol<bool> g_loadedImpureScript{0x168F308};
|
||||
WEAK symbol<char> g_EndPos{0x1912598};
|
||||
|
||||
WEAK symbol<unsigned char*> g_largeLocalBuf{0x195AAF8};
|
||||
|
||||
WEAK symbol<int> g_largeLocalPos{0x1963998};
|
||||
WEAK symbol<int> g_maxLargeLocalPos{0x195AAFC};
|
||||
|
||||
WEAK symbol<int> g_largeLocalRightPos{0x195AAE8};
|
||||
WEAK symbol<int> g_minLargeLocalRightPos{0x195AB00};
|
||||
|
||||
WEAK symbol<unsigned long> g_dwTlsIndex{0x1BFC750};
|
||||
|
||||
WEAK symbol<int> com_frameTime{0x145EC7C};
|
||||
|
||||
WEAK symbol<bool> cin_skippable{0x73264C};
|
||||
|
||||
WEAK symbol<int> com_fixedConsolePosition{0x145EC10};
|
||||
|
||||
WEAK symbol<field_t> g_consoleField{0x88C700};
|
||||
WEAK symbol<ConDrawInputGlob> conDrawInputGlob{0x86E788};
|
||||
WEAK symbol<Console> con{0x86ED88};
|
||||
WEAK symbol<float> g_console_char_height{0x732658};
|
||||
WEAK symbol<int> g_console_field_width{0x732654};
|
||||
WEAK symbol<ScreenPlacementMode> activeScreenPlacementMode{0x93AAF4};
|
||||
WEAK symbol<ScreenPlacement> scrPlaceFullUnsafe{0x93AB70};
|
||||
|
||||
WEAK symbol<int> logfile{0x145EC6C};
|
||||
|
||||
WEAK symbol<level_locals_t> level{0x10A7190};
|
||||
|
||||
WEAK symbol<RTL_CRITICAL_SECTION> s_criticalSection{0x19FBA28};
|
||||
WEAK symbol<SOCKET> ip_socket{0x1A040C8};
|
||||
|
||||
WEAK symbol<source_s*> sourceFiles{0x7440E8};
|
||||
WEAK symbol<int> numtokens{0x7441F0};
|
||||
|
||||
WEAK symbol<uiInfo_s> uiInfoArray{0x1920470};
|
||||
|
||||
WEAK symbol<void*> DB_GetXAssetSizeHandlers{0x733408};
|
||||
WEAK symbol<void*> DB_XAssetPool{0x7337F8};
|
||||
WEAK symbol<unsigned int> g_poolSize{0x733510};
|
||||
} // namespace game
|
30
src/client/loader/binary_loader.cpp
Normal file
30
src/client/loader/binary_loader.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include <std_include.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/cryptography.hpp>
|
||||
|
||||
#include "binary_loader.hpp"
|
||||
|
||||
#define SP_HASH \
|
||||
"39E9C4FACEA4B19017BB8C2FC64E4149708927AC40C7C3DEACC25BDD25E93D32"
|
||||
|
||||
#define SP_XLABS_HASH \
|
||||
"05D499D77028859D4BA30C852DA85CCA5F02678B22AEA9E27D7C56973B14A0BC"
|
||||
|
||||
namespace binary_loader {
|
||||
std::string load_base() {
|
||||
std::string data;
|
||||
if (!utils::io::read_file("iw4sp.exe", &data)) {
|
||||
throw std::runtime_error("Failed to read game binary (iw4sp.exe)!");
|
||||
}
|
||||
|
||||
const auto hash = utils::cryptography::sha256::compute(data, true);
|
||||
if ((hash != SP_XLABS_HASH) && (hash != SP_HASH)) {
|
||||
throw std::runtime_error(
|
||||
"Your iw4sp.exe is incompatible with this client.");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string load() { return load_base(); }
|
||||
} // namespace binary_loader
|
5
src/client/loader/binary_loader.hpp
Normal file
5
src/client/loader/binary_loader.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace binary_loader {
|
||||
std::string load();
|
||||
}
|
19
src/client/loader/component_interface.hpp
Normal file
19
src/client/loader/component_interface.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
class component_interface {
|
||||
public:
|
||||
virtual ~component_interface() = default;
|
||||
|
||||
virtual void post_start() {}
|
||||
|
||||
virtual void post_load() {}
|
||||
|
||||
virtual void pre_destroy() {}
|
||||
|
||||
virtual void* load_import([[maybe_unused]] const std::string& library,
|
||||
[[maybe_unused]] const std::string& function) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool is_supported() { return true; }
|
||||
};
|
100
src/client/loader/component_loader.cpp
Normal file
100
src/client/loader/component_loader.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include <std_include.hpp>
|
||||
#include "component_loader.hpp"
|
||||
|
||||
void component_loader::register_component(
|
||||
std::unique_ptr<component_interface>&& component_) {
|
||||
get_components().push_back(std::move(component_));
|
||||
}
|
||||
|
||||
bool component_loader::post_start() {
|
||||
static auto handled = false;
|
||||
if (handled)
|
||||
return true;
|
||||
handled = true;
|
||||
|
||||
try {
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->post_start();
|
||||
}
|
||||
} catch (premature_shutdown_trigger&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool component_loader::post_load() {
|
||||
static auto handled = false;
|
||||
if (handled)
|
||||
return true;
|
||||
handled = true;
|
||||
|
||||
clean();
|
||||
|
||||
try {
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->post_load();
|
||||
}
|
||||
} catch (premature_shutdown_trigger&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void component_loader::pre_destroy() {
|
||||
static auto handled = false;
|
||||
if (handled)
|
||||
return;
|
||||
handled = true;
|
||||
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->pre_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void component_loader::clean() {
|
||||
auto& components = get_components();
|
||||
for (auto i = components.begin(); i != components.end();) {
|
||||
if (!(*i)->is_supported()) {
|
||||
(*i)->pre_destroy();
|
||||
i = components.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* component_loader::load_import(const std::string& library,
|
||||
const std::string& function) {
|
||||
void* function_ptr = nullptr;
|
||||
|
||||
for (const auto& component_ : get_components()) {
|
||||
auto* const component_function_ptr =
|
||||
component_->load_import(library, function);
|
||||
if (component_function_ptr) {
|
||||
function_ptr = component_function_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return function_ptr;
|
||||
}
|
||||
|
||||
void component_loader::trigger_premature_shutdown() {
|
||||
throw premature_shutdown_trigger();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<component_interface>>&
|
||||
component_loader::get_components() {
|
||||
using component_vector = std::vector<std::unique_ptr<component_interface>>;
|
||||
using component_vector_container =
|
||||
std::unique_ptr<component_vector, std::function<void(component_vector*)>>;
|
||||
|
||||
static component_vector_container components(
|
||||
new component_vector, [](component_vector* component_vector) {
|
||||
pre_destroy();
|
||||
delete component_vector;
|
||||
});
|
||||
|
||||
return *components;
|
||||
}
|
50
src/client/loader/component_loader.hpp
Normal file
50
src/client/loader/component_loader.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include "component_interface.hpp"
|
||||
|
||||
class component_loader final {
|
||||
public:
|
||||
class premature_shutdown_trigger final : public std::exception {
|
||||
[[nodiscard]] const char* what() const noexcept override {
|
||||
return "Premature shutdown requested";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class installer final {
|
||||
static_assert(std::is_base_of<component_interface, T>::value,
|
||||
"component has invalid base class");
|
||||
|
||||
public:
|
||||
installer() { register_component(std::make_unique<T>()); }
|
||||
};
|
||||
|
||||
template <typename T> static T* get() {
|
||||
for (const auto& component_ : get_components()) {
|
||||
if (typeid(*component_.get()) == typeid(T)) {
|
||||
return reinterpret_cast<T*>(component_.get());
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
register_component(std::unique_ptr<component_interface>&& component);
|
||||
|
||||
static bool post_start();
|
||||
static bool post_load();
|
||||
static void pre_destroy();
|
||||
static void clean();
|
||||
|
||||
static void* load_import(const std::string& library,
|
||||
const std::string& function);
|
||||
|
||||
static void trigger_premature_shutdown();
|
||||
|
||||
private:
|
||||
static std::vector<std::unique_ptr<component_interface>>& get_components();
|
||||
};
|
||||
|
||||
#define REGISTER_COMPONENT(name) \
|
||||
namespace { \
|
||||
static component_loader::installer<name> __component; \
|
||||
}
|
190
src/client/loader/loader.cpp
Normal file
190
src/client/loader/loader.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "loader.hpp"
|
||||
#include "tls.hpp"
|
||||
|
||||
FARPROC loader::load(const utils::nt::library& library,
|
||||
const std::string& buffer) const {
|
||||
|
||||
if (buffer.empty())
|
||||
return nullptr;
|
||||
|
||||
const utils::nt::library source(HMODULE(buffer.data()));
|
||||
if (!source)
|
||||
return nullptr;
|
||||
|
||||
this->load_sections(library, source);
|
||||
this->load_imports(library, source);
|
||||
this->load_tls(library, source);
|
||||
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(library.get_nt_headers(), 0x1000, PAGE_EXECUTE_READWRITE,
|
||||
&oldProtect);
|
||||
|
||||
library.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] =
|
||||
source.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
std::memmove(library.get_nt_headers(), source.get_nt_headers(),
|
||||
sizeof(IMAGE_NT_HEADERS) +
|
||||
source.get_nt_headers()->FileHeader.NumberOfSections *
|
||||
sizeof(IMAGE_SECTION_HEADER));
|
||||
|
||||
return FARPROC(library.get_ptr() + source.get_relative_entry_point());
|
||||
}
|
||||
|
||||
FARPROC loader::load_library(const std::string& filename) const {
|
||||
const auto target = utils::nt::library::load(filename);
|
||||
if (!target) {
|
||||
throw std::runtime_error{"Failed to map binary!"};
|
||||
}
|
||||
|
||||
const auto base = size_t(target.get_ptr());
|
||||
if (base != 0x400000) {
|
||||
throw std::runtime_error{
|
||||
utils::string::va("Binary was mapped at {0:#x} (instead of {1:#x}). "
|
||||
"Something is severely broken :(",
|
||||
base, 0x400000)};
|
||||
}
|
||||
|
||||
this->load_imports(target, target);
|
||||
this->load_tls(target, target);
|
||||
|
||||
return FARPROC(target.get_ptr() + target.get_relative_entry_point());
|
||||
}
|
||||
|
||||
void loader::set_import_resolver(
|
||||
const std::function<void*(const std::string&, const std::string&)>&
|
||||
resolver) {
|
||||
this->import_resolver_ = resolver;
|
||||
}
|
||||
|
||||
void loader::load_section(const utils::nt::library& target,
|
||||
const utils::nt::library& source,
|
||||
IMAGE_SECTION_HEADER* section) {
|
||||
void* target_ptr = target.get_ptr() + section->VirtualAddress;
|
||||
const void* source_ptr = source.get_ptr() + section->PointerToRawData;
|
||||
|
||||
if (PBYTE(target_ptr) >= (target.get_ptr() + BINARY_PAYLOAD_SIZE)) {
|
||||
throw std::runtime_error(
|
||||
"Section exceeds the binary payload size, please increase it!");
|
||||
}
|
||||
|
||||
if (section->SizeOfRawData > 0) {
|
||||
DWORD old_protect;
|
||||
VirtualProtect(target_ptr, section->Misc.VirtualSize,
|
||||
PAGE_EXECUTE_READWRITE, &old_protect);
|
||||
|
||||
std::memmove(target_ptr, source_ptr, section->SizeOfRawData);
|
||||
}
|
||||
}
|
||||
|
||||
void loader::load_sections(const utils::nt::library& target,
|
||||
const utils::nt::library& source) const {
|
||||
for (auto& section : source.get_section_headers()) {
|
||||
this->load_section(target, source, section);
|
||||
}
|
||||
}
|
||||
|
||||
void loader::load_imports(const utils::nt::library& target,
|
||||
const utils::nt::library& source) const {
|
||||
const auto import_directory =
|
||||
&source.get_optional_header()
|
||||
->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
|
||||
auto descriptor = PIMAGE_IMPORT_DESCRIPTOR(target.get_ptr() +
|
||||
import_directory->VirtualAddress);
|
||||
|
||||
while (descriptor->Name) {
|
||||
std::string name = LPSTR(target.get_ptr() + descriptor->Name);
|
||||
|
||||
auto name_table_entry = reinterpret_cast<uintptr_t*>(
|
||||
target.get_ptr() + descriptor->OriginalFirstThunk);
|
||||
auto address_table_entry =
|
||||
reinterpret_cast<uintptr_t*>(target.get_ptr() + descriptor->FirstThunk);
|
||||
|
||||
if (!descriptor->OriginalFirstThunk) {
|
||||
name_table_entry = reinterpret_cast<uintptr_t*>(target.get_ptr() +
|
||||
descriptor->FirstThunk);
|
||||
}
|
||||
|
||||
while (*name_table_entry) {
|
||||
FARPROC function = nullptr;
|
||||
std::string function_name;
|
||||
const char* function_procname;
|
||||
|
||||
if (IMAGE_SNAP_BY_ORDINAL(*name_table_entry)) {
|
||||
function_name = "#" + std::to_string(IMAGE_ORDINAL(*name_table_entry));
|
||||
function_procname = MAKEINTRESOURCEA(IMAGE_ORDINAL(*name_table_entry));
|
||||
} else {
|
||||
auto* import =
|
||||
PIMAGE_IMPORT_BY_NAME(target.get_ptr() + *name_table_entry);
|
||||
function_name = import->Name;
|
||||
function_procname = function_name.data();
|
||||
}
|
||||
|
||||
if (this->import_resolver_)
|
||||
function = FARPROC(this->import_resolver_(name, function_name));
|
||||
|
||||
if (!function) {
|
||||
auto library = utils::nt::library::load(name);
|
||||
if (library) {
|
||||
function = GetProcAddress(library, function_procname);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function) {
|
||||
throw std::runtime_error(
|
||||
utils::string::va("Unable to load import '{0}' from library {1}'",
|
||||
function_name, name));
|
||||
}
|
||||
|
||||
utils::hook::set(address_table_entry,
|
||||
reinterpret_cast<uintptr_t>(function));
|
||||
|
||||
name_table_entry++;
|
||||
address_table_entry++;
|
||||
}
|
||||
|
||||
descriptor++;
|
||||
}
|
||||
}
|
||||
|
||||
void loader::load_tls(const utils::nt::library& target,
|
||||
const utils::nt::library& source) const {
|
||||
if (!target.get_optional_header()
|
||||
->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]
|
||||
.Size) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* target_tls = tls::allocate_tls_index();
|
||||
const auto* const source_tls = reinterpret_cast<PIMAGE_TLS_DIRECTORY>(
|
||||
target.get_ptr() + target.get_optional_header()
|
||||
->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]
|
||||
.VirtualAddress);
|
||||
|
||||
auto* target_tls_start = PVOID(target_tls->StartAddressOfRawData);
|
||||
auto* tls_start = PVOID(source_tls->StartAddressOfRawData);
|
||||
const auto tls_size =
|
||||
source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData;
|
||||
const auto tls_index = *reinterpret_cast<DWORD*>(target_tls->AddressOfIndex);
|
||||
|
||||
utils::hook::set<DWORD>(source_tls->AddressOfIndex, tls_index);
|
||||
|
||||
if (target_tls->AddressOfCallBacks) {
|
||||
utils::hook::set<void*>(target_tls->AddressOfCallBacks, nullptr);
|
||||
}
|
||||
|
||||
DWORD old_protect;
|
||||
VirtualProtect(target_tls_start, tls_size, PAGE_READWRITE, &old_protect);
|
||||
|
||||
auto* const tls_base =
|
||||
*reinterpret_cast<LPVOID*>(__readfsdword(0x2C) + 4 * tls_index);
|
||||
std::memmove(tls_base, tls_start, tls_size);
|
||||
std::memmove(target_tls_start, tls_start, tls_size);
|
||||
|
||||
VirtualProtect(target_tls, sizeof(*target_tls), PAGE_READWRITE, &old_protect);
|
||||
*target_tls = *source_tls;
|
||||
}
|
25
src/client/loader/loader.hpp
Normal file
25
src/client/loader/loader.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
class loader final {
|
||||
public:
|
||||
FARPROC load(const utils::nt::library& library,
|
||||
const std::string& buffer) const;
|
||||
FARPROC load_library(const std::string& filename) const;
|
||||
|
||||
void set_import_resolver(
|
||||
const std::function<void*(const std::string&, const std::string&)>&
|
||||
resolver);
|
||||
|
||||
private:
|
||||
std::function<void*(const std::string&, const std::string&)> import_resolver_;
|
||||
|
||||
static void load_section(const utils::nt::library& target,
|
||||
const utils::nt::library& source,
|
||||
IMAGE_SECTION_HEADER* section);
|
||||
void load_sections(const utils::nt::library& target,
|
||||
const utils::nt::library& source) const;
|
||||
void load_imports(const utils::nt::library& target,
|
||||
const utils::nt::library& source) const;
|
||||
void load_tls(const utils::nt::library& target,
|
||||
const utils::nt::library& source) const;
|
||||
};
|
34
src/client/loader/tls.cpp
Normal file
34
src/client/loader/tls.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include <std_include.hpp>
|
||||
#include "tls.hpp"
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/binary_resource.hpp>
|
||||
|
||||
#include <resource.hpp>
|
||||
|
||||
namespace tls {
|
||||
namespace {
|
||||
utils::binary_resource tls_dll_file(TLS_DLL, "iw4sp-tlsdll.dll");
|
||||
}
|
||||
|
||||
PIMAGE_TLS_DIRECTORY allocate_tls_index() {
|
||||
static auto already_allocated = false;
|
||||
if (already_allocated) {
|
||||
throw std::runtime_error(
|
||||
"Currently only a single allocation is supported!");
|
||||
}
|
||||
|
||||
already_allocated = true;
|
||||
|
||||
const auto dll_path = tls_dll_file.get_extracted_file();
|
||||
const auto tls_dll = utils::nt::library::load(dll_path);
|
||||
if (!tls_dll) {
|
||||
throw std::runtime_error("Failed to load TLS DLL");
|
||||
}
|
||||
|
||||
return reinterpret_cast<PIMAGE_TLS_DIRECTORY>(
|
||||
tls_dll.get_ptr() + tls_dll.get_optional_header()
|
||||
->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]
|
||||
.VirtualAddress);
|
||||
}
|
||||
} // namespace tls
|
5
src/client/loader/tls.hpp
Normal file
5
src/client/loader/tls.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace tls {
|
||||
PIMAGE_TLS_DIRECTORY allocate_tls_index();
|
||||
}
|
150
src/client/main.cpp
Normal file
150
src/client/main.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
#include "std_include.hpp"
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "loader/binary_loader.hpp"
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "loader/loader.hpp"
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
#include <version.hpp>
|
||||
#include <DbgHelp.h>
|
||||
|
||||
LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info) {
|
||||
if (exception_info->ExceptionRecord->ExceptionCode == 0x406D1388) {
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
if (exception_info->ExceptionRecord->ExceptionCode < 0x80000000 ||
|
||||
exception_info->ExceptionRecord->ExceptionCode == 0xE06D7363) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
MINIDUMP_EXCEPTION_INFORMATION exception_information = {
|
||||
GetCurrentThreadId(), exception_info, FALSE};
|
||||
const auto type = MiniDumpIgnoreInaccessibleMemory //
|
||||
| MiniDumpWithHandleData //
|
||||
| MiniDumpScanMemory //
|
||||
| MiniDumpWithProcessThreadData //
|
||||
| MiniDumpWithFullMemoryInfo //
|
||||
| MiniDumpWithThreadInfo;
|
||||
|
||||
CreateDirectoryA("minidumps", nullptr);
|
||||
const auto* file_name =
|
||||
utils::string::va("minidumps\\iw4-sp_{0}.dmp", SHORTVERSION);
|
||||
constexpr auto file_share = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
const auto file_handle =
|
||||
CreateFileA(file_name, GENERIC_WRITE | GENERIC_READ, file_share, nullptr,
|
||||
CREATE_ALWAYS, NULL, nullptr);
|
||||
|
||||
if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
|
||||
file_handle, static_cast<MINIDUMP_TYPE>(type),
|
||||
&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);
|
||||
MessageBoxA(nullptr, buf, "Fatal Error", MB_ICONERROR);
|
||||
}
|
||||
|
||||
CloseHandle(file_handle);
|
||||
TerminateProcess(GetCurrentProcess(),
|
||||
exception_info->ExceptionRecord->ExceptionCode);
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
DECLSPEC_NORETURN void WINAPI exit_hook(const int code) {
|
||||
component_loader::pre_destroy();
|
||||
std::exit(code);
|
||||
}
|
||||
|
||||
FARPROC load_binary() {
|
||||
loader loader;
|
||||
utils::nt::library self;
|
||||
|
||||
loader.set_import_resolver(
|
||||
[self](const std::string& library, const std::string& function) -> void* {
|
||||
if (library == "steam_api.dll") {
|
||||
return self.get_proc<FARPROC>(function);
|
||||
}
|
||||
if (function == "ExitProcess") {
|
||||
return exit_hook;
|
||||
}
|
||||
|
||||
return component_loader::load_import(library, function);
|
||||
});
|
||||
|
||||
const auto buffer = binary_loader::load();
|
||||
return loader.load(self, buffer);
|
||||
}
|
||||
|
||||
void enable_dpi_awareness() {
|
||||
const utils::nt::library user32{"user32.dll"};
|
||||
const auto set_dpi =
|
||||
user32 ? user32.get_proc<BOOL(WINAPI*)(DPI_AWARENESS_CONTEXT)>(
|
||||
"SetProcessDpiAwarenessContext")
|
||||
: nullptr;
|
||||
if (set_dpi) {
|
||||
set_dpi(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
}
|
||||
}
|
||||
|
||||
void apply_environment() {
|
||||
char* buffer{};
|
||||
std::size_t size{};
|
||||
if (_dupenv_s(&buffer, &size, "MW2_INSTALL") != 0 || buffer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto _ = gsl::finally([&] { std::free(buffer); });
|
||||
|
||||
SetCurrentDirectoryA(buffer);
|
||||
SetDllDirectoryA(buffer);
|
||||
}
|
||||
|
||||
int main() {
|
||||
AddVectoredExceptionHandler(0, exception_handler);
|
||||
SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
|
||||
|
||||
FARPROC entry_point;
|
||||
enable_dpi_awareness();
|
||||
|
||||
std::srand(std::uint32_t(time(nullptr)) ^
|
||||
~(GetTickCount() * GetCurrentProcessId()));
|
||||
|
||||
{
|
||||
auto premature_shutdown = true;
|
||||
const auto _0 = gsl::finally([&premature_shutdown] {
|
||||
if (premature_shutdown) {
|
||||
component_loader::pre_destroy();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
apply_environment();
|
||||
|
||||
if (!component_loader::post_start())
|
||||
return 0;
|
||||
|
||||
entry_point = load_binary();
|
||||
if (!entry_point) {
|
||||
throw std::runtime_error("Unable to load binary into memory");
|
||||
}
|
||||
|
||||
if (!component_loader::post_load())
|
||||
return 0;
|
||||
|
||||
premature_shutdown = false;
|
||||
} catch (const std::exception& ex) {
|
||||
MessageBoxA(nullptr, ex.what(), "ERROR", MB_ICONERROR);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return entry_point();
|
||||
}
|
||||
|
||||
int APIENTRY WinMain(HINSTANCE, HINSTANCE, PSTR, int) { return main(); }
|
8
src/client/resource.hpp
Normal file
8
src/client/resource.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#define ID_ICON 102
|
||||
|
||||
#define TLS_DLL 301
|
||||
#define RUNNER 302
|
||||
|
||||
#define ICON_IMAGE 303
|
117
src/client/resource.rc
Normal file
117
src/client/resource.rc
Normal file
@ -0,0 +1,117 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#pragma code_page(65001)
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "windows.h"
|
||||
#include "version.h"
|
||||
#include "resource.hpp"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""windows.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,0
|
||||
PRODUCTVERSION 1,0,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "AlterWare"
|
||||
VALUE "FileDescription", "IW4 SP client modification"
|
||||
VALUE "FileVersion", VERSION_FILE
|
||||
VALUE "InternalName", "iw4-sp"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2023 AlterWare. All rights reserved."
|
||||
VALUE "Licence", "GPLv3"
|
||||
VALUE "Info", "https://alterware.dev"
|
||||
VALUE "OriginalFilename", "iw4-sp.exe"
|
||||
VALUE "ProductName", "iw4-sp"
|
||||
VALUE "ProductVersion", VERSION_PRODUCT
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Binary Data
|
||||
//
|
||||
|
||||
ID_ICON ICON "resources/icon.ico"
|
||||
|
||||
#ifdef _DEBUG
|
||||
TLS_DLL RCDATA "../../build/bin/Win32/Debug/tlsdll.dll"
|
||||
#else
|
||||
TLS_DLL RCDATA "../../build/bin/Win32/Release/tlsdll.dll"
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
RUNNER RCDATA "../../build/bin/Win32/Debug/runner.exe"
|
||||
#else
|
||||
RUNNER RCDATA "../../build/bin/Win32/Release/runner.exe"
|
||||
#endif
|
||||
|
||||
ICON_IMAGE RCDATA "resources/icon.png"
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
BIN
src/client/resources/icon.ico
Normal file
BIN
src/client/resources/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 200 KiB |
BIN
src/client/resources/icon.png
Normal file
BIN
src/client/resources/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
27
src/client/std_include.cpp
Normal file
27
src/client/std_include.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "std_include.hpp"
|
||||
|
||||
#pragma comment(linker, "/merge:.data=.cld")
|
||||
#pragma comment(linker, "/merge:.rdata=.clr")
|
||||
#pragma comment(linker, "/merge:.cl=.main")
|
||||
#pragma comment(linker, "/merge:.text=.main")
|
||||
#pragma comment(linker, "/base:0x400000")
|
||||
|
||||
#pragma bss_seg(".payload")
|
||||
char payload_data[BINARY_PAYLOAD_SIZE];
|
||||
|
||||
extern "C" {
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
|
||||
__declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
char* get_payload_data() { return payload_data; }
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
int s_read_arc4random(void*, std::size_t) { return -1; }
|
||||
|
||||
int s_read_getrandom(void*, std::size_t) { return -1; }
|
||||
|
||||
int s_read_urandom(void*, std::size_t) { return -1; }
|
||||
}
|
57
src/client/std_include.hpp
Normal file
57
src/client/std_include.hpp
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#define BINARY_PAYLOAD_SIZE 0x0A000000
|
||||
|
||||
#define DLL_EXPORT extern "C" __declspec(dllexport)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#define BASEGAME "spdata"
|
||||
#define CLIENT_CONFIG "iw4_sp_config.cfg"
|
||||
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#undef GetObject
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
#pragma comment(lib, "Crypt32.lib")
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
||||
#pragma warning(disable : 4100)
|
||||
#pragma warning(disable : 4244)
|
||||
|
||||
// I have witnessed clang re-ordering my included even when
|
||||
// it was set not to do it. :(
|
||||
// clang-format off
|
||||
#include "game/structs.hpp"
|
||||
#include "game/game.hpp"
|
||||
// clang-format on
|
117
src/client/steam/interface.cpp
Normal file
117
src/client/steam/interface.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
#include <std_include.hpp>
|
||||
#include "interface.hpp"
|
||||
|
||||
#include <utils/memory.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
|
||||
#include <udis86.h>
|
||||
|
||||
namespace steam {
|
||||
interface::interface() : interface(nullptr) {}
|
||||
|
||||
interface::interface(void* interface_ptr)
|
||||
: interface_ptr_(static_cast<void***>(interface_ptr)) {}
|
||||
|
||||
interface::operator bool() const { return this->interface_ptr_ != nullptr; }
|
||||
|
||||
interface::method interface::find_method(const std::string& name) {
|
||||
const auto method_entry = this->methods_.find(name);
|
||||
if (method_entry != this->methods_.end()) {
|
||||
return method_entry->second;
|
||||
}
|
||||
|
||||
return this->search_method(name);
|
||||
}
|
||||
|
||||
interface::method interface::search_method(const std::string& name) {
|
||||
if (!utils::memory::is_bad_read_ptr(this->interface_ptr_)) {
|
||||
auto vftbl = *this->interface_ptr_;
|
||||
|
||||
while (!utils::memory::is_bad_read_ptr(vftbl) &&
|
||||
!utils::memory::is_bad_code_ptr(*vftbl)) {
|
||||
const auto result = this->analyze_method(*vftbl);
|
||||
if (result.param_size_found && result.name_found) {
|
||||
const method method_result{*vftbl, result.param_size};
|
||||
this->methods_[result.name] = method_result;
|
||||
|
||||
if (result.name == name) {
|
||||
return method_result;
|
||||
}
|
||||
}
|
||||
|
||||
++vftbl;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
interface::method_result interface::analyze_method(const void* method_ptr) {
|
||||
method_result result;
|
||||
if (utils::memory::is_bad_code_ptr(method_ptr))
|
||||
return result;
|
||||
|
||||
ud_t ud;
|
||||
ud_init(&ud);
|
||||
ud_set_mode(&ud, 32);
|
||||
ud_set_pc(&ud, reinterpret_cast<std::uint64_t>(method_ptr));
|
||||
ud_set_input_buffer(&ud, static_cast<const std::uint8_t*>(method_ptr),
|
||||
INT32_MAX);
|
||||
|
||||
while (true) {
|
||||
ud_disassemble(&ud);
|
||||
|
||||
if (ud_insn_mnemonic(&ud) == UD_Iret && !result.param_size_found) {
|
||||
const ud_operand* operand = ud_insn_opr(&ud, 0);
|
||||
if (operand && operand->type == UD_OP_IMM && operand->size == 16) {
|
||||
result.param_size = operand->lval.uword;
|
||||
} else {
|
||||
result.param_size = 0;
|
||||
}
|
||||
|
||||
result.param_size_found = true;
|
||||
}
|
||||
|
||||
if (ud_insn_mnemonic(&ud) == UD_Ipush && !result.name_found) {
|
||||
const auto operand = ud_insn_opr(&ud, 0);
|
||||
if (operand->type == UD_OP_IMM && operand->size == 32) {
|
||||
auto* operand_ptr = reinterpret_cast<char*>(operand->lval.udword);
|
||||
if (!utils::memory::is_bad_read_ptr(operand_ptr) &&
|
||||
this->is_rdata(operand_ptr)) {
|
||||
result.name = operand_ptr;
|
||||
result.name_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*reinterpret_cast<unsigned char*>(ud.pc) == 0xCC)
|
||||
break; // int 3
|
||||
if (result.param_size_found && result.name_found)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool interface::is_rdata(void* pointer) {
|
||||
const auto pointer_lib = utils::nt::library::get_by_address(pointer);
|
||||
|
||||
for (const auto& section : pointer_lib.get_section_headers()) {
|
||||
const auto size = sizeof(section->Name);
|
||||
char name[size + 1];
|
||||
name[size] = 0;
|
||||
std::memcpy(name, section->Name, size);
|
||||
|
||||
if (std::strcmp(name, ".rdata") == 0) {
|
||||
const auto target = reinterpret_cast<std::size_t>(pointer);
|
||||
const size_t source_start =
|
||||
size_t(pointer_lib.get_ptr()) + section->PointerToRawData;
|
||||
const size_t source_end = source_start + section->SizeOfRawData;
|
||||
|
||||
return target >= source_start && target <= source_end;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace steam
|
82
src/client/steam/interface.hpp
Normal file
82
src/client/steam/interface.hpp
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef interface
|
||||
#undef interface
|
||||
#endif
|
||||
|
||||
namespace steam {
|
||||
template <std::size_t...>
|
||||
struct argument_size_calculator final : std::integral_constant<std::size_t, 0> {
|
||||
};
|
||||
|
||||
template <std::size_t X, std::size_t... Xs>
|
||||
struct argument_size_calculator<X, Xs...> final
|
||||
: std::integral_constant<std::size_t,
|
||||
X + ((argument_size_calculator<Xs...>::value +
|
||||
(sizeof(void*) - 1)) &
|
||||
~(sizeof(void*) - 1))> {};
|
||||
|
||||
class interface final {
|
||||
public:
|
||||
class method final {
|
||||
public:
|
||||
void* pointer = nullptr;
|
||||
std::size_t param_size = 0;
|
||||
};
|
||||
|
||||
class method_result final {
|
||||
public:
|
||||
std::string name;
|
||||
std::size_t param_size = 0;
|
||||
bool name_found = false;
|
||||
bool param_size_found = false;
|
||||
};
|
||||
|
||||
interface();
|
||||
interface(void* interface_ptr);
|
||||
|
||||
operator bool() const;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke(const std::string& method_name, Args... args) {
|
||||
if (!this->interface_ptr_) {
|
||||
throw std::runtime_error("Invalid interface pointer");
|
||||
}
|
||||
|
||||
const auto method_result = this->find_method(method_name);
|
||||
if (!method_result.pointer) {
|
||||
throw std::runtime_error("Unable to find desired method");
|
||||
}
|
||||
|
||||
constexpr std::size_t passed_argc =
|
||||
argument_size_calculator<sizeof(Args)...>::value;
|
||||
if (passed_argc != method_result.param_size) {
|
||||
throw std::runtime_error("Invalid argument count");
|
||||
}
|
||||
|
||||
return static_cast<T(__thiscall*)(void*, Args...)>(method_result.pointer)(
|
||||
this->interface_ptr_, args...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke(const std::size_t table_entry, Args... args) {
|
||||
if (!this->interface_ptr_) {
|
||||
throw std::runtime_error("Invalid interface pointer");
|
||||
}
|
||||
|
||||
return static_cast<T(__thiscall*)(void*, Args...)>(
|
||||
(*this->interface_ptr_)[table_entry])(this->interface_ptr_, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
void*** interface_ptr_;
|
||||
std::unordered_map<std::string, method> methods_;
|
||||
|
||||
method find_method(const std::string& name);
|
||||
method search_method(const std::string& name);
|
||||
|
||||
method_result analyze_method(const void* method_ptr);
|
||||
|
||||
bool is_rdata(void* pointer);
|
||||
};
|
||||
} // namespace steam
|
37
src/client/steam/interfaces/apps.cpp
Normal file
37
src/client/steam/interfaces/apps.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
namespace steam {
|
||||
bool apps::BIsSubscribed() { return true; }
|
||||
|
||||
bool apps::BIsLowViolence() { return false; }
|
||||
|
||||
bool apps::BIsCybercafe() { return false; }
|
||||
|
||||
bool apps::BIsVACBanned() { return false; }
|
||||
|
||||
const char* apps::GetCurrentGameLanguage() { return "english"; }
|
||||
|
||||
const char* apps::GetAvailableGameLanguages() { return "english"; }
|
||||
|
||||
bool apps::BIsSubscribedApp(unsigned int appID) { return true; }
|
||||
|
||||
bool apps::BIsDlcInstalled(unsigned int appID) { return true; }
|
||||
|
||||
unsigned int apps::GetEarliestPurchaseUnixTime(unsigned int nAppID) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool apps::BIsSubscribedFromFreeWeekend() { return false; }
|
||||
|
||||
int apps::GetDLCCount() { return 0; }
|
||||
|
||||
bool apps::BGetDLCDataByIndex(int iDLC, unsigned int* pAppID, bool* pbAvailable,
|
||||
char* pchName, int cchNameBufferSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void apps::InstallDLC(unsigned int nAppID) {}
|
||||
|
||||
void apps::UninstallDLC(unsigned int nAppID) {}
|
||||
} // namespace steam
|
26
src/client/steam/interfaces/apps.hpp
Normal file
26
src/client/steam/interfaces/apps.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
namespace steam {
|
||||
class apps {
|
||||
protected:
|
||||
~apps() = default;
|
||||
|
||||
public:
|
||||
virtual bool BIsSubscribed();
|
||||
virtual bool BIsLowViolence();
|
||||
virtual bool BIsCybercafe();
|
||||
virtual bool BIsVACBanned();
|
||||
virtual const char* GetCurrentGameLanguage();
|
||||
virtual const char* GetAvailableGameLanguages();
|
||||
virtual bool BIsSubscribedApp(unsigned int appID);
|
||||
virtual bool BIsDlcInstalled(unsigned int appID);
|
||||
virtual unsigned int GetEarliestPurchaseUnixTime(unsigned int nAppID);
|
||||
virtual bool BIsSubscribedFromFreeWeekend();
|
||||
virtual int GetDLCCount();
|
||||
virtual bool BGetDLCDataByIndex(int iDLC, unsigned int* pAppID,
|
||||
bool* pbAvailable, char* pchName,
|
||||
int cchNameBufferSize);
|
||||
virtual void InstallDLC(unsigned int nAppID);
|
||||
virtual void UninstallDLC(unsigned int nAppID);
|
||||
};
|
||||
} // namespace steam
|
130
src/client/steam/interfaces/friends.cpp
Normal file
130
src/client/steam/interfaces/friends.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
namespace steam {
|
||||
const char* friends::GetPersonaName() { return "GlaDos"; }
|
||||
|
||||
void friends::SetPersonaName(const char* pchPersonaName) {}
|
||||
|
||||
int friends::GetPersonaState() { return 1; }
|
||||
|
||||
int friends::GetFriendCount(int eFriendFlags) { return 0; }
|
||||
|
||||
steam_id friends::GetFriendByIndex(int iFriend, int iFriendFlags) {
|
||||
return steam_id();
|
||||
}
|
||||
|
||||
int friends::GetFriendRelationship(steam_id steamIDFriend) { return 0; }
|
||||
|
||||
int friends::GetFriendPersonaState(steam_id steamIDFriend) { return 0; }
|
||||
|
||||
const char* friends::GetFriendPersonaName(steam_id steamIDFriend) { return ""; }
|
||||
|
||||
bool friends::GetFriendGamePlayed(steam_id steamIDFriend,
|
||||
void* pFriendGameInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* friends::GetFriendPersonaNameHistory(steam_id steamIDFriend,
|
||||
int iPersonaName) {
|
||||
return "";
|
||||
}
|
||||
|
||||
bool friends::HasFriend(steam_id steamIDFriend, int eFriendFlags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int friends::GetClanCount() { return 0; }
|
||||
|
||||
steam_id friends::GetClanByIndex(int iClan) { return steam_id(); }
|
||||
|
||||
const char* friends::GetClanName(steam_id steamIDClan) { return "3arc"; }
|
||||
|
||||
const char* friends::GetClanTag(steam_id steamIDClan) {
|
||||
return this->GetClanName(steamIDClan);
|
||||
}
|
||||
|
||||
int friends::GetFriendCountFromSource(steam_id steamIDSource) { return 0; }
|
||||
|
||||
steam_id friends::GetFriendFromSourceByIndex(steam_id steamIDSource,
|
||||
int iFriend) {
|
||||
return steam_id();
|
||||
}
|
||||
|
||||
bool friends::IsUserInSource(steam_id steamIDUser, steam_id steamIDSource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void friends::SetInGameVoiceSpeaking(steam_id steamIDUser, bool bSpeaking) {}
|
||||
|
||||
void friends::ActivateGameOverlay(const char* pchDialog) {}
|
||||
|
||||
void friends::ActivateGameOverlayToUser(const char* pchDialog,
|
||||
steam_id steamID) {}
|
||||
|
||||
void friends::ActivateGameOverlayToWebPage(const char* pchURL) {}
|
||||
|
||||
void friends::ActivateGameOverlayToStore(unsigned int nAppID) {
|
||||
OutputDebugStringA("Store requested!");
|
||||
}
|
||||
|
||||
void friends::SetPlayedWith(steam_id steamIDUserPlayedWith) {}
|
||||
|
||||
void friends::ActivateGameOverlayInviteDialog(steam_id steamIDLobby) {}
|
||||
|
||||
int friends::GetSmallFriendAvatar(steam_id steamIDFriend) { return 0; }
|
||||
|
||||
int friends::GetMediumFriendAvatar(steam_id steamIDFriend) { return 0; }
|
||||
|
||||
int friends::GetLargeFriendAvatar(steam_id steamIDFriend) { return 0; }
|
||||
|
||||
bool friends::RequestUserInformation(steam_id steamIDUser,
|
||||
bool bRequireNameOnly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned __int64 friends::RequestClanOfficerList(steam_id steamIDClan) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
steam_id friends::GetClanOwner(steam_id steamIDClan) { return steam_id(); }
|
||||
|
||||
int friends::GetClanOfficerCount(steam_id steamIDClan) { return 0; }
|
||||
|
||||
steam_id friends::GetClanOfficerByIndex(steam_id steamIDClan, int iOfficer) {
|
||||
return steam_id();
|
||||
}
|
||||
|
||||
int friends::GetUserRestrictions() { return 0; }
|
||||
|
||||
bool friends::SetRichPresence(const char* pchKey, const char* pchValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void friends::ClearRichPresence() {}
|
||||
|
||||
const char* friends::GetFriendRichPresence(steam_id steamIDFriend,
|
||||
const char* pchKey) {
|
||||
return "";
|
||||
}
|
||||
|
||||
int friends::GetFriendRichPresenceKeyCount(steam_id steamIDFriend) { return 0; }
|
||||
|
||||
const char* friends::GetFriendRichPresenceKeyByIndex(steam_id steamIDFriend,
|
||||
int iKey) {
|
||||
return "a";
|
||||
}
|
||||
|
||||
bool friends::InviteUserToGame(steam_id steamIDFriend,
|
||||
const char* pchConnectString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int friends::GetCoplayFriendCount() { return 0; }
|
||||
|
||||
steam_id friends::GetCoplayFriend(int iCoplayFriend) { return steam_id(); }
|
||||
|
||||
int friends::GetFriendCoplayTime(steam_id steamIDFriend) { return 0; }
|
||||
|
||||
unsigned int friends::GetFriendCoplayGame(steam_id steamIDFriend) { return 0; }
|
||||
} // namespace steam
|
62
src/client/steam/interfaces/friends.hpp
Normal file
62
src/client/steam/interfaces/friends.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
namespace steam {
|
||||
class friends {
|
||||
protected:
|
||||
~friends() = default;
|
||||
|
||||
public:
|
||||
virtual const char* GetPersonaName();
|
||||
virtual void SetPersonaName(const char* pchPersonaName);
|
||||
virtual int GetPersonaState();
|
||||
virtual int GetFriendCount(int eFriendFlags);
|
||||
virtual steam_id GetFriendByIndex(int iFriend, int iFriendFlags);
|
||||
virtual int GetFriendRelationship(steam_id steamIDFriend);
|
||||
virtual int GetFriendPersonaState(steam_id steamIDFriend);
|
||||
virtual const char* GetFriendPersonaName(steam_id steamIDFriend);
|
||||
virtual bool GetFriendGamePlayed(steam_id steamIDFriend,
|
||||
void* pFriendGameInfo);
|
||||
virtual const char* GetFriendPersonaNameHistory(steam_id steamIDFriend,
|
||||
int iPersonaName);
|
||||
virtual bool HasFriend(steam_id steamIDFriend, int eFriendFlags);
|
||||
virtual int GetClanCount();
|
||||
virtual steam_id GetClanByIndex(int iClan);
|
||||
virtual const char* GetClanName(steam_id steamIDClan);
|
||||
virtual const char* GetClanTag(steam_id steamIDClan);
|
||||
virtual int GetFriendCountFromSource(steam_id steamIDSource);
|
||||
virtual steam_id GetFriendFromSourceByIndex(steam_id steamIDSource,
|
||||
int iFriend);
|
||||
virtual bool IsUserInSource(steam_id steamIDUser, steam_id steamIDSource);
|
||||
virtual void SetInGameVoiceSpeaking(steam_id steamIDUser, bool bSpeaking);
|
||||
virtual void ActivateGameOverlay(const char* pchDialog);
|
||||
virtual void ActivateGameOverlayToUser(const char* pchDialog,
|
||||
steam_id steamID);
|
||||
virtual void ActivateGameOverlayToWebPage(const char* pchURL);
|
||||
virtual void ActivateGameOverlayToStore(unsigned int nAppID);
|
||||
virtual void SetPlayedWith(steam_id steamIDUserPlayedWith);
|
||||
virtual void ActivateGameOverlayInviteDialog(steam_id steamIDLobby);
|
||||
virtual int GetSmallFriendAvatar(steam_id steamIDFriend);
|
||||
virtual int GetMediumFriendAvatar(steam_id steamIDFriend);
|
||||
virtual int GetLargeFriendAvatar(steam_id steamIDFriend);
|
||||
virtual bool RequestUserInformation(steam_id steamIDUser,
|
||||
bool bRequireNameOnly);
|
||||
virtual unsigned __int64 RequestClanOfficerList(steam_id steamIDClan);
|
||||
virtual steam_id GetClanOwner(steam_id steamIDClan);
|
||||
virtual int GetClanOfficerCount(steam_id steamIDClan);
|
||||
virtual steam_id GetClanOfficerByIndex(steam_id steamIDClan, int iOfficer);
|
||||
virtual int GetUserRestrictions();
|
||||
virtual bool SetRichPresence(const char* pchKey, const char* pchValue);
|
||||
virtual void ClearRichPresence();
|
||||
virtual const char* GetFriendRichPresence(steam_id steamIDFriend,
|
||||
const char* pchKey);
|
||||
virtual int GetFriendRichPresenceKeyCount(steam_id steamIDFriend);
|
||||
virtual const char* GetFriendRichPresenceKeyByIndex(steam_id steamIDFriend,
|
||||
int iKey);
|
||||
virtual bool InviteUserToGame(steam_id steamIDFriend,
|
||||
const char* pchConnectString);
|
||||
virtual int GetCoplayFriendCount();
|
||||
virtual steam_id GetCoplayFriend(int iCoplayFriend);
|
||||
virtual int GetFriendCoplayTime(steam_id steamIDFriend);
|
||||
virtual unsigned int GetFriendCoplayGame(steam_id steamIDFriend);
|
||||
};
|
||||
} // namespace steam
|
74
src/client/steam/interfaces/game_server.cpp
Normal file
74
src/client/steam/interfaces/game_server.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
namespace steam {
|
||||
void game_server::LogOn() {}
|
||||
|
||||
void game_server::LogOff() {}
|
||||
|
||||
bool game_server::LoggedOn() { return true; }
|
||||
|
||||
bool game_server::Secure() { return false; }
|
||||
|
||||
steam_id game_server::GetSteamID() { return steam_id(); }
|
||||
|
||||
bool game_server::SendUserConnectAndAuthenticate(unsigned int unIPClient,
|
||||
const void* pvAuthBlob,
|
||||
unsigned int cubAuthBlobSize,
|
||||
steam_id* pSteamIDUser) {
|
||||
return true;
|
||||
}
|
||||
|
||||
steam_id game_server::CreateUnauthenticatedUserConnection() {
|
||||
return steam_id();
|
||||
}
|
||||
|
||||
void game_server::SendUserDisconnect(steam_id steamIDUser) {}
|
||||
|
||||
bool game_server::UpdateUserData(steam_id steamIDUser,
|
||||
const char* pchPlayerName,
|
||||
unsigned int uScore) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game_server::SetServerType(unsigned int unServerFlags,
|
||||
unsigned int unGameIP,
|
||||
unsigned short unGamePort,
|
||||
unsigned short unSpectatorPort,
|
||||
unsigned short usQueryPort,
|
||||
const char* pchGameDir, const char* pchVersion,
|
||||
bool bLANMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void game_server::UpdateServerStatus(int cPlayers, int cPlayersMax,
|
||||
int cBotPlayers, const char* pchServerName,
|
||||
const char* pSpectatorServerName,
|
||||
const char* pchMapName) {}
|
||||
|
||||
void game_server::UpdateSpectatorPort(unsigned short unSpectatorPort) {}
|
||||
|
||||
void game_server::SetGameType(const char* pchGameType) {}
|
||||
|
||||
bool game_server::GetUserAchievementStatus(steam_id steamID,
|
||||
const char* pchAchievementName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void game_server::GetGameplayStats() {}
|
||||
|
||||
unsigned __int64 game_server::GetServerReputation() { return 0; }
|
||||
|
||||
bool game_server::RequestUserGroupStatus(steam_id steamIDUser,
|
||||
steam_id steamIDGroup) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int game_server::GetPublicIP() { return 0; }
|
||||
|
||||
void game_server::SetGameData(const char* pchGameData) {}
|
||||
|
||||
int game_server::UserHasLicenseForApp(steam_id steamID, unsigned int appID) {
|
||||
return 0;
|
||||
}
|
||||
} // namespace steam
|
43
src/client/steam/interfaces/game_server.hpp
Normal file
43
src/client/steam/interfaces/game_server.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
namespace steam {
|
||||
class game_server {
|
||||
protected:
|
||||
~game_server() = default;
|
||||
|
||||
public:
|
||||
virtual void LogOn();
|
||||
virtual void LogOff();
|
||||
virtual bool LoggedOn();
|
||||
virtual bool Secure();
|
||||
virtual steam_id GetSteamID();
|
||||
virtual bool SendUserConnectAndAuthenticate(unsigned int unIPClient,
|
||||
const void* pvAuthBlob,
|
||||
unsigned int cubAuthBlobSize,
|
||||
steam_id* pSteamIDUser);
|
||||
virtual steam_id CreateUnauthenticatedUserConnection();
|
||||
virtual void SendUserDisconnect(steam_id steamIDUser);
|
||||
virtual bool UpdateUserData(steam_id steamIDUser, const char* pchPlayerName,
|
||||
unsigned int uScore);
|
||||
virtual bool SetServerType(unsigned int unServerFlags, unsigned int unGameIP,
|
||||
unsigned short unGamePort,
|
||||
unsigned short unSpectatorPort,
|
||||
unsigned short usQueryPort, const char* pchGameDir,
|
||||
const char* pchVersion, bool bLANMode);
|
||||
virtual void UpdateServerStatus(int cPlayers, int cPlayersMax,
|
||||
int cBotPlayers, const char* pchServerName,
|
||||
const char* pSpectatorServerName,
|
||||
const char* pchMapName);
|
||||
virtual void UpdateSpectatorPort(unsigned short unSpectatorPort);
|
||||
virtual void SetGameType(const char* pchGameType);
|
||||
virtual bool GetUserAchievementStatus(steam_id steamID,
|
||||
const char* pchAchievementName);
|
||||
virtual void GetGameplayStats();
|
||||
virtual unsigned __int64 GetServerReputation();
|
||||
virtual bool RequestUserGroupStatus(steam_id steamIDUser,
|
||||
steam_id steamIDGroup);
|
||||
virtual unsigned int GetPublicIP();
|
||||
virtual void SetGameData(const char* pchGameData);
|
||||
virtual int UserHasLicenseForApp(steam_id steamID, unsigned int appID);
|
||||
};
|
||||
} // namespace steam
|
51
src/client/steam/interfaces/master_server_updater.cpp
Normal file
51
src/client/steam/interfaces/master_server_updater.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
namespace steam {
|
||||
void master_server_updater::SetActive(bool bActive) {}
|
||||
|
||||
void master_server_updater::SetHeartbeatInterval(int iHeartbeatInterval) {}
|
||||
|
||||
bool master_server_updater::HandleIncomingPacket(const void* pData, int cbData,
|
||||
unsigned int srcIP,
|
||||
unsigned short srcPort) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int master_server_updater::GetNextOutgoingPacket(void* pOut, int cbMaxOut,
|
||||
unsigned int* pNetAdr,
|
||||
unsigned short* pPort) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void master_server_updater::SetBasicServerData(
|
||||
unsigned short nProtocolVersion, bool bDedicatedServer,
|
||||
const char* pRegionName, const char* pProductName,
|
||||
unsigned short nMaxReportedClients, bool bPasswordProtected,
|
||||
const char* pGameDescription) {}
|
||||
|
||||
void master_server_updater::ClearAllKeyValues() {}
|
||||
|
||||
void master_server_updater::SetKeyValue(const char* pKey, const char* pValue) {}
|
||||
|
||||
void master_server_updater::NotifyShutdown() {}
|
||||
|
||||
bool master_server_updater::WasRestartRequested() { return false; }
|
||||
|
||||
void master_server_updater::ForceHeartbeat() {}
|
||||
|
||||
bool master_server_updater::AddMasterServer(const char* pServerAddress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool master_server_updater::RemoveMasterServer(const char* pServerAddress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int master_server_updater::GetNumMasterServers() { return 0; }
|
||||
|
||||
int master_server_updater::GetMasterServerAddress(int iServer, char* pOut,
|
||||
int outBufferSize) {
|
||||
return 0;
|
||||
}
|
||||
} // namespace steam
|
32
src/client/steam/interfaces/master_server_updater.hpp
Normal file
32
src/client/steam/interfaces/master_server_updater.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
namespace steam {
|
||||
class master_server_updater {
|
||||
protected:
|
||||
~master_server_updater() = default;
|
||||
|
||||
public:
|
||||
virtual void SetActive(bool bActive);
|
||||
virtual void SetHeartbeatInterval(int iHeartbeatInterval);
|
||||
virtual bool HandleIncomingPacket(const void* pData, int cbData,
|
||||
unsigned int srcIP, unsigned short srcPort);
|
||||
virtual int GetNextOutgoingPacket(void* pOut, int cbMaxOut,
|
||||
unsigned int* pNetAdr,
|
||||
unsigned short* pPort);
|
||||
virtual void
|
||||
SetBasicServerData(unsigned short nProtocolVersion, bool bDedicatedServer,
|
||||
const char* pRegionName, const char* pProductName,
|
||||
unsigned short nMaxReportedClients,
|
||||
bool bPasswordProtected, const char* pGameDescription);
|
||||
virtual void ClearAllKeyValues();
|
||||
virtual void SetKeyValue(const char* pKey, const char* pValue);
|
||||
virtual void NotifyShutdown();
|
||||
virtual bool WasRestartRequested();
|
||||
virtual void ForceHeartbeat();
|
||||
virtual bool AddMasterServer(const char* pServerAddress);
|
||||
virtual bool RemoveMasterServer(const char* pServerAddress);
|
||||
virtual int GetNumMasterServers();
|
||||
virtual int GetMasterServerAddress(int iServer, char* pOut,
|
||||
int outBufferSize);
|
||||
};
|
||||
} // namespace steam
|
184
src/client/steam/interfaces/matchmaking.cpp
Normal file
184
src/client/steam/interfaces/matchmaking.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
namespace steam {
|
||||
int matchmaking::GetFavoriteGameCount() { return 0; }
|
||||
|
||||
bool matchmaking::GetFavoriteGame(int iGame, unsigned int* pnAppID,
|
||||
unsigned int* pnIP,
|
||||
unsigned short* pnConnPort,
|
||||
unsigned short* pnQueryPort,
|
||||
unsigned int* punFlags,
|
||||
unsigned int* pRTime32LastPlayedOnServer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int matchmaking::AddFavoriteGame(unsigned int nAppID, unsigned int nIP,
|
||||
unsigned short nConnPort,
|
||||
unsigned short nQueryPort,
|
||||
unsigned int unFlags,
|
||||
unsigned int rTime32LastPlayedOnServer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool matchmaking::RemoveFavoriteGame(unsigned int nAppID, unsigned int nIP,
|
||||
unsigned short nConnPort,
|
||||
unsigned short nQueryPort,
|
||||
unsigned int unFlags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned __int64 matchmaking::RequestLobbyList() { return 0; }
|
||||
|
||||
void matchmaking::AddRequestLobbyListStringFilter(const char* pchKeyToMatch,
|
||||
const char* pchValueToMatch,
|
||||
int eComparisonType) {}
|
||||
|
||||
void matchmaking::AddRequestLobbyListNumericalFilter(const char* pchKeyToMatch,
|
||||
int nValueToMatch,
|
||||
int eComparisonType) {}
|
||||
|
||||
void matchmaking::AddRequestLobbyListNearValueFilter(const char* pchKeyToMatch,
|
||||
int nValueToBeCloseTo) {}
|
||||
|
||||
void matchmaking::AddRequestLobbyListFilterSlotsAvailable(int nSlotsAvailable) {
|
||||
}
|
||||
|
||||
void matchmaking::AddRequestLobbyListDistanceFilter(int eLobbyDistanceFilter) {}
|
||||
|
||||
void matchmaking::AddRequestLobbyListResultCountFilter(int cMaxResults) {}
|
||||
|
||||
steam_id matchmaking::GetLobbyByIndex(int iLobby) { return steam_id(); }
|
||||
|
||||
unsigned __int64 matchmaking::CreateLobby(int eLobbyType, int cMaxMembers) {
|
||||
const auto result = callbacks::register_call();
|
||||
auto retvals = static_cast<lobby_created*>(calloc(1, sizeof(lobby_created)));
|
||||
//::Utils::Memory::AllocateArray<LobbyCreated>();
|
||||
steam_id id;
|
||||
|
||||
id.raw.account_id = 1337132;
|
||||
id.raw.universe = 1;
|
||||
id.raw.account_type = 8;
|
||||
id.raw.account_instance = 0x40000;
|
||||
|
||||
retvals->m_e_result = 1;
|
||||
retvals->m_ul_steam_id_lobby = id;
|
||||
|
||||
callbacks::return_call(retvals, sizeof(lobby_created),
|
||||
lobby_created::callback_id, result);
|
||||
|
||||
matchmaking::JoinLobby(id);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned __int64 matchmaking::JoinLobby(steam_id steamIDLobby) {
|
||||
const auto result = callbacks::register_call();
|
||||
auto* retvals = static_cast<lobby_enter*>(calloc(1, sizeof(lobby_enter)));
|
||||
//::Utils::Memory::AllocateArray<LobbyEnter>();
|
||||
retvals->m_b_locked = false;
|
||||
retvals->m_e_chat_room_enter_response = 1;
|
||||
retvals->m_rgf_chat_permissions = 0xFFFFFFFF;
|
||||
retvals->m_ul_steam_id_lobby = steamIDLobby;
|
||||
|
||||
callbacks::return_call(retvals, sizeof(lobby_enter), lobby_enter::callback_id,
|
||||
result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void matchmaking::LeaveLobby(steam_id steamIDLobby) {
|
||||
// Components::Party::RemoveLobby(steamIDLobby);
|
||||
}
|
||||
|
||||
bool matchmaking::InviteUserToLobby(steam_id steamIDLobby,
|
||||
steam_id steamIDInvitee) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int matchmaking::GetNumLobbyMembers(steam_id steamIDLobby) { return 1; }
|
||||
|
||||
steam_id matchmaking::GetLobbyMemberByIndex(steam_id steamIDLobby,
|
||||
int iMember) {
|
||||
return SteamUser()->GetSteamID();
|
||||
}
|
||||
|
||||
const char* matchmaking::GetLobbyData(steam_id steamIDLobby,
|
||||
const char* pchKey) {
|
||||
return "212"; // Components::Party::GetLobbyInfo(steamIDLobby, pchKey);
|
||||
}
|
||||
|
||||
bool matchmaking::SetLobbyData(steam_id steamIDLobby, const char* pchKey,
|
||||
const char* pchValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int matchmaking::GetLobbyDataCount(steam_id steamIDLobby) { return 0; }
|
||||
|
||||
bool matchmaking::GetLobbyDataByIndex(steam_id steamIDLobby, int iLobbyData,
|
||||
char* pchKey, int cchKeyBufferSize,
|
||||
char* pchValue, int cchValueBufferSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool matchmaking::DeleteLobbyData(steam_id steamIDLobby, const char* pchKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* matchmaking::GetLobbyMemberData(steam_id steamIDLobby,
|
||||
steam_id steamIDUser,
|
||||
const char* pchKey) {
|
||||
return "";
|
||||
}
|
||||
|
||||
void matchmaking::SetLobbyMemberData(steam_id steamIDLobby, const char* pchKey,
|
||||
const char* pchValue) {}
|
||||
|
||||
bool matchmaking::SendLobbyChatMsg(steam_id steamIDLobby, const void* pvMsgBody,
|
||||
int cubMsgBody) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int matchmaking::GetLobbyChatEntry(steam_id steamIDLobby, int iChatID,
|
||||
steam_id* pSteamIDUser, void* pvData,
|
||||
int cubData, int* peChatEntryType) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool matchmaking::RequestLobbyData(steam_id steamIDLobby) { return false; }
|
||||
|
||||
void matchmaking::SetLobbyGameServer(steam_id steamIDLobby,
|
||||
unsigned int unGameServerIP,
|
||||
unsigned short unGameServerPort,
|
||||
steam_id steamIDGameServer) {}
|
||||
|
||||
bool matchmaking::GetLobbyGameServer(steam_id steamIDLobby,
|
||||
unsigned int* punGameServerIP,
|
||||
unsigned short* punGameServerPort,
|
||||
steam_id* psteamIDGameServer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool matchmaking::SetLobbyMemberLimit(steam_id steamIDLobby, int cMaxMembers) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int matchmaking::GetLobbyMemberLimit(steam_id steamIDLobby) { return 0; }
|
||||
|
||||
bool matchmaking::SetLobbyType(steam_id steamIDLobby, int eLobbyType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool matchmaking::SetLobbyJoinable(steam_id steamIDLobby, bool bLobbyJoinable) {
|
||||
return true;
|
||||
}
|
||||
|
||||
steam_id matchmaking::GetLobbyOwner(steam_id steamIDLobby) {
|
||||
return SteamUser()->GetSteamID();
|
||||
}
|
||||
|
||||
bool matchmaking::SetLobbyOwner(steam_id steamIDLobby,
|
||||
steam_id steamIDNewOwner) {
|
||||
return true;
|
||||
}
|
||||
} // namespace steam
|
94
src/client/steam/interfaces/matchmaking.hpp
Normal file
94
src/client/steam/interfaces/matchmaking.hpp
Normal file
@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
namespace steam {
|
||||
struct lobby_created final {
|
||||
enum { callback_id = 513 };
|
||||
|
||||
int m_e_result;
|
||||
int m_pad;
|
||||
steam_id m_ul_steam_id_lobby;
|
||||
};
|
||||
|
||||
struct lobby_enter final {
|
||||
enum { callback_id = 504 };
|
||||
|
||||
steam_id m_ul_steam_id_lobby;
|
||||
int m_rgf_chat_permissions;
|
||||
bool m_b_locked;
|
||||
int m_e_chat_room_enter_response;
|
||||
};
|
||||
|
||||
class matchmaking {
|
||||
protected:
|
||||
~matchmaking() = default;
|
||||
|
||||
public:
|
||||
virtual int GetFavoriteGameCount();
|
||||
virtual bool GetFavoriteGame(int iGame, unsigned int* pnAppID,
|
||||
unsigned int* pnIP, unsigned short* pnConnPort,
|
||||
unsigned short* pnQueryPort,
|
||||
unsigned int* punFlags,
|
||||
unsigned int* pRTime32LastPlayedOnServer);
|
||||
virtual int AddFavoriteGame(unsigned int nAppID, unsigned int nIP,
|
||||
unsigned short nConnPort,
|
||||
unsigned short nQueryPort, unsigned int unFlags,
|
||||
unsigned int rTime32LastPlayedOnServer);
|
||||
virtual bool RemoveFavoriteGame(unsigned int nAppID, unsigned int nIP,
|
||||
unsigned short nConnPort,
|
||||
unsigned short nQueryPort,
|
||||
unsigned int unFlags);
|
||||
virtual unsigned __int64 RequestLobbyList();
|
||||
virtual void AddRequestLobbyListStringFilter(const char* pchKeyToMatch,
|
||||
const char* pchValueToMatch,
|
||||
int eComparisonType);
|
||||
virtual void AddRequestLobbyListNumericalFilter(const char* pchKeyToMatch,
|
||||
int nValueToMatch,
|
||||
int eComparisonType);
|
||||
virtual void AddRequestLobbyListNearValueFilter(const char* pchKeyToMatch,
|
||||
int nValueToBeCloseTo);
|
||||
virtual void AddRequestLobbyListFilterSlotsAvailable(int nSlotsAvailable);
|
||||
virtual void AddRequestLobbyListDistanceFilter(int eLobbyDistanceFilter);
|
||||
virtual void AddRequestLobbyListResultCountFilter(int cMaxResults);
|
||||
virtual steam_id GetLobbyByIndex(int iLobby);
|
||||
virtual unsigned __int64 CreateLobby(int eLobbyType, int cMaxMembers);
|
||||
virtual unsigned __int64 JoinLobby(steam_id steamIDLobby);
|
||||
virtual void LeaveLobby(steam_id steamIDLobby);
|
||||
virtual bool InviteUserToLobby(steam_id steamIDLobby,
|
||||
steam_id steamIDInvitee);
|
||||
virtual int GetNumLobbyMembers(steam_id steamIDLobby);
|
||||
virtual steam_id GetLobbyMemberByIndex(steam_id steamIDLobby, int iMember);
|
||||
virtual const char* GetLobbyData(steam_id steamIDLobby, const char* pchKey);
|
||||
virtual bool SetLobbyData(steam_id steamIDLobby, const char* pchKey,
|
||||
const char* pchValue);
|
||||
virtual int GetLobbyDataCount(steam_id steamIDLobby);
|
||||
virtual bool GetLobbyDataByIndex(steam_id steamIDLobby, int iLobbyData,
|
||||
char* pchKey, int cchKeyBufferSize,
|
||||
char* pchValue, int cchValueBufferSize);
|
||||
virtual bool DeleteLobbyData(steam_id steamIDLobby, const char* pchKey);
|
||||
virtual const char* GetLobbyMemberData(steam_id steamIDLobby,
|
||||
steam_id steamIDUser,
|
||||
const char* pchKey);
|
||||
virtual void SetLobbyMemberData(steam_id steamIDLobby, const char* pchKey,
|
||||
const char* pchValue);
|
||||
virtual bool SendLobbyChatMsg(steam_id steamIDLobby, const void* pvMsgBody,
|
||||
int cubMsgBody);
|
||||
virtual int GetLobbyChatEntry(steam_id steamIDLobby, int iChatID,
|
||||
steam_id* pSteamIDUser, void* pvData,
|
||||
int cubData, int* peChatEntryType);
|
||||
virtual bool RequestLobbyData(steam_id steamIDLobby);
|
||||
virtual void SetLobbyGameServer(steam_id steamIDLobby,
|
||||
unsigned int unGameServerIP,
|
||||
unsigned short unGameServerPort,
|
||||
steam_id steamIDGameServer);
|
||||
virtual bool GetLobbyGameServer(steam_id steamIDLobby,
|
||||
unsigned int* punGameServerIP,
|
||||
unsigned short* punGameServerPort,
|
||||
steam_id* psteamIDGameServer);
|
||||
virtual bool SetLobbyMemberLimit(steam_id steamIDLobby, int cMaxMembers);
|
||||
virtual int GetLobbyMemberLimit(steam_id steamIDLobby);
|
||||
virtual bool SetLobbyType(steam_id steamIDLobby, int eLobbyType);
|
||||
virtual bool SetLobbyJoinable(steam_id steamIDLobby, bool bLobbyJoinable);
|
||||
virtual steam_id GetLobbyOwner(steam_id steamIDLobby);
|
||||
virtual bool SetLobbyOwner(steam_id steamIDLobby, steam_id steamIDNewOwner);
|
||||
};
|
||||
} // namespace steam
|
72
src/client/steam/interfaces/matchmaking_servers.cpp
Normal file
72
src/client/steam/interfaces/matchmaking_servers.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
namespace steam {
|
||||
void* matchmaking_servers::RequestInternetServerList(
|
||||
unsigned int iApp, void** ppchFilters, unsigned int nFilters,
|
||||
void* pRequestServersResponse) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* matchmaking_servers::RequestLANServerList(unsigned int iApp,
|
||||
void* pRequestServersResponse) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* matchmaking_servers::RequestFriendsServerList(
|
||||
unsigned int iApp, void** ppchFilters, unsigned int nFilters,
|
||||
void* pRequestServersResponse) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* matchmaking_servers::RequestFavoritesServerList(
|
||||
unsigned int iApp, void** ppchFilters, unsigned int nFilters,
|
||||
void* pRequestServersResponse) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* matchmaking_servers::RequestHistoryServerList(
|
||||
unsigned int iApp, void** ppchFilters, unsigned int nFilters,
|
||||
void* pRequestServersResponse) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* matchmaking_servers::RequestSpectatorServerList(
|
||||
unsigned int iApp, void** ppchFilters, unsigned int nFilters,
|
||||
void* pRequestServersResponse) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void matchmaking_servers::ReleaseRequest(void* hServerListRequest) {}
|
||||
|
||||
void* matchmaking_servers::GetServerDetails(void* hRequest, int iServer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void matchmaking_servers::CancelQuery(void* hRequest) {}
|
||||
|
||||
void matchmaking_servers::RefreshQuery(void* hRequest) {}
|
||||
|
||||
bool matchmaking_servers::IsRefreshing(void* hRequest) { return false; }
|
||||
|
||||
int matchmaking_servers::GetServerCount(void* hRequest) { return 0; }
|
||||
|
||||
void matchmaking_servers::RefreshServer(void* hRequest, int iServer) {}
|
||||
|
||||
int matchmaking_servers::PingServer(unsigned int unIP, unsigned short usPort,
|
||||
void* pRequestServersResponse) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int matchmaking_servers::PlayerDetails(unsigned int unIP, unsigned short usPort,
|
||||
void* pRequestServersResponse) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int matchmaking_servers::ServerRules(unsigned int unIP, unsigned short usPort,
|
||||
void* pRequestServersResponse) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void matchmaking_servers::CancelServerQuery(int hServerQuery) {}
|
||||
} // namespace steam
|
43
src/client/steam/interfaces/matchmaking_servers.hpp
Normal file
43
src/client/steam/interfaces/matchmaking_servers.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
namespace steam {
|
||||
class matchmaking_servers {
|
||||
protected:
|
||||
~matchmaking_servers() = default;
|
||||
|
||||
public:
|
||||
virtual void* RequestInternetServerList(unsigned int iApp, void** ppchFilters,
|
||||
unsigned int nFilters,
|
||||
void* pRequestServersResponse);
|
||||
virtual void* RequestLANServerList(unsigned int iApp,
|
||||
void* pRequestServersResponse);
|
||||
virtual void* RequestFriendsServerList(unsigned int iApp, void** ppchFilters,
|
||||
unsigned int nFilters,
|
||||
void* pRequestServersResponse);
|
||||
virtual void* RequestFavoritesServerList(unsigned int iApp,
|
||||
void** ppchFilters,
|
||||
unsigned int nFilters,
|
||||
void* pRequestServersResponse);
|
||||
virtual void* RequestHistoryServerList(unsigned int iApp, void** ppchFilters,
|
||||
unsigned int nFilters,
|
||||
void* pRequestServersResponse);
|
||||
virtual void* RequestSpectatorServerList(unsigned int iApp,
|
||||
void** ppchFilters,
|
||||
unsigned int nFilters,
|
||||
void* pRequestServersResponse);
|
||||
virtual void ReleaseRequest(void* hServerListRequest);
|
||||
virtual void* GetServerDetails(void* hRequest, int iServer);
|
||||
virtual void CancelQuery(void* hRequest);
|
||||
virtual void RefreshQuery(void* hRequest);
|
||||
virtual bool IsRefreshing(void* hRequest);
|
||||
virtual int GetServerCount(void* hRequest);
|
||||
virtual void RefreshServer(void* hRequest, int iServer);
|
||||
virtual int PingServer(unsigned int unIP, unsigned short usPort,
|
||||
void* pRequestServersResponse);
|
||||
virtual int PlayerDetails(unsigned int unIP, unsigned short usPort,
|
||||
void* pRequestServersResponse);
|
||||
virtual int ServerRules(unsigned int unIP, unsigned short usPort,
|
||||
void* pRequestServersResponse);
|
||||
virtual void CancelServerQuery(int hServerQuery);
|
||||
};
|
||||
} // namespace steam
|
112
src/client/steam/interfaces/networking.cpp
Normal file
112
src/client/steam/interfaces/networking.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
namespace steam {
|
||||
bool networking::SendP2PPacket(steam_id steamIDRemote, const void* pubData,
|
||||
unsigned int cubData, int eP2PSendType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::IsP2PPacketAvailable(unsigned int* pcubMsgSize, int idk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::ReadP2PPacket(void* pubDest, unsigned int cubDest,
|
||||
unsigned int* pcubMsgSize,
|
||||
steam_id* psteamIDRemote) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::AcceptP2PSessionWithUser(steam_id steamIDRemote) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::CloseP2PSessionWithUser(steam_id steamIDRemote) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::CloseP2PChannelWithUser(steam_id steamIDRemote,
|
||||
int iVirtualPort) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::GetP2PSessionState(steam_id steamIDRemote,
|
||||
void* pConnectionState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::AllowP2PPacketRelay(bool bAllow) { return false; }
|
||||
|
||||
unsigned int networking::CreateListenSocket(int nVirtualP2PPort,
|
||||
unsigned int nIP,
|
||||
unsigned short nPort,
|
||||
bool bAllowUseOfPacketRelay) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
networking::CreateP2PConnectionSocket(steam_id steamIDTarget, int nVirtualPort,
|
||||
int nTimeoutSec,
|
||||
bool bAllowUseOfPacketRelay) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int networking::CreateConnectionSocket(unsigned int nIP,
|
||||
unsigned short nPort,
|
||||
int nTimeoutSec) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool networking::DestroySocket(unsigned int hSocket, bool bNotifyRemoteEnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::DestroyListenSocket(unsigned int hSocket,
|
||||
bool bNotifyRemoteEnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::SendDataOnSocket(unsigned int hSocket, void* pubData,
|
||||
unsigned int cubData, bool bReliable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::IsDataAvailableOnSocket(unsigned int hSocket,
|
||||
unsigned int* pcubMsgSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::RetrieveDataFromSocket(unsigned int hSocket, void* pubDest,
|
||||
unsigned int cubDest,
|
||||
unsigned int* pcubMsgSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::IsDataAvailable(unsigned int hListenSocket,
|
||||
unsigned int* pcubMsgSize,
|
||||
unsigned int* phSocket) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::RetrieveData(unsigned int hListenSocket, void* pubDest,
|
||||
unsigned int cubDest, unsigned int* pcubMsgSize,
|
||||
unsigned int* phSocket) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::GetSocketInfo(unsigned int hSocket, steam_id* pSteamIDRemote,
|
||||
int* peSocketStatus, unsigned int* punIPRemote,
|
||||
unsigned short* punPortRemote) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networking::GetListenSocketInfo(unsigned int hListenSocket,
|
||||
unsigned int* pnIP,
|
||||
unsigned short* pnPort) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int networking::GetSocketConnectionType(unsigned int hSocket) { return 0; }
|
||||
|
||||
int networking::GetMaxPacketSize(unsigned int hSocket) { return 0; }
|
||||
} // namespace steam
|
55
src/client/steam/interfaces/networking.hpp
Normal file
55
src/client/steam/interfaces/networking.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
namespace steam {
|
||||
class networking {
|
||||
protected:
|
||||
~networking() = default;
|
||||
|
||||
public:
|
||||
virtual bool SendP2PPacket(steam_id steamIDRemote, const void* pubData,
|
||||
unsigned int cubData, int eP2PSendType);
|
||||
virtual bool IsP2PPacketAvailable(unsigned int* pcubMsgSize, int idk);
|
||||
virtual bool ReadP2PPacket(void* pubDest, unsigned int cubDest,
|
||||
unsigned int* pcubMsgSize,
|
||||
steam_id* psteamIDRemote);
|
||||
virtual bool AcceptP2PSessionWithUser(steam_id steamIDRemote);
|
||||
virtual bool CloseP2PSessionWithUser(steam_id steamIDRemote);
|
||||
virtual bool CloseP2PChannelWithUser(steam_id steamIDRemote,
|
||||
int iVirtualPort);
|
||||
virtual bool GetP2PSessionState(steam_id steamIDRemote,
|
||||
void* pConnectionState);
|
||||
virtual bool AllowP2PPacketRelay(bool bAllow);
|
||||
virtual unsigned int CreateListenSocket(int nVirtualP2PPort, unsigned int nIP,
|
||||
unsigned short nPort,
|
||||
bool bAllowUseOfPacketRelay);
|
||||
virtual unsigned int CreateP2PConnectionSocket(steam_id steamIDTarget,
|
||||
int nVirtualPort,
|
||||
int nTimeoutSec,
|
||||
bool bAllowUseOfPacketRelay);
|
||||
virtual unsigned int CreateConnectionSocket(unsigned int nIP,
|
||||
unsigned short nPort,
|
||||
int nTimeoutSec);
|
||||
virtual bool DestroySocket(unsigned int hSocket, bool bNotifyRemoteEnd);
|
||||
virtual bool DestroyListenSocket(unsigned int hSocket, bool bNotifyRemoteEnd);
|
||||
virtual bool SendDataOnSocket(unsigned int hSocket, void* pubData,
|
||||
unsigned int cubData, bool bReliable);
|
||||
virtual bool IsDataAvailableOnSocket(unsigned int hSocket,
|
||||
unsigned int* pcubMsgSize);
|
||||
virtual bool RetrieveDataFromSocket(unsigned int hSocket, void* pubDest,
|
||||
unsigned int cubDest,
|
||||
unsigned int* pcubMsgSize);
|
||||
virtual bool IsDataAvailable(unsigned int hListenSocket,
|
||||
unsigned int* pcubMsgSize,
|
||||
unsigned int* phSocket);
|
||||
virtual bool RetrieveData(unsigned int hListenSocket, void* pubDest,
|
||||
unsigned int cubDest, unsigned int* pcubMsgSize,
|
||||
unsigned int* phSocket);
|
||||
virtual bool GetSocketInfo(unsigned int hSocket, steam_id* pSteamIDRemote,
|
||||
int* peSocketStatus, unsigned int* punIPRemote,
|
||||
unsigned short* punPortRemote);
|
||||
virtual bool GetListenSocketInfo(unsigned int hListenSocket,
|
||||
unsigned int* pnIP, unsigned short* pnPort);
|
||||
virtual int GetSocketConnectionType(unsigned int hSocket);
|
||||
virtual int GetMaxPacketSize(unsigned int hSocket);
|
||||
};
|
||||
} // namespace steam
|
33
src/client/steam/interfaces/remote_storage.cpp
Normal file
33
src/client/steam/interfaces/remote_storage.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
namespace steam {
|
||||
bool remote_storage::FileWrite(const char* pchFile, const void* pvData,
|
||||
int cubData) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int remote_storage::GetFileSize(const char* pchFile) { return 0; }
|
||||
|
||||
int remote_storage::FileRead(const char* pchFile, void* pvData,
|
||||
int cubDataToRead) {
|
||||
OutputDebugStringA(pchFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool remote_storage::FileExists(const char* pchFile) { return false; }
|
||||
|
||||
int remote_storage::GetFileCount() { return 0; }
|
||||
|
||||
const char* remote_storage::GetFileNameAndSize(int iFile,
|
||||
int* pnFileSizeInBytes) {
|
||||
*pnFileSizeInBytes = 0;
|
||||
return "";
|
||||
}
|
||||
|
||||
bool remote_storage::GetQuota(int* pnTotalBytes, int* puAvailableBytes) {
|
||||
*pnTotalBytes = 0x10000000;
|
||||
*puAvailableBytes = 0x10000000;
|
||||
return false;
|
||||
}
|
||||
} // namespace steam
|
17
src/client/steam/interfaces/remote_storage.hpp
Normal file
17
src/client/steam/interfaces/remote_storage.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
namespace steam {
|
||||
class remote_storage {
|
||||
protected:
|
||||
~remote_storage() = default;
|
||||
|
||||
public:
|
||||
virtual bool FileWrite(const char* pchFile, const void* pvData, int cubData);
|
||||
virtual int GetFileSize(const char* pchFile);
|
||||
virtual int FileRead(const char* pchFile, void* pvData, int cubDataToRead);
|
||||
virtual bool FileExists(const char* pchFile);
|
||||
virtual int GetFileCount();
|
||||
virtual const char* GetFileNameAndSize(int iFile, int* pnFileSizeInBytes);
|
||||
virtual bool GetQuota(int* pnTotalBytes, int* puAvailableBytes);
|
||||
};
|
||||
} // namespace steam
|
126
src/client/steam/interfaces/user.cpp
Normal file
126
src/client/steam/interfaces/user.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
#include "component/auth.hpp"
|
||||
|
||||
namespace steam {
|
||||
namespace {
|
||||
std::string auth_ticket;
|
||||
} // namespace
|
||||
|
||||
int user::GetHSteamUser() { return NULL; }
|
||||
|
||||
bool user::LoggedOn() { return true; }
|
||||
|
||||
steam_id user::GetSteamID() {
|
||||
static std::uint64_t seed = 0;
|
||||
if (!seed) {
|
||||
seed = auth::get_guid();
|
||||
}
|
||||
|
||||
steam_id id;
|
||||
id.bits = seed;
|
||||
return id;
|
||||
}
|
||||
|
||||
int user::InitiateGameConnection(void* pAuthBlob, int cbMaxAuthBlob,
|
||||
steam_id steamIDGameServer,
|
||||
unsigned int unIPServer,
|
||||
unsigned short usPortServer, bool bSecure) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void user::TerminateGameConnection(unsigned int unIPServer,
|
||||
unsigned short usPortServer) {}
|
||||
|
||||
void user::TrackAppUsageEvent(steam_id gameID, int eAppUsageEvent,
|
||||
const char* pchExtraInfo) {}
|
||||
|
||||
bool user::GetUserDataFolder(char* pchBuffer, int cubBuffer) { return false; }
|
||||
|
||||
void user::StartVoiceRecording() {}
|
||||
|
||||
void user::StopVoiceRecording() {}
|
||||
|
||||
int user::GetAvailableVoice(unsigned int* pcbCompressed,
|
||||
unsigned int* pcbUncompressed,
|
||||
unsigned int nUncompressedVoiceDesiredSampleRate) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int user::GetVoice(bool bWantCompressed, void* pDestBuffer,
|
||||
unsigned int cbDestBufferSize, unsigned int* nBytesWritten,
|
||||
bool bWantUncompressed, void* pUncompressedDestBuffer,
|
||||
unsigned int cbUncompressedDestBufferSize,
|
||||
unsigned int* nUncompressBytesWritten,
|
||||
unsigned int nUncompressedVoiceDesiredSampleRate) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int user::DecompressVoice(void* pCompressed, unsigned int cbCompressed,
|
||||
void* pDestBuffer, unsigned int cbDestBufferSize,
|
||||
unsigned int* nBytesWritten) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int user::GetVoiceOptimalSampleRate() { return 0; }
|
||||
|
||||
unsigned int user::GetAuthSessionTicket(void* pTicket, int cbMaxTicket,
|
||||
unsigned int* pcbTicket) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int user::BeginAuthSession(const void* pAuthTicket, int cbAuthTicket,
|
||||
steam_id steamID) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void user::EndAuthSession(steam_id steamID) {}
|
||||
|
||||
void user::CancelAuthTicket(unsigned int hAuthTicket) {}
|
||||
|
||||
unsigned int user::UserHasLicenseForApp(steam_id steamID, unsigned int appID) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool user::BIsBehindNAT() { return false; }
|
||||
|
||||
void user::AdvertiseGame(steam_id steamIDGameServer, unsigned int unIPServer,
|
||||
unsigned short usPortServer) {}
|
||||
|
||||
unsigned __int64 user::RequestEncryptedAppTicket(void* pUserData,
|
||||
int cbUserData) {
|
||||
// Generate the authentication ticket
|
||||
const auto id = this->GetSteamID();
|
||||
|
||||
auth_ticket = "iw4-sp";
|
||||
auth_ticket.resize(32);
|
||||
auth_ticket.append(static_cast<char*>(pUserData), cbUserData);
|
||||
auth_ticket.append(reinterpret_cast<const char*>(&id.bits), sizeof(id.bits));
|
||||
|
||||
// Create the call response
|
||||
const auto result = callbacks::register_call();
|
||||
auto retvals = static_cast<encrypted_app_ticket_response*>(
|
||||
calloc(1, sizeof(encrypted_app_ticket_response)));
|
||||
retvals->m_e_result = 1;
|
||||
|
||||
// Return the call response
|
||||
callbacks::return_call(retvals, sizeof(encrypted_app_ticket_response),
|
||||
encrypted_app_ticket_response::callback_id, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool user::GetEncryptedAppTicket(void* pTicket, int cbMaxTicket,
|
||||
unsigned int* pcbTicket) {
|
||||
if (cbMaxTicket < 0 || auth_ticket.empty())
|
||||
return false;
|
||||
|
||||
const auto size =
|
||||
std::min(static_cast<std::size_t>(cbMaxTicket), auth_ticket.size());
|
||||
std::memcpy(pTicket, auth_ticket.data(), size);
|
||||
*pcbTicket = size;
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace steam
|
61
src/client/steam/interfaces/user.hpp
Normal file
61
src/client/steam/interfaces/user.hpp
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
namespace steam {
|
||||
struct encrypted_app_ticket_response final {
|
||||
enum { callback_id = 154 };
|
||||
|
||||
int m_e_result;
|
||||
};
|
||||
|
||||
class user {
|
||||
protected:
|
||||
~user() = default;
|
||||
|
||||
public:
|
||||
virtual int GetHSteamUser();
|
||||
virtual bool LoggedOn();
|
||||
virtual steam_id GetSteamID();
|
||||
|
||||
virtual int InitiateGameConnection(void* pAuthBlob, int cbMaxAuthBlob,
|
||||
steam_id steamIDGameServer,
|
||||
unsigned int unIPServer,
|
||||
unsigned short usPortServer, bool bSecure);
|
||||
virtual void TerminateGameConnection(unsigned int unIPServer,
|
||||
unsigned short usPortServer);
|
||||
virtual void TrackAppUsageEvent(steam_id gameID, int eAppUsageEvent,
|
||||
const char* pchExtraInfo = "");
|
||||
virtual bool GetUserDataFolder(char* pchBuffer, int cubBuffer);
|
||||
virtual void StartVoiceRecording();
|
||||
virtual void StopVoiceRecording();
|
||||
virtual int
|
||||
GetAvailableVoice(unsigned int* pcbCompressed, unsigned int* pcbUncompressed,
|
||||
unsigned int nUncompressedVoiceDesiredSampleRate);
|
||||
virtual int GetVoice(bool bWantCompressed, void* pDestBuffer,
|
||||
unsigned int cbDestBufferSize,
|
||||
unsigned int* nBytesWritten, bool bWantUncompressed,
|
||||
void* pUncompressedDestBuffer,
|
||||
unsigned int cbUncompressedDestBufferSize,
|
||||
unsigned int* nUncompressBytesWritten,
|
||||
unsigned int nUncompressedVoiceDesiredSampleRate);
|
||||
virtual int DecompressVoice(void* pCompressed, unsigned int cbCompressed,
|
||||
void* pDestBuffer, unsigned int cbDestBufferSize,
|
||||
unsigned int* nBytesWritten);
|
||||
virtual unsigned int GetVoiceOptimalSampleRate();
|
||||
virtual unsigned int GetAuthSessionTicket(void* pTicket, int cbMaxTicket,
|
||||
unsigned int* pcbTicket);
|
||||
virtual int BeginAuthSession(const void* pAuthTicket, int cbAuthTicket,
|
||||
steam_id steamID);
|
||||
virtual void EndAuthSession(steam_id steamID);
|
||||
virtual void CancelAuthTicket(unsigned int hAuthTicket);
|
||||
virtual unsigned int UserHasLicenseForApp(steam_id steamID,
|
||||
unsigned int appID);
|
||||
virtual bool BIsBehindNAT();
|
||||
virtual void AdvertiseGame(steam_id steamIDGameServer,
|
||||
unsigned int unIPServer,
|
||||
unsigned short usPortServer);
|
||||
virtual unsigned __int64 RequestEncryptedAppTicket(void* pUserData,
|
||||
int cbUserData);
|
||||
virtual bool GetEncryptedAppTicket(void* pTicket, int cbMaxTicket,
|
||||
unsigned int* pcbTicket);
|
||||
};
|
||||
} // namespace steam
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user