Merge branch 'main' of https://github.com/JariKCoding/boiii
This commit is contained in:
commit
3729c0a6f1
@ -7,11 +7,11 @@
|
|||||||
|
|
||||||
# BOIII ☄️
|
# BOIII ☄️
|
||||||
|
|
||||||
Reverse engineering and analysis of Call of Duty: Black Ops 3.
|
An attempt at reverse engineering and analyzing of Call of Duty: Black Ops 3.
|
||||||
|
|
||||||
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQeSXYzQITJrcjiifN1nqX1fsVE7VwLZ3vl2g&usqp=CAU">
|
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQeSXYzQITJrcjiifN1nqX1fsVE7VwLZ3vl2g&usqp=CAU">
|
||||||
|
|
||||||
## Roadmap
|
## Technical Features
|
||||||
|
|
||||||
- [x] Steam API Emulation
|
- [x] Steam API Emulation
|
||||||
- [x] Steam Integrity Bypass
|
- [x] Steam Integrity Bypass
|
||||||
@ -24,6 +24,8 @@ Reverse engineering and analysis of Call of Duty: Black Ops 3.
|
|||||||
- [x] P2P multiplayer
|
- [x] P2P multiplayer
|
||||||
- [x] Dedicated Servers
|
- [x] Dedicated Servers
|
||||||
|
|
||||||
|
Check out the <a href="https://github.com/momo5502/boiii/issues?q=is%3Aissue+is%3Aclosed+reason%3Acompleted">closed issues</a> for more gameplay related features and fixes that have been added!
|
||||||
|
|
||||||
## Writeups & Articles
|
## Writeups & Articles
|
||||||
|
|
||||||
- <a href="https://momo5502.com/posts/2022-11-17-reverse-engineering-integrity-checks-in-black-ops-3/">Reverse engineering integrity checks in Black Ops 3</a>
|
- <a href="https://momo5502.com/posts/2022-11-17-reverse-engineering-integrity-checks-in-black-ops-3/">Reverse engineering integrity checks in Black Ops 3</a>
|
||||||
|
26
data/gamesettings/mp/gamesettings_escort.cfg
Normal file
26
data/gamesettings/mp/gamesettings_escort.cfg
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
gametype_setting timelimit 5
|
||||||
|
gametype_setting scorelimit 0
|
||||||
|
gametype_setting roundscorelimit 1
|
||||||
|
gametype_setting roundwinlimit 2
|
||||||
|
gametype_setting roundlimit 2
|
||||||
|
gametype_setting preroundperiod 10
|
||||||
|
gametype_setting teamCount 2
|
||||||
|
|
||||||
|
gametype_setting shutdownDamage 3
|
||||||
|
gametype_setting bootTime 5
|
||||||
|
gametype_setting rebootTime 15
|
||||||
|
gametype_setting rebootPlayers 0
|
||||||
|
gametype_setting movePlayers 1
|
||||||
|
gametype_setting robotSpeed 1
|
||||||
|
gametype_setting robotShield 0
|
||||||
|
|
||||||
|
|
||||||
|
gametype_setting scoreHeroPowerGainFactor 0.788 //Score earned towards Hero Weapons and Abilities are multiplied by this factor
|
||||||
|
gametype_setting scoreHeroPowerTimeFactor 0.788
|
||||||
|
|
||||||
|
gametype_setting spawntraptriggertime 5
|
||||||
|
|
||||||
|
gametype_setting disableVehicleSpawners 1
|
||||||
|
|
||||||
|
gametype_setting gameAdvertisementRuleTimeLeft 3.5
|
||||||
|
gametype_setting gameAdvertisementRuleRound 3
|
@ -108,6 +108,8 @@ namespace auth
|
|||||||
}
|
}
|
||||||
|
|
||||||
int send_connect_data_stub(const game::netsrc_t sock, game::netadr_t* adr, const char* data, int len)
|
int send_connect_data_stub(const game::netsrc_t sock, game::netadr_t* adr, const char* data, int len)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
std::string buffer{};
|
std::string buffer{};
|
||||||
|
|
||||||
@ -124,6 +126,13 @@ namespace auth
|
|||||||
|
|
||||||
return reinterpret_cast<decltype(&send_connect_data_stub)>(0x142173600_g)(sock, adr, data, len);
|
return reinterpret_cast<decltype(&send_connect_data_stub)>(0x142173600_g)(sock, adr, data, len);
|
||||||
}
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
printf("Error: %s\n", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void handle_connect_packet(const game::netadr_t& target, const network::data_view& data)
|
void handle_connect_packet(const game::netadr_t& target, const network::data_view& data)
|
||||||
{
|
{
|
||||||
|
@ -104,6 +104,9 @@ namespace dedicated_patches
|
|||||||
utils::hook::jump(0x14052F0F5_g, 0x14052F139_g);
|
utils::hook::jump(0x14052F0F5_g, 0x14052F139_g);
|
||||||
|
|
||||||
utils::hook::call(0x1402853D7_g, sv_get_player_xuid_stub); // PlayerCmd_GetXuid
|
utils::hook::call(0x1402853D7_g, sv_get_player_xuid_stub); // PlayerCmd_GetXuid
|
||||||
|
|
||||||
|
// Stop executing default_dedicated.cfg & language_settings.cfg
|
||||||
|
utils::hook::set<uint8_t>(0x1405063C0_g, 0xC3);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
133
src/client/component/game_settings.cpp
Normal file
133
src/client/component/game_settings.cpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/io.hpp>
|
||||||
|
|
||||||
|
namespace gamesettings
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// <name, path>
|
||||||
|
std::unordered_map<std::string, std::string> game_settings_files;
|
||||||
|
|
||||||
|
std::string get_game_settings_name(const std::vector<std::string>& sub_strings)
|
||||||
|
{
|
||||||
|
if (sub_strings.size() > 2)
|
||||||
|
{
|
||||||
|
return sub_strings[sub_strings.size() - 2] + '/' + sub_strings[sub_strings.size() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_game_settings_path(const std::string& name)
|
||||||
|
{
|
||||||
|
const auto itr = game_settings_files.find(name);
|
||||||
|
return (itr == game_settings_files.end()) ? std::string() : itr->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_game_settings_folder(const std::string& game_settings_dir)
|
||||||
|
{
|
||||||
|
if (!utils::io::directory_exists(game_settings_dir))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto files = utils::io::list_files(game_settings_dir, true);
|
||||||
|
|
||||||
|
for (const auto& path : files)
|
||||||
|
{
|
||||||
|
if (!std::filesystem::is_directory(path))
|
||||||
|
{
|
||||||
|
auto sub_strings = utils::string::split(path.generic_string(), '/');
|
||||||
|
game_settings_files.insert_or_assign(get_game_settings_name(sub_strings), path.generic_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_game_settings_file_on_disk(const char* path)
|
||||||
|
{
|
||||||
|
if (!path)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto sub_strings = utils::string::split(path, '/');
|
||||||
|
const auto game_settings_name = get_game_settings_name(sub_strings);
|
||||||
|
|
||||||
|
return !get_game_settings_path(game_settings_name).empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_exec_stub(utils::hook::assembler& a)
|
||||||
|
{
|
||||||
|
const auto exec_from_fastfile = a.newLabel();
|
||||||
|
const auto exec_from_disk = a.newLabel();
|
||||||
|
|
||||||
|
a.pushad64();
|
||||||
|
|
||||||
|
a.mov(rcx, r10);
|
||||||
|
a.call_aligned(has_game_settings_file_on_disk);
|
||||||
|
a.cmp(rax, 1);
|
||||||
|
;
|
||||||
|
a.popad64();
|
||||||
|
|
||||||
|
a.jnz(exec_from_fastfile);
|
||||||
|
|
||||||
|
a.bind(exec_from_disk);
|
||||||
|
a.jmp(game::select(0x1420ED087, 0x1404F855E));
|
||||||
|
|
||||||
|
a.bind(exec_from_fastfile);
|
||||||
|
a.lea(rdx, ptr(rsp, (game::is_server() ? 0x30 : 0x40)));
|
||||||
|
a.jmp(game::select(0x1420ED007, 0x1404F853F));
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_file_stub(const char* qpath, void** buffer)
|
||||||
|
{
|
||||||
|
const auto sub_strings = utils::string::split(qpath, '/');
|
||||||
|
const auto game_settings_name = get_game_settings_name(sub_strings);
|
||||||
|
|
||||||
|
std::string gamesettings_data;
|
||||||
|
utils::io::read_file(get_game_settings_path(game_settings_name), &gamesettings_data);
|
||||||
|
|
||||||
|
if (!gamesettings_data.empty())
|
||||||
|
{
|
||||||
|
++(*game::fs_loadStack);
|
||||||
|
|
||||||
|
auto len = static_cast<int>(gamesettings_data.length());
|
||||||
|
auto buf = game::FS_AllocMem(len + 1);
|
||||||
|
|
||||||
|
*buffer = buf;
|
||||||
|
gamesettings_data.copy(reinterpret_cast<char*>(*buffer), len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::hook::invoke<int>(game::select(0x1422A48D0, 0x140564F70), qpath, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_gamesettings_files_on_disk()
|
||||||
|
{
|
||||||
|
const utils::nt::library host{};
|
||||||
|
|
||||||
|
search_game_settings_folder((game::get_appdata_path() / "data/gamesettings").string());
|
||||||
|
search_game_settings_folder((host.get_folder() / "boiii/gamesettings").string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct component final : generic_component
|
||||||
|
{
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
search_gamesettings_files_on_disk();
|
||||||
|
|
||||||
|
utils::hook::call(game::select(0x1420ED0A1, 0x1404F857D), read_file_stub);
|
||||||
|
utils::hook::jump(game::select(0x1420ED002, 0x1404F853A), utils::hook::assemble(cmd_exec_stub));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(gamesettings::component)
|
@ -256,6 +256,38 @@ namespace network
|
|||||||
return a.port == b.port && a.addr == b.addr;
|
return a.port == b.port && a.addr == b.addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int net_sendpacket_stub(const game::netsrc_t sock, const int length, const char* data, const game::netadr_t* to)
|
||||||
|
{
|
||||||
|
printf("Sending packet of size: %X\n", length);
|
||||||
|
|
||||||
|
if (to->type != game::NA_RAWIP)
|
||||||
|
{
|
||||||
|
printf("NET_SendPacket: bad address type\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto s = *game::ip_socket;
|
||||||
|
if (!s || sock > game::NS_MAXCLIENTS)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr_in address{};
|
||||||
|
address.sin_family = AF_INET;
|
||||||
|
address.sin_port = htons(to->port);
|
||||||
|
address.sin_addr.s_addr = htonl(((to->ipv4.c | ((to->ipv4.b | (to->ipv4.a << 8)) << 8)) << 8) | to->ipv4.d);
|
||||||
|
|
||||||
|
const auto size = static_cast<size_t>(length);
|
||||||
|
|
||||||
|
std::vector<char> buffer{};
|
||||||
|
buffer.resize(size + 1);
|
||||||
|
buffer[0] = static_cast<char>((static_cast<uint32_t>(sock) & 0xF) | ((to->localNetID & 0xF) << 4));
|
||||||
|
memcpy(buffer.data() + 1, data, size);
|
||||||
|
|
||||||
|
return sendto(s, buffer.data(), static_cast<int>(buffer.size()), 0, reinterpret_cast<sockaddr*>(&address),
|
||||||
|
sizeof(address));
|
||||||
|
}
|
||||||
|
|
||||||
struct component final : generic_component
|
struct component final : generic_component
|
||||||
{
|
{
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
@ -268,6 +300,9 @@ namespace network
|
|||||||
// skip checksum verification
|
// skip checksum verification
|
||||||
utils::hook::set<uint8_t>(game::select(0x14233249E, 0x140596F2E), 0); // don't add checksum to packet
|
utils::hook::set<uint8_t>(game::select(0x14233249E, 0x140596F2E), 0); // don't add checksum to packet
|
||||||
|
|
||||||
|
// Recreate NET_SendPacket to increase max packet size
|
||||||
|
utils::hook::jump(game::select(0x1423323B0, 0x140596E40), net_sendpacket_stub);
|
||||||
|
|
||||||
utils::hook::set<uint32_t>(game::select(0x14134C6E0, 0x14018E574), 5);
|
utils::hook::set<uint32_t>(game::select(0x14134C6E0, 0x14018E574), 5);
|
||||||
// set initial connection state to challenging
|
// set initial connection state to challenging
|
||||||
|
|
||||||
|
@ -100,6 +100,10 @@ namespace profile_infos
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#ifdef DEV_BUILD
|
||||||
|
printf("Erasing profile info: %llX\n", i->first);
|
||||||
|
#endif
|
||||||
|
|
||||||
i = profiles.erase(i);
|
i = profiles.erase(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,6 +130,10 @@ namespace profile_infos
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEV_BUILD
|
||||||
|
printf("Adding profile info: %llX\n", user_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
profile_mapping.access([&](profile_map& profiles)
|
profile_mapping.access([&](profile_map& profiles)
|
||||||
{
|
{
|
||||||
profiles[user_id] = info;
|
profiles[user_id] = info;
|
||||||
@ -202,7 +210,17 @@ namespace profile_infos
|
|||||||
if (profile_entry != profiles.end())
|
if (profile_entry != profiles.end())
|
||||||
{
|
{
|
||||||
result = profile_entry->second;
|
result = profile_entry->second;
|
||||||
|
|
||||||
|
#ifdef DEV_BUILD
|
||||||
|
printf("Requesting profile info: %llX - good\n", user_id);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#ifdef DEV_BUILD
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Requesting profile info: %llX - bad\n", user_id);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
@ -689,6 +689,7 @@ namespace game
|
|||||||
byte color[4];
|
byte color[4];
|
||||||
const dvar_t* indirect[3];
|
const dvar_t* indirect[3];
|
||||||
} value;
|
} value;
|
||||||
|
|
||||||
uint64_t encryptedValue;
|
uint64_t encryptedValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -699,26 +700,31 @@ namespace game
|
|||||||
int stringCount;
|
int stringCount;
|
||||||
const char** strings;
|
const char** strings;
|
||||||
} enumeration;
|
} enumeration;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
int min;
|
int min;
|
||||||
int max;
|
int max;
|
||||||
} integer;
|
} integer;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
int64_t min;
|
int64_t min;
|
||||||
int64_t max;
|
int64_t max;
|
||||||
} integer64;
|
} integer64;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
uint64_t min;
|
uint64_t min;
|
||||||
uint64_t max;
|
uint64_t max;
|
||||||
} unsignedInt64;
|
} unsignedInt64;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
float min;
|
float min;
|
||||||
float max;
|
float max;
|
||||||
} value;
|
} value;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
vec_t min;
|
vec_t min;
|
||||||
@ -1028,7 +1034,7 @@ namespace game
|
|||||||
JoinResult joinResult;
|
JoinResult joinResult;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
namespace hks
|
namespace hks
|
||||||
{
|
{
|
||||||
struct lua_State;
|
struct lua_State;
|
||||||
@ -1051,7 +1057,7 @@ namespace game
|
|||||||
|
|
||||||
typedef size_t hksSize;
|
typedef size_t hksSize;
|
||||||
typedef void* (*lua_Alloc)(void*, void*, size_t, size_t);
|
typedef void* (*lua_Alloc)(void*, void*, size_t, size_t);
|
||||||
typedef hksInt32(*lua_CFunction)(lua_State*);
|
typedef hksInt32 (*lua_CFunction)(lua_State*);
|
||||||
|
|
||||||
struct GenericChunkHeader
|
struct GenericChunkHeader
|
||||||
{
|
{
|
||||||
@ -1108,11 +1114,14 @@ namespace game
|
|||||||
TNUMBER = 0x3,
|
TNUMBER = 0x3,
|
||||||
TSTRING = 0x4,
|
TSTRING = 0x4,
|
||||||
TTABLE = 0x5,
|
TTABLE = 0x5,
|
||||||
TFUNCTION = 0x6, // idk
|
TFUNCTION = 0x6,
|
||||||
|
// idk
|
||||||
TUSERDATA = 0x7,
|
TUSERDATA = 0x7,
|
||||||
TTHREAD = 0x8,
|
TTHREAD = 0x8,
|
||||||
TIFUNCTION = 0x9, // Lua function
|
TIFUNCTION = 0x9,
|
||||||
TCFUNCTION = 0xA, // C function
|
// Lua function
|
||||||
|
TCFUNCTION = 0xA,
|
||||||
|
// C function
|
||||||
TUI64 = 0xB,
|
TUI64 = 0xB,
|
||||||
TSTRUCT = 0xC,
|
TSTRUCT = 0xC,
|
||||||
NUM_TYPE_OBJECTS = 0xE,
|
NUM_TYPE_OBJECTS = 0xE,
|
||||||
@ -1294,7 +1303,7 @@ namespace game
|
|||||||
int _m_isHksGlobalMemoTestingMode;
|
int _m_isHksGlobalMemoTestingMode;
|
||||||
HksCompilerSettings_BytecodeSharingFormat m_bytecodeSharingFormat;
|
HksCompilerSettings_BytecodeSharingFormat m_bytecodeSharingFormat;
|
||||||
HksCompilerSettings_IntLiteralOptions m_enableIntLiterals;
|
HksCompilerSettings_IntLiteralOptions m_enableIntLiterals;
|
||||||
int(*m_debugMap)(const char*, int);
|
int (*m_debugMap)(const char*, int);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum HksBytecodeSharingMode : __int64
|
enum HksBytecodeSharingMode : __int64
|
||||||
@ -1504,7 +1513,7 @@ namespace game
|
|||||||
void* m_profiler;
|
void* m_profiler;
|
||||||
RuntimeProfileData m_runProfilerData;
|
RuntimeProfileData m_runProfilerData;
|
||||||
HksCompilerSettings m_compilerSettings;
|
HksCompilerSettings m_compilerSettings;
|
||||||
int(*m_panicFunction)(lua_State*);
|
int (*m_panicFunction)(lua_State*);
|
||||||
void* m_luaplusObjectList;
|
void* m_luaplusObjectList;
|
||||||
int m_heapAssertionFrequency;
|
int m_heapAssertionFrequency;
|
||||||
int m_heapAssertionCount;
|
int m_heapAssertionCount;
|
||||||
@ -1533,6 +1542,7 @@ namespace game
|
|||||||
HksError m_error;
|
HksError m_error;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef uint32_t ScrVarCanonicalName_t;
|
typedef uint32_t ScrVarCanonicalName_t;
|
||||||
|
|
||||||
@ -1556,19 +1566,23 @@ namespace game
|
|||||||
char __pad4[0x29DAC];
|
char __pad4[0x29DAC];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
static_assert(sizeof(client_s) == 0xE5110);
|
static_assert(sizeof(client_s) == 0xE5110);
|
||||||
|
|
||||||
static_assert(offsetof(game::client_s, address) == 0x2C);
|
static_assert(offsetof(game::client_s, address) == 0x2C);
|
||||||
static_assert(offsetof(game::client_s, xuid) == 0x55C8);
|
static_assert(offsetof(game::client_s, xuid) == 0x55C8);
|
||||||
static_assert(offsetof(game::client_s, guid) == 0xBB354);
|
static_assert(offsetof(game::client_s, guid) == 0xBB354);
|
||||||
static_assert(offsetof(game::client_s, bIsTestClient) == 0xBB360);
|
static_assert(offsetof(game::client_s, bIsTestClient) == 0xBB360);
|
||||||
|
#endif
|
||||||
|
|
||||||
struct client_s_cl : client_s
|
struct client_s_cl : client_s
|
||||||
{
|
{
|
||||||
char __pad1_0[0x60];
|
char __pad1_0[0x60];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
static_assert(sizeof(client_s_cl) == 0xE5170);
|
static_assert(sizeof(client_s_cl) == 0xE5170);
|
||||||
|
#endif
|
||||||
|
|
||||||
enum scriptInstance_t
|
enum scriptInstance_t
|
||||||
{
|
{
|
||||||
@ -1598,7 +1612,9 @@ namespace game
|
|||||||
unsigned char __pad1[0x2A0];
|
unsigned char __pad1[0x2A0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
static_assert(sizeof(gentity_s) == 0x4F8);
|
static_assert(sizeof(gentity_s) == 0x4F8);
|
||||||
|
#endif
|
||||||
|
|
||||||
enum workshop_type
|
enum workshop_type
|
||||||
{
|
{
|
||||||
@ -1623,7 +1639,9 @@ namespace game
|
|||||||
workshop_type type;
|
workshop_type type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
static_assert(sizeof(workshop_data) == 0x4C8);
|
static_assert(sizeof(workshop_data) == 0x4C8);
|
||||||
|
#endif
|
||||||
|
|
||||||
struct DDLMember
|
struct DDLMember
|
||||||
{
|
{
|
||||||
@ -1695,7 +1713,7 @@ namespace game
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct DDLContext;
|
struct DDLContext;
|
||||||
typedef void(* DDLWriteCB)(DDLContext*, void*);
|
typedef void (* DDLWriteCB)(DDLContext*, void*);
|
||||||
|
|
||||||
struct DDLContext
|
struct DDLContext
|
||||||
{
|
{
|
||||||
|
@ -35,6 +35,7 @@ namespace game
|
|||||||
WEAK symbol<void(int localClientNum, eModes fromMode, eModes toMode, uint32_t flags)> Com_SwitchMode{
|
WEAK symbol<void(int localClientNum, eModes fromMode, eModes toMode, uint32_t flags)> Com_SwitchMode{
|
||||||
0x14214A4D0
|
0x14214A4D0
|
||||||
};
|
};
|
||||||
|
WEAK symbol<const char*(const char* fullpath)> Com_LoadRawTextFile{0x1420F61B0};
|
||||||
|
|
||||||
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x1420EC010, 0x1404F75B0};
|
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x1420EC010, 0x1404F75B0};
|
||||||
WEAK symbol<void(int localClientNum, ControllerIndex_t controllerIndex, const char* buffer)> Cbuf_ExecuteBuffer{
|
WEAK symbol<void(int localClientNum, ControllerIndex_t controllerIndex, const char* buffer)> Cbuf_ExecuteBuffer{
|
||||||
@ -184,6 +185,9 @@ namespace game
|
|||||||
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0x1420EF130, 0x1404FA6C0};
|
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0x1420EF130, 0x1404FA6C0};
|
||||||
WEAK symbol<void()> SV_Cmd_EndTokenizedString{0x1420EF0E0, 0x1404FA670};
|
WEAK symbol<void()> SV_Cmd_EndTokenizedString{0x1420EF0E0, 0x1404FA670};
|
||||||
|
|
||||||
|
// FS
|
||||||
|
WEAK symbol<char*(int bytes)> FS_AllocMem{0x1422AC9F0, 0x14056C340};
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
WEAK symbol<const char*(char* str)> I_CleanStr{0x1422E9050, 0x140580E80};
|
WEAK symbol<const char*(char* str)> I_CleanStr{0x1422E9050, 0x140580E80};
|
||||||
|
|
||||||
@ -202,6 +206,8 @@ namespace game
|
|||||||
WEAK symbol<char> s_dvarPool{0x157AC6220, 0x14A3CB620};
|
WEAK symbol<char> s_dvarPool{0x157AC6220, 0x14A3CB620};
|
||||||
WEAK symbol<int> g_dvarCount{0x157AC61CC, 0x14A3CB5FC};
|
WEAK symbol<int> g_dvarCount{0x157AC61CC, 0x14A3CB5FC};
|
||||||
|
|
||||||
|
WEAK symbol<int> fs_loadStack{0x157A65310, 0x14A39C650};
|
||||||
|
|
||||||
// Client and dedi struct size differs :(
|
// Client and dedi struct size differs :(
|
||||||
WEAK symbol<client_s_cl*> svs_clients_cl{0x1576F9318, 0};
|
WEAK symbol<client_s_cl*> svs_clients_cl{0x1576F9318, 0};
|
||||||
WEAK symbol<client_s*> svs_clients{0x0, 0x14A178E98};
|
WEAK symbol<client_s*> svs_clients{0x0, 0x14A178E98};
|
||||||
|
Loading…
Reference in New Issue
Block a user