mods: untested code (YOLO)
This commit is contained in:
parent
6239fb8a10
commit
b25aeb38a5
61
src/client/component/assets/weapons.cpp
Normal file
61
src/client/component/assets/weapons.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "component/console.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace weapons
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void g_setup_level_weapon_def_stub()
|
||||
{
|
||||
game::G_SetupLevelWeaponDef();
|
||||
|
||||
// The count on this game seems pretty high
|
||||
std::array<game::WeaponCompleteDef*, 2048> weapons{};
|
||||
const auto count = game::DB_GetAllXAssetOfType_FastFile(game::ASSET_TYPE_WEAPON, (void**)weapons.data(), static_cast<int>(weapons.max_size()));
|
||||
|
||||
std::sort(weapons.begin(), weapons.begin() + count, [](game::WeaponCompleteDef* weapon1, game::WeaponCompleteDef* weapon2)
|
||||
{
|
||||
assert(weapon1->szInternalName);
|
||||
assert(weapon2->szInternalName);
|
||||
|
||||
return std::strcmp(weapon1->szInternalName, weapon2->szInternalName) < 0;
|
||||
});
|
||||
|
||||
#ifdef _DEBUG
|
||||
console::info("Found %i weapons to precache\n", count);
|
||||
#endif
|
||||
|
||||
for (auto i = 0; i < count; ++i)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
console::info("Precaching weapon \"%s\"\n", weapons[i]->szInternalName);
|
||||
#endif
|
||||
(void)game::G_GetWeaponForName(weapons[i]->szInternalName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
if (game::environment::is_sp()) return;
|
||||
|
||||
// Kill Scr_PrecacheItem (We are going to do this from code)
|
||||
utils::hook::nop(0x1403101D0, 4);
|
||||
utils::hook::set<std::uint8_t>(0x1403101D0, 0xC3);
|
||||
|
||||
// Load weapons from the DB
|
||||
utils::hook::call(0x1402F6EF4, g_setup_level_weapon_def_stub);
|
||||
utils::hook::call(0x140307401, g_setup_level_weapon_def_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(weapons::component)
|
@ -11,7 +11,7 @@
|
||||
|
||||
namespace dvar_cheats
|
||||
{
|
||||
void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::dvar_value* value)
|
||||
void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::DvarValue* value)
|
||||
{
|
||||
if (dvar && dvar->name == "sv_cheats"s)
|
||||
{
|
||||
|
@ -137,21 +137,6 @@ namespace fastfiles
|
||||
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);
|
||||
|
||||
command::add("loadzone", [](const command::params& params)
|
||||
{
|
||||
if (params.size() < 2)
|
||||
{
|
||||
console::info("usage: loadzone <zone>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
game::XZoneInfo info{};
|
||||
info.name = params.get(1);
|
||||
info.allocFlags = 1;
|
||||
info.freeFlags = 0;
|
||||
game::DB_LoadXAssets(&info, 1u, game::DBSyncMode::DB_LOAD_SYNC);
|
||||
});
|
||||
|
||||
command::add("g_poolSizes", []()
|
||||
{
|
||||
for (auto i = 0; i < game::ASSET_TYPE_COUNT; i++)
|
||||
|
233
src/client/component/mods.cpp
Normal file
233
src/client/component/mods.cpp
Normal file
@ -0,0 +1,233 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
#include "console.hpp"
|
||||
#include "mods.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace mods
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour sys_create_file_hook;
|
||||
|
||||
void db_build_os_path_from_source(const char* zone_name, game::FF_DIR source, int size, char* filename)
|
||||
{
|
||||
char user_map[MAX_PATH]{};
|
||||
|
||||
switch (source)
|
||||
{
|
||||
case game::FFD_DEFAULT:
|
||||
(void)game::Com_sprintf(filename, size, "%s\\%s.ff", std::filesystem::current_path().string().c_str(), zone_name);
|
||||
break;
|
||||
case game::FFD_MOD_DIR:
|
||||
assert(mods::is_using_mods());
|
||||
|
||||
(void)game::Com_sprintf(filename, size, "%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), (*dvars::fs_gameDirVar)->current.string, zone_name);
|
||||
break;
|
||||
case game::FFD_USER_MAP:
|
||||
game::I_strncpyz(user_map, zone_name, sizeof(user_map));
|
||||
|
||||
(void)game::Com_sprintf(filename, size, "%s\\%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), "usermaps", user_map, zone_name);
|
||||
break;
|
||||
default:
|
||||
assert(false && "inconceivable");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool fs_game_dir_domain_func(game::dvar_t* dvar, game::DvarValue new_value)
|
||||
{
|
||||
if (*new_value.string == '\0')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (game::I_strnicmp(new_value.string, "mods", 4) != 0)
|
||||
{
|
||||
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
|
||||
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (5 < std::strlen(new_value.string) && (new_value.string[4] == '\\' || new_value.string[4] == '/'))
|
||||
{
|
||||
const auto* s1 = std::strstr(new_value.string, "..");
|
||||
const auto* s2 = std::strstr(new_value.string, "::");
|
||||
if (s1 == nullptr && s2 == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
|
||||
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Invalid path specified
|
||||
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
|
||||
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto skip_extra_zones_stub = utils::hook::assemble([](utils::hook::assembler& a) -> void
|
||||
{
|
||||
const auto skip = a.newLabel();
|
||||
const auto original = a.newLabel();
|
||||
|
||||
a.pushad64();
|
||||
a.test(esi, game::DB_ZONE_CUSTOM); // allocFlags
|
||||
a.jnz(skip);
|
||||
|
||||
a.bind(original);
|
||||
a.popad64();
|
||||
a.mov(rdx, 0x140809D40);
|
||||
a.mov(rcx, rbp);
|
||||
a.call(0x1406FE120);
|
||||
a.jmp(0x140271B63);
|
||||
|
||||
a.bind(skip);
|
||||
a.popad64();
|
||||
a.mov(r13d, game::DB_ZONE_CUSTOM);
|
||||
a.not_(r13d);
|
||||
a.and_(esi, r13d);
|
||||
a.jmp(0x140271D02);
|
||||
});
|
||||
|
||||
game::Sys_File sys_create_file_stub(game::Sys_Folder folder, const char* base_filename)
|
||||
{
|
||||
auto result = sys_create_file_hook.invoke<game::Sys_File>(folder, base_filename);
|
||||
|
||||
if (result.handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!is_using_mods())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// .ff extension was added previously
|
||||
if (!std::strcmp(base_filename, "mod.ff") && mods::db_mod_file_exists())
|
||||
{
|
||||
char file_path[MAX_PATH]{};
|
||||
db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(file_path), file_path);
|
||||
result.handle = game::Sys_OpenFileReliable(file_path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void db_load_x_assets_stub(game::XZoneInfo* zone_info, unsigned int zone_count, game::DBSyncMode sync_mode)
|
||||
{
|
||||
std::vector<game::XZoneInfo> zones(zone_info, zone_info + zone_count);
|
||||
|
||||
if (db_mod_file_exists())
|
||||
{
|
||||
zones.emplace_back("mod", game::DB_ZONE_COMMON | game::DB_ZONE_CUSTOM, 0);
|
||||
}
|
||||
|
||||
game::DB_LoadXAssets(zones.data(), static_cast<unsigned int>(zones.size()), sync_mode);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_using_mods()
|
||||
{
|
||||
return (*dvars::fs_gameDirVar) && *(*dvars::fs_gameDirVar)->current.string;
|
||||
}
|
||||
|
||||
bool db_mod_file_exists()
|
||||
{
|
||||
if (!*(*dvars::fs_gameDirVar)->current.string)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char filename[MAX_PATH]{};
|
||||
db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(filename), filename);
|
||||
|
||||
if (auto zone_file = game::Sys_OpenFileReliable(filename); zone_file != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
::CloseHandle(zone_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
static_assert(sizeof(game::Sys_File) == 8);
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
dvars::fs_gameDirVar = reinterpret_cast<game::dvar_t**>(SELECT_VALUE(0x14A6A7D98, 0x14B20EB48));
|
||||
|
||||
// Remove DVAR_INIT from fs_game
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x14036137F + 2, 0x1404AE4CB + 2), SELECT_VALUE(game::DVAR_FLAG_NONE, game::DVAR_FLAG_SERVERINFO));
|
||||
|
||||
utils::hook::inject(SELECT_VALUE(0x140361391 + 3, 0x1404AE4D6 + 3), &fs_game_dir_domain_func);
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::nop(0x140271B54, 15);
|
||||
utils::hook::jump(0x140271B54, skip_extra_zones_stub, true);
|
||||
|
||||
// Add custom zone paths
|
||||
sys_create_file_hook.create(game::Sys_CreateFile, sys_create_file_stub);
|
||||
|
||||
// Load mod.ff
|
||||
utils::hook::call(0x1405A562A, db_load_x_assets_stub); // R_LoadGraphicsAssets According to myself but I don't remember where I got it from
|
||||
|
||||
command::add("loadmod", [](const command::params& params) -> void
|
||||
{
|
||||
if (params.size() != 2)
|
||||
{
|
||||
console::info("USAGE: %s \"mods/<mod name>\"", params.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
std::string mod_name = utils::string::to_lower(params.get(1));
|
||||
|
||||
if (!mod_name.empty() && !mod_name.starts_with("mods/"))
|
||||
{
|
||||
mod_name = "mods/" + mod_name;
|
||||
}
|
||||
|
||||
// change fs_game if needed
|
||||
if (mod_name != (*dvars::fs_gameDirVar)->current.string)
|
||||
{
|
||||
game::Dvar_SetString((*dvars::fs_gameDirVar), mod_name.c_str());
|
||||
command::execute("vid_restart\n");
|
||||
}
|
||||
});
|
||||
|
||||
command::add("unloadmod", []([[maybe_unused]] const command::params& params) -> void
|
||||
{
|
||||
if (*dvars::fs_gameDirVar == nullptr || *(*dvars::fs_gameDirVar)->current.string == '\0')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
game::Dvar_SetString(*dvars::fs_gameDirVar, "");
|
||||
command::execute("vid_restart\n");
|
||||
});
|
||||
|
||||
// TODO: without a way to monitor all the ways fs_game can be changed there is no way to detect when we
|
||||
// should unregister the path from the internal filesystem we use
|
||||
// HINT: It could be done in fs_game_dir_domain_func, but I haven't tested if that's the best place to monitor for changes and register/unregister the mods folder
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(mods::component)
|
7
src/client/component/mods.hpp
Normal file
7
src/client/component/mods.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace mods
|
||||
{
|
||||
bool is_using_mods();
|
||||
bool db_mod_file_exists();
|
||||
}
|
@ -36,13 +36,13 @@ namespace dvars
|
||||
|
||||
game::dvar_t* r_fullbright = nullptr;
|
||||
|
||||
game::dvar_t* cg_legacyCrashHandling = nullptr;
|
||||
|
||||
game::dvar_t* sv_cheats = nullptr;
|
||||
|
||||
game::dvar_t* com_developer = nullptr;
|
||||
game::dvar_t* com_developer_script = nullptr;
|
||||
|
||||
game::dvar_t** fs_gameDirVar = nullptr;
|
||||
|
||||
std::string get_dvar_string(const std::string& dvar)
|
||||
{
|
||||
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
|
||||
|
@ -35,13 +35,13 @@ namespace dvars
|
||||
|
||||
extern game::dvar_t* r_fullbright;
|
||||
|
||||
extern game::dvar_t* cg_legacyCrashHandling;
|
||||
|
||||
extern game::dvar_t* sv_cheats;
|
||||
|
||||
extern game::dvar_t* com_developer;
|
||||
extern game::dvar_t* com_developer_script;
|
||||
|
||||
extern game::dvar_t** fs_gameDirVar;
|
||||
|
||||
std::string get_dvar_string(const std::string& dvar);
|
||||
bool get_dvar_bool(const std::string& dvar);
|
||||
|
||||
|
@ -30,6 +30,13 @@ namespace game
|
||||
return !game::environment::is_sp() && *mp::virtualLobby_loaded == 1;
|
||||
}
|
||||
|
||||
HANDLE Sys_OpenFileReliable(const char* filename)
|
||||
{
|
||||
return ::CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr);
|
||||
}
|
||||
|
||||
namespace environment
|
||||
{
|
||||
launcher::mode mode = launcher::mode::none;
|
||||
|
@ -66,6 +66,8 @@ namespace game
|
||||
|
||||
[[nodiscard]] bool VirtualLobby_Loaded();
|
||||
|
||||
[[nodiscard]] HANDLE Sys_OpenFileReliable(const char* filename);
|
||||
|
||||
[[nodiscard]] bool is_headless();
|
||||
void show_error(const std::string& text, const std::string& title = "Error");
|
||||
}
|
||||
|
@ -25,6 +25,14 @@ namespace game
|
||||
typedef void(*BuiltinMethod)(scr_entref_t);
|
||||
typedef void(*BuiltinFunction)();
|
||||
|
||||
enum ControllerIndex_t
|
||||
{
|
||||
INVALID_CONTROLLER_PORT = -1,
|
||||
CONTROLLER_INDEX_0 = 0x0,
|
||||
CONTROLLER_INDEX_FIRST = 0x0,
|
||||
CONTROLLER_INDEX_COUNT = 0x1,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
VAR_UNDEFINED = 0x0,
|
||||
@ -905,6 +913,7 @@ namespace game
|
||||
DVAR_FLAG_LATCHED = 0x2,
|
||||
DVAR_FLAG_CHEAT = 0x4,
|
||||
DVAR_FLAG_REPLICATED = 0x8,
|
||||
DVAR_FLAG_SERVERINFO = 0x400,
|
||||
DVAR_FLAG_WRITE = 0x800,
|
||||
DVAR_FLAG_READ = 0x2000,
|
||||
};
|
||||
@ -923,7 +932,7 @@ namespace game
|
||||
rgb = 9 // Color without alpha
|
||||
};
|
||||
|
||||
union dvar_value
|
||||
union DvarValue
|
||||
{
|
||||
bool enabled;
|
||||
int integer;
|
||||
@ -966,9 +975,9 @@ namespace game
|
||||
unsigned int flags;
|
||||
dvar_type type;
|
||||
bool modified;
|
||||
dvar_value current;
|
||||
dvar_value latched;
|
||||
dvar_value reset;
|
||||
DvarValue current;
|
||||
DvarValue latched;
|
||||
DvarValue reset;
|
||||
dvar_limits domain;
|
||||
};
|
||||
|
||||
@ -1319,6 +1328,15 @@ namespace game
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct WeaponDef
|
||||
{};
|
||||
|
||||
struct WeaponCompleteDef
|
||||
{
|
||||
const char* szInternalName;
|
||||
WeaponDef* weapDef;
|
||||
}; // Incomplete
|
||||
|
||||
union XAssetHeader
|
||||
{
|
||||
void* data;
|
||||
@ -1443,6 +1461,31 @@ namespace game
|
||||
netProfileStream_t recieve;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DB_ZONE_COMMON = 0x1,
|
||||
DB_ZONE_UI = 0x2,
|
||||
DB_ZONE_GAME = 0x4,
|
||||
DB_ZONE_LOAD = 0x8,
|
||||
DB_ZONE_DEV = 0x10,
|
||||
DB_ZONE_BASEMAP = 0x20,
|
||||
DB_ZONE_TRANSIENT_POOL = 0x40,
|
||||
DB_ZONE_TRANSIENT_MASK = 0x40,
|
||||
DB_ZONE_CUSTOM = 0x80,
|
||||
};
|
||||
|
||||
enum FF_DIR
|
||||
{
|
||||
FFD_DEFAULT = 0x0,
|
||||
FFD_MOD_DIR = 0x1,
|
||||
FFD_USER_MAP = 0x2,
|
||||
};
|
||||
|
||||
struct Sys_File
|
||||
{
|
||||
HANDLE handle;
|
||||
};
|
||||
|
||||
namespace mp
|
||||
{
|
||||
enum
|
||||
|
@ -60,6 +60,7 @@ namespace game
|
||||
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x14017E890, 0x14026FCC0};
|
||||
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x14017E750, 0x14026FB90};
|
||||
WEAK symbol<char*(const char* filename, char* buf, int size)> DB_ReadRawFile{0x140180E30, 0x140273080};
|
||||
WEAK symbol<int(XAssetType type, void** assets, int maxCount)> DB_GetAllXAssetOfType_FastFile{0x0, 0x14026F970};
|
||||
|
||||
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140370860, 0x1404BF8B0};
|
||||
WEAK symbol<void(const dvar_t* dvar)> Dvar_ClearModified{0x140370700, 0x1404BF690};
|
||||
@ -70,7 +71,7 @@ namespace game
|
||||
WEAK symbol<void(const dvar_t* dvar, const char* string)> Dvar_SetString{0x140373DE0, 0x1404C3610};
|
||||
WEAK symbol<void(const dvar_t* dvar, bool value)> Dvar_SetBool{0x0, 0x1404C1F30};
|
||||
WEAK symbol<void(const char*, const char*, DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x1403737D0, 0x1404C2E40};
|
||||
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x140374E10, 0x1404C47B0};
|
||||
WEAK symbol<const char*(dvar_t* dvar, DvarValue value)> Dvar_ValueToString{0x140374E10, 0x1404C47B0};
|
||||
|
||||
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned int flags)> Dvar_RegisterBool{0x140371850, 0x1404C0BE0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char** valueList, int defaultIndex, unsigned int flags)> Dvar_RegisterEnum{0x140371B30, 0x1404C0EC0};
|
||||
@ -98,13 +99,13 @@ namespace game
|
||||
WEAK symbol<unsigned int(unsigned int, unsigned int)> GetVariable{0x0, 0x1403F3730};
|
||||
|
||||
WEAK symbol<void()> G_Glass_Update{0x14021D540, 0x1402EDEE0};
|
||||
|
||||
WEAK symbol<int(int clientNum)> G_GetClientScore{0, 0x1402F6AB0};
|
||||
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x140274590, 0x14033FF60};
|
||||
WEAK symbol<int(playerState_s* ps, unsigned int weapon, int dualWield, int startInAltMode, int, int, int, char)> G_GivePlayerWeapon{0x1402749B0, 0x140340470};
|
||||
WEAK symbol<void(playerState_s* ps, unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x1402217F0, 0x1402F22B0};
|
||||
WEAK symbol<void(int clientNum, unsigned int weapon)> G_SelectWeapon{0x140275380, 0x140340D50};
|
||||
WEAK symbol<int(playerState_s* ps, unsigned int weapon)> G_TakePlayerWeapon{0x1402754E0, 0x1403411D0};
|
||||
WEAK symbol<void()> G_SetupLevelWeaponDef{0x0, 0x140340DE0};
|
||||
|
||||
WEAK symbol<char*(char* string)> I_CleanStr{0x140379010, 0x1404C99A0};
|
||||
|
||||
@ -113,6 +114,7 @@ namespace game
|
||||
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x14013F380, 0x140207C50};
|
||||
|
||||
WEAK symbol<unsigned int(int)> Live_SyncOnlineDataFlags{0x1404459A0, 0x140562830};
|
||||
WEAK symbol<void(int localControllerIndex)> LiveStorage_StatsWriteNotNeeded{0x1402F51F0, 0x1403C3CD0};
|
||||
|
||||
WEAK symbol<void(int localClientNum, const char* menuName, int isPopup, int isModal, unsigned int isExclusive)> LUI_OpenMenu{0, 0x14048E450};
|
||||
WEAK symbol<void()> LUI_EnterCriticalSection{0, 0x1400D2B10};
|
||||
@ -226,6 +228,8 @@ namespace game
|
||||
|
||||
WEAK symbol<void(const char* pszCommand, char* pszBuffer, int iBufferSize)> MSG_WriteReliableCommandToBuffer{0x0, 0x1403E1090};
|
||||
|
||||
WEAK symbol<int(const char* s0, const char* s1, int n)> I_strnicmp{0x1403793E0, 0x1404C9E90};
|
||||
|
||||
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14059C5C0, 0x1406FD930};
|
||||
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x14059CD00, 0x1406FE070};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user