reimplement acts gsc debug objects (#55)

This commit is contained in:
Antoine Willerval 2024-11-28 11:58:49 +01:00 committed by GitHub
parent e325ec18fc
commit dc46fdfad5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 624 additions and 12 deletions

View File

@ -1,6 +1,7 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "definitions/game.hpp" #include "definitions/game.hpp"
#include "component/scheduler.hpp" #include "component/scheduler.hpp"
#include "component/gsc_custom.hpp"
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include <utilities/hook.hpp> #include <utilities/hook.hpp>
@ -73,6 +74,12 @@ namespace debugging
void sys_error_stub(uint32_t code, const char* message) void sys_error_stub(uint32_t code, const char* message)
{ {
if (code == gsc_custom::linking_error)
{
gsc_custom::find_linking_issues();
return; // converted to runtime error to avoid the crash
}
const char* error_message = runtime_errors::get_error_message(code); const char* error_message = runtime_errors::get_error_message(code);
if (error_message) if (error_message)

View File

@ -1,10 +1,13 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "gsc_custom.hpp" #include "gsc_custom.hpp"
#include "gsc_funcs.hpp" #include "gsc_funcs.hpp"
#include "hashes.hpp"
#include "definitions/game.hpp" #include "definitions/game.hpp"
#include "definitions/xassets.hpp"
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include <utilities/hook.hpp> #include <utilities/hook.hpp>
#include <utilities/json_config.hpp> #include <utilities/json_config.hpp>
#include <utilities/string.hpp>
namespace gsc_custom namespace gsc_custom
{ {
@ -208,6 +211,20 @@ namespace gsc_custom
inst_data.emplace_back(info); inst_data.emplace_back(info);
} }
void vm_op_custom_devblock(game::scriptInstance_t inst, game::function_stack_t* fs_0, game::ScrVmContext_t* vmc, bool* terminate)
{
byte* base = align_ptr<int16_t>(fs_0->pos);
int16_t delta = *(int16_t*)base;
fs_0->pos = base + 2;
if (!gsc_funcs::enable_dev_blocks) {
// default action, jump after the dev block
fs_0->pos = fs_0->pos + delta;
}
}
void vm_op_custom_lazylink(game::scriptInstance_t inst, game::function_stack_t* fs_0, game::ScrVmContext_t* vmc, bool* terminate) void vm_op_custom_lazylink(game::scriptInstance_t inst, game::function_stack_t* fs_0, game::ScrVmContext_t* vmc, bool* terminate)
{ {
byte* base = align_ptr<uint32_t>(fs_0->pos); byte* base = align_ptr<uint32_t>(fs_0->pos);
@ -290,7 +307,212 @@ namespace gsc_custom
} }
void find_linking_issues()
{
logger::write(logger::LOG_TYPE_ERROR, "Linking error detected, searching cause...");
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> availables{};
for (size_t _inst = 0; _inst < game::SCRIPTINSTANCE_MAX; _inst++)
{
size_t error{};
game::scriptInstance_t inst = (game::scriptInstance_t)_inst;
for (size_t obj = 0; obj < game::gObjFileInfoCount[inst]; obj++)
{
game::objFileInfo_t& info = (*game::gObjFileInfo)[inst][obj];
if (!info.activeVersion)
{
continue;
}
game::GSC_OBJ* prime_obj = info.activeVersion;
availables.clear();
// load exports
game::GSC_EXPORT_ITEM* exports = (game::GSC_EXPORT_ITEM*)(prime_obj->magic + prime_obj->exports_offset);
for (size_t i = 0; i < prime_obj->exports_count; i++)
{
availables[exports[i].name_space].insert(exports[i].name);
}
// load using exports
int64_t* usings = (int64_t*)(prime_obj->magic + prime_obj->include_offset);
for (size_t i = 0; i < prime_obj->include_count; i++)
{
game::BO4_AssetRef_t ref{ usings[i], 0 };
xassets::scriptparsetree_header* spt = xassets::DB_FindXAssetHeader(xassets::ASSET_TYPE_SCRIPTPARSETREE, &ref, false, -1).scriptparsetree;
if (!spt || !spt->buffer)
{
error++;
logger::write(logger::LOG_TYPE_ERROR, "[%s] Can't find #using %s in %s", inst ? "CSC" : "GSC", hashes::lookup_tmp("script", ref.hash), hashes::lookup_tmp("script", prime_obj->name));
continue;
}
game::GSC_EXPORT_ITEM* exports_using = (game::GSC_EXPORT_ITEM*)(spt->buffer->magic + spt->buffer->exports_offset);
for (size_t j = 0; j < spt->buffer->exports_count; j++)
{
if (exports_using[j].flags & game::GSC_EXPORT_FLAGS::GEF_PRIVATE)
{
continue; // can't import private exports
}
availables[exports_using[j].name_space].insert(exports_using[j].name);
}
}
game::GSC_IMPORT_ITEM* imports = (game::GSC_IMPORT_ITEM*)(prime_obj->magic + prime_obj->imports_offset);
for (size_t i = 0; i < prime_obj->imports_count; i++)
{
game::GSC_IMPORT_ITEM* imp = imports;
uint32_t* locations = reinterpret_cast<uint32_t*>(imp + 1);
imports = reinterpret_cast<game::GSC_IMPORT_ITEM*>(locations + imp->num_address);
if (imp->flags & game::GSC_IMPORT_FLAGS::GIF_DEV_CALL)
{
// ignore dev calls
continue;
}
auto itn = availables.find(imp->name_space);
if (itn != availables.end() && itn->second.contains(imp->name))
{
continue;
}
byte import_type = imp->flags & game::GSC_IMPORT_FLAGS::GIF_CALLTYPE_MASK;
// search builtin calls
if ((imp->flags & game::GSC_IMPORT_FLAGS::GIF_GET_CALL) != 0 || imp->name_space == 0xC1243180 || imp->name_space == 0x222276A9)
{
int type{};
int ignored{};
if (import_type == game::GSC_IMPORT_FLAGS::GIF_FUNC_METHOD || import_type == game::GSC_IMPORT_FLAGS::GIF_FUNCTION)
{
// &func or func()
if (inst)
{
if (game::CScr_GetFunction(imp->name, &type, &ignored, &ignored) && !type)
{
continue;
}
}
else
{
if (game::Scr_GetFunction(imp->name, &type, &ignored, &ignored) && !type)
{
continue;
}
}
}
if (import_type == game::GSC_IMPORT_FLAGS::GIF_FUNC_METHOD || import_type == game::GSC_IMPORT_FLAGS::GIF_METHOD)
{
// &meth or <x> meth()
if (inst)
{
if (game::CScr_GetMethod(imp->name, &type, &ignored, &ignored) && !type)
{
continue;
}
}
else
{
if (game::Scr_GetMethod(imp->name, &type, &ignored, &ignored) && !type)
{
continue;
}
}
}
}
const char* func;
if ((imp->flags & game::GSC_IMPORT_FLAGS::GIF_GET_CALL) != 0 || imp->name_space == 0xC1243180 || imp->name_space == 0x222276A9)
{
func = hashes::lookup_tmp("function", imp->name);
}
else
{
func = utilities::string::va("%s::%s", hashes::lookup_tmp("namespace", imp->name_space), hashes::lookup_tmp("function", imp->name));
}
const char* prefix;
switch (import_type)
{
case game::GIF_FUNC_METHOD:
prefix = "&";
break;
case game::GIF_FUNCTION:
case game::GIF_METHOD:
prefix = "";
break;
case game::GIF_FUNCTION_THREAD:
case game::GIF_METHOD_THREAD:
prefix = "thread ";
break;
case game::GIF_FUNCTION_CHILDTHREAD:
case game::GIF_METHOD_CHILDTHREAD:
prefix = "childthread ";
break;
default:
prefix = "<error>";
break;
}
logger::write(logger::LOG_TYPE_ERROR, "[%s] Unknown import %s%s in %s",
inst ? "CSC" : "GSC", prefix, func, hashes::lookup_tmp("script", prime_obj->name)
);
for (size_t j = 0; j < imp->num_address; j++)
{
const char* scriptname{};
int32_t sloc{};
int32_t crc{};
int32_t vm{};
game::Scr_GetGscExportInfo(inst, prime_obj->magic + locations[j], &scriptname, &sloc, &crc, &vm);
if (scriptname)
{
logger::write(logger::LOG_TYPE_ERROR, "[%s] at %s", inst ? "CSC" : "GSC", scriptname);
}
else
{
logger::write(logger::LOG_TYPE_ERROR, "[%s] at %s@%lx", inst ? "CSC" : "GSC", hashes::lookup_tmp("script", prime_obj->name), locations[j]);
}
}
}
}
if (error)
{
// convert the error to a terminal error to avoid a game crash
gsc_funcs::gsc_error("Find %lld GSC Linking error(s), see logs for more details", inst, true, error);
break;
}
}
// Can't find the error, we crash the server by default
gsc_funcs::gsc_error("GSC Linking error, see logs for more details", game::SCRIPTINSTANCE_SERVER, true);
}
void patch_linking_sys_error()
{
auto scr_get_gsc_obj = 0x142748BB0_g;
// skip the error and the autoexec
// 1C1 = syserr start
// 19F = end
utilities::hook::jump(scr_get_gsc_obj + 0x1C1, scr_get_gsc_obj + 0x19F);
}
utilities::hook::detour scr_get_gsc_obj_hook; utilities::hook::detour scr_get_gsc_obj_hook;
utilities::hook::detour gsc_obj_resolve_hook;
void scr_get_gsc_obj_stub(game::scriptInstance_t inst, game::BO4_AssetRef_t* name, bool runScript) void scr_get_gsc_obj_stub(game::scriptInstance_t inst, game::BO4_AssetRef_t* name, bool runScript)
{ {
if (game::gObjFileInfoCount[inst] == 0) if (game::gObjFileInfoCount[inst] == 0)
@ -305,16 +527,63 @@ namespace gsc_custom
gsc_custom::link_detours(inst); gsc_custom::link_detours(inst);
} }
int32_t gsc_obj_resolve_stub(game::scriptInstance_t inst, game::GSC_OBJ* prime_obj)
{
game::GSC_IMPORT_ITEM* import_item = prime_obj->get_imports();
for (size_t i = 0; i < prime_obj->imports_count; i++)
{
if ((import_item->flags & game::GIF_SHIELD_DEV_BLOCK_FUNC) != 0)
{
// enable or disable this dev import
if (gsc_funcs::enable_dev_blocks)
{
import_item->flags &= ~game::GIF_DEV_CALL;
}
else
{
import_item->flags |= game::GIF_DEV_CALL;
}
}
// goto to the next element after the addresses
uint32_t* addresses = reinterpret_cast<uint32_t*>(import_item + 1);
import_item = reinterpret_cast<game::GSC_IMPORT_ITEM*>(addresses + import_item->num_address);
}
bool dv_func_back = gsc_funcs::enable_dev_func;
if (gsc_funcs::enable_dev_blocks)
{
gsc_funcs::enable_dev_func = true;
}
int32_t ret = gsc_obj_resolve_hook.invoke<int32_t>(inst, prime_obj);
gsc_funcs::enable_dev_func = dv_func_back;
return ret;
}
class component final : public component_interface class component final : public component_interface
{ {
public: public:
void post_unpack() override void post_unpack() override
{ {
// t8compiler custom opcode // t8compiler custom opcode
game::gVmOpJumpTable[0x16] = vm_op_custom_lazylink; game::gVmOpJumpTable[lazylink_opcode] = vm_op_custom_lazylink;
game::gVmOpJumpTable[shield_devblock_opcode] = vm_op_custom_devblock;
// group gsc link // group gsc link
scr_get_gsc_obj_hook.create(0x142748BB0_g, scr_get_gsc_obj_stub); scr_get_gsc_obj_hook.create(0x142748BB0_g, scr_get_gsc_obj_stub);
if (gsc_funcs::enable_dev_blocks)
{
gsc_obj_resolve_hook.create(0x142746A30_g, gsc_obj_resolve_stub);
}
patch_linking_sys_error();
} }
}; };
} }

View File

@ -3,6 +3,10 @@
namespace gsc_custom namespace gsc_custom
{ {
constexpr uint32_t linking_error = 1670707254;
constexpr uint16_t lazylink_opcode = 0x16;
constexpr uint16_t shield_devblock_opcode = 0x21;
enum gsic_field_type enum gsic_field_type
{ {
GSIC_FIELD_DETOUR = 0 GSIC_FIELD_DETOUR = 0
@ -22,8 +26,10 @@ namespace gsc_custom
struct gsic_info struct gsic_info
{ {
bool sync{}; bool sync{};
bool dev_blocks{};
std::vector<gsic_detour> detours{}; std::vector<gsic_detour> detours{};
}; };
void sync_gsic(game::scriptInstance_t inst, gsic_info& info); void sync_gsic(game::scriptInstance_t inst, gsic_info& info);
void find_linking_issues();
} }

View File

@ -947,6 +947,59 @@ namespace gsc_funcs
shield_from_json_push_struct(inst, doc); shield_from_json_push_struct(inst, doc);
} }
void add_debug_command(game::scriptInstance_t inst)
{
int localclientnum;
const char* cmd;
if (inst == game::SCRIPTINSTANCE_CLIENT)
{
// client, using param
localclientnum = (int)game::ScrVm_GetInt(inst, 0);
cmd = game::ScrVm_GetString(inst, 1);
}
else
{
// server, using primary
localclientnum = game::Com_LocalClients_GetPrimary();
cmd = game::ScrVm_GetString(inst, 0);
}
game::Cbuf_AddText(localclientnum, cmd);
}
void hash_lookup(game::scriptInstance_t inst)
{
game::ScrVarType_t type = game::ScrVm_GetType(inst, 0);
switch (type)
{
case game::TYPE_STRING:
game::ScrVm_AddConstString(inst, game::ScrVm_GetConstString(inst, 0));
return;
case game::TYPE_HASH:
{
game::BO4_AssetRef_t hash;
const char* lookup = hashes::lookup(game::ScrVm_GetHash(&hash, inst, 0)->hash);
if (lookup)
{
game::ScrVm_AddString(inst, lookup);
}
else
{
// can't find value, return base hash
game::ScrVm_AddHash(inst, &hash);
}
return;
}
default:
gsc_error("invalid hash param, a hash or a string should be used, received: %s", inst, false, game::var_typename[type]);
return;
}
}
game::BO4_BuiltinFunctionDef custom_functions_gsc[] = game::BO4_BuiltinFunctionDef custom_functions_gsc[] =
{ {
@ -1047,6 +1100,13 @@ namespace gsc_funcs
.max_args = 2, .max_args = 2,
.actionFunc = shield_to_json, .actionFunc = shield_to_json,
.type = 0 .type = 0
},
{// ShieldHashLookup(hash hash) -> string
.canonId = canon_hash("ShieldHashLookup"),
.min_args = 1,
.max_args = 1,
.actionFunc = hash_lookup,
.type = 0
} }
}; };
game::BO4_BuiltinFunctionDef custom_functions_csc[] = game::BO4_BuiltinFunctionDef custom_functions_csc[] =
@ -1141,6 +1201,13 @@ namespace gsc_funcs
.max_args = 2, .max_args = 2,
.actionFunc = shield_to_json, .actionFunc = shield_to_json,
.type = 0 .type = 0
},
{// ShieldHashLookup(hash hash) -> string
.canonId = canon_hash("ShieldHashLookup"),
.min_args = 1,
.max_args = 1,
.actionFunc = hash_lookup,
.type = 0
} }
}; };
@ -1175,6 +1242,7 @@ namespace gsc_funcs
bool enable_dev_func = false; bool enable_dev_func = false;
bool enable_dev_blocks = false;
utilities::hook::detour scr_get_function_reverse_lookup; utilities::hook::detour scr_get_function_reverse_lookup;
utilities::hook::detour cscr_get_function_reverse_lookup; utilities::hook::detour cscr_get_function_reverse_lookup;
@ -1394,7 +1462,7 @@ namespace gsc_funcs
game::GSC_OBJ* obj = info.activeVersion; game::GSC_OBJ* obj = info.activeVersion;
if (codepos >= obj->magic + obj->start_data && codepos < obj->magic + obj->start_data + obj->data_length) if (codepos >= obj->magic + obj->cseg_offset && codepos < obj->magic + obj->cseg_offset + obj->cseg_size)
{ {
script_obj = obj; script_obj = obj;
break; break;
@ -1461,10 +1529,19 @@ namespace gsc_funcs
class component final : public component_interface class component final : public component_interface
{ {
public: public:
void pre_start() override
{
// enable dev functions
enable_dev_func = utilities::json_config::ReadBoolean("gsc", "dev_funcs", false);
// enable custom compiled dev blocks
enable_dev_blocks = utilities::json_config::ReadBoolean("gsc", "dev_blocks", false);
}
void post_unpack() override void post_unpack() override
{ {
// enable dev functions still available in the game // replace nulled function references
enable_dev_func = utilities::json_config::ReadBoolean("gsc", "dev_funcs", false); reinterpret_cast<game::BO4_BuiltinFunctionDef*>(0x144ED5D90_g)->actionFunc = add_debug_command; // csc
reinterpret_cast<game::BO4_BuiltinFunctionDef*>(0x1449BAD60_g)->actionFunc = add_debug_command; // gsc
scr_get_function_reverse_lookup.create(0x1433AF8A0_g, scr_get_function_reverse_lookup_stub); scr_get_function_reverse_lookup.create(0x1433AF8A0_g, scr_get_function_reverse_lookup_stub);
cscr_get_function_reverse_lookup.create(0x141F132A0_g, cscr_get_function_reverse_lookup_stub); cscr_get_function_reverse_lookup.create(0x141F132A0_g, cscr_get_function_reverse_lookup_stub);

View File

@ -7,6 +7,7 @@ namespace gsc_funcs
constexpr auto serious_custom_func_name = "SeriousCustom"; constexpr auto serious_custom_func_name = "SeriousCustom";
extern bool enable_dev_func; extern bool enable_dev_func;
extern bool enable_dev_blocks;
uint32_t canon_hash(const char* str); uint32_t canon_hash(const char* str);
uint32_t canon_hash_pattern(const char* str); uint32_t canon_hash_pattern(const char* str);

View File

@ -6,8 +6,9 @@
#include "command.hpp" #include "command.hpp"
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "definitions/xassets.hpp"
#include "definitions/game.hpp" #include "definitions/game.hpp"
#include "definitions/xassets.hpp"
#include "definitions/scripting.hpp"
#include <utilities/io.hpp> #include <utilities/io.hpp>
#include <utilities/hook.hpp> #include <utilities/hook.hpp>
@ -22,6 +23,13 @@ namespace mods {
std::filesystem::path mod_dir = "project-bo4/mods"; std::filesystem::path mod_dir = "project-bo4/mods";
namespace { namespace {
template<typename T>
inline byte* align_ptr(byte* ptr)
{
return reinterpret_cast<byte*>((reinterpret_cast<uintptr_t>(ptr) + sizeof(T) - 1) & ~(sizeof(T) - 1));
}
struct raw_file struct raw_file
{ {
xassets::raw_file_header header{}; xassets::raw_file_header header{};
@ -144,6 +152,89 @@ namespace mods {
return true; return true;
} }
bool load_acts_debug()
{
if ((data.length() < sizeof(game::GSC_OBJ) + sizeof(game::acts_debug::MAGIC))
|| *reinterpret_cast<decltype(game::acts_debug::MAGIC)*>(&data[sizeof(game::GSC_OBJ)]) != game::acts_debug::MAGIC)
{
return true; // too small to be a debug file
}
game::acts_debug::GSC_ACTS_DEBUG& dbg = *reinterpret_cast<game::acts_debug::GSC_ACTS_DEBUG*>(data.data() + sizeof(game::GSC_OBJ));
logger::write(logger::LOG_TYPE_DEBUG, "loading acts debug file v%x.%llx", dbg.version, dbg.actsVersion);
byte* ptr = (byte*)data.data();
if (dbg.has_feature(game::acts_debug::ADF_STRING))
{
char* start = data.data();
for (uint32_t* strs = dbg.get_strings(ptr); strs != dbg.get_strings_end(ptr); strs++)
{
const char* val = data.data() + *strs;
hashes::add_hash(fnv1a::generate_hash(val), val);
hashes::add_hash(gsc_funcs::canon_hash(val), val);
}
}
if (dbg.has_feature(game::acts_debug::ADF_DETOUR))
{
for (game::acts_debug::GSC_ACTS_DETOUR* detours = dbg.get_detours(ptr); detours != dbg.get_detours_end(ptr); detours++)
{
gsc_custom::gsic_detour& detour = gsic.detours.emplace_back();
detour.fixup_name = 0;
detour.replace_namespace = detours->name_space;
detour.replace_function = detours->name;
detour.fixup_offset = detours->location;
detour.fixup_size = detours->size;
detour.target_script = detours->script;
logger::write(logger::LOG_TYPE_DEBUG, std::format(
"read detour : namespace_{:x}<script_{:x}>::function_{:x} / offset=0x{:x}+0x{:x}",
detour.replace_namespace, detour.target_script, detour.replace_function,
detour.fixup_offset, detour.fixup_size
));
}
}
if (gsc_funcs::enable_dev_blocks)
{
if (dbg.has_feature(game::acts_debug::ADF_DEVBLOCK_BEGIN))
{
if (dbg.devblock_count)
{
gsic.dev_blocks = true;
// we need to patch the dev function imports to load them
game::GSC_OBJ* prime_obj = (game::GSC_OBJ*)data.data();
game::GSC_IMPORT_ITEM* import_item = prime_obj->get_imports();
for (size_t i = 0; i < prime_obj->imports_count; i++)
{
// mark this function for the custom linker
if ((import_item->flags & game::GIF_DEV_CALL) != 0)
{
import_item->flags |= game::GIF_SHIELD_DEV_BLOCK_FUNC;
}
// goto to the next element after the addresses
uint32_t* addresses = reinterpret_cast<uint32_t*>(import_item + 1);
import_item = reinterpret_cast<game::GSC_IMPORT_ITEM*>(addresses + import_item->num_address);
}
}
for (uint32_t* devblock = dbg.get_devblocks(ptr); devblock != dbg.get_devblocks_end(ptr); devblock++)
{
byte* base = align_ptr<uint16_t>(ptr + *devblock);
*(uint16_t*)base = gsc_custom::shield_devblock_opcode;
}
}
}
return true;
}
}; };
struct lua_file struct lua_file
{ {
@ -427,17 +518,23 @@ namespace mods {
return false; return false;
} }
if (tmp.gsic.detours.size())
{
logger::write(logger::LOG_TYPE_DEBUG, std::format("loaded {} detours", tmp.gsic.detours.size()));
}
if (tmp.data.length() < sizeof(game::GSC_OBJ) || *reinterpret_cast<uint64_t*>(tmp.data.data()) != gsc_magic) if (tmp.data.length() < sizeof(game::GSC_OBJ) || *reinterpret_cast<uint64_t*>(tmp.data.data()) != gsc_magic)
{ {
logger::write(logger::LOG_TYPE_ERROR, std::format("bad scriptparsetree magic in {} for mod {}", spt_path.string(), mod_name)); logger::write(logger::LOG_TYPE_ERROR, std::format("bad scriptparsetree magic in {} for mod {}", spt_path.string(), mod_name));
return false; return false;
} }
if (!tmp.load_acts_debug())
{
logger::write(logger::LOG_TYPE_ERROR, std::format("error when reading ACTS DEBUG header of {} for mod {}", spt_path.string(), mod_name));
return false;
}
if (tmp.gsic.detours.size())
{
logger::write(logger::LOG_TYPE_DEBUG, std::format("loaded {} detours", tmp.gsic.detours.size()));
}
// after this point we assume that the GSC file is well formatted // after this point we assume that the GSC file is well formatted
game::GSC_OBJ* script_obj = reinterpret_cast<game::GSC_OBJ*>(tmp.data.data()); game::GSC_OBJ* script_obj = reinterpret_cast<game::GSC_OBJ*>(tmp.data.data());

View File

@ -491,6 +491,7 @@ namespace game
// Cmd // Cmd
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{ 0x143CDE880_g }; 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(int localClientNum, int controllerIndex, const char* buffer)> Cbuf_ExecuteBuffer{ 0x143CDEBE0_g };
WEAK symbol<int()> Com_LocalClients_GetPrimary{ 0x142893AF0_g };
WEAK symbol<void(BO4_AssetRef_t* cmdName, xcommand_t function, cmd_function_t* allocedCmd)> Cmd_AddCommandInternal{ 0x143CDEE80_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()> Cbuf_AddServerText_f{ 0x143CDE870_g };
@ -569,6 +570,7 @@ namespace game
WEAK symbol<BuiltinFunction(uint32_t canonId, int* type, int* min_args, int* max_args)> Scr_GetFunction{ 0x1433AF840_g }; WEAK symbol<BuiltinFunction(uint32_t canonId, int* type, int* min_args, int* max_args)> Scr_GetFunction{ 0x1433AF840_g };
WEAK symbol<void*(uint32_t canonId, int* type, int* min_args, int* max_args)> CScr_GetMethod{ 0x141F13650_g }; WEAK symbol<void*(uint32_t canonId, int* type, int* min_args, int* max_args)> CScr_GetMethod{ 0x141F13650_g };
WEAK symbol<void*(uint32_t canonId, int* type, int* min_args, int* max_args)> Scr_GetMethod{ 0x1433AFC20_g }; WEAK symbol<void*(uint32_t canonId, int* type, int* min_args, int* max_args)> Scr_GetMethod{ 0x1433AFC20_g };
WEAK symbol<void(game::scriptInstance_t inst, byte* codepos, const char** scriptname, int32_t* sloc, int32_t* crc, int32_t* vm)> Scr_GetGscExportInfo{ 0x142748550_g };
WEAK symbol<void(uint64_t code, scriptInstance_t inst, char* unused, bool terminal)> ScrVm_Error{ 0x142770330_g }; WEAK symbol<void(uint64_t code, scriptInstance_t inst, char* unused, bool terminal)> ScrVm_Error{ 0x142770330_g };
WEAK symbol<BO4_scrVarPub> scrVarPub{ 0x148307880_g }; WEAK symbol<BO4_scrVarPub> scrVarPub{ 0x148307880_g };

View File

@ -34,7 +34,7 @@ namespace game
int32_t include_offset; int32_t include_offset;
uint16_t string_count; uint16_t string_count;
uint16_t exports_count; uint16_t exports_count;
int32_t start_data; int32_t cseg_offset;
int32_t string_offset; int32_t string_offset;
int16_t imports_count; int16_t imports_count;
uint16_t fixup_count; uint16_t fixup_count;
@ -48,7 +48,7 @@ namespace game
int32_t script_size; int32_t script_size;
int32_t requires_implements_offset; int32_t requires_implements_offset;
int32_t ukn50; int32_t ukn50;
int32_t data_length; int32_t cseg_size;
uint16_t include_count; uint16_t include_count;
byte ukn5a; byte ukn5a;
byte requires_implements_count; byte requires_implements_count;
@ -283,4 +283,157 @@ namespace game
int refCount; int refCount;
uint32_t groupId; uint32_t groupId;
}; };
enum GSC_EXPORT_FLAGS : byte
{
GEF_LINKED = 0x01,
GEF_AUTOEXEC = 0x02,
GEF_PRIVATE = 0x04,
GEF_CLASS_MEMBER = 0x08,
GEF_CLASS_DESTRUCTOR = 0x10,
GEF_VE = 0x20,
GEF_EVENT = 0x40,
GEF_CLASS_LINKED = 0x80,
GEF_CLASS_VTABLE = 0x86
};
enum GSC_IMPORT_FLAGS : byte
{
GIF_FUNC_METHOD = 0x1,
GIF_FUNCTION = 0x2,
GIF_FUNCTION_THREAD = 0x3,
GIF_FUNCTION_CHILDTHREAD = 0x4,
GIF_METHOD = 0x5,
GIF_METHOD_THREAD = 0x6,
GIF_METHOD_CHILDTHREAD = 0x7,
GIF_CALLTYPE_MASK = 0xF,
GIF_DEV_CALL = 0x10,
GIF_GET_CALL = 0x20,
GIF_SHIELD_DEV_BLOCK_FUNC = 0x80,
};
namespace acts_debug
{
constexpr uint64_t MAGIC = 0x0d0a42444124;
constexpr byte CURRENT_VERSION = 0x12;
enum GSC_ACTS_DEBUG_FEATURES : byte
{
ADF_STRING = 0x10,
ADF_DETOUR = 0x11,
ADF_DEVBLOCK_BEGIN = 0x12,
ADF_LAZYLINK = 0x12,
ADF_CRC_LOC = 0x13,
ADF_DEVSTRING = 0x14,
ADF_LINES = 0x15,
ADF_FILES = 0x15,
ADF_FLAGS = 0x15,
};
enum GSC_ACTS_DEBUG_FLAGS : uint32_t
{
ADFG_OBFUSCATED = 1 << 0,
ADFG_DEBUG = 1 << 1,
ADFG_CLIENT = 1 << 2,
ADFG_PLATFORM_SHIFT = 3,
ADFG_PLATFORM_MASK = 0xF << ADFG_PLATFORM_SHIFT,
};
struct GSC_ACTS_DETOUR
{
uint64_t name_space;
uint64_t name;
uint64_t script;
uint32_t location;
uint32_t size;
};
struct GSC_ACTS_LAZYLINK
{
uint64_t name_space;
uint64_t name;
uint64_t script;
uint32_t num_address;
};
struct GSC_ACTS_DEVSTRING
{
uint32_t string;
uint32_t num_address;
};
struct GSC_ACTS_LINES
{
uint32_t start;
uint32_t end;
size_t lineNum;
};
struct GSC_ACTS_FILES
{
uint32_t filename;
size_t lineStart;
size_t lineEnd;
};
struct GSC_ACTS_DEBUG
{
byte magic[sizeof(MAGIC)];
byte version;
uint32_t flags;
uint64_t actsVersion;
uint32_t strings_offset{};
uint32_t strings_count{};
uint32_t detour_offset{};
uint32_t detour_count{};
uint32_t devblock_offset{};
uint32_t devblock_count{};
uint32_t lazylink_offset{};
uint32_t lazylink_count{};
uint32_t crc_offset{};
uint32_t devstrings_offset{};
uint32_t devstrings_count{};
uint32_t lines_offset{};
uint32_t lines_count{};
uint32_t files_offset{};
uint32_t files_count{};
constexpr bool has_feature(GSC_ACTS_DEBUG_FEATURES feature) const
{
return version >= feature;
}
inline GSC_ACTS_DETOUR* get_detours(byte* start)
{
return reinterpret_cast<GSC_ACTS_DETOUR*>(start + detour_offset);
}
inline GSC_ACTS_DETOUR* get_detours_end(byte* start)
{
return get_detours(start) + detour_count;
}
inline uint32_t* get_devblocks(byte* start)
{
return reinterpret_cast<uint32_t*>(start + devblock_offset);
}
inline uint32_t* get_devblocks_end(byte* start)
{
return get_devblocks(start) + devblock_count;
}
inline uint32_t* get_strings(byte* start)
{
return reinterpret_cast<uint32_t*>(start + strings_offset);
}
inline uint32_t* get_strings_end(byte* start)
{
return get_strings(start) + strings_count;
}
};
}
} }