Custom CMD & Structural Upgrade

+ Added custom commands interface
+ Tidy up and reorder game interface
This commit is contained in:
project-bo4 2024-05-31 15:35:20 -07:00
parent 324fcc2425
commit 81a92f69a4
24 changed files with 1267 additions and 1027 deletions

View File

@ -218,7 +218,7 @@ namespace blackbox
std::string generate_crash_info(const LPEXCEPTION_POINTERS exceptioninfo)
{
const auto& build_info = game::version_string;
std::string build_info = game::Com_GetVersionString();
const auto main_module = utilities::nt::library{};
const auto rip_address = exceptioninfo->ExceptionRecord->ExceptionAddress;

View File

@ -0,0 +1,212 @@
#include <std_include.hpp>
#include "command.hpp"
#include "definitions/game.hpp"
#include <utilities/hook.hpp>
#include <utilities/string.hpp>
#include <utilities/memory.hpp>
namespace command
{
namespace
{
std::unordered_map<std::string, command_param_function>& get_command_map()
{
static std::unordered_map<std::string, command_param_function> command_map{};
return command_map;
}
std::unordered_map<std::string, sv_command_param_function>& get_sv_command_map()
{
static std::unordered_map<std::string, sv_command_param_function> command_map{};
return command_map;
}
void execute_custom_command()
{
const params params{};
const auto command = utilities::string::to_lower(params[0]);
auto& map = get_command_map();
const auto entry = map.find(command);
if (entry != map.end())
{
entry->second(params);
}
}
void execute_custom_sv_command()
{
const params_sv params{};
const auto command = utilities::string::to_lower(params[0]);
auto& map = get_sv_command_map();
const auto entry = map.find(command);
if (entry != map.end())
{
entry->second(params);
}
}
game::CmdArgs* get_cmd_args()
{
return game::Sys_GetTLS()->cmdArgs;
}
}
params::params()
: nesting_(get_cmd_args()->nesting)
{
assert(this->nesting_ < game::CMD_MAX_NESTING);
}
params::params(const std::string& text)
: needs_end_(true)
{
auto* cmd_args = get_cmd_args();
game::Cmd_TokenizeStringKernel(0, 0, text.data(),
512 - cmd_args->totalUsedArgvPool, false, cmd_args);
this->nesting_ = cmd_args->nesting;
}
params::~params()
{
if (this->needs_end_)
{
game::Cmd_EndTokenizedString();
}
}
int params::size() const
{
return get_cmd_args()->argc[this->nesting_];
}
params_sv::params_sv()
: nesting_(game::sv_cmd_args->nesting)
{
assert(this->nesting_ < game::CMD_MAX_NESTING);
}
params_sv::params_sv(const std::string& text)
: needs_end_(true)
{
game::SV_Cmd_TokenizeString(text.data());
this->nesting_ = game::sv_cmd_args->nesting;
}
params_sv::~params_sv()
{
if (this->needs_end_)
{
game::SV_Cmd_EndTokenizedString();
}
}
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;
}
const char* params::get(const int index) const
{
if (index >= this->size())
{
return "";
}
return get_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;
}
void add(const std::string& command, command_function function, const char* desc)
{
add(command, [f = std::move(function)](const params&)
{
f();
});
}
void add(const std::string& command, command_param_function function, const char* desc)
{
auto lower_command = utilities::string::to_lower(command);
auto& map = get_command_map();
const auto is_registered = map.contains(lower_command);
map[std::move(lower_command)] = std::move(function);
if (is_registered)
{
return;
}
auto& allocator = *utilities::memory::get_allocator();
auto* cmd_function = allocator.allocate<game::cmd_function_t>();
auto cmdRef = game::AssetRef(command.data());
variables::commands_table.push_back({ command, desc, fnv1a::generate_hash(command.data()) });
game::Cmd_AddCommandInternal(&cmdRef, execute_custom_command, cmd_function);
}
void add_sv(const std::string& command, sv_command_param_function function, const char* desc)
{
auto lower_command = utilities::string::to_lower(command);
auto& map = get_sv_command_map();
const auto is_registered = map.contains(lower_command);
map[std::move(lower_command)] = std::move(function);
if (is_registered)
{
return;
}
auto& allocator = *utilities::memory::get_allocator();
auto cmdRef = game::AssetRef(command.data());
variables::commands_table.push_back({ command, desc, fnv1a::generate_hash(command.data()) });
game::Cmd_AddCommandInternal(&cmdRef, game::Cbuf_AddServerText_f,
allocator.allocate<game::cmd_function_t>());
game::Cmd_AddServerCommandInternal(&cmdRef, execute_custom_sv_command,
allocator.allocate<game::cmd_function_t>());
}
}

View File

@ -0,0 +1,67 @@
#pragma once
namespace command
{
class params
{
public:
params();
params(const std::string& text);
~params();
params(params&&) = delete;
params(const params&) = delete;
params& operator=(params&&) = delete;
params& operator=(const params&) = delete;
[[nodiscard]] int size() const;
[[nodiscard]] const char* get(int index) const;
[[nodiscard]] std::string join(int index) const;
[[nodiscard]] const char* operator[](const int index) const
{
return this->get(index); //
}
private:
bool needs_end_{false};
int nesting_;
};
class params_sv
{
public:
params_sv();
params_sv(const std::string& text);
~params_sv();
params_sv(params_sv&&) = delete;
params_sv(const params_sv&) = delete;
params_sv& operator=(params_sv&&) = delete;
params_sv& operator=(const params_sv&) = delete;
[[nodiscard]] int size() const;
[[nodiscard]] const char* get(int index) const;
[[nodiscard]] std::string join(int index) const;
[[nodiscard]] const char* operator[](const int index) const
{
return this->get(index); //
}
private:
bool needs_end_{false};
int nesting_;
};
using command_function = std::function<void()>;
using command_param_function = std::function<void(const params&)>;
using sv_command_param_function = std::function<void(const params_sv&)>;
void add(const std::string& command, command_function function, const char* desc = "");
void add(const std::string& command, command_param_function function, const char* desc = "");
void add_sv(const std::string& command, sv_command_param_function function, const char* desc = "");
}

View File

@ -1,6 +1,5 @@
#include <std_include.hpp>
#include "definitions/game.hpp"
#include "definitions/game_runtime_errors.hpp"
#include "component/scheduler.hpp"
#include "loader/component_loader.hpp"
@ -74,7 +73,7 @@ namespace debugging
void sys_error_stub(uint32_t code, const char* message)
{
const char* error_message = game::runtime_errors::get_error_message(code);
const char* error_message = runtime_errors::get_error_message(code);
if (error_message)
{

View File

@ -13,7 +13,7 @@ namespace dvars
{
void fetch_dvar_pointers()
{
for (auto& dvar : variables::dvars_record)
for (auto& dvar : variables::dvars_table)
{
dvar.pointer = spoofcall::invoke<uintptr_t>(game::Dvar_FindVar, dvar.name.data());
}
@ -248,9 +248,9 @@ namespace dvars
{
if (hashRef == 0) return NULL;
auto it = std::find_if(variables::dvars_record.begin(), variables::dvars_record.end(), [&hashRef](variables::varEntry& i) { return i.fnv1a == hashRef; });
auto it = std::find_if(variables::dvars_table.begin(), variables::dvars_table.end(), [&hashRef](variables::varEntry& i) { return i.fnv1a == hashRef; });
if (it != variables::dvars_record.end() && it->pointer)
if (it != variables::dvars_table.end() && it->pointer)
{
return reinterpret_cast<game::dvar_t*>(it->pointer);
}
@ -265,9 +265,9 @@ namespace dvars
game::dvar_t* find_dvar(const std::string& nameRef)
{
auto it = std::find_if(variables::dvars_record.begin(), variables::dvars_record.end(), [&nameRef](variables::varEntry& i) { return utilities::string::compare(i.name, nameRef); });
auto it = std::find_if(variables::dvars_table.begin(), variables::dvars_table.end(), [&nameRef](variables::varEntry& i) { return utilities::string::compare(i.name, nameRef); });
if (it != variables::dvars_record.end() && it->pointer)
if (it != variables::dvars_table.end() && it->pointer)
{
return reinterpret_cast<game::dvar_t*>(it->pointer);
}

View File

@ -184,7 +184,7 @@ namespace game_console
{
double required_ratio = exact ? 1.00 : 0.01;
for (const auto& dvar : variables::dvars_record)
for (const auto& dvar : variables::dvars_table)
{
if (dvars::find_dvar(dvar.fnv1a) && utilities::string::match(input, dvar.name) >= required_ratio)
{
@ -202,7 +202,7 @@ namespace game_console
suggestions.push_back({ input, "", fnv1a::generate_hash(input.data()), reinterpret_cast<uintptr_t>(dvars::find_dvar(input)) });
}
for (const auto& cmd : variables::commands_record)
for (const auto& cmd : variables::commands_table)
{
if (utilities::string::match(input, cmd.name) >= required_ratio)
{
@ -374,7 +374,13 @@ namespace game_console
auto width = (con.screen_max[0] - con.screen_min[0]) - 12.0f;
auto height = ((con.screen_max[1] - con.screen_min[1]) - 32.0f) - 12.0f;
game::R_AddCmdDrawText(game::version_string.data(), 0x7FFFFFFF, R_DrawTextFont, x, ((height - 16.0f) + y) + con.font_height, con.font_scale, con.font_scale, 0.0f, con_outputVersionStringColor, 0);
char sysinfo_version[256];
bool result1 = utilities::hook::invoke<bool>(game::Live_SystemInfo, 0, 0, sysinfo_version, 256);
char sysinfo_livebits[256];
bool result2 = utilities::hook::invoke<bool>(game::Live_SystemInfo, 0, 1, sysinfo_livebits, 256);
const char* info = utilities::string::va("Project-BO4 1.0.0, Engine Version: %s, LiveBits: %s", sysinfo_version, sysinfo_livebits);
game::R_AddCmdDrawText(info, 0x7FFFFFFF, R_DrawTextFont, x, ((height - 16.0f) + y) + con.font_height, con.font_scale, con.font_scale, 0.0f, con_outputVersionStringColor, 0);
draw_output_scrollbar(x, y, width, height, output);
draw_output_text(x, y, output);

View File

@ -3,7 +3,6 @@
#include "gsc_custom.hpp"
#include "hashes.hpp"
#include "definitions/game.hpp"
#include "definitions/game_runtime_errors.hpp"
#include "definitions/xassets.hpp"
#include "loader/component_loader.hpp"
#include "component/scheduler.hpp"
@ -53,7 +52,7 @@ namespace gsc_funcs
vsprintf_s(buffer[inst], message, va);
va_end(va);
game::ScrVm_Error(game::runtime_errors::custom_error_id, inst, buffer[inst], terminal);
game::ScrVm_Error(runtime_errors::custom_error_id, inst, buffer[inst], terminal);
}
const char* lookup_hash(game::scriptInstance_t inst, const char* type, uint64_t hash)
@ -1329,13 +1328,13 @@ namespace gsc_funcs
break;
default:
// put custom message for our id
if (code == game::runtime_errors::custom_error_id)
if (code == runtime_errors::custom_error_id)
{
game::scrVarPub[inst].error_message = unused;
}
else
{
game::scrVarPub[inst].error_message = game::runtime_errors::get_error_message(code);
game::scrVarPub[inst].error_message = runtime_errors::get_error_message(code);
}
break;
}

View File

@ -72,11 +72,6 @@ namespace logger
write(LOG_TYPE_INFO, " Project-BO4 Initializing ... %s[0x%llX]", utilities::nt::library{}.get_name().c_str(), utilities::nt::library{}.get_ptr());
write(LOG_TYPE_INFO, "=======================================================================================================");
}
void post_unpack() override
{
/* PLACE_HOLDER */
}
};
}
REGISTER_COMPONENT(logger::component)

View File

@ -1,15 +1,15 @@
#include <std_include.hpp>
#include "definitions/xassets.hpp"
#include "definitions/game.hpp"
#include "game_console.hpp"
#include "gsc_funcs.hpp"
#include "gsc_custom.hpp"
#include "dvars.hpp"
#include "hashes.hpp"
#include "command.hpp"
#include "game_console.hpp"
#include "loader/component_loader.hpp"
#include "definitions/xassets.hpp"
#include "definitions/game.hpp"
#include <utilities/io.hpp>
#include <utilities/hook.hpp>
@ -1094,7 +1094,7 @@ namespace mods {
mod_storage storage{};
void load_mods_cmd()
void mods_reload_f()
{
if (!game::Com_IsRunningUILevel())
{
@ -1267,8 +1267,7 @@ namespace mods {
hksl_loadfile_hook.create(0x14375D6A0_g, hksl_loadfile_stub);
bg_cache_sync_hook.create(0x1405CE0B0_g, bg_cache_sync_stub);
// register load mods command
Cmd_AddCommand("reload_mods", load_mods_cmd);
command::add("reload_mods", mods_reload_f, "Reload the shield mods");
}
};
}

View File

@ -1,168 +0,0 @@
#include <std_include.hpp>
#include "discovery.hpp"
#include "loader/component_loader.hpp"
#include <utilities/hook.hpp>
#include <utilities/signature.hpp>
std::unordered_map<std::string, size_t> symbols_list;
size_t operator"" _d(const char* str, const size_t len)
{
auto itr = symbols_list.find(std::string(str, len));
if (itr != symbols_list.end()) return itr->second; // found you!
//return find_missing_symbol(name);
}
namespace discovery
{
void export_address_list_to_json()
{
/* PLACE_HOLDER */
}
void import_address_list_from_json()
{
/* PLACE_HOLDER */
}
void start_address_list_discovery()
{
auto follow_jmp = [](size_t addr) -> size_t {
return *(int32_t*)(addr + 1) + addr + 5;
};
auto follow_lea = [](size_t addr) -> size_t {
return *(int32_t*)(addr + 3) + addr + 7;
};
std::vector<sig_instance> signature_list; // SYMBOL NAME , SIGNATURE, DISTANCE, SIGNATURE RELATION
// Main Symbols
signature_list.push_back({ "com_error", "4C 89 4C 24 ? 55 53 56 57 48", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "live_get_connectivity_info", "48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B DA C7 02", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "draw_text_cmd", "48 89 6C 24 ? 41 54 41 56 41 57 48 83 EC 30 80", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "ui_get_font_handle", "48 89 5C 24 ? 57 48 83 EC 30 8B DA 48 8B F9 83", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "scr_place_get_view", "E8 ? ? ? ? 48 85 C0 74 11 4C 8D", 0, SIG_RELEVANCE_JMP_FROM });
signature_list.push_back({ "r_text_height", "E8 ? ? ? ? 66 0F 6E C0 0F 5B C0 F3 0F 59 C6 F3 0F", 0, SIG_RELEVANCE_JMP_FROM });
// Platform Symbols
signature_list.push_back({ "bnet_is_disabled", "40 55 48 8D 6C 24 ? 48 81 EC ? ? ? ? 45 85", 0x17, SIG_RELEVANCE_JMP_FROM });
signature_list.push_back({ "bnet_is_connected", "48 83 EC 28 48 8B 0D ? ? ? ? E8 ? ? ? ? 84 C0 74 5A", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "bnet_patch_unk1", "48 83 EC 28 48 8B 0D ? ? ? ? E8 ? ? ? ? 84 C0 75 11", 0, SIG_RELEVANCE_DIRECT_HIT }); // Annoying function related to bnet; crashes game
signature_list.push_back({ "bnet_patch_unk2", "E8 ? ? ? ? 8B CF E8 ? ? ? ? 45 33 C0 48", 0, SIG_RELEVANCE_JMP_FROM }); // Annoying function related to bnet; crashes game
signature_list.push_back({ "bnet_patch_unk3", "E8 ? ? ? ? 88 85 ? ? ? ? 48 8B", 0, SIG_RELEVANCE_JMP_FROM }); // BattleNet_IsModeAvailable?
signature_list.push_back({ "bnet_patch_auth3", "E8 ? ? ? ? 84 C0 0F 85 ? ? ? ? 8B CB E8 ? ? ? ? 4C", 0, SIG_RELEVANCE_DIRECT_HIT }); // LiveConnect_BeginCrossAuthPlatform
// Frame Hooks
signature_list.push_back({ "r_end_frame", "E8 ? ? ? ? 41 F6 DD 1B", 0, SIG_RELEVANCE_JMP_FROM });
signature_list.push_back({ "com_frame", "48 83 EC 48 48 C7 44 24 ? ? ? ? ? 48 8D 0D", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "g_run_frame", "48 8B C4 48 89 58 10 48 89 70 18 48 89 78 20 55 41 54 41 55 41 56 41 57 48 8D A8 C8 F7 FF FF", 0, SIG_RELEVANCE_DIRECT_HIT });
// Demonware
signature_list.push_back({ "curl_setup_ssl_verify_peer", "40 53 48 83 EC 20 BA ? ? ? ? 48 8B D9 48 8B", 0x29, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "curl_setup_ssl_verify_host", "40 53 48 83 EC 20 BA ? ? ? ? 48 8B D9 48 8B", 0x15, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "dw_https", "68 74 74 70 73 00", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "dw_prod_umbrella_url", "68 74 74 70 73 3A 2F 2F 70 72 6F 64 2E 75 6D 62 72 65 6C 6C 61 2E 64 65 6D 6F 6E 77 61 72 65 2E 6E 65 74 00", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "dw_prod_uno_url", "68 74 74 70 73 3A 2F 2F 70 72 6F 64 2E 75 6E 6F 2E 64 65 6D 6F 6E 77 61 72 65 2E 6E 65 74 2F 76 31 2E 30 00", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "dw_auth3_url_frmt", "68 74 74 70 73 3A 2F 2F 25 73 3A 25 64 2F 61 75 74 68 2F 00", 0, SIG_RELEVANCE_DIRECT_HIT });
// BuildNumber
signature_list.push_back({ "com_get_build_version", "40 53 48 83 EC 40 44 8B 0D", 0, SIG_RELEVANCE_DIRECT_HIT });
// UI Symbols
signature_list.push_back({ "ui_get_model_for_controller", "48 63 C1 48 8D 0D ? ? ? ? 0F B7 04", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "ui_create_model_from_path", "45 33 C9 41 B0 01 E9", 0, SIG_RELEVANCE_DIRECT_HIT });
signature_list.push_back({ "ui_model_set_string", "48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 8B DA 49 8B F1", 0x4E, SIG_RELEVANCE_JMP_FROM });
signature_list.push_back({ "ui_model_set_int", "48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 8B DA 49 8B F1", 0x20, SIG_RELEVANCE_JMP_FROM });
signature_list.push_back({ "ui_model_set_bool", "48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 8B DA 49 8B F1", 0x31, SIG_RELEVANCE_JMP_FROM });
signature_list.push_back({ "ui_model_set_real", "48 83 EC 28 66 85 C9 74 3A", 0, SIG_RELEVANCE_DIRECT_HIT });
//// Impossible to make signature; should be updated manually
//signature_list.push_back({ "bnet_process_auth3_data", "", 0x000000000_g, SIG_RELEVANCE_IMPOSSIBLE });
//signature_list.push_back({ "bnet_patch_text_chat", "", 0x000000000_g, SIG_RELEVANCE_IMPOSSIBLE });
logger::write(logger::LOG_TYPE_DEBUG, "[ DISCOVERY ]: Starting signature scan; total defined symbols: %u", signature_list.size());
symbols_list.clear();
int error_count = 0;
for (sig_instance i : signature_list)
{
if (i.relv == SIG_RELEVANCE_IMPOSSIBLE)
{
symbols_list.insert({ i.name, i.dist });
continue;
}
utilities::hook::signature::signature_result scan = utilities::hook::signature(std::string(i.sig, strlen(i.sig))).process();
if (scan.size() == 0 || scan.size() > 1)
{
logger::write(logger::LOG_TYPE_DEBUG, "[ DISCOVERY ]: %s while searching for %s", scan.size() ? "Multiple Matches" : "No Hits", i.name);
error_count++;
continue;
}
if (i.relv == SIG_RELEVANCE_DIRECT_HIT)
{
symbols_list.insert({ i.name, reinterpret_cast<size_t>(scan[0]) + i.dist });
}
else if (i.relv == SIG_RELEVANCE_JMP_FROM)
{
symbols_list.insert({ i.name, follow_jmp(reinterpret_cast<size_t>(scan[0]) + i.dist) });
}
else
{
symbols_list.insert({ i.name, follow_lea(reinterpret_cast<size_t>(scan[0]) + i.dist) });
}
}
#ifdef DEBUG
logger::write(logger::LOG_TYPE_DEBUG, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
if (symbols_list.find("com_get_build_version") != symbols_list.end())
{
const char* build_version = utilities::hook::invoke<const char*>(symbols_list["com_get_build_version"]);
logger::write(logger::LOG_TYPE_DEBUG, "Address-List Discovery Results for BlackOps4 %s", build_version);
}
for (auto symbol : symbols_list)
{
logger::write(logger::LOG_TYPE_DEBUG, "- %-28s: 0x%llX_g", symbol.first.c_str(), reverse_g(symbol.second));
}
logger::write(logger::LOG_TYPE_DEBUG, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
#endif // DEBUG
logger::write(logger::LOG_TYPE_DEBUG, "[ DISCOVERY ]: Signature scanning complete. %u/%u successful", signature_list.size() - error_count, signature_list.size());
}
class component final : public component_interface
{
public:
void post_unpack() override
{
#ifdef DEBUG
start_address_list_discovery();
#endif // DEBUG
/*************************************************************************************************************
** NOTE : Updating game code by developers depending on compiler and where and what changes made, most likely
** will shift addresses. using signature patterns to find addresses at runtime is a good counter to this
** problem when game gets updated frequently but there should be decent fail-safe mechanism implemented
** to detect sig-scanning errors when there is signature-breaking changes in binary to prevent misleadings
**
*************************************************************************************************************/
}
int priority() override
{
return 9998;
}
};
}
//REGISTER_COMPONENT(discovery::component)

View File

@ -1,22 +0,0 @@
#pragma once
size_t operator"" _d(const char* str, size_t len);
namespace discovery
{
enum sig_relation
{
SIG_RELEVANCE_IMPOSSIBLE = -1,
SIG_RELEVANCE_DIRECT_HIT = 0,
SIG_RELEVANCE_LEA_FROM = 1,
SIG_RELEVANCE_JMP_FROM = 2
};
struct sig_instance
{
const char* name;
const char* sig;
size_t dist;
sig_relation relv;
};
}

View File

@ -1,14 +1,30 @@
#include <std_include.hpp>
#include "game.hpp"
#include "loader/component_loader.hpp"
namespace game
{
std::string version_string = "VERSION STRING UN-INITIALIZED";
const char* Com_GetVersionString()
{
static std::string version_string{};
typedef const char* (__fastcall* Com_GetBuildVersion_t)();
Com_GetBuildVersion_t Com_GetBuildVersion = (Com_GetBuildVersion_t)0x142892F40_g;
if (version_string.empty()) {
version_string = std::format("BlackOps4 {}", Com_GetBuildVersion());
}
return version_string.data();
}
void verify_game_version()
{
if (*(int*)0x1449CA7E8_g != 13869365) // BlackOps4 CL(13869365) BEYQBBUILD106 DEV [Wed Feb 22 16:31:32 2023]
{
throw std::runtime_error("Unsupported BlackOps4.exe Version. This DLL Expects Latest BNET Build");
}
#ifdef DEBUG
logger::write(logger::LOG_TYPE_DEBUG, "[ SYSTEM ]: Verified Game Version '%s'", Com_GetVersionString());
#endif // DEBUG
}
scoped_critical_section::scoped_critical_section(int32_t s, scoped_critical_section_type type) : _s(0), _hasOwnership(false), _isScopedRelease(false), _next(nullptr)
{
@ -19,42 +35,4 @@ namespace game
{
game::ScopedCriticalSectionDestructor(this);
}
namespace
{
void verify_game_version()
{
if (*(int*)0x1449CA7E8_g != 13869365) // BlackOps4 CL(13869365) BEYQBBUILD106 DEV [Wed Feb 22 16:31:32 2023]
{
throw std::runtime_error("Unsupported BlackOps4.exe Version. Update Your game using Battle.net Launcher");
}
version_string = std::format("BlackOps4 {}", Com_GetBuildVersion());
#ifdef DEBUG
logger::write(logger::LOG_TYPE_DEBUG, "[ SYSTEM ]: game version string: %s", version_string.c_str());
#endif // DEBUG
}
}
class component final : public component_interface
{
public:
void pre_start() override
{
/* PLACE_HOLDER */
}
void post_unpack() override
{
verify_game_version();
}
int priority() override
{
return 9997;
}
};
}
REGISTER_COMPONENT(game::component)
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "definitions/discovery.hpp"
#include "keys.hpp"
#include "network.hpp"
#include "scripting.hpp"
#include "definitions/variables.hpp"
#define WEAK __declspec(selectany)
@ -8,22 +9,25 @@
namespace game
{
//////////////////////////////////////////////////////////////////////////
// VARIABLES //
// SHIELD //
//////////////////////////////////////////////////////////////////////////
extern std::string version_string;
void verify_game_version();
const char* Com_GetVersionString();
///////////////////////////////////////////////////////////////////////////
// TYPES //
///////////////////////////////////////////////////////////////////////////
typedef float vec_t;
typedef vec_t vec2_t[2];
typedef vec_t vec3_t[3];
typedef vec_t vec4_t[4];
typedef uint32_t ScrVarIndex_t;
typedef uint64_t ScrVarNameIndex_t;
//////////////////////////////////////////////////////////////////////////
// STRUCTS //
//////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// STRUCTS //
///////////////////////////////////////////////////////////////////////////
struct BO4_AssetRef_t
{
@ -40,508 +44,14 @@ namespace game
return m128i;
}
typedef void (*xcommand_t)(void);
struct cmd_function_t
inline BO4_AssetRef_t
AssetRef(const char* nameRef)
{
cmd_function_t* next;
uint64_t name;
uint64_t pad0;
uint64_t pad1;
uint64_t pad2;
xcommand_t function;
};
BO4_AssetRef_t m128i;
m128i.hash = fnv1a::generate_hash(nameRef);
struct GSC_IMPORT_ITEM
{
uint32_t name;
uint32_t name_space;
uint16_t num_address;
uint8_t param_count;
uint8_t flags;
};
struct GSC_EXPORT_ITEM
{
uint32_t checksum;
uint32_t address;
uint32_t name;
uint32_t name_space;
uint32_t callback_event;
uint8_t param_count;
uint8_t flags;
};
struct GSC_OBJ
{
byte magic[8];
int32_t crc;
int32_t pad;
uint64_t name;
int32_t include_offset;
uint16_t string_count;
uint16_t exports_count;
int32_t start_data;
int32_t string_offset;
int16_t imports_count;
uint16_t fixup_count;
int32_t ukn2c;
int32_t exports_offset;
int32_t ukn34;
int32_t imports_offset;
uint16_t globalvar_count;
int32_t fixup_offset;
int32_t globalvar_offset;
int32_t script_size;
int32_t requires_implements_offset;
int32_t ukn50;
int32_t data_length;
uint16_t include_count;
byte ukn5a;
byte requires_implements_count;
inline GSC_EXPORT_ITEM* get_exports()
{
return reinterpret_cast<GSC_EXPORT_ITEM*>(magic + exports_offset);
}
inline GSC_IMPORT_ITEM* get_imports()
{
return reinterpret_cast<GSC_IMPORT_ITEM*>(magic + imports_offset);
}
inline uint64_t* get_includes()
{
return reinterpret_cast<uint64_t*>(magic + include_offset);
}
inline GSC_EXPORT_ITEM* get_exports_end()
{
return get_exports() + exports_count;
}
inline uint64_t* get_includes_end()
{
return get_includes() + include_count;
}
};
enum scriptInstance_t : int32_t
{
SCRIPTINSTANCE_SERVER = 0x0,
SCRIPTINSTANCE_CLIENT = 0x1,
SCRIPTINSTANCE_MAX = 0x2,
};
typedef void (*BuiltinFunction)(scriptInstance_t);
struct BO4_BuiltinFunctionDef
{
uint32_t canonId;
uint32_t min_args;
uint32_t max_args;
BuiltinFunction actionFunc;
uint32_t type;
};
struct __declspec(align(4)) BO4_scrVarGlobalVars_t
{
uint32_t name;
ScrVarIndex_t id;
bool persist;
};
enum ScrVarType_t : uint32_t {
TYPE_UNDEFINED = 0x0,
TYPE_POINTER = 0x1,
TYPE_STRING = 0x2,
TYPE_VECTOR = 0x3,
TYPE_HASH = 0x4,
TYPE_FLOAT = 0x5,
TYPE_INTEGER = 0x6,
TYPE_UINTPTR = 0x7,
TYPE_ENTITY_OFFSET = 0x8,
TYPE_CODEPOS = 0x9,
TYPE_PRECODEPOS = 0xA,
TYPE_API_FUNCTION = 0xB,
TYPE_SCRIPT_FUNCTION = 0xC,
TYPE_STACK = 0xD,
TYPE_THREAD = 0xE,
TYPE_NOTIFY_THREAD = 0xF,
TYPE_TIME_THREAD = 0x10,
TYPE_FRAME_THREAD = 0x11,
TYPE_CHILD_THREAD = 0x12,
TYPE_CLASS = 0x13,
TYPE_SHARED_STRUCT = 0x14,
TYPE_STRUCT = 0x15,
TYPE_REMOVED_ENTITY = 0x16,
TYPE_ENTITY = 0x17,
TYPE_ARRAY = 0x18,
TYPE_REMOVED_THREAD = 0x19,
TYPE_FREE = 0x1a,
TYPE_THREAD_LIST = 0x1b,
TYPE_ENT_LIST = 0x1c,
TYPE_COUNT
};
struct BO4_scrVarPub {
const char* fieldBuffer;
const char* error_message;
byte* programBuffer;
byte* endScriptBuffer;
byte* programHunkUser; // HunkUser
BO4_scrVarGlobalVars_t globalVars[16];
ScrVarNameIndex_t entFieldNameIndex;
ScrVarIndex_t freeEntList;
ScrVarIndex_t tempVariable;
uint32_t checksum;
uint32_t entId;
uint32_t varHighWatermark;
uint32_t numScriptThreads;
uint32_t numVarAllocations;
int32_t varHighWatermarkId;
};
union ScrVarValueUnion_t
{
int64_t intValue;
uintptr_t uintptrValue;
float floatValue;
int32_t stringValue;
const float* vectorValue;
byte* codePosValue;
ScrVarIndex_t pointerValue;
};
struct ScrVarValue_t
{
ScrVarValueUnion_t u;
ScrVarType_t type;
};
struct ScrVar_t_Info
{
uint32_t nameType : 3;
uint32_t flags : 5;
uint32_t refCount : 24;
};
struct ScrVar_t
{
ScrVarNameIndex_t nameIndex;
ScrVar_t_Info _anon_0;
ScrVarIndex_t nextSibling;
ScrVarIndex_t prevSibling;
ScrVarIndex_t parentId;
ScrVarIndex_t nameSearchHashList;
uint32_t pad0;
};
union ScrVarObjectInfo1_t
{
uint64_t object_o;
unsigned int size;
ScrVarIndex_t nextEntId;
ScrVarIndex_t self;
ScrVarIndex_t free;
};
union ScrVarObjectInfo2_t
{
uint32_t object_w;
ScrVarIndex_t stackId;
};
struct function_stack_t
{
byte* pos;
ScrVarValue_t* top;
ScrVarValue_t* startTop;
ScrVarIndex_t threadId;
uint16_t localVarCount;
uint16_t profileInfoCount;
};
struct function_frame_t
{
function_stack_t fs;
};
struct ScrVmContext_t
{
ScrVarIndex_t fieldValueId;
ScrVarIndex_t objectId;
byte* lastGoodPos;
ScrVarValue_t* lastGoodTop;
};
typedef void (*VM_OP_FUNC)(scriptInstance_t, function_stack_t*, ScrVmContext_t*, bool*);
struct BO4_scrVarGlob
{
ScrVarIndex_t* scriptNameSearchHashList;
ScrVar_t* scriptVariables;
ScrVarObjectInfo1_t* scriptVariablesObjectInfo1;
ScrVarObjectInfo2_t* scriptVariablesObjectInfo2;
ScrVarValue_t* scriptValues;
};
struct BO4_scrVmPub
{
void* unk0;
void* unk8;
void* executionQueueHeap; // HunkUser
void* timeByValueQueue; // VmExecutionQueueData_t
void* timeByThreadQueue[1024]; // VmExecutionQueue_t
void* frameByValueQueue; // VmExecutionQueueData_t
void* frameByThreadQueue[1024]; // VmExecutionQueue_t
void* timeoutByValueList; // VmExecutionQueueData_t
void* timeoutByThreadList[1024]; // VmExecutionQueue_t
void* notifyByObjectQueue[1024]; // VmExecutionNotifyQueue_t
void* notifyByThreadQueue[1024]; // VmExecutionNotifyQueue_t
void* endonByObjectList[1024]; // VmExecutionNotifyQueue_t
void* endonByThreadList[1024]; // VmExecutionNotifyQueue_t
ScrVarIndex_t* localVars;
ScrVarValue_t* maxstack;
function_frame_t* function_frame;
ScrVarValue_t* top;
function_frame_t function_frame_start[64];
ScrVarValue_t stack[2048];
uint32_t time;
uint32_t frame;
int function_count;
int callNesting;
unsigned int inparamcount;
bool showError;
bool systemInitialized;
bool vmInitialized;
bool isShutdown;
};
struct objFileInfo_t
{
GSC_OBJ* activeVersion;
int slot;
int refCount;
uint32_t groupId;
};
enum keyNum_t
{
K_NONE = 0x00,
K_BUTTON_A = 0x01,
K_BUTTON_B = 0x02,
K_BUTTON_X = 0x03,
K_BUTTON_Y = 0x04,
K_BUTTON_LSHLDR = 0x05,
K_BUTTON_RSHLDR = 0x06,
K_UNK7 = 0x07,
K_UNK8 = 0x08,
K_TAB = 0x09,
K_UNK10 = 0x0A,
K_UNK11 = 0x0B,
K_UNK12 = 0x0C,
K_ENTER = 0x0D,
K_BUTTON_START = 0x0E,
K_BUTTON_BACK = 0x0F,
K_BUTTON_LSTICK = 0x10,
K_BUTTON_RSTICK = 0x11,
K_BUTTON_LTRIG = 0x12,
K_BUTTON_RTRIG = 0x13,
K_UNK20 = 0x14,
K_UNK21 = 0x15,
K_DPAD_UP = 0x16,
K_DPAD_DOWN = 0x17,
K_DPAD_LEFT = 0x18,
K_DPAD_RIGHT = 0x19,
K_UNK26 = 0x1A,
K_ESCAPE = 0x1B,
K_APAD_UP = 0x1C,
K_APAD_DOWN = 0x1D,
K_APAD_LEFT = 0x1E,
K_APAD_RIGHT = 0x1F,
K_SPACE = 0x20,
K_UNK33 = 0x21,
K_UNK34 = 0x22,
K_UNK35 = 0x23,
K_UNK36 = 0x24,
K_UNK37 = 0x25,
K_UNK38 = 0x26,
K_UNK39 = 0x27,
K_UNK40 = 0x28,
K_UNK41 = 0x29,
K_UNK42 = 0x2A,
K_UNK43 = 0x2B,
K_UNK44 = 0x2C,
K_UNK45 = 0x2D,
K_UNK46 = 0x2E,
K_UNK47 = 0x2F,
K_UNK48 = 0x30,
K_UNK49 = 0x31,
K_UNK50 = 0x32,
K_UNK51 = 0x33,
K_UNK52 = 0x34,
K_UNK53 = 0x35,
K_UNK54 = 0x36,
K_UNK55 = 0x37,
K_UNK56 = 0x38,
K_UNK57 = 0x39,
K_UNK58 = 0x3A,
K_SEMICOLON = 0x3B,
K_UNK60 = 0x3C,
K_UNK61 = 0x3D,
K_UNK62 = 0x3E,
K_UNK63 = 0x3F,
K_UNK64 = 0x40,
K_UNK65 = 0x41,
K_UNK66 = 0x42,
K_UNK67 = 0x43,
K_UNK68 = 0x44,
K_UNK69 = 0x45,
K_UNK70 = 0x46,
K_UNK71 = 0x47,
K_UNK72 = 0x48,
K_UNK73 = 0x49,
K_UNK74 = 0x4A,
K_UNK75 = 0x4B,
K_UNK76 = 0x4C,
K_UNK77 = 0x4D,
K_UNK78 = 0x4E,
K_UNK79 = 0x4F,
K_UNK80 = 0x50,
K_UNK81 = 0x51,
K_UNK82 = 0x52,
K_UNK83 = 0x53,
K_UNK84 = 0x54,
K_UNK85 = 0x55,
K_UNK86 = 0x56,
K_UNK87 = 0x57,
K_UNK88 = 0x58,
K_UNK89 = 0x59,
K_UNK90 = 0x5A,
K_UNK91 = 0x5B,
K_UNK92 = 0x5C,
K_UNK93 = 0x5D,
K_UNK94 = 0x5E,
K_UNK95 = 0x5F,
K_GRAVE = 0x60,
K_UNK97 = 0x61,
K_UNK98 = 0x62,
K_UNK99 = 0x63,
K_UNK100 = 0x64,
K_UNK101 = 0x65,
K_UNK102 = 0x66,
K_UNK103 = 0x67,
K_UNK104 = 0x68,
K_UNK105 = 0x69,
K_UNK106 = 0x6A,
K_UNK107 = 0x6B,
K_UNK108 = 0x6C,
K_UNK109 = 0x6D,
K_UNK110 = 0x6E,
K_UNK111 = 0x6F,
K_UNK112 = 0x70,
K_UNK113 = 0x71,
K_UNK114 = 0x72,
K_UNK115 = 0x73,
K_UNK116 = 0x74,
K_UNK117 = 0x75,
K_UNK118 = 0x76,
K_UNK119 = 0x77,
K_UNK120 = 0x78,
K_UNK121 = 0x79,
K_UNK122 = 0x7A,
K_UNK123 = 0x7B,
K_UNK124 = 0x7C,
K_UNK125 = 0x7D,
K_TILDE = 0x7E,
K_BACKSPACE = 0x7F,
K_CAPSLOCK = 0x80,
K_PAUSE = 0x81,
K_PRINTSCREEN = 0x82,
K_SCROLLLOCK = 0x83,
K_UPARROW = 0x84,
K_DOWNARROW = 0x85,
K_LEFTARROW = 0x86,
K_RIGHTARROW = 0x87,
K_LALT = 0x88,
K_RALT = 0x89,
K_LCTRL = 0x8A,
K_RCTRL = 0x8B,
K_LSHIFT = 0x8C,
K_RSHIFT = 0x8D,
K_HIRAGANA = 0x8E,
K_HENKAN = 0x8F,
K_MUHENKAN = 0x90,
K_LWIN = 0x91,
K_RWIN = 0x92,
K_MENU = 0x93,
K_INS = 0x94,
K_DEL = 0x95,
K_PGDN = 0x96,
K_PGUP = 0x97,
K_HOME = 0x98,
K_END = 0x99,
K_F1 = 0x9A,
K_F2 = 0x9B,
K_F3 = 0x9C,
K_F4 = 0x9D,
K_F5 = 0x9E,
K_F6 = 0x9F,
K_F7 = 0xA0,
K_F8 = 0xA1,
K_F9 = 0xA2,
K_F10 = 0xA3,
K_F11 = 0xA4,
K_F12 = 0xA5,
K_UNK166 = 0xA6,
K_UNK167 = 0xA7,
K_UNK168 = 0xA8,
K_KP_HOME = 0xA9,
K_KP_UPARROW = 0xAA,
K_KP_PGUP = 0xAB,
K_KP_LEFTARROW = 0xAC,
K_KP_5 = 0xAD,
K_KP_RIGHTARROW = 0xAE,
K_KP_END = 0xAF,
K_KP_DOWNARROW = 0xB0,
K_KP_PGDN = 0xB1,
K_KP_ENTER = 0xB2,
K_KP_INS = 0xB3,
K_KP_DEL = 0xB4,
K_KP_SLASH = 0xB5,
K_KP_MINUS = 0xB6,
K_KP_PLUS = 0xB7,
K_KP_NUMLOCK = 0xB8,
K_KP_STAR = 0xB9,
K_MOUSE1 = 0xBA,
K_MOUSE2 = 0xBB,
K_MOUSE3 = 0xBC,
K_MOUSE4 = 0xBD,
K_MOUSE5 = 0xBE,
K_MWHEELDOWN = 0xBF,
K_MWHEELUP = 0xC0
};
struct KeyState
{
int down;
int repeats;
int binding;
char pad[20];
}; // size = 32
struct PlayerKeyState
{
bool overstrikeMode;
int anyKeyDown;
KeyState keys[256];
};
return m128i;
}
struct AssetCache
{
@ -596,18 +106,6 @@ namespace game
ITEM_TEXTSTYLE_MONOSPACESHADOWED = 132,
};
enum errorParm
{
ERR_FATAL = 0,
ERR_DROP = 1,
ERR_SERVERDISCONNECT = 2,
ERR_DISCONNECT = 3,
ERR_SCRIPT = 4,
ERR_SCRIPT_DROP = 5,
ERR_LOCALIZATION = 6,
ERR_MAPLOADERRORSUMMARY = 7,
};
enum dvarType_t
{
DVAR_TYPE_INVALID = 0x0,
@ -724,7 +222,76 @@ namespace game
DvarLimits domain;
char padding_unk2[8];
};
typedef void (*xcommand_t)(void);
struct cmd_function_t
{
cmd_function_t* next;
BO4_AssetRef_t name;
const char* autoCompleteDir;
const char* autoCompleteExt;
xcommand_t function;
int autoComplete;
};
struct CmdArgs
{
int nesting;
int localClientNum[8];
int controllerIndex[8];
int argshift[8];
int argc[8];
const char** argv[8];
char textPool[8192];
const char* argvPool[512];
int usedTextPool[8];
int totalUsedArgvPool;
int totalUsedTextPool;
};
struct va_info_t
{
char va_string[4][1024];
int index;
};
struct TLSData
{
va_info_t* vaInfo;
jmp_buf* errorJmpBuf;
void* traceInfo;
CmdArgs* cmdArgs;
void* errorData;
};
///////////////////////////////////////////////////////////////////////////
// ENUMS //
///////////////////////////////////////////////////////////////////////////
enum errorParm
{
ERR_FATAL = 0,
ERR_DROP = 1,
ERR_SERVERDISCONNECT = 2,
ERR_DISCONNECT = 3,
ERR_SCRIPT = 4,
ERR_SCRIPT_DROP = 5,
ERR_LOCALIZATION = 6,
ERR_MAPLOADERRORSUMMARY = 7,
};
enum eGameModes
{
MODE_GAME_MATCHMAKING_PLAYLIST = 0x0,
MODE_GAME_MATCHMAKING_MANUAL = 0x1,
MODE_GAME_DEFAULT = 0x2,
MODE_GAME_LEAGUE = 0x3,
MODE_GAME_THEATER = 0x4,
MODE_GAME_COUNT = 0x5,
MODE_GAME_INVALID = 0x5,
};
enum eModes : int32_t
{
MODE_ZOMBIES = 0x0,
@ -803,6 +370,10 @@ namespace game
CON_LABEL_COUNT = 0x3F
};
///////////////////////////////////////////////////////////////////////////
// CLASSES //
///////////////////////////////////////////////////////////////////////////
enum scoped_critical_section_type : int32_t
{
SCOPED_CRITSECT_NORMAL = 0x0,
@ -822,6 +393,10 @@ namespace game
~scoped_critical_section();
};
///////////////////////////////////////////////////////////////////////////
// HAVOK //
///////////////////////////////////////////////////////////////////////////
struct hks_global {};
struct hks_callstack
{
@ -866,9 +441,9 @@ namespace game
// ...
};
//////////////////////////////////////////////////////////////////////////
// SYMBOLS //
//////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// SYMBOLS //
///////////////////////////////////////////////////////////////////////////
template <typename T>
class symbol
@ -898,25 +473,49 @@ namespace game
T* address_;
};
// Main Functions
// Main
WEAK symbol<void(const char* file, int line, int code, const char* fmt, ...)> Com_Error_{ 0x14288B410_g };
// mutex
// Sys
WEAK symbol<int()> Sys_Milliseconds{ 0x143D89E80_g };
WEAK symbol<TLSData* ()> Sys_GetTLS{ 0x143C56140_g };
// Mutex
WEAK symbol<void(scoped_critical_section* sec, int32_t s, scoped_critical_section_type type)> ScopedCriticalSectionConstructor{ 0x14289E3C0_g };
WEAK symbol<void(scoped_critical_section* sec)> ScopedCriticalSectionDestructor{ 0x14289E440_g };
// CMD
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{ 0x143CDE880_g };
// Dvar
WEAK symbol<void* (const char* dvarName)> Dvar_FindVar{ 0x143CEBE40_g };
WEAK symbol<void* (void* dvarHash)> Dvar_FindVar_Hash{ 0x143CEBED0_g };
// Live Functions
// Cmd
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{ 0x143CDE880_g };
WEAK symbol<void(int localClientNum, int controllerIndex, const char* buffer)> Cbuf_ExecuteBuffer{ 0x143CDEBE0_g };
WEAK symbol<void(BO4_AssetRef_t* cmdName, xcommand_t function, cmd_function_t* allocedCmd)> Cmd_AddCommandInternal{ 0x143CDEE80_g };
WEAK symbol<void()> Cbuf_AddServerText_f{ 0x143CDE870_g };
WEAK symbol<void(BO4_AssetRef_t* cmdName, xcommand_t function, cmd_function_t* allocedCmd)> Cmd_AddServerCommandInternal{ 0x143CDEEF0_g };
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text, bool fromRemoteConsole)> Cmd_ExecuteSingleCommand{ 0x143CDF490_g };
WEAK symbol<void(int localClientNum, int localControllerIndex, const char* text_in,
int max_tokens, bool evalExpressions, CmdArgs* args)> Cmd_TokenizeStringKernel{ 0x143CE0750_g };
WEAK symbol<void()> Cmd_EndTokenizedString{ 0x143CDF070_g };
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{ 0x143CE0A10_g };
WEAK symbol<void()> SV_Cmd_EndTokenizedString{ 0x143CE09C0_g };
// NET
WEAK symbol<bool(netsrc_t sock, netadr_t* adr, const void* data, int len)> NET_OutOfBandData{ 0x142E06390_g };
WEAK symbol<bool(netsrc_t sock, int length, const void* data, const netadr_t* to)> Sys_SendPacket{ 0x143D89900_g };
WEAK symbol<bool(netadr_t* adr, const char* s)> NetAdr_InitFromString{ 0x142E05230_g };
WEAK symbol<bool(const netadr_t addr1, const netadr_t addr2)> NetAdr_IsTheSameAddr{ 0x142E053A0_g };
// Live
WEAK symbol<bool(uint64_t, int*)> Live_GetConnectivityInformation{ 0x1437FA460_g };
// Rendering Functions
WEAK symbol<void(const char* text, int maxChars, void* font, float x, float y, float xScale, float yScale, float rotation, float* color, int style, int cursorPos, char cursor, float padding)> T8_AddBaseDrawTextCmd{ 0x143616B60_g };
// Renderer
WEAK symbol<void(const char* text, int maxChars, void* font, float x, float y, float xScale, float yScale, float rotation,
float* color, int style, int cursorPos, char cursor, float padding)> T8_AddBaseDrawTextCmd{ 0x143616B60_g };
WEAK symbol<void(float x, float y, float w, float h, float, float, float, float, float* color, void* material)> R_AddCmdDrawStretchPic{ 0x143616790_g };
WEAK symbol<int(void* font)> R_TextHeight{ 0x1435B2350_g };
@ -927,19 +526,18 @@ namespace game
WEAK symbol<ScreenPlacement* (int localClientNum)> ScrPlace_GetView{ 0x142876E70_g };
// BuildInfo
WEAK symbol<const char* ()> Com_GetBuildVersion{ 0x142892F40_g };
WEAK symbol<bool(int controllerIndex, int info, char* outputString, const int outputLen)> Live_SystemInfo{ 0x143804B00_g };
// ETC
WEAK symbol<bool()> Com_IsInGame{ 0x14288FDB0_g };
WEAK symbol<bool()> Com_IsRunningUILevel{ 0x14288FDF0_g };
WEAK symbol<eModes()> Com_SessionMode_GetMode{ 0x14289EFF0_g };
WEAK symbol<eModes(const char* str)> Com_SessionMode_GetModeForAbbreviation{ 0x14289F000_g };
WEAK symbol<const char*(eModes mode)> Com_SessionMode_GetAbbreviationForMode{0x14289EC70_g};
WEAK symbol<int> keyCatchers{ 0x148A53F84_g };
WEAK symbol<PlayerKeyState> playerKeys{ 0x148A3EF80_g };
WEAK symbol<sharedUiInfo_t> sharedUiInfo{ 0x14F956850_g };
// Scr Functions
// SCR
WEAK symbol<void(scriptInstance_t inst, int value)> ScrVm_AddBool{ 0x14276E760_g };
WEAK symbol<void(scriptInstance_t inst, float value)> ScrVm_AddFloat{ 0x14276E9B0_g };
WEAK symbol<void(scriptInstance_t inst, BO4_AssetRef_t* value)> ScrVm_AddHash{ 0x14276EAB0_g };
@ -981,23 +579,12 @@ namespace game
WEAK symbol<uint32_t> gObjFileInfoCount{ 0x1482F76B0_g };
WEAK symbol<objFileInfo_t[SCRIPTINSTANCE_MAX][650]> gObjFileInfo{ 0x1482EFCD0_g };
// lua functions
// LUA
WEAK symbol<bool(lua_state* luaVM, const char* file)> Lua_CoD_LoadLuaFile{ 0x143962DF0_g };
WEAK symbol<void(int code, const char* error, lua_state* s)> Lua_CoD_LuaStateManager_Error{ 0x14398A860_g };
WEAK symbol<const char*(lua_state* luaVM, hks_object* obj, size_t* len)> hks_obj_tolstring{ 0x143755730_g };
WEAK symbol<float(lua_state* luaVM, const hks_object* obj)> hks_obj_tonumber{ 0x143755A90_g };
// console labels
WEAK symbol<const char*> builtinLabels{ 0x144F11530_g };
// gsc types
WEAK symbol<const char*> var_typename{ 0x144EED240_g };
WEAK symbol<void(BO4_AssetRef_t* cmdName, xcommand_t function, cmd_function_t* allocedCmd)> Cmd_AddCommandInternal{0x143CDEE80_g};
#define Cmd_AddCommand(name, function) \
static game::cmd_function_t __cmd_func_##function; \
game::BO4_AssetRef_t __cmd_func_name_##function { (int64_t)fnv1a::generate_hash(name), 0 }; \
game::Cmd_AddCommandInternal(&__cmd_func_name_##function, function, &__cmd_func_##function)
#define R_AddCmdDrawText(TXT, MC, F, X, Y, XS, YS, R, C, S) \
T8_AddBaseDrawTextCmd(TXT, MC, F, X, Y, XS, YS, R, C, S, -1, 0, 0)
@ -1008,11 +595,26 @@ namespace game
#define Com_Error(code, fmt, ...) \
Com_Error_(__FILE__, __LINE__, code, fmt, ##__VA_ARGS__)
class scoped_critical_section_guard_lock
{
///////////////////////////////////////////////////////////////////////////
// NAME TABLES //
///////////////////////////////////////////////////////////////////////////
WEAK symbol<const char*> var_typename{ 0x144EED240_g }; // GSC Types
WEAK symbol<const char*> builtinLabels{ 0x144F11530_g }; // Console Labels
///////////////////////////////////////////////////////////////////////////
// VARIABLES //
///////////////////////////////////////////////////////////////////////////
WEAK symbol<sharedUiInfo_t> sharedUiInfo{ 0x14F956850_g };
WEAK symbol<cmd_function_t> cmd_functions{ 0x14F99B188_g };
WEAK symbol<CmdArgs> sv_cmd_args{ 0x14F998070_g };
};
WEAK symbol<int> keyCatchers{ 0x148A53F84_g };
WEAK symbol<PlayerKeyState> playerKeys{ 0x148A3EF80_g };
// Global game definitions
constexpr auto CMD_MAX_NESTING = 8;
}

View File

@ -1,185 +0,0 @@
#include <std_include.hpp>
#include "definitions/game.hpp"
#include "definitions/xassets.hpp"
#include "game_runtime_errors.hpp"
namespace game::runtime_errors
{
namespace
{
std::unordered_map<uint64_t, const char*> errors =
{
{ 1045192683, "Scr_RandomFloatRange's second parameter must be greater than the first." },
{ 1047729873, "exitlevel already called" },
{ 104978404, "cannot cast type to canon" },
{ 1072585573, "Raw file is not a file of the right type" },
{ 1088278929, "Raw file is not a file of the right type" },
{ 1099145600, "Can't find asset" },
{ 1132507782, "bad opcode" },
{ 1137123674, "GScr_LUINotifyEvent: Expected Istrings, hash or integers only" },
{ 1252503459, "caller is not an entity" },
{ 1273214009, "CScr_PlayFX: invalid effect" },
{ 1333649786, "IsMature can only be called on a player." },
{ 1364331101, "Object must be an array" },
{ 1377489376, "Gesture key can't have the higher bit set" },
{ 1385570291, "assert fail (with message)" },
{ 1402557361, "bad opcode" },
{ 1403832952, "Attempt to register ClientField failed. Client Field set either already contains a field, or a hash collision has occurred." },
{ 1412130119, "parameter can't be cast to a hash" },
{ 1427704235, "expected struct type to add value pair" },
{ 1480037573, "Invalid canonical name hash" },
{ 1480821566, "Error registering client field. Attempted field size is not acceptable bit number range 1->32" },
{ 1517473035, "Value out of range. Allowed values: 0 to 2" },
{ 1609894461, "bad entity" },
{ 1670707254, "linking error" },
{ 1850691545, "Debug Break" },
{ 1895566756, "dvar is not a 3d vector, but GetDvarVector3D has been called on it" },
{ 1909233687, "Optional argument must be a vector type" },
{ 1915758537, "RegisterClientField can only accept bit ranges between 1 and 32." },
{ 1957162421, "Can't find bgCache entry" },
{ 1999906612, "type is not a integer or float" },
{ 2078816051, "not a valid name for a clientfield set." },
{ 209668787, "RandomInt parm must be positive integer." },
{ 2116335949, "function called with too many parameters" },
{ 219569925, "hasperk() can only be called on local players" },
{ 219686544, "object is not an array index" },
{ 2253722136, "CamAnimScripted can only be called on a player." },
{ 2269096660, "vector scale expecting vector" },
{ 2279633554, "SV_SetConfigstring: bad index" },
{ 2331090760, "stack does not have the pool" },
{ 2344222932, "assert fail" },
{ 2355618801, "Gesture table key can't have the higher bit set" },
{ 2408700928, "not a valid name for a clientfield set." },
{ 2448966512, "string too long" },
{ 245612264, "foreach should be used with an array" },
{ 247375020, "Attempting to register client field in client field set using bits, but system is out of space." },
{ 2479879368, "RegisterClientField can only accept bit ranges between 1 and 32." },
{ 249068885, "Failed to allocate from state pool" },
{ 2517242050, "parameter does not exist" },
{ 2532286589, "error message" },
{ 2538360978, "not a function pointer" },
{ 2572009355, "vector scale expecting vector" },
{ 2606724305, "parameter does not exist" },
{ 2626909173, "exceeded maximum number of script strings" },
{ 2681972741, "bad clientfield for name" },
{ 2687742442, "Forced script exception." },
{ 269518924, "Ammo count must not be negative" },
{ 2706458388, "Trying to get a local client index for a client that is not a local client." },
{ 2734491973, "Error need at least one argument for LUINotifyEvent." },
{ 2737681163, "assert fail (with message)" },
{ 2751867714, "self isn't a field object" },
{ 2792722947, "RegisterClientField can only accept 5 parameters" },
{ 280703902, "parameter does not exist" },
{ 2838301872, "Gesture table key can't have the higher bit set" },
{ 2855209542, "Cannot call IncrementClientField on a non-'counter' type clientfield." },
{ 2940210599, "Invalid Version Handling. Grab Bat !!!" },
{ 3015158315, "getperks() can only be called on local players" },
{ 3016026156, "Can't find asset" },
{ 3030895505, "Whitelist failure for title" },
{ 3059411687, "Argument and parameter count mismatch for LUINotifyEvent" },
{ 3086288875, "playFx called with (0 0 0) forward direction" },
{ 3122940489, "caller is not an entity" },
{ 312545010, "not a vector" },
{ 3143575744, "parameter does not exist" },
{ 317100267, "unmatching types" },
{ 3189465155, "Invalid bgCache type" },
{ 3221522156, "Failed to alloc client field" },
{ 3222417139, "size cannot be applied to type" },
{ 3251676101, "Could not load raw file" },
{ 3255107847, "LUINotifyEvent: entity must be a player entity" },
{ 3288551912, "expected array type to add value pair" },
{ 3385008561, "cannot set field on type" },
{ 3459949409, "Failed to alloc client field - MAX_CLIENTFIELD_FIELDS_IN_SET=512 exceeded." },
{ 3523382186, "ScrEvent map is full, unable to register new event" },
{ 3524483616, "Ammo count must not be negative" },
{ 3592841213, "cannot directly set the origin on AI. Use the teleport command instead." },
{ 359760836, "G_Spawn: no free entities" },
{ 3654063291, "ScrEvent map is full, unable to register new event" },
{ 3679846953, "LUINotifyEvent: entity must be a player entity" },
{ 3699844858, "parameter does not exist" },
{ 3761634992, "not a pointer" },
{ 3894031202, "Can't find gamedata/playeranim/playeranimtypes.txt" },
{ 3967909977, "Trying to get version of a demo when the demo system is idle." },
{ 3990130335, "player getperks(<localClientNum>): localClientNum out of range" },
{ 4039057166, "LUINotifyEvent: entity must be a player entity" },
{ 4047738848, "Invalid opcode (Recovery)" },
{ 409067247, "No clientfield named found in set." },
{ 4100218247, "Error sending LUI notify event: LUI event name is not precached." },
{ 4103906837, "Entity is not an item." },
{ 4104994143, "can't allocate more script variables" },
{ 4106063796, "key value provided for array is not valid" },
{ 4163774148, "Optional argument must be a float or integer type" },
{ 4178589916, "Model was not cached by the linker." },
{ 4196473479, "parameter does not exist" },
{ 4213634562, "precacheLeaderboards must be called before any wait statements in the gametype or level script" },
{ 4220857104, "Cannot call IncrementClientField on a 'counter' type clientfield on the frame it is spawned, since newEnts on the clientside will not process 'counter' type clientfields." },
{ 467754466, "Error registering client field. bCallbacksFor0WhenNew (CF_CALLBACK_ZERO_ON_NEW_ENT) is disallowed for CF_SCRIPTMOVERFIELDS." },
{ 512306404, "not a function pointer" },
{ 57350207, "Unknown perk" },
{ 580674660, "Unknown ent type in GScr_GetFieldSetForEntityType." },
{ 619241173, "Failed to allocate from element pool" },
{ 647662103, "parameter can't be cast to a string" },
{ 657813230, "Error registering client field. bCallbacksFor0WhenNew (CF_CALLBACK_ZERO_ON_NEW_ENT) is disallowed for counter type clientfields. Due to it's treatment of the old and new val as a ring buffer, the counter type is not valid on a new snapshot, new ent, or demojump" },
{ 665902298, "Parameter must be an array" },
{ 71894325, "CamAnimScripted or ExtraCamAnimScripted can only be called on a player." },
{ 732489269, "Non-player entity passed to UploadStats()" },
{ 744499668, "too many vehicles" },
{ 750896894, "Invalid bgCache type" },
{ 753495682, "RandomIntRange's second parameter must be greater than the first." },
{ 754846421, "invalid vehicle spawn origin" },
{ 829015102, "var isn't a field object" },
{ 876169112, "key value provided for struct is not valid" },
{ 887228744, "origin being set to NAN." },
{ 904544970, "Attempt to register Client Field post finalization of Client Field registration period failed." },
{ 941828720, "exitlevel already called" },
{ 946363963, "Invalid opcode" },
{ 949934674, "Invalid Version Handling. Grab Bat !!!" },
{ 952690413, "parameter can't be cast to a boolean" },
{ 962032109, "Entity does not exist." },
{ 968521323, "player hasperk(<localClientNum>, <perk>): localClientNum out of range" },
{ 4088624643, "Can't find asset" },
{ game::runtime_errors::custom_error_id, "Shield Error" }
};
}
const char* get_error_message(uint64_t code)
{
auto it = errors.find(code);
// known error
if (it != errors.end())
{
return it->second;
}
// read from the csv
static game::BO4_AssetRef_t custom_errors_file = game::AssetRef(fnv1a::generate_hash("gamedata/shield/custom_errors.csv"));
xassets::stringtable_header* table = xassets::DB_FindXAssetHeader(xassets::ASSET_TYPE_STRINGTABLE, &custom_errors_file, false, -1).stringtable;
if (!table || !table->columns_count || table->columns_count < 2)
{
return nullptr;
}
for (size_t i = 0; i < table->rows_count; i++)
{
auto* rows = &table->values[i * table->columns_count];
if (rows[0].type != xassets::STC_TYPE_INT || rows[1].type != xassets::STC_TYPE_STRING)
{
continue; // bad types
}
if (rows[0].value.hash_value == code)
{
return rows[1].value.string_value;
}
}
// unknown
return nullptr;
}
}

View File

@ -1,7 +0,0 @@
#pragma once
namespace game::runtime_errors
{
constexpr uint64_t custom_error_id = 0x42693201;
const char* get_error_message(uint64_t code);
}

View File

@ -0,0 +1,7 @@
#include <std_include.hpp>
#include "keys.hpp"
namespace game
{
/* NAMESPACE_PLACE_HOLDER */
}

View File

@ -0,0 +1,216 @@
#pragma once
namespace game
{
enum keyNum_t
{
K_NONE = 0x00,
K_BUTTON_A = 0x01,
K_BUTTON_B = 0x02,
K_BUTTON_X = 0x03,
K_BUTTON_Y = 0x04,
K_BUTTON_LSHLDR = 0x05,
K_BUTTON_RSHLDR = 0x06,
K_UNK7 = 0x07,
K_UNK8 = 0x08,
K_TAB = 0x09,
K_UNK10 = 0x0A,
K_UNK11 = 0x0B,
K_UNK12 = 0x0C,
K_ENTER = 0x0D,
K_BUTTON_START = 0x0E,
K_BUTTON_BACK = 0x0F,
K_BUTTON_LSTICK = 0x10,
K_BUTTON_RSTICK = 0x11,
K_BUTTON_LTRIG = 0x12,
K_BUTTON_RTRIG = 0x13,
K_UNK20 = 0x14,
K_UNK21 = 0x15,
K_DPAD_UP = 0x16,
K_DPAD_DOWN = 0x17,
K_DPAD_LEFT = 0x18,
K_DPAD_RIGHT = 0x19,
K_UNK26 = 0x1A,
K_ESCAPE = 0x1B,
K_APAD_UP = 0x1C,
K_APAD_DOWN = 0x1D,
K_APAD_LEFT = 0x1E,
K_APAD_RIGHT = 0x1F,
K_SPACE = 0x20,
K_UNK33 = 0x21,
K_UNK34 = 0x22,
K_UNK35 = 0x23,
K_UNK36 = 0x24,
K_UNK37 = 0x25,
K_UNK38 = 0x26,
K_UNK39 = 0x27,
K_UNK40 = 0x28,
K_UNK41 = 0x29,
K_UNK42 = 0x2A,
K_UNK43 = 0x2B,
K_UNK44 = 0x2C,
K_UNK45 = 0x2D,
K_UNK46 = 0x2E,
K_UNK47 = 0x2F,
K_UNK48 = 0x30,
K_UNK49 = 0x31,
K_UNK50 = 0x32,
K_UNK51 = 0x33,
K_UNK52 = 0x34,
K_UNK53 = 0x35,
K_UNK54 = 0x36,
K_UNK55 = 0x37,
K_UNK56 = 0x38,
K_UNK57 = 0x39,
K_UNK58 = 0x3A,
K_SEMICOLON = 0x3B,
K_UNK60 = 0x3C,
K_UNK61 = 0x3D,
K_UNK62 = 0x3E,
K_UNK63 = 0x3F,
K_UNK64 = 0x40,
K_UNK65 = 0x41,
K_UNK66 = 0x42,
K_UNK67 = 0x43,
K_UNK68 = 0x44,
K_UNK69 = 0x45,
K_UNK70 = 0x46,
K_UNK71 = 0x47,
K_UNK72 = 0x48,
K_UNK73 = 0x49,
K_UNK74 = 0x4A,
K_UNK75 = 0x4B,
K_UNK76 = 0x4C,
K_UNK77 = 0x4D,
K_UNK78 = 0x4E,
K_UNK79 = 0x4F,
K_UNK80 = 0x50,
K_UNK81 = 0x51,
K_UNK82 = 0x52,
K_UNK83 = 0x53,
K_UNK84 = 0x54,
K_UNK85 = 0x55,
K_UNK86 = 0x56,
K_UNK87 = 0x57,
K_UNK88 = 0x58,
K_UNK89 = 0x59,
K_UNK90 = 0x5A,
K_UNK91 = 0x5B,
K_UNK92 = 0x5C,
K_UNK93 = 0x5D,
K_UNK94 = 0x5E,
K_UNK95 = 0x5F,
K_GRAVE = 0x60,
K_UNK97 = 0x61,
K_UNK98 = 0x62,
K_UNK99 = 0x63,
K_UNK100 = 0x64,
K_UNK101 = 0x65,
K_UNK102 = 0x66,
K_UNK103 = 0x67,
K_UNK104 = 0x68,
K_UNK105 = 0x69,
K_UNK106 = 0x6A,
K_UNK107 = 0x6B,
K_UNK108 = 0x6C,
K_UNK109 = 0x6D,
K_UNK110 = 0x6E,
K_UNK111 = 0x6F,
K_UNK112 = 0x70,
K_UNK113 = 0x71,
K_UNK114 = 0x72,
K_UNK115 = 0x73,
K_UNK116 = 0x74,
K_UNK117 = 0x75,
K_UNK118 = 0x76,
K_UNK119 = 0x77,
K_UNK120 = 0x78,
K_UNK121 = 0x79,
K_UNK122 = 0x7A,
K_UNK123 = 0x7B,
K_UNK124 = 0x7C,
K_UNK125 = 0x7D,
K_TILDE = 0x7E,
K_BACKSPACE = 0x7F,
K_CAPSLOCK = 0x80,
K_PAUSE = 0x81,
K_PRINTSCREEN = 0x82,
K_SCROLLLOCK = 0x83,
K_UPARROW = 0x84,
K_DOWNARROW = 0x85,
K_LEFTARROW = 0x86,
K_RIGHTARROW = 0x87,
K_LALT = 0x88,
K_RALT = 0x89,
K_LCTRL = 0x8A,
K_RCTRL = 0x8B,
K_LSHIFT = 0x8C,
K_RSHIFT = 0x8D,
K_HIRAGANA = 0x8E,
K_HENKAN = 0x8F,
K_MUHENKAN = 0x90,
K_LWIN = 0x91,
K_RWIN = 0x92,
K_MENU = 0x93,
K_INS = 0x94,
K_DEL = 0x95,
K_PGDN = 0x96,
K_PGUP = 0x97,
K_HOME = 0x98,
K_END = 0x99,
K_F1 = 0x9A,
K_F2 = 0x9B,
K_F3 = 0x9C,
K_F4 = 0x9D,
K_F5 = 0x9E,
K_F6 = 0x9F,
K_F7 = 0xA0,
K_F8 = 0xA1,
K_F9 = 0xA2,
K_F10 = 0xA3,
K_F11 = 0xA4,
K_F12 = 0xA5,
K_UNK166 = 0xA6,
K_UNK167 = 0xA7,
K_UNK168 = 0xA8,
K_KP_HOME = 0xA9,
K_KP_UPARROW = 0xAA,
K_KP_PGUP = 0xAB,
K_KP_LEFTARROW = 0xAC,
K_KP_5 = 0xAD,
K_KP_RIGHTARROW = 0xAE,
K_KP_END = 0xAF,
K_KP_DOWNARROW = 0xB0,
K_KP_PGDN = 0xB1,
K_KP_ENTER = 0xB2,
K_KP_INS = 0xB3,
K_KP_DEL = 0xB4,
K_KP_SLASH = 0xB5,
K_KP_MINUS = 0xB6,
K_KP_PLUS = 0xB7,
K_KP_NUMLOCK = 0xB8,
K_KP_STAR = 0xB9,
K_MOUSE1 = 0xBA,
K_MOUSE2 = 0xBB,
K_MOUSE3 = 0xBC,
K_MOUSE4 = 0xBD,
K_MOUSE5 = 0xBE,
K_MWHEELDOWN = 0xBF,
K_MWHEELUP = 0xC0
};
struct KeyState
{
int down;
int repeats;
int binding;
char pad[20];
}; // size = 32
struct PlayerKeyState
{
bool overstrikeMode;
int anyKeyDown;
KeyState keys[256];
};
}

View File

@ -0,0 +1,7 @@
#include <std_include.hpp>
#include "network.hpp"
namespace game
{
/* NAMESPACE_PLACE_HOLDER */
}

View File

@ -0,0 +1,59 @@
#pragma once
namespace game
{
struct netipv4_t
{
byte a;
byte b;
byte c;
byte d;
};
enum netadrtype_t
{
NA_BOT = 0x0,
NA_BAD = 0x1,
NA_LOOPBACK = 0x2,
NA_RAWIP = 0x3,
NA_IP = 0x4,
};
enum netsrc_t
{
NS_NULL = -1,
NS_CLIENT1 = 0x0,
NS_CLIENT2 = 0x1,
NS_CLIENT3 = 0x2,
NS_CLIENT4 = 0x3,
NS_SERVER = 0x4,
NS_MAXCLIENTS = 0x4,
NS_PACKET = 0x5,
};
struct netadr_t
{
union
{
netipv4_t ipv4;
uint32_t addr;
};
uint16_t port;
netadrtype_t type;
netsrc_t localNetID;
};
struct msg_t
{
byte* data; //0x0000
int maxsize; //0x0008
int cursize; //0x000C
int readcount; //0x0010
char pad_unknown[32]; //0x0014
bool unknown; //0x0034
bool overflowed; //0x0035
bool readOnly; //0x0036
};
}

View File

@ -0,0 +1,7 @@
#include <std_include.hpp>
#include "scripting.hpp"
namespace game
{
/* NAMESPACE_PLACE_HOLDER */
}

View File

@ -0,0 +1,286 @@
#pragma once
namespace game
{
typedef uint32_t ScrVarIndex_t;
typedef uint64_t ScrVarNameIndex_t;
struct GSC_IMPORT_ITEM
{
uint32_t name;
uint32_t name_space;
uint16_t num_address;
uint8_t param_count;
uint8_t flags;
};
struct GSC_EXPORT_ITEM
{
uint32_t checksum;
uint32_t address;
uint32_t name;
uint32_t name_space;
uint32_t callback_event;
uint8_t param_count;
uint8_t flags;
};
struct GSC_OBJ
{
byte magic[8];
int32_t crc;
int32_t pad;
uint64_t name;
int32_t include_offset;
uint16_t string_count;
uint16_t exports_count;
int32_t start_data;
int32_t string_offset;
int16_t imports_count;
uint16_t fixup_count;
int32_t ukn2c;
int32_t exports_offset;
int32_t ukn34;
int32_t imports_offset;
uint16_t globalvar_count;
int32_t fixup_offset;
int32_t globalvar_offset;
int32_t script_size;
int32_t requires_implements_offset;
int32_t ukn50;
int32_t data_length;
uint16_t include_count;
byte ukn5a;
byte requires_implements_count;
inline GSC_EXPORT_ITEM* get_exports()
{
return reinterpret_cast<GSC_EXPORT_ITEM*>(magic + exports_offset);
}
inline GSC_IMPORT_ITEM* get_imports()
{
return reinterpret_cast<GSC_IMPORT_ITEM*>(magic + imports_offset);
}
inline uint64_t* get_includes()
{
return reinterpret_cast<uint64_t*>(magic + include_offset);
}
inline GSC_EXPORT_ITEM* get_exports_end()
{
return get_exports() + exports_count;
}
inline uint64_t* get_includes_end()
{
return get_includes() + include_count;
}
};
enum scriptInstance_t : int32_t
{
SCRIPTINSTANCE_SERVER = 0x0,
SCRIPTINSTANCE_CLIENT = 0x1,
SCRIPTINSTANCE_MAX = 0x2,
};
typedef void (*BuiltinFunction)(scriptInstance_t);
struct BO4_BuiltinFunctionDef
{
uint32_t canonId;
uint32_t min_args;
uint32_t max_args;
BuiltinFunction actionFunc;
uint32_t type;
};
struct __declspec(align(4)) BO4_scrVarGlobalVars_t
{
uint32_t name;
ScrVarIndex_t id;
bool persist;
};
enum ScrVarType_t : uint32_t {
TYPE_UNDEFINED = 0x0,
TYPE_POINTER = 0x1,
TYPE_STRING = 0x2,
TYPE_VECTOR = 0x3,
TYPE_HASH = 0x4,
TYPE_FLOAT = 0x5,
TYPE_INTEGER = 0x6,
TYPE_UINTPTR = 0x7,
TYPE_ENTITY_OFFSET = 0x8,
TYPE_CODEPOS = 0x9,
TYPE_PRECODEPOS = 0xA,
TYPE_API_FUNCTION = 0xB,
TYPE_SCRIPT_FUNCTION = 0xC,
TYPE_STACK = 0xD,
TYPE_THREAD = 0xE,
TYPE_NOTIFY_THREAD = 0xF,
TYPE_TIME_THREAD = 0x10,
TYPE_FRAME_THREAD = 0x11,
TYPE_CHILD_THREAD = 0x12,
TYPE_CLASS = 0x13,
TYPE_SHARED_STRUCT = 0x14,
TYPE_STRUCT = 0x15,
TYPE_REMOVED_ENTITY = 0x16,
TYPE_ENTITY = 0x17,
TYPE_ARRAY = 0x18,
TYPE_REMOVED_THREAD = 0x19,
TYPE_FREE = 0x1a,
TYPE_THREAD_LIST = 0x1b,
TYPE_ENT_LIST = 0x1c,
TYPE_COUNT
};
struct BO4_scrVarPub {
const char* fieldBuffer;
const char* error_message;
byte* programBuffer;
byte* endScriptBuffer;
byte* programHunkUser; // HunkUser
BO4_scrVarGlobalVars_t globalVars[16];
ScrVarNameIndex_t entFieldNameIndex;
ScrVarIndex_t freeEntList;
ScrVarIndex_t tempVariable;
uint32_t checksum;
uint32_t entId;
uint32_t varHighWatermark;
uint32_t numScriptThreads;
uint32_t numVarAllocations;
int32_t varHighWatermarkId;
};
union ScrVarValueUnion_t
{
int64_t intValue;
uintptr_t uintptrValue;
float floatValue;
int32_t stringValue;
const float* vectorValue;
byte* codePosValue;
ScrVarIndex_t pointerValue;
};
struct ScrVarValue_t
{
ScrVarValueUnion_t u;
ScrVarType_t type;
};
struct ScrVar_t_Info
{
uint32_t nameType : 3;
uint32_t flags : 5;
uint32_t refCount : 24;
};
struct ScrVar_t
{
ScrVarNameIndex_t nameIndex;
ScrVar_t_Info _anon_0;
ScrVarIndex_t nextSibling;
ScrVarIndex_t prevSibling;
ScrVarIndex_t parentId;
ScrVarIndex_t nameSearchHashList;
uint32_t pad0;
};
union ScrVarObjectInfo1_t
{
uint64_t object_o;
unsigned int size;
ScrVarIndex_t nextEntId;
ScrVarIndex_t self;
ScrVarIndex_t free;
};
union ScrVarObjectInfo2_t
{
uint32_t object_w;
ScrVarIndex_t stackId;
};
struct function_stack_t
{
byte* pos;
ScrVarValue_t* top;
ScrVarValue_t* startTop;
ScrVarIndex_t threadId;
uint16_t localVarCount;
uint16_t profileInfoCount;
};
struct function_frame_t
{
function_stack_t fs;
};
struct ScrVmContext_t
{
ScrVarIndex_t fieldValueId;
ScrVarIndex_t objectId;
byte* lastGoodPos;
ScrVarValue_t* lastGoodTop;
};
typedef void (*VM_OP_FUNC)(scriptInstance_t, function_stack_t*, ScrVmContext_t*, bool*);
struct BO4_scrVarGlob
{
ScrVarIndex_t* scriptNameSearchHashList;
ScrVar_t* scriptVariables;
ScrVarObjectInfo1_t* scriptVariablesObjectInfo1;
ScrVarObjectInfo2_t* scriptVariablesObjectInfo2;
ScrVarValue_t* scriptValues;
};
struct BO4_scrVmPub
{
void* unk0;
void* unk8;
void* executionQueueHeap; // HunkUser
void* timeByValueQueue; // VmExecutionQueueData_t
void* timeByThreadQueue[1024]; // VmExecutionQueue_t
void* frameByValueQueue; // VmExecutionQueueData_t
void* frameByThreadQueue[1024]; // VmExecutionQueue_t
void* timeoutByValueList; // VmExecutionQueueData_t
void* timeoutByThreadList[1024]; // VmExecutionQueue_t
void* notifyByObjectQueue[1024]; // VmExecutionNotifyQueue_t
void* notifyByThreadQueue[1024]; // VmExecutionNotifyQueue_t
void* endonByObjectList[1024]; // VmExecutionNotifyQueue_t
void* endonByThreadList[1024]; // VmExecutionNotifyQueue_t
ScrVarIndex_t* localVars;
ScrVarValue_t* maxstack;
function_frame_t* function_frame;
ScrVarValue_t* top;
function_frame_t function_frame_start[64];
ScrVarValue_t stack[2048];
uint32_t time;
uint32_t frame;
int function_count;
int callNesting;
unsigned int inparamcount;
bool showError;
bool systemInitialized;
bool vmInitialized;
bool isShutdown;
};
struct objFileInfo_t
{
GSC_OBJ* activeVersion;
int slot;
int refCount;
uint32_t groupId;
};
}

View File

@ -1,5 +1,7 @@
#include <std_include.hpp>
#include "variables.hpp"
#include "game.hpp"
#include "xassets.hpp"
#include "component/hashes.hpp"
namespace fnv1a
@ -67,7 +69,7 @@ namespace fnv1a
namespace variables
{
std::vector<varEntry> dvars_record =
std::vector<varEntry> dvars_table =
{
{
"aim_slowdown_enabled",
@ -6521,17 +6523,12 @@ namespace variables
}
};
std::vector<varEntry> commands_record =
std::vector<varEntry> commands_table =
{
{
"quit",
"Shutdown the Game [Com_Quit_f]",
0x1DEE6107B26F8BB6
},
{
"reload_mods",
"Reload the shield mods",
0x6cb53357b4ef835c
}
};
@ -6540,7 +6537,7 @@ namespace variables
static std::vector<const char*> dvars;
if (!dvars.empty()) return dvars;
for (const auto& dvar : dvars_record)
for (const auto& dvar : dvars_table)
{
dvars.push_back(dvar.name.data());
}
@ -6553,11 +6550,186 @@ namespace variables
static std::vector<const char*> commands;
if (!commands.empty()) return commands;
for (const auto& cmd : commands_record)
for (const auto& cmd : commands_table)
{
commands.push_back(cmd.name.data());
}
return commands;
}
}
}
namespace runtime_errors
{
std::unordered_map<uint64_t, const char*> errors =
{
{ 1045192683, "Scr_RandomFloatRange's second parameter must be greater than the first." },
{ 1047729873, "exitlevel already called" },
{ 104978404, "cannot cast type to canon" },
{ 1072585573, "Raw file is not a file of the right type" },
{ 1088278929, "Raw file is not a file of the right type" },
{ 1099145600, "Can't find asset" },
{ 1132507782, "bad opcode" },
{ 1137123674, "GScr_LUINotifyEvent: Expected Istrings, hash or integers only" },
{ 1252503459, "caller is not an entity" },
{ 1273214009, "CScr_PlayFX: invalid effect" },
{ 1333649786, "IsMature can only be called on a player." },
{ 1364331101, "Object must be an array" },
{ 1377489376, "Gesture key can't have the higher bit set" },
{ 1385570291, "assert fail (with message)" },
{ 1402557361, "bad opcode" },
{ 1403832952, "Attempt to register ClientField failed. Client Field set either already contains a field, or a hash collision has occurred." },
{ 1412130119, "parameter can't be cast to a hash" },
{ 1427704235, "expected struct type to add value pair" },
{ 1480037573, "Invalid canonical name hash" },
{ 1480821566, "Error registering client field. Attempted field size is not acceptable bit number range 1->32" },
{ 1517473035, "Value out of range. Allowed values: 0 to 2" },
{ 1609894461, "bad entity" },
{ 1670707254, "linking error" },
{ 1850691545, "Debug Break" },
{ 1895566756, "dvar is not a 3d vector, but GetDvarVector3D has been called on it" },
{ 1909233687, "Optional argument must be a vector type" },
{ 1915758537, "RegisterClientField can only accept bit ranges between 1 and 32." },
{ 1957162421, "Can't find bgCache entry" },
{ 1999906612, "type is not a integer or float" },
{ 2078816051, "not a valid name for a clientfield set." },
{ 209668787, "RandomInt parm must be positive integer." },
{ 2116335949, "function called with too many parameters" },
{ 219569925, "hasperk() can only be called on local players" },
{ 219686544, "object is not an array index" },
{ 2253722136, "CamAnimScripted can only be called on a player." },
{ 2269096660, "vector scale expecting vector" },
{ 2279633554, "SV_SetConfigstring: bad index" },
{ 2331090760, "stack does not have the pool" },
{ 2344222932, "assert fail" },
{ 2355618801, "Gesture table key can't have the higher bit set" },
{ 2408700928, "not a valid name for a clientfield set." },
{ 2448966512, "string too long" },
{ 245612264, "foreach should be used with an array" },
{ 247375020, "Attempting to register client field in client field set using bits, but system is out of space." },
{ 2479879368, "RegisterClientField can only accept bit ranges between 1 and 32." },
{ 249068885, "Failed to allocate from state pool" },
{ 2517242050, "parameter does not exist" },
{ 2532286589, "error message" },
{ 2538360978, "not a function pointer" },
{ 2572009355, "vector scale expecting vector" },
{ 2606724305, "parameter does not exist" },
{ 2626909173, "exceeded maximum number of script strings" },
{ 2681972741, "bad clientfield for name" },
{ 2687742442, "Forced script exception." },
{ 269518924, "Ammo count must not be negative" },
{ 2706458388, "Trying to get a local client index for a client that is not a local client." },
{ 2734491973, "Error need at least one argument for LUINotifyEvent." },
{ 2737681163, "assert fail (with message)" },
{ 2751867714, "self isn't a field object" },
{ 2792722947, "RegisterClientField can only accept 5 parameters" },
{ 280703902, "parameter does not exist" },
{ 2838301872, "Gesture table key can't have the higher bit set" },
{ 2855209542, "Cannot call IncrementClientField on a non-'counter' type clientfield." },
{ 2940210599, "Invalid Version Handling. Grab Bat !!!" },
{ 3015158315, "getperks() can only be called on local players" },
{ 3016026156, "Can't find asset" },
{ 3030895505, "Whitelist failure for title" },
{ 3059411687, "Argument and parameter count mismatch for LUINotifyEvent" },
{ 3086288875, "playFx called with (0 0 0) forward direction" },
{ 3122940489, "caller is not an entity" },
{ 312545010, "not a vector" },
{ 3143575744, "parameter does not exist" },
{ 317100267, "unmatching types" },
{ 3189465155, "Invalid bgCache type" },
{ 3221522156, "Failed to alloc client field" },
{ 3222417139, "size cannot be applied to type" },
{ 3251676101, "Could not load raw file" },
{ 3255107847, "LUINotifyEvent: entity must be a player entity" },
{ 3288551912, "expected array type to add value pair" },
{ 3385008561, "cannot set field on type" },
{ 3459949409, "Failed to alloc client field - MAX_CLIENTFIELD_FIELDS_IN_SET=512 exceeded." },
{ 3523382186, "ScrEvent map is full, unable to register new event" },
{ 3524483616, "Ammo count must not be negative" },
{ 3592841213, "cannot directly set the origin on AI. Use the teleport command instead." },
{ 359760836, "G_Spawn: no free entities" },
{ 3654063291, "ScrEvent map is full, unable to register new event" },
{ 3679846953, "LUINotifyEvent: entity must be a player entity" },
{ 3699844858, "parameter does not exist" },
{ 3761634992, "not a pointer" },
{ 3894031202, "Can't find gamedata/playeranim/playeranimtypes.txt" },
{ 3967909977, "Trying to get version of a demo when the demo system is idle." },
{ 3990130335, "player getperks(<localClientNum>): localClientNum out of range" },
{ 4039057166, "LUINotifyEvent: entity must be a player entity" },
{ 4047738848, "Invalid opcode (Recovery)" },
{ 409067247, "No clientfield named found in set." },
{ 4100218247, "Error sending LUI notify event: LUI event name is not precached." },
{ 4103906837, "Entity is not an item." },
{ 4104994143, "can't allocate more script variables" },
{ 4106063796, "key value provided for array is not valid" },
{ 4163774148, "Optional argument must be a float or integer type" },
{ 4178589916, "Model was not cached by the linker." },
{ 4196473479, "parameter does not exist" },
{ 4213634562, "precacheLeaderboards must be called before any wait statements in the gametype or level script" },
{ 4220857104, "Cannot call IncrementClientField on a 'counter' type clientfield on the frame it is spawned, since newEnts on the clientside will not process 'counter' type clientfields." },
{ 467754466, "Error registering client field. bCallbacksFor0WhenNew (CF_CALLBACK_ZERO_ON_NEW_ENT) is disallowed for CF_SCRIPTMOVERFIELDS." },
{ 512306404, "not a function pointer" },
{ 57350207, "Unknown perk" },
{ 580674660, "Unknown ent type in GScr_GetFieldSetForEntityType." },
{ 619241173, "Failed to allocate from element pool" },
{ 647662103, "parameter can't be cast to a string" },
{ 657813230, "Error registering client field. bCallbacksFor0WhenNew (CF_CALLBACK_ZERO_ON_NEW_ENT) is disallowed for counter type clientfields. Due to it's treatment of the old and new val as a ring buffer, the counter type is not valid on a new snapshot, new ent, or demojump" },
{ 665902298, "Parameter must be an array" },
{ 71894325, "CamAnimScripted or ExtraCamAnimScripted can only be called on a player." },
{ 732489269, "Non-player entity passed to UploadStats()" },
{ 744499668, "too many vehicles" },
{ 750896894, "Invalid bgCache type" },
{ 753495682, "RandomIntRange's second parameter must be greater than the first." },
{ 754846421, "invalid vehicle spawn origin" },
{ 829015102, "var isn't a field object" },
{ 876169112, "key value provided for struct is not valid" },
{ 887228744, "origin being set to NAN." },
{ 904544970, "Attempt to register Client Field post finalization of Client Field registration period failed." },
{ 941828720, "exitlevel already called" },
{ 946363963, "Invalid opcode" },
{ 949934674, "Invalid Version Handling. Grab Bat !!!" },
{ 952690413, "parameter can't be cast to a boolean" },
{ 962032109, "Entity does not exist." },
{ 968521323, "player hasperk(<localClientNum>, <perk>): localClientNum out of range" },
{ 4088624643, "Can't find asset" },
{ custom_error_id, "Shield Error" }
};
const char* get_error_message(uint64_t code)
{
auto it = errors.find(code);
// known error
if (it != errors.end())
{
return it->second;
}
// read from the csv
static game::BO4_AssetRef_t custom_errors_file = game::AssetRef(fnv1a::generate_hash("gamedata/shield/custom_errors.csv"));
xassets::stringtable_header* table = xassets::DB_FindXAssetHeader(xassets::ASSET_TYPE_STRINGTABLE, &custom_errors_file, false, -1).stringtable;
if (!table || !table->columns_count || table->columns_count < 2)
{
return nullptr;
}
for (size_t i = 0; i < table->rows_count; i++)
{
auto* rows = &table->values[i * table->columns_count];
if (rows[0].type != xassets::STC_TYPE_INT || rows[1].type != xassets::STC_TYPE_STRING)
{
continue; // bad types
}
if (rows[0].value.hash_value == code)
{
return rows[1].value.string_value;
}
}
// unknown
return nullptr;
}
}

View File

@ -21,9 +21,15 @@ namespace variables
uintptr_t pointer = 0;
};
extern std::vector<varEntry> dvars_record;
extern std::vector<varEntry> commands_record;
extern std::vector<varEntry> dvars_table;
extern std::vector<varEntry> commands_table;
std::vector<const char*> get_dvars_list();
std::vector<const char*> get_commands_list();
}
namespace runtime_errors
{
constexpr uint64_t custom_error_id = 0x42693201;
const char* get_error_message(uint64_t code);
}

View File

@ -1,6 +1,7 @@
#include <std_include.hpp>
#include "definitions/game.hpp"
#include "loader/component_loader.hpp"
#include <utilities/io.hpp>
#include <utilities/nt.hpp>
#include <utilities/hook.hpp>
@ -36,11 +37,15 @@ namespace
INT WINAPI get_system_metrics(int nIndex)
{
#ifdef DEBUG
logger::write(logger::LOG_TYPE_DEBUG, "get_system_metrics(%i)", nIndex);
#endif // DEBUG
static bool initialized = false;
component_loader::post_unpack();
if (!initialized) {
game::verify_game_version();
component_loader::post_unpack();
initialized = true;
}
return GetSystemMetrics(nIndex);
}