diff --git a/src/client/component/database.cpp b/src/client/component/database.cpp new file mode 100644 index 00000000..14647371 --- /dev/null +++ b/src/client/component/database.cpp @@ -0,0 +1,60 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/dvars.hpp" +#include "game/game.hpp" + +#include +#include + +namespace database +{ + namespace + { + game::dvar_t* db_filesysImpl = nullptr; + utils::hook::detour db_fs_initialize_hook; + + game::DB_FileSysInterface* db_fs_initialize_stub() + { + switch (db_filesysImpl->current.integer) + { + case 0: + return reinterpret_cast(0x140BE82F8); // ptr to vtable of BnetTACTVFSManager (implements DB_FileSysInterface) + case 1: + return reinterpret_cast(0x140BEFDC0); // ptr to vtable of DiskFS (implements DB_FileSysInterface) + default: + return nullptr; // this should not happen + } + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + static const char* values[] = + { + "BnetTACTVFSManager", // (load files from CASC) + "DiskFS", // (load files from disk) + nullptr + }; + + const auto default_value = static_cast(!utils::io::directory_exists("Data/data") + || !utils::io::directory_exists("Data/config") + || !utils::io::directory_exists("Data/indices")); + + db_filesysImpl = dvars::register_enum("db_filesysImpl", values, default_value, game::DVAR_FLAG_READ); + + if (default_value == 1) + { + utils::hook::nop(0x1405A4868, 22); // TACT related stuff that's pointless if we're using DiskFS + utils::hook::nop(0x14071AF83, 45); // Skip setting Bink file OS callbacks (not necessary since we're loading from disk) + } + + db_fs_initialize_hook.create(game::DB_FSInitialize, db_fs_initialize_stub); + } + }; +} + +REGISTER_COMPONENT(database::component) diff --git a/src/client/game/dvars.cpp b/src/client/game/dvars.cpp index f02f8d9f..488b5b3e 100644 --- a/src/client/game/dvars.cpp +++ b/src/client/game/dvars.cpp @@ -2223,7 +2223,7 @@ namespace dvars }; game::dvar_t* register_int(const std::string& name, int value, int min, int max, - game::DvarFlags flags) + unsigned int flags) { dvar_list.insert(name); const auto hash = game::generateHashValue(name.data()); @@ -2231,15 +2231,22 @@ namespace dvars } game::dvar_t* register_bool(const std::string& name, bool value, - game::DvarFlags flags) + unsigned int flags) { dvar_list.insert(name); const auto hash = game::generateHashValue(name.data()); return game::Dvar_RegisterBool(hash, "", value, flags); } + game::dvar_t* register_enum(const std::string& name, const char** valueList, int defaultIndex, unsigned int flags) + { + dvar_list.insert(name); + const auto hash = game::generateHashValue(name.data()); + return game::Dvar_RegisterEnum(hash, "", valueList, defaultIndex, flags); + } + game::dvar_t* register_float(const std::string& name, float value, float min, - float max, game::DvarFlags flags) + float max, unsigned int flags) { dvar_list.insert(name); const auto hash = game::generateHashValue(name.data()); @@ -2247,7 +2254,7 @@ namespace dvars } game::dvar_t* register_vec4(const std::string& name, float x, float y, float z, - float w, float min, float max, game::DvarFlags flags) + float w, float min, float max, unsigned int flags) { dvar_list.insert(name); const auto hash = game::generateHashValue(name.data()); diff --git a/src/client/game/dvars.hpp b/src/client/game/dvars.hpp index 6a3a4854..37b91eae 100644 --- a/src/client/game/dvars.hpp +++ b/src/client/game/dvars.hpp @@ -29,8 +29,9 @@ namespace dvars std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain); std::string dvar_get_domain(const game::dvar_type type, const game::dvar_limits& domain); - game::dvar_t* register_int(const std::string& name, int value, int min, int max, game::DvarFlags flags); - game::dvar_t* register_bool(const std::string& name, bool value, game::DvarFlags flags); - game::dvar_t* register_float(const std::string& name, float value, float min, float max, game::DvarFlags flags); - game::dvar_t* register_vec4(const std::string& name, float x, float y, float z, float w, float min, float max, game::DvarFlags flags); + game::dvar_t* register_int(const std::string& name, int value, int min, int max, unsigned int flags); + game::dvar_t* register_bool(const std::string& name, bool value, unsigned int flags); + game::dvar_t* register_enum(const std::string& name, const char** valueList, int defaultIndex, unsigned int flags); + game::dvar_t* register_float(const std::string& name, float value, float min, float max, unsigned int flags); + game::dvar_t* register_vec4(const std::string& name, float x, float y, float z, float w, float min, float max, unsigned int flags); } diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index a9c5d12b..b05e5a50 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1079,6 +1079,52 @@ namespace game bool startsolid; // Confirmed in PM_JitterPoint }; + enum Sys_Folder : std::int32_t + { + SF_ZONE = 0x0, + SF_ZONE_LOC = 0x1, + SF_VIDEO = 0x2, + SF_VIDEO_LOC = 0x3, + SF_PAKFILE = 0x4, + SF_PAKFILE_LOC = 0x5, + SF_ZONE_REGION = 0x6, + SF_COUNT = 0x7, + }; + + enum FileSysResult : std::int32_t + { + FILESYSRESULT_SUCCESS = 0x0, + FILESYSRESULT_EOF = 0x1, + FILESYSRESULT_ERROR = 0x2, + }; + + struct DB_IFileSysFile; + + struct DB_FileSysInterface; + + // this is a best guess, interface doesn't match up exactly w/other games (IW8, T9) + struct DB_FileSysInterface_vtbl + { + DB_IFileSysFile* (__fastcall* OpenFile)(DB_FileSysInterface* _this, Sys_Folder folder, const char* filename); + FileSysResult (__fastcall* Read)(DB_FileSysInterface* _this, DB_IFileSysFile* handle, unsigned __int64 offset, unsigned __int64 size, void* dest); + FileSysResult (__fastcall* Tell)(DB_FileSysInterface* _this, DB_IFileSysFile* handle, unsigned __int64* bytesRead); + __int64 (__fastcall* Size)(DB_FileSysInterface* _this, DB_IFileSysFile* handle); + void (__fastcall* Close)(DB_FileSysInterface* _this, DB_IFileSysFile* handle); + bool (__fastcall* Exists)(DB_FileSysInterface* _this, Sys_Folder folder, const char* filename); + }; + + struct DB_FileSysInterface + { + DB_FileSysInterface_vtbl* vftbl; + }; + + __declspec(align(8)) struct DiskFile + { + DWORD status; + HANDLE handle; + _OVERLAPPED overlapped; + }; + namespace hks { struct lua_State; diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 1bfdbc53..142d5c7b 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -37,12 +37,14 @@ namespace game WEAK symbol DB_GetXAssetName{0x1403E4090}; WEAK symbol DB_LoadXAssets{0x140414FF0}; WEAK symbol DB_FindXAssetHeader{0x140412F60}; + WEAK symbol DB_FSInitialize{0x140272EC0}; WEAK symbol DB_GetRawFileLen{0x140413D80}; WEAK symbol DB_GetRawBuffer{0x140413C40}; WEAK symbol Dvar_FindVar{0x140618F90}; WEAK symbol Dvar_GetCombinedString{0x1405A75D0}; WEAK symbol Dvar_RegisterBool{0x140617BB0}; + WEAK symbol Dvar_RegisterEnum{0x140617E90}; WEAK symbol Dvar_RegisterInt{0x140618090}; WEAK symbol Dvar_RegisterFloat{0x140617F80}; @@ -126,6 +128,7 @@ namespace game WEAK symbol R_WaitWorkerCmds{0x140794330}; WEAK symbol R_AddDObjToScene{0x140775C40}; + WEAK symbol R_Cinematic_SysIO_BinkRead{0x1407191B0}; WEAK symbol ScrPlace_GetViewPlacement{0x1403E16A0}; WEAK symbol ScrPlace_GetView{0x1403E1660}; @@ -162,6 +165,7 @@ namespace game WEAK symbol hWnd{0x14CCF81C0}; WEAK symbol g_assetNames{0x140BEF280}; + WEAK symbol g_poolSize{0x140BF2E40}; WEAK symbol g_entities{0x1452DDDA0}; @@ -190,6 +194,8 @@ namespace game WEAK symbol scr_VmPub{0x14BA9EE40}; WEAK symbol scr_function_stack{0x14BAA93C0}; + WEAK symbol g_fileSystem{0x1420B27E8}; + namespace hks { WEAK symbol lua_state{0x1419D83E8};