gsc: initial loading code

This commit is contained in:
Diavolo 2022-09-14 09:33:48 +02:00
parent a7a9d39e8f
commit 5ab7734fdd
No known key found for this signature in database
GPG Key ID: FA77F074E98D98A5
16 changed files with 707 additions and 25 deletions

3
.gitmodules vendored
View File

@ -44,3 +44,6 @@
[submodule "deps/stb"]
path = deps/stb
url = https://github.com/nothings/stb.git
[submodule "deps/gsc-tool"]
path = deps/gsc-tool
url = https://github.com/xensik/gsc-tool.git

18
deps/extra/gsc-tool/interface.cpp vendored Normal file
View File

@ -0,0 +1,18 @@
#include <stdafx.hpp>
#include <xsk/s1.hpp>
#include "interface.hpp"
namespace gsc
{
std::unique_ptr<xsk::gsc::compiler> compiler()
{
auto compiler = std::make_unique<xsk::gsc::s1::compiler>();
compiler->mode(xsk::gsc::build::prod);
return compiler;
}
std::unique_ptr<xsk::gsc::assembler> assembler()
{
return std::make_unique<xsk::gsc::s1::assembler>();
}
}

7
deps/extra/gsc-tool/interface.hpp vendored Normal file
View File

@ -0,0 +1,7 @@
#pragma once
namespace gsc
{
std::unique_ptr<xsk::gsc::compiler> compiler();
std::unique_ptr<xsk::gsc::assembler> assembler();
}

1
deps/gsc-tool vendored Submodule

@ -0,0 +1 @@
Subproject commit 7cc15969db1ffdf74f770ae335aef710ec5239dd

63
deps/premake/gsc-tool.lua vendored Normal file
View File

@ -0,0 +1,63 @@
gsc_tool = {
source = path.join(dependencies.basePath, "gsc-tool/src"),
}
function gsc_tool.import()
links { "xsk-gsc-s1", "xsk-gsc-utils" }
gsc_tool.includes()
end
function gsc_tool.includes()
includedirs {
path.join(gsc_tool.source, "s1"),
path.join(gsc_tool.source, "utils"),
path.join(dependencies.basePath, "extra/gsc-tool"),
}
end
function gsc_tool.project()
project "xsk-gsc-utils"
kind "StaticLib"
language "C++"
pchheader "stdafx.hpp"
pchsource (path.join(gsc_tool.source, "utils/stdafx.cpp"))
files {
path.join(gsc_tool.source, "utils/**.hpp"),
path.join(gsc_tool.source, "utils/**.cpp"),
}
includedirs {
path.join(gsc_tool.source, "utils"),
gsc_tool.source,
}
zlib.includes()
project "xsk-gsc-s1"
kind "StaticLib"
language "C++"
filter "action:vs*"
buildoptions "/bigobj"
buildoptions "/Zc:__cplusplus"
filter {}
pchheader "stdafx.hpp"
pchsource (path.join(gsc_tool.source, "s1/stdafx.cpp"))
files {
path.join(gsc_tool.source, "s1/**.hpp"),
path.join(gsc_tool.source, "s1/**.cpp"),
path.join(dependencies.basePath, "extra/gsc-tool/interface.cpp"),
}
includedirs {
path.join(gsc_tool.source, "s1"),
gsc_tool.source,
path.join(dependencies.basePath, "extra/gsc-tool"),
}
end
table.insert(dependencies, gsc_tool)

View File

@ -1,11 +1,13 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "fastfiles.hpp"
#include "game/dvars.hpp"
#include "fastfiles.hpp"
#include "command.hpp"
#include "console.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/concurrency.hpp>
namespace fastfiles
@ -15,6 +17,7 @@ namespace fastfiles
namespace
{
utils::hook::detour db_try_load_x_file_internal_hook;
utils::hook::detour db_find_x_asset_header_hook;
void db_try_load_x_file_internal(const char* zone_name, const int flags)
{
@ -25,15 +28,60 @@ namespace fastfiles
});
return db_try_load_x_file_internal_hook.invoke<void>(zone_name, flags);
}
void dump_gsc_script(const std::string& name, game::XAssetHeader header)
{
if (!dvars::g_dump_scripts->current.enabled)
{
return;
}
std::string buffer;
buffer.append(header.scriptfile->name, std::strlen(header.scriptfile->name) + 1);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->compressedLen), 4);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->len), 4);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->bytecodeLen), 4);
buffer.append(header.scriptfile->buffer, header.scriptfile->compressedLen);
buffer.append(reinterpret_cast<char*>(header.scriptfile->bytecode), header.scriptfile->bytecodeLen);
const auto out_name = std::format("gsc_dump/{}.gscbin", name);
utils::io::write_file(out_name, buffer);
console::info("Dumped %s\n", out_name.data());
}
game::XAssetHeader db_find_x_asset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
{
const auto start = game::Sys_Milliseconds();
const auto result = db_find_x_asset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default);
const auto diff = game::Sys_Milliseconds() - start;
if (type == game::ASSET_TYPE_SCRIPTFILE)
{
dump_gsc_script(name, result);
}
if (diff > 100)
{
console::print(
result.data == nullptr ? console::con_type_error : console::con_type_warning, "Waited %i msec for asset '%s' of type '%s'.\n",
diff,
name,
game::g_assetNames[type]
);
}
return result;
}
}
std::string get_current_fastfile()
{
std::string fastfile_copy;
current_fastfile.access([&](std::string& fastfile)
auto fastfile_copy = current_fastfile.access<std::string>([&](std::string& fastfile)
{
fastfile_copy = fastfile;
return fastfile;
});
return fastfile_copy;
}
@ -70,6 +118,15 @@ namespace fastfiles
return new_pool;
}
void enum_assets(const game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, const bool include_override)
{
game::DB_EnumXAssets_Internal(type, static_cast<void(*)(game::XAssetHeader, void*)>([](game::XAssetHeader header, void* data)
{
const auto& cb = *static_cast<const std::function<void(game::XAssetHeader)>*>(data);
cb(header);
}), &callback, include_override);
}
class component final : public component_interface
{
public:
@ -78,6 +135,9 @@ namespace fastfiles
db_try_load_x_file_internal_hook.create(
SELECT_VALUE(0x1401816F0, 0x1402741C0), &db_try_load_x_file_internal);
db_find_x_asset_header_hook.create(game::DB_FindXAssetHeader, db_find_x_asset_header_stub);
dvars::g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts to binary format");
command::add("loadzone", [](const command::params& params)
{
if (params.size() < 2)

View File

@ -5,4 +5,6 @@
namespace fastfiles
{
std::string get_current_fastfile();
void enum_assets(game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, bool include_override);
}

View File

@ -1,20 +1,31 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "filesystem.hpp"
#include "game_module.hpp"
#include "console.hpp"
#include "game/game.hpp"
#include "dvars.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/io.hpp>
namespace filesystem
{
namespace
{
bool initialized = false;
bool custom_path_registered = false;
std::deque<std::filesystem::path>& get_search_paths_internal()
{
static std::deque<std::filesystem::path> search_paths{};
return search_paths;
}
std::string get_binary_directory()
{
const auto dir = game_module::get_host_module().get_folder();
@ -36,9 +47,43 @@ namespace filesystem
void fs_startup_stub(const char* gamename)
{
console::info("[FS] Startup\n");
initialized = true;
custom_path_registered = false;
register_path(get_binary_directory() + "\\data");
register_path(".");
register_path("s1x");
game::FS_Startup(gamename);
}
std::vector<std::filesystem::path> get_paths(const std::filesystem::path& path)
{
std::vector<std::filesystem::path> paths{};
const auto code = game::SEH_GetCurrentLanguageName();
paths.push_back(path);
paths.push_back(path / code);
return paths;
}
bool can_insert_path(const std::filesystem::path& path)
{
for (const auto& path_ : get_search_paths_internal())
{
if (path_ == path)
{
return false;
}
}
return true;
}
}
file::file(std::string name)
@ -70,6 +115,137 @@ namespace filesystem
return this->name_;
}
std::string read_file(const std::string& path)
{
for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path / path;
if (utils::io::file_exists(path_.generic_string()))
{
return utils::io::read_file(path_.generic_string());
}
}
return {};
}
bool read_file(const std::string& path, std::string* data, std::string* real_path)
{
for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path / path;
if (utils::io::read_file(path_.generic_string(), data))
{
if (real_path != nullptr)
{
*real_path = path_.generic_string();
}
return true;
}
}
return false;
}
bool find_file(const std::string& path, std::string* real_path)
{
for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path / path;
if (utils::io::file_exists(path_.generic_string()))
{
*real_path = path_.generic_string();
return true;
}
}
return false;
}
bool exists(const std::string& path)
{
for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path / path;
if (utils::io::file_exists(path_.generic_string()))
{
return true;
}
}
return false;
}
void register_path(const std::filesystem::path& path)
{
if (!initialized)
{
return;
}
const auto paths = get_paths(path);
for (const auto& path_ : paths)
{
if (can_insert_path(path_))
{
console::info("[FS] Registering path '%s'\n", path_.generic_string().data());
get_search_paths_internal().push_front(path_);
}
}
}
void unregister_path(const std::filesystem::path& path)
{
if (!initialized)
{
return;
}
const auto paths = get_paths(path);
for (const auto& path_ : paths)
{
auto& search_paths = get_search_paths_internal();
for (auto i = search_paths.begin(); i != search_paths.end();)
{
if (*i == path_)
{
console::info("[FS] Unregistering path '%s'\n", path_.generic_string().data());
i = search_paths.erase(i);
}
else
{
++i;
}
}
}
}
std::vector<std::string> get_search_paths()
{
std::vector<std::string> paths{};
for (const auto& path : get_search_paths_internal())
{
paths.push_back(path.generic_string());
}
return paths;
}
std::vector<std::string> get_search_paths_rev()
{
std::vector<std::string> paths{};
const auto& search_paths = get_search_paths_internal();
for (auto i = search_paths.rbegin(); i != search_paths.rend(); ++i)
{
paths.push_back(i->generic_string());
}
return paths;
}
class component final : public component_interface
{
public:

View File

@ -7,13 +7,24 @@ namespace filesystem
public:
file(std::string name);
bool exists() const;
const std::string& get_buffer() const;
const std::string& get_name() const;
[[nodiscard]] bool exists() const;
[[nodiscard]] const std::string& get_buffer() const;
[[nodiscard]] const std::string& get_name() const;
private:
bool valid_ = false;
std::string name_;
std::string buffer_;
};
std::string read_file(const std::string& path);
bool read_file(const std::string& path, std::string* data, std::string* real_path = nullptr);
bool find_file(const std::string& path, std::string* real_path);
bool exists(const std::string& path);
void register_path(const std::filesystem::path& path);
void unregister_path(const std::filesystem::path& path);
std::vector<std::string> get_search_paths();
std::vector<std::string> get_search_paths_rev();
}

View File

@ -0,0 +1,320 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include <utils/io.hpp>
#include <utils/hook.hpp>
#include "component/filesystem.hpp"
#include "component/console.hpp"
#include "component/scripting.hpp"
#include "component/fastfiles.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/gsc/interfaces/compiler.hpp>
#include <xsk/gsc/interfaces/assembler.hpp>
#include <xsk/resolver.hpp>
#include <interface.hpp>
namespace gsc
{
namespace
{
auto compiler = ::gsc::compiler();
auto assembler = ::gsc::assembler();
std::unordered_map<std::string, unsigned int> main_handles;
std::unordered_map<std::string, unsigned int> init_handles;
std::unordered_map<std::string, game::ScriptFile*> loaded_scripts;
void clear()
{
main_handles.clear();
init_handles.clear();
loaded_scripts.clear();
}
bool read_script_file(const std::string& name, std::string* data)
{
if (filesystem::read_file(name, data))
{
return true;
}
const auto* name_str = name.data();
if (game::DB_XAssetExists(game::ASSET_TYPE_RAWFILE, name_str) &&
!game::DB_IsXAssetDefault(game::ASSET_TYPE_RAWFILE, name_str))
{
const auto asset = game::DB_FindXAssetHeader(game::ASSET_TYPE_RAWFILE, name.data(), false);
const auto len = game::DB_GetRawFileLen(asset.rawfile);
data->resize(len);
game::DB_GetRawBuffer(asset.rawfile, data->data(), len);
if (len > 0)
{
data->pop_back();
}
return true;
}
return false;
}
game::ScriptFile* load_custom_script(const char* file_name, const std::string& real_name)
{
if (const auto got = loaded_scripts.find(real_name); got != loaded_scripts.end())
{
return got->second;
}
std::string source_buffer{};
if (!read_script_file(real_name + ".gsc", &source_buffer))
{
return nullptr;
}
std::vector<std::uint8_t> data;
data.assign(source_buffer.begin(), source_buffer.end());
try
{
compiler->compile(real_name, data);
}
catch (const std::exception& ex)
{
console::error("*********** script compile error *************\n");
console::error("failed to compile '%s':\n%s", real_name.data(), ex.what());
console::error("**********************************************\n");
return nullptr;
}
auto assembly = compiler->output();
try
{
assembler->assemble(real_name, assembly);
}
catch (const std::exception& ex)
{
console::error("*********** script compile error *************\n");
console::error("failed to assemble '%s':\n%s", real_name.data(), ex.what());
console::error("**********************************************\n");
return nullptr;
}
const auto script_file_ptr = static_cast<game::ScriptFile*>(game::PMem_AllocFromSource_NoDebug(sizeof(game::ScriptFile), 4, 1, 5));
script_file_ptr->name = file_name;
const auto stack = assembler->output_stack();
script_file_ptr->len = static_cast<int>(stack.size());
const auto script = assembler->output_script();
script_file_ptr->bytecodeLen = static_cast<int>(script.size());
const auto script_size = script.size();
// Use PMem for both stack and byte code
const auto buffer_size = script_size + stack.size() + 2;
const auto buffer = static_cast<std::uint8_t*>(game::PMem_AllocFromSource_NoDebug(static_cast<std::uint32_t>(buffer_size), 4, 1, 5));
std::memcpy(buffer, script.data(), script_size);
std::memcpy(&buffer[script_size], stack.data(), stack.size());
script_file_ptr->bytecode = &buffer[0];
script_file_ptr->buffer = reinterpret_cast<char*>(&buffer[script.size()]);
script_file_ptr->compressedLen = 0;
loaded_scripts[real_name] = script_file_ptr;
return script_file_ptr;
}
void load_script(const std::string& name)
{
if (!game::Scr_LoadScript(name.data()))
{
return;
}
const auto main_handle = game::Scr_GetFunctionHandle(name.data(), xsk::gsc::s1::resolver::token_id("main"));
const auto init_handle = game::Scr_GetFunctionHandle(name.data(), xsk::gsc::s1::resolver::token_id("init"));
if (main_handle)
{
console::info("Loaded '%s::main'\n", name.data());
main_handles[name] = main_handle;
}
if (init_handle)
{
console::info("Loaded '%s::init'\n", name.data());
init_handles[name] = init_handle;
}
}
void load_scripts(const std::filesystem::path& root_dir)
{
const std::filesystem::path script_dir = root_dir / "scripts";
if (!utils::io::directory_exists(script_dir.generic_string()))
{
return;
}
const auto scripts = utils::io::list_files(script_dir.generic_string());
for (const auto& script : scripts)
{
if (!script.ends_with(".gsc"))
{
continue;
}
std::filesystem::path path(script);
const auto relative = path.lexically_relative(root_dir).generic_string();
const auto base_name = relative.substr(0, relative.size() - 4);
load_script(base_name);
}
}
game::ScriptFile* find_script(game::XAssetType /*type*/, const char* name, int /*allow_create_default*/)
{
std::string real_name = name;
const auto id = static_cast<std::uint16_t>(std::atoi(name));
if (id)
{
real_name = xsk::gsc::s1::resolver::token_name(id);
}
const auto script = load_custom_script(name, real_name);
if (script)
{
return script;
}
return game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, name, 1).scriptfile;
}
int db_is_xasset_default(game::XAssetType type, const char* name)
{
if (loaded_scripts.contains(name))
{
return 0;
}
return game::DB_IsXAssetDefault(type, name);
}
void gscr_load_game_type_script_stub()
{
utils::hook::invoke<void>(0x140330490);
clear();
fastfiles::enum_assets(game::ASSET_TYPE_RAWFILE, [](const game::XAssetHeader header)
{
const std::string name = header.rawfile->name;
if (name.ends_with(".gsc") && name.starts_with("scripts/"))
{
// Remove .gsc from the file name as it will be re-added by the game later
const auto base_name = name.substr(0, name.size() - 4);
load_script(base_name);
}
}, true);
for (const auto& path : filesystem::get_search_paths())
{
load_scripts(path);
}
}
void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size)
{
if (rawfile->len > 0 && rawfile->compressedLen == 0)
{
std::memset(buf, 0, size);
std::memcpy(buf, rawfile->buffer, std::min(rawfile->len, size));
return;
}
game::DB_GetRawBuffer(rawfile, buf, size);
}
void g_load_structs_stub()
{
for (auto& function_handle : main_handles)
{
console::info("Executing '%s::main'\n", function_handle.first.data());
const auto thread = game::Scr_ExecThread(function_handle.second, 0);
game::RemoveRefToObject(thread);
}
utils::hook::invoke<void>(0x1403380D0);
}
void scr_load_level_stub()
{
utils::hook::invoke<void>(0x140325B90);
for (auto& function_handle : init_handles)
{
console::info("Executing '%s::init'\n", function_handle.first.data());
const auto thread = game::Scr_ExecThread(function_handle.second, 0);
game::RemoveRefToObject(thread);
}
}
}
class loading final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
// Allow custom scripts to include other custom scripts
xsk::gsc::s1::resolver::init([](const auto& include_name) -> std::vector<std::uint8_t>
{
const auto real_name = include_name + ".gsc";
std::string file_buffer;
if (!read_script_file(real_name, &file_buffer) || file_buffer.empty())
{
throw std::runtime_error(std::format("could not load gsc file '{}'", real_name));
}
std::vector<std::uint8_t> result;
result.assign(file_buffer.begin(), file_buffer.end());
return result;
});
// ProcessScript
utils::hook::call(0x1403F7317, find_script);
utils::hook::call(0x1403F7327, db_is_xasset_default);
// GScr_LoadScripts
utils::hook::call(0x140330B19, gscr_load_game_type_script_stub);
// Load our scripts with an uncompressed stack
utils::hook::call(0x1403F7380, db_get_raw_buffer_stub);
// Exec script handles
utils::hook::call(0x1402F71AE, g_load_structs_stub);
utils::hook::call(0x1402F71C7, scr_load_level_stub);
scripting::on_shutdown([](int free_scripts)
{
if (free_scripts)
{
clear();
}
});
}
};
}
REGISTER_COMPONENT(gsc::loading)

View File

@ -28,6 +28,8 @@ namespace scripting
std::string current_file;
std::vector<std::function<void(int)>> shutdown_callbacks;
void vm_notify_stub(const unsigned int notify_list_owner_id, const game::scr_string_t string_value,
game::VariableValue* top)
{
@ -69,6 +71,12 @@ namespace scripting
void g_shutdown_game_stub(const int free_scripts)
{
lua::engine::stop();
for (const auto& callback : shutdown_callbacks)
{
callback(free_scripts);
}
return g_shutdown_game_hook.invoke<void>(free_scripts);
}
@ -95,6 +103,11 @@ namespace scripting
}
}
void on_shutdown(const std::function<void(int)>& callback)
{
shutdown_callbacks.push_back(callback);
}
class component final : public component_interface
{
public:

View File

@ -3,4 +3,6 @@
namespace scripting
{
extern std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
void on_shutdown(const std::function<void(int)>& callback);
}

View File

@ -21,6 +21,7 @@ namespace dvars
game::dvar_t* g_playerCollision = nullptr;
game::dvar_t* pm_bouncing = nullptr;
game::dvar_t* g_dump_scripts = nullptr;
game::dvar_t* g_gravity = nullptr;
game::dvar_t* g_speed = nullptr;
game::dvar_t* g_elevators = nullptr;

View File

@ -20,6 +20,7 @@ namespace dvars
extern game::dvar_t* g_playerEjection;
extern game::dvar_t* pm_bouncing;
extern game::dvar_t* g_dump_scripts;
extern game::dvar_t* g_gravity;
extern game::dvar_t* g_speed;
extern game::dvar_t* g_elevators;

View File

@ -1202,7 +1202,7 @@ namespace game
int len;
int bytecodeLen;
const char* buffer;
char* bytecode;
unsigned char* bytecode;
};
struct StringTableCell

View File

@ -46,23 +46,19 @@ namespace game
WEAK symbol<bool()> CL_IsCgameInitialized{0x140136560, 0x1401FD510};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x1400EE500, 0x1401A3050};
WEAK symbol<void(int localClientNum, /*mp::cg_s**/void* cg, const char* dvar, const char* value)>
CG_SetClientDvarFromServer
{0, 0x1401BF0A0};
WEAK symbol<void(int localClientNum, /*mp::cg_s**/void* cg, const char* dvar, const char* value)> CG_SetClientDvarFromServer{0, 0x1401BF0A0};
WEAK symbol<void(XAssetType type, void (__cdecl* func)(XAssetHeader, void*), void* inData, bool includeOverride)>
DB_EnumXAssets_FastFile{0x14017D7C0, 0x14026EC10};
WEAK symbol<void(XAssetType type, void(__cdecl* func)(XAssetHeader, void*), const void* inData, bool includeOverride)>
DB_EnumXAssets_Internal{0x14017D830, 0x14026EC80};
WEAK symbol<XAssetEntry(XAssetType type, const char* name)>
DB_FindXAssetEntry{0x14017D830, 0x14026F020};
WEAK symbol<XAssetHeader(XAssetType type, const char *name, int allowCreateDefault)>
DB_FindXAssetHeader{0x14017DCA0, 0x14026F0F0};
WEAK symbol<void(XAssetType type, void (__cdecl* func)(XAssetHeader, void*), void* inData, bool includeOverride)> DB_EnumXAssets_FastFile{0x14017D7C0, 0x14026EC10};
WEAK symbol<void(XAssetType type, void(__cdecl* func)(XAssetHeader, void*), const void* inData, bool includeOverride)> DB_EnumXAssets_Internal{0x14017D830, 0x14026EC80};
WEAK symbol<XAssetEntry(XAssetType type, const char* name)> DB_FindXAssetEntry{0x14017D830, 0x14026F020};
WEAK symbol<XAssetHeader(XAssetType type, const char *name, int allowCreateDefault)> DB_FindXAssetHeader{0x14017DCA0, 0x14026F0F0};
WEAK symbol<const char* (const XAsset* asset)> DB_GetXAssetName{0x140151C00, 0x140240DD0};
WEAK symbol<int(XAssetType type)> DB_GetXAssetTypeSize{0x140151C20, 0x140240DF0};
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, DBSyncMode syncMode)> DB_LoadXAssets{
0x1402F8B50, 0x140270F30
};
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, DBSyncMode syncMode)> DB_LoadXAssets{0x1402F8B50, 0x140270F30};
WEAK symbol<bool(XAssetType type, const char* name)> DB_XAssetExists{0x0, 0x1402750F0};
WEAK symbol<bool(XAssetType type, const char* name)> DB_IsXAssetDefault{0x0, 0x140270320};
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x0, 0x14026FCC0};
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x0, 0x14026FB90};
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140370860, 0x1404BF8B0};
WEAK symbol<void(const dvar_t* dvar)> Dvar_ClearModified{0x140370700, 0x1404BF690};
@ -169,9 +165,12 @@ namespace game
WEAK symbol<void()> Scr_ClearOutParams{0x14031B7C0, 0x1403F8040};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x14031A0D0, 0x1403F68A0};
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x14026B620, 0x140339450};
WEAK symbol<void(unsigned int id, scr_string_t stringValue, unsigned int paramcount)> Scr_NotifyId{
0x14031CB80, 0x1403F92D0
};
WEAK symbol<void(unsigned int id, scr_string_t stringValue, unsigned int paramcount)> Scr_NotifyId{0x14031CB80, 0x1403F92D0};
WEAK symbol<unsigned __int16(int handle, unsigned int paramcount)> Scr_ExecThread{0x0, 0x1403F8120};
WEAK symbol<unsigned int(const char* name)> Scr_LoadScript{0x0, 0x1403EE250};
WEAK symbol<unsigned int(const char* script, unsigned int name)> Scr_GetFunctionHandle{0x0, 0x1403EE0D0};
WEAK symbol<unsigned int(void* func, int type, unsigned int name)> Scr_RegisterFunction{0x0, 0x1403EDAE0};
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x0, 0x1403F9E40};
@ -229,6 +228,11 @@ namespace game
WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0, 0x140490060};
WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)> UI_TextWidth{0, 0x140492380};
WEAK symbol<const char*()> SEH_GetCurrentLanguageName{0x140339300, 0x1404745C0};
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, int source)> PMem_AllocFromSource_NoDebug{0x0, 0x1404C7BA0};
WEAK symbol<void*(unsigned int size)> Hunk_AllocateTempMemoryHighInternal{0x0, 0x1404B68B0};
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14059C5C0, 0x1406FD930};
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x14059CD00, 0x1406FE070};