From 3b2a59a9444653533fd831024c2b9040952b4f98 Mon Sep 17 00:00:00 2001 From: Clayton Gilmer Date: Fri, 15 Apr 2022 12:54:49 -0400 Subject: [PATCH 1/4] initial draft of database component --- src/client/component/database.cpp | 58 +++++++++++++++++++++++++++++++ src/client/game/dvars.cpp | 7 ++++ src/client/game/dvars.hpp | 1 + src/client/game/structs.hpp | 39 +++++++++++++++++++++ src/client/game/symbols.hpp | 1 + 5 files changed, 106 insertions(+) create mode 100644 src/client/component/database.cpp diff --git a/src/client/component/database.cpp b/src/client/component/database.cpp new file mode 100644 index 00000000..bd47c246 --- /dev/null +++ b/src/client/component/database.cpp @@ -0,0 +1,58 @@ +#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 sub_140272EC0_hook; + + game::DB_FileSysInterface* sub_140272EC0_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 + }; + + int default_value = utils::flags::has_flag("disk") ? 1 : 0; + 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 + } + + sub_140272EC0_hook.create(0x140272EC0, sub_140272EC0_stub); + } + }; +} + +REGISTER_COMPONENT(database::component) diff --git a/src/client/game/dvars.cpp b/src/client/game/dvars.cpp index f02f8d9f..a76d9cda 100644 --- a/src/client/game/dvars.cpp +++ b/src/client/game/dvars.cpp @@ -2238,6 +2238,13 @@ namespace dvars return game::Dvar_RegisterBool(hash, "", value, flags); } + game::dvar_t* register_enum(const std::string& name, const char** valueList, int defaultIndex, game::DvarFlags 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) { diff --git a/src/client/game/dvars.hpp b/src/client/game/dvars.hpp index 6a3a4854..b0ebc2ba 100644 --- a/src/client/game/dvars.hpp +++ b/src/client/game/dvars.hpp @@ -31,6 +31,7 @@ namespace dvars 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_enum(const std::string& name, const char** valueList, int defaultIndex, 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); } diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 8c3017c2..0d33a298 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1079,6 +1079,45 @@ 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) + struct DB_FileSysInterface_vtbl + { + DB_IFileSysFile* (__fastcall* OpenFile)(DB_FileSysInterface* _this, Sys_Folder folder, const char* filename); + FileSysResult (__fastcall* StartRead)(DB_FileSysInterface* _this, DB_IFileSysFile* handle, unsigned __int64 /* idk */, unsigned __int64 offset, unsigned __int64 size); + FileSysResult (__fastcall* NumberOfBytesRead)(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; + }; + namespace hks { struct GenericChunkHeader diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 6a885aa0..4e8a6cc8 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -43,6 +43,7 @@ namespace game 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}; From 91e25f97ac9f44eedded4627fc5ffb70d2e61d60 Mon Sep 17 00:00:00 2001 From: Clayton Gilmer Date: Mon, 2 May 2022 19:00:08 -0400 Subject: [PATCH 2/4] fix up everything --- src/client/component/database.cpp | 14 ++++++-------- src/client/game/dvars.cpp | 10 +++++----- src/client/game/dvars.hpp | 10 +++++----- src/client/game/structs.hpp | 13 ++++++++++--- src/client/game/symbols.hpp | 5 +++++ 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/client/component/database.cpp b/src/client/component/database.cpp index bd47c246..16a625a3 100644 --- a/src/client/component/database.cpp +++ b/src/client/component/database.cpp @@ -4,7 +4,7 @@ #include "game/dvars.hpp" #include "game/game.hpp" -#include +#include #include namespace database @@ -12,10 +12,9 @@ namespace database namespace { game::dvar_t* db_filesysImpl = nullptr; + utils::hook::detour db_fsinitialize_hook; - utils::hook::detour sub_140272EC0_hook; - - game::DB_FileSysInterface* sub_140272EC0_stub() + game::DB_FileSysInterface* db_fsinitialize_stub() { switch (db_filesysImpl->current.integer) { @@ -27,8 +26,6 @@ namespace database return nullptr; // this should not happen } } - - } class component final : public component_interface @@ -42,15 +39,16 @@ namespace database nullptr }; - int default_value = utils::flags::has_flag("disk") ? 1 : 0; + int default_value = utils::io::directory_exists("Data/data") ? 0 : 1; 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) } - sub_140272EC0_hook.create(0x140272EC0, sub_140272EC0_stub); + db_fsinitialize_hook.create(game::DB_FSInitialize, db_fsinitialize_stub); } }; } diff --git a/src/client/game/dvars.cpp b/src/client/game/dvars.cpp index a76d9cda..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,14 +2231,14 @@ 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, game::DvarFlags 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()); @@ -2246,7 +2246,7 @@ namespace dvars } 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()); @@ -2254,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 b0ebc2ba..37b91eae 100644 --- a/src/client/game/dvars.hpp +++ b/src/client/game/dvars.hpp @@ -29,9 +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_enum(const std::string& name, const char** valueList, int defaultIndex, 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 0d33a298..a449cfcf 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1102,12 +1102,12 @@ namespace game struct DB_FileSysInterface; - // this is a best guess, interface doesn't match up exactly w/other games (IW8) + // 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* StartRead)(DB_FileSysInterface* _this, DB_IFileSysFile* handle, unsigned __int64 /* idk */, unsigned __int64 offset, unsigned __int64 size); - FileSysResult (__fastcall* NumberOfBytesRead)(DB_FileSysInterface* _this, DB_IFileSysFile* handle, unsigned __int64* bytesRead); + 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); @@ -1118,6 +1118,13 @@ namespace game DB_FileSysInterface_vtbl* vftbl; }; + __declspec(align(8)) struct DiskFile + { + DWORD status; + HANDLE handle; + _OVERLAPPED overlapped; + }; + namespace hks { struct GenericChunkHeader diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 4e8a6cc8..07a7e3e7 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -37,6 +37,7 @@ 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}; @@ -127,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}; @@ -163,6 +165,7 @@ namespace game WEAK symbol hWnd{0x14CCF81C0}; WEAK symbol g_assetNames{0x140BEF280}; + WEAK symbol g_poolSize{0x140BF2E40}; WEAK symbol g_entities{0x1452DDDA0}; @@ -191,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}; From afdde41a1bf7dc4b7e8d6ff80af7842511d25ff5 Mon Sep 17 00:00:00 2001 From: Clayton Gilmer Date: Mon, 2 May 2022 19:05:32 -0400 Subject: [PATCH 3/4] minor change related to how default is set --- src/client/component/database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/component/database.cpp b/src/client/component/database.cpp index 16a625a3..37300a2a 100644 --- a/src/client/component/database.cpp +++ b/src/client/component/database.cpp @@ -39,7 +39,7 @@ namespace database nullptr }; - int default_value = utils::io::directory_exists("Data/data") ? 0 : 1; + int default_value = (utils::io::directory_exists("Data/data") && utils::io::directory_exists("Data/config") && utils::io::directory_exists("Data/indices")) ? 0 : 1; db_filesysImpl = dvars::register_enum("db_filesysImpl", values, default_value, game::DVAR_FLAG_READ); if (default_value == 1) From 97d565a6e1a834bae0f3f2b3737798cedd171d41 Mon Sep 17 00:00:00 2001 From: Clayton Gilmer Date: Mon, 2 May 2022 19:41:05 -0400 Subject: [PATCH 4/4] make requested changes --- src/client/component/database.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/client/component/database.cpp b/src/client/component/database.cpp index 37300a2a..14647371 100644 --- a/src/client/component/database.cpp +++ b/src/client/component/database.cpp @@ -12,9 +12,9 @@ namespace database namespace { game::dvar_t* db_filesysImpl = nullptr; - utils::hook::detour db_fsinitialize_hook; + utils::hook::detour db_fs_initialize_hook; - game::DB_FileSysInterface* db_fsinitialize_stub() + game::DB_FileSysInterface* db_fs_initialize_stub() { switch (db_filesysImpl->current.integer) { @@ -33,13 +33,17 @@ namespace database public: void post_unpack() override { - static const char* values[] = { + static const char* values[] = + { "BnetTACTVFSManager", // (load files from CASC) "DiskFS", // (load files from disk) nullptr }; - int default_value = (utils::io::directory_exists("Data/data") && utils::io::directory_exists("Data/config") && utils::io::directory_exists("Data/indices")) ? 0 : 1; + 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) @@ -48,7 +52,7 @@ namespace database utils::hook::nop(0x14071AF83, 45); // Skip setting Bink file OS callbacks (not necessary since we're loading from disk) } - db_fsinitialize_hook.create(game::DB_FSInitialize, db_fsinitialize_stub); + db_fs_initialize_hook.create(game::DB_FSInitialize, db_fs_initialize_stub); } }; }