From 7f25ee41cc39771cdccc6a46a14f6b56c2ecbdcf Mon Sep 17 00:00:00 2001 From: "Quentin E. / iDeath" Date: Wed, 19 Oct 2022 00:30:32 +0200 Subject: [PATCH] Added local caching for peds, vehicles and weapons (#457) --- .gitmodules | 4 + BigBaseV2/src/function_types.hpp | 8 +- BigBaseV2/src/gta/fidevice.cpp | 263 +++++++ BigBaseV2/src/gta/fidevice.hpp | 286 +++++++ BigBaseV2/src/gta/fwddec.hpp | 5 +- BigBaseV2/src/gta/gxt2.hpp | 21 + BigBaseV2/src/pointers.cpp | 51 ++ BigBaseV2/src/pointers.hpp | 12 + .../src/services/gta_data/cache_file.cpp | 84 ++ .../src/services/gta_data/cache_file.hpp | 71 ++ .../services/gta_data/gta_data_service.cpp | 725 +++++++++--------- .../services/gta_data/gta_data_service.hpp | 95 ++- BigBaseV2/src/services/gta_data/ped_item.cpp | 20 - BigBaseV2/src/services/gta_data/ped_item.hpp | 17 +- .../src/services/gta_data/vehicle_item.cpp | 46 -- .../src/services/gta_data/vehicle_item.hpp | 21 +- .../src/services/gta_data/weapon_item.cpp | 76 -- .../src/services/gta_data/weapon_item.hpp | 25 +- .../src/services/gta_data/yim_fipackfile.cpp | 216 ++++++ .../src/services/gta_data/yim_fipackfile.hpp | 27 + .../src/services/pickups/pickup_service.cpp | 13 +- BigBaseV2/src/util/session.hpp | 17 +- BigBaseV2/src/views/network/view_session.cpp | 8 +- BigBaseV2/src/views/self/view_weapons.cpp | 14 +- BigBaseV2/src/views/vehicle/view_pv.cpp | 10 +- .../src/views/vehicle/view_spawn_vehicle.cpp | 34 +- BigBaseV2/src/views/view.hpp | 5 + BigBaseV2/src/views/view_gta_data.cpp | 75 ++ BigBaseV2/src/views/world/view_spawn_ped.cpp | 74 +- premake5.lua | 28 +- vendor/pugixml | 1 + 31 files changed, 1714 insertions(+), 638 deletions(-) create mode 100644 BigBaseV2/src/gta/fidevice.cpp create mode 100644 BigBaseV2/src/gta/fidevice.hpp create mode 100644 BigBaseV2/src/gta/gxt2.hpp create mode 100644 BigBaseV2/src/services/gta_data/cache_file.cpp create mode 100644 BigBaseV2/src/services/gta_data/cache_file.hpp delete mode 100644 BigBaseV2/src/services/gta_data/ped_item.cpp delete mode 100644 BigBaseV2/src/services/gta_data/vehicle_item.cpp delete mode 100644 BigBaseV2/src/services/gta_data/weapon_item.cpp create mode 100644 BigBaseV2/src/services/gta_data/yim_fipackfile.cpp create mode 100644 BigBaseV2/src/services/gta_data/yim_fipackfile.hpp create mode 100644 BigBaseV2/src/views/view_gta_data.cpp create mode 160000 vendor/pugixml diff --git a/.gitmodules b/.gitmodules index 92bb0b8a..3c65ef9a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,7 @@ path = vendor/ImGui url = https://github.com/Yimura/gtav-imgui.git ignore = dirty +[submodule "vendor/pugixml"] + path = vendor/pugixml + url = https://github.com/zeux/pugixml.git + ignore = dirty \ No newline at end of file diff --git a/BigBaseV2/src/function_types.hpp b/BigBaseV2/src/function_types.hpp index 92255b45..5d73886d 100644 --- a/BigBaseV2/src/function_types.hpp +++ b/BigBaseV2/src/function_types.hpp @@ -53,4 +53,10 @@ namespace big::functions //Sync signatures END using reset_network_complaints = void(*)(CNetComplaintMgr* mgr); -} \ No newline at end of file + + using fidevice_get_device = rage::fiDevice*(*)(const char* path, bool allow_root); + using fipackfile_ctor = rage::fiPackfile*(*)(rage::fiPackfile* this_); + using fipackfile_open_archive = bool(*)(rage::fiPackfile* this_, const char* archive, bool b_true, int type, intptr_t very_false); + using fipackfile_mount = bool(*)(rage::fiPackfile* this_, const char* mount_point); + using fipackfile_unmount = bool(*)(const char* mount_point); +} diff --git a/BigBaseV2/src/gta/fidevice.cpp b/BigBaseV2/src/gta/fidevice.cpp new file mode 100644 index 00000000..4aa22670 --- /dev/null +++ b/BigBaseV2/src/gta/fidevice.cpp @@ -0,0 +1,263 @@ +#include "fidevice.hpp" +#include "pointers.hpp" + +namespace rage +{ + #define PURECALL() LOG(FATAL) << "pure fiDevice call (" __FUNCTION__ ")"; return 0 + + fiDeviceImplemented::fiDeviceImplemented() + { + } + + fiDeviceImplemented::~fiDeviceImplemented() + { + + } + + fiDevice::~fiDevice() + { + + } + + uint64_t fiDeviceImplemented::Open(const char* fileName, bool) { PURECALL(); } + + uint64_t fiDeviceImplemented::OpenBulk(const char* fileName, uint64_t* ptr) + { + PURECALL(); + } + + uint64_t fiDeviceImplemented::OpenBulkWrap(const char* fileName, uint64_t* ptr, void* a3) + { + PURECALL(); + } + + uint64_t fiDeviceImplemented::CreateLocal(const char* fileName) + { + PURECALL(); + } + + uint64_t fiDeviceImplemented::Create(const char*) + { + PURECALL(); + } + + uint32_t fiDeviceImplemented::Read(uint64_t handle, void* buffer, uint32_t toRead) + { + PURECALL(); + } + + uint32_t fiDeviceImplemented::ReadBulk(uint64_t handle, uint64_t ptr, void* buffer, uint32_t toRead) + { + PURECALL(); + } + + uint32_t fiDeviceImplemented::WriteBulk(uint64_t, int, int, int, int) + { + PURECALL(); + } + + uint32_t fiDeviceImplemented::Write(uint64_t, void*, int) + { + PURECALL(); + } + + uint32_t fiDeviceImplemented::Seek(uint64_t handle, int32_t distance, uint32_t method) + { + PURECALL(); + } + + uint64_t fiDeviceImplemented::SeekLong(uint64_t handle, int64_t distance, uint32_t method) + { + PURECALL(); + } + + int32_t fiDeviceImplemented::Close(uint64_t handle) + { + PURECALL(); + } + + int32_t fiDeviceImplemented::CloseBulk(uint64_t handle) + { + PURECALL(); + } + + int fiDeviceImplemented::GetFileLength(uint64_t handle) + { + PURECALL(); + } + + int fiDeviceImplemented::m_40(int) + { + PURECALL(); + } + + bool fiDeviceImplemented::RemoveFile(const char* file) + { + PURECALL(); + } + + int fiDeviceImplemented::RenameFile(const char* from, const char* to) + { + PURECALL(); + } + + int fiDeviceImplemented::CreateDirectory(const char* dir) + { + PURECALL(); + } + + int fiDeviceImplemented::RemoveDirectory(const char* dir) + { + PURECALL(); + } + + uint64_t fiDeviceImplemented::GetFileLengthUInt64(uint64_t file) + { + PURECALL(); + } + + uint64_t fiDeviceImplemented::GetFileLengthLong(const char* fileName) + { + PURECALL(); + } + + uint64_t fiDeviceImplemented::GetFileTime(const char* file) + { + PURECALL(); + } + + bool fiDeviceImplemented::SetFileTime(const char* file, FILETIME fileTime) + { + PURECALL(); + } + + uint64_t fiDeviceImplemented::FindFirst(const char* path, fiFindData* findData) + { + PURECALL(); + } + + bool fiDeviceImplemented::FindNext(uint64_t handle, fiFindData* findData) + { + PURECALL(); + } + + int fiDeviceImplemented::FindClose(uint64_t handle) + { + PURECALL(); + } + + rage::fiDevice* fiDeviceImplemented::GetUnkDevice() + { + PURECALL(); + } + + void* fiDeviceImplemented::m_xy(void* a1, int a2, void* a3) + { + PURECALL(); + } + + bool fiDeviceImplemented::Truncate(uint64_t handle) + { + PURECALL(); + } + + uint32_t fiDeviceImplemented::GetFileAttributes(const char* path) + { + PURECALL(); + } + + bool fiDeviceImplemented::m_xz() + { + PURECALL(); + } + + bool fiDeviceImplemented::SetFileAttributes(const char* file, uint32_t FileAttributes) + { + PURECALL(); + } + + void fiDeviceImplemented::m_xx() + { + return; + } + + bool fiDeviceImplemented::ReadFull(uint64_t handle, void* buffer, uint32_t length) + { + PURECALL(); + } + + bool fiDeviceImplemented::WriteFull(uint64_t handle, void* buffer, uint32_t length) + { + PURECALL(); + } + + int32_t fiDeviceImplemented::GetResourceVersion(const char* fileName, ResourceFlags* flags) + { + PURECALL(); + } + + int fiDeviceImplemented::m_yx() + { + PURECALL(); + } + + int fiDeviceImplemented::m_yy() + { + PURECALL(); + } + + int fiDeviceImplemented::m_yz(void*) + { + PURECALL(); + } + + int fiDeviceImplemented::m_zx(void*) + { + PURECALL(); + } + + bool fiDeviceImplemented::IsCollection() + { + PURECALL(); + } + + bool fiDeviceImplemented::m_addedIn1290() + { + PURECALL(); + } + + fiDevice* fiDeviceImplemented::GetCollection() + { + PURECALL(); + } + + bool fiDeviceImplemented::m_ax() + { + PURECALL(); + } + + int fiDeviceImplemented::GetCollectionId() + { + PURECALL(); + } + + const char* fiDeviceImplemented::GetName() + { + PURECALL(); + } + + fiPackfile::fiPackfile() + { + big::g_pointers->m_fipackfile_ctor(this); + } + + bool fiPackfile::OpenPackfile(const char* archive, bool b_true, int type, intptr_t very_false) + { + return big::g_pointers->m_fipackfile_open_archive(this, archive, b_true, type, very_false); + } + + bool fiPackfile::Mount(const char* mount_point) + { + return big::g_pointers->m_fipackfile_mount(this, mount_point); + } +} diff --git a/BigBaseV2/src/gta/fidevice.hpp b/BigBaseV2/src/gta/fidevice.hpp new file mode 100644 index 00000000..7be0f682 --- /dev/null +++ b/BigBaseV2/src/gta/fidevice.hpp @@ -0,0 +1,286 @@ +#pragma once + +#include "sysMemAllocator.hpp" +#include "pointers.hpp" + +#include + +namespace rage +{ + struct fiFindData + { + char fileName[256]; + uint64_t fileSize; + FILETIME lastWriteTime; + DWORD fileAttributes; + }; + + struct ResourceFlags + { + // TODO: figure out which is physical and which is virtual + uint32_t flag1; + uint32_t flag2; + }; + + // since Payne, RAGE devices are thread-safe (might not apply to V?) + // in V, RAGE devices use UTF-8 + class fiDevice + { + public: + static inline fiDevice* GetDevice(const char* path, bool allowRoot) + { + return big::g_pointers->m_fidevice_get_device(path, allowRoot); + } + + static bool MountGlobal(const char* mountPoint, fiDevice* device, bool allowRoot); + + static void Unmount(const char* rootPath); + + static void Unmount(fiDevice const& device); + + public: + virtual ~fiDevice() = 0; + + virtual uint64_t Open(const char* fileName, bool readOnly) = 0; + + virtual uint64_t OpenBulk(const char* fileName, uint64_t* ptr) = 0; + + virtual uint64_t OpenBulkWrap(const char* fileName, uint64_t* ptr, void*) = 0; + + virtual uint64_t CreateLocal(const char* fileName) = 0; + + virtual uint64_t Create(const char* fileName) = 0; + + virtual uint32_t Read(uint64_t handle, void* buffer, uint32_t toRead) = 0; + + virtual uint32_t ReadBulk(uint64_t handle, uint64_t ptr, void* buffer, uint32_t toRead) = 0; + + virtual uint32_t WriteBulk(uint64_t, int, int, int, int) = 0; + + virtual uint32_t Write(uint64_t, void*, int) = 0; + + virtual uint32_t Seek(uint64_t handle, int32_t distance, uint32_t method) = 0; + + virtual uint64_t SeekLong(uint64_t handle, int64_t distance, uint32_t method) = 0; + + virtual int32_t Close(uint64_t handle) = 0; + + virtual int32_t CloseBulk(uint64_t handle) = 0; + + virtual int GetFileLength(uint64_t handle) = 0; + + virtual uint64_t GetFileLengthUInt64(uint64_t handle) = 0; + + // dummy! + virtual int m_40(int) = 0; + + virtual bool RemoveFile(const char* file) = 0; + virtual int RenameFile(const char* from, const char* to) = 0; + virtual int CreateDirectory(const char* dir) = 0; + + virtual int RemoveDirectory(const char* dir) = 0; + + virtual void m_xx() = 0; + + virtual uint64_t GetFileLengthLong(const char* fileName) = 0; + + virtual uint64_t GetFileTime(const char* file) = 0; + virtual bool SetFileTime(const char* file, FILETIME fileTime) = 0; + + virtual uint64_t FindFirst(const char* path, fiFindData* findData) = 0; + virtual bool FindNext(uint64_t handle, fiFindData* findData) = 0; + virtual int FindClose(uint64_t handle) = 0; + + virtual rage::fiDevice* GetUnkDevice() = 0; + + virtual void* m_xy(void*, int, void*) = 0; + + virtual bool Truncate(uint64_t handle) = 0; + + virtual uint32_t GetFileAttributes(const char* path) = 0; + + virtual bool m_xz() = 0; + + virtual bool SetFileAttributes(const char* file, uint32_t FileAttributes) = 0; + + virtual int m_yx() = 0; + + // read even if read() returns less than length + virtual bool ReadFull(uint64_t handle, void* buffer, uint32_t length) = 0; + + virtual bool WriteFull(uint64_t handle, void* buffer, uint32_t length) = 0; + + virtual int32_t GetResourceVersion(const char* fileName, ResourceFlags* flags) = 0; + + virtual int32_t m_yy() = 0; + + virtual int32_t m_yz(void*) = 0; + + virtual int32_t m_zx(void*) = 0; // return 0x40000000 + + virtual bool IsCollection() = 0; + + virtual bool m_addedIn1290() = 0; + + virtual fiDevice* GetCollection() = 0; // return this + + virtual bool m_ax() = 0; + + virtual int32_t GetCollectionId() = 0; + + virtual const char* GetName() = 0; + }; + + class fiDeviceImplemented : public fiDevice + { + protected: + fiDeviceImplemented(); + + public: + virtual ~fiDeviceImplemented(); + + virtual uint64_t Open(const char* fileName, bool readOnly); + + virtual uint64_t OpenBulk(const char* fileName, uint64_t* ptr); + + virtual uint64_t OpenBulkWrap(const char* fileName, uint64_t* ptr, void* a3); + + virtual uint64_t CreateLocal(const char* fileName); + + virtual uint64_t Create(const char* fileName); + + virtual uint32_t Read(uint64_t handle, void* buffer, uint32_t toRead); + + virtual uint32_t ReadBulk(uint64_t handle, uint64_t ptr, void* buffer, uint32_t toRead); + + virtual uint32_t WriteBulk(uint64_t, int, int, int, int); + + virtual uint32_t Write(uint64_t, void*, int); + + virtual uint32_t Seek(uint64_t handle, int32_t distance, uint32_t method); + + virtual uint64_t SeekLong(uint64_t handle, int64_t distance, uint32_t method); + + virtual int32_t Close(uint64_t handle); + + virtual int32_t CloseBulk(uint64_t handle); + + virtual int GetFileLength(uint64_t handle); + + virtual uint64_t GetFileLengthUInt64(uint64_t handle); + + // dummy! + virtual int m_40(int); + + virtual bool RemoveFile(const char* file); + virtual int RenameFile(const char* from, const char* to); + virtual int CreateDirectory(const char* dir); + + virtual int RemoveDirectory(const char* dir); + + virtual void m_xx(); + + virtual uint64_t GetFileLengthLong(const char* fileName); + + virtual uint64_t GetFileTime(const char* file); + virtual bool SetFileTime(const char* file, FILETIME fileTime); + + virtual uint64_t FindFirst(const char* path, fiFindData* findData); + virtual bool FindNext(uint64_t handle, fiFindData* findData); + virtual int FindClose(uint64_t handle); + + virtual rage::fiDevice* GetUnkDevice(); + + virtual void* m_xy(void* a1, int a2, void* a3); + + virtual bool Truncate(uint64_t handle); + + virtual uint32_t GetFileAttributes(const char* path); + + virtual bool m_xz(); + + virtual bool SetFileAttributes(const char* file, uint32_t FileAttributes); + + virtual int m_yx(); + + // read even if read() returns less than length + virtual bool ReadFull(uint64_t handle, void* buffer, uint32_t length); + + virtual bool WriteFull(uint64_t handle, void* buffer, uint32_t length); + + virtual int32_t GetResourceVersion(const char* fileName, ResourceFlags* version); + + virtual int32_t m_yy(); + + virtual int32_t m_yz(void*); + + virtual int32_t m_zx(void*); // return 0x40000000 + + virtual bool IsCollection(); + + virtual bool m_addedIn1290(); + + virtual fiDevice* GetCollection(); // return this + + virtual bool m_ax(); + + virtual int32_t GetCollectionId(); + + virtual const char* GetName(); + }; + + class fiDeviceRelative : public fiDeviceImplemented + { + private: + char m_pad[272]; + public: + fiDeviceRelative(); + + // any RAGE path can be used; including root-relative paths + void SetPath(const char* relativeTo, rage::fiDevice* baseDevice, bool allowRoot); + + // compatibility wrapper for NY code + inline void SetPath(const char* relativeTo, bool allowRoot) + { + SetPath(relativeTo, nullptr, allowRoot); + } + + // mounts the device in the device stack + void Mount(const char* mountPoint); + }; + + class fiEncryptingDevice : public fiDeviceImplemented + { + private: + void* m_keyState; + void* m_0010; + char m_buffer[4096]; + bool m_1018; + alignas(int) char m_pad[64]; // unsure + + private: + void* AllocKeyState(const uint8_t* key); + + public: + fiEncryptingDevice(const std::array& key); + + void FreeKeyState(); + }; + + class fiPackfile : public fiDeviceImplemented + { + private: + char m_pad[368 + (0x650 - 0x590)]; + public: + fiPackfile(); + + // any RAGE path can be used; including root-relative paths + bool OpenPackfile(const char* archive, bool bTrue, int type, intptr_t veryFalse); + + // mounts the device in the device stack + bool Mount(const char* mountPoint); + + // closes the package file + void ClosePackfile(); + }; +} diff --git a/BigBaseV2/src/gta/fwddec.hpp b/BigBaseV2/src/gta/fwddec.hpp index edd9cea3..44351877 100644 --- a/BigBaseV2/src/gta/fwddec.hpp +++ b/BigBaseV2/src/gta/fwddec.hpp @@ -32,7 +32,7 @@ namespace rage class netEventMgr; class netSyncTree; - + class netObject; class netObjectMgrBase; @@ -43,6 +43,9 @@ namespace rage class fwRefAwareBase; class fwExtensibleBase; class fwArchetype; + + class fiDevice; + class fiPackfile; } class GtaThread; diff --git a/BigBaseV2/src/gta/gxt2.hpp b/BigBaseV2/src/gta/gxt2.hpp new file mode 100644 index 00000000..651c8566 --- /dev/null +++ b/BigBaseV2/src/gta/gxt2.hpp @@ -0,0 +1,21 @@ +#pragma once + +struct GXT2_metadata +{ + static constexpr auto header = "2TXG"; +}; + +#pragma pack(push, 1) +struct GXT2_key +{ + rage::joaat_t key_hash = -1; + uint32_t file_offset_to_text = -1; +}; +static_assert(sizeof(GXT2_key) == 8); +#pragma pack(pop) + +struct GXT2_entry +{ + rage::joaat_t hash = -1; + std::string text; +}; diff --git a/BigBaseV2/src/pointers.cpp b/BigBaseV2/src/pointers.cpp index 0251ec9f..6a4c9876 100644 --- a/BigBaseV2/src/pointers.cpp +++ b/BigBaseV2/src/pointers.cpp @@ -344,6 +344,57 @@ namespace big m_reset_network_complaints = ptr.add(1).rip().as(); }); + // fiDevice Get Device + main_batch.add("FDGD", "41 B8 07 00 00 00 48 8B F1 E8", [this](memory::handle ptr) + { + m_fidevice_get_device = ptr.sub(0x1F).as(); + }); + + // fiDevices + main_batch.add("FDS", "74 1B 48 8D 0D ? ? ? ? 41 8B D6", [this](memory::handle ptr) + { + m_fidevices = ptr.add(5).rip().as(); + m_fidevices_len = ptr.add(5).rip().add(8).as(); + }); + + // fiPackfile ctor + main_batch.add("FPFC", "44 89 41 28 4C 89 41 38 4C 89 41 50 48 8D", [this](memory::handle ptr) + { + m_fipackfile_ctor = ptr.sub(0x1E).as(); + m_fipackfile_instances = ptr.add(26).rip().as(); + }); + + // fiPackfile open archive + main_batch.add("FPFOA", "48 8D 68 98 48 81 EC 40 01 00 00 41 8B F9", [this](memory::handle ptr) + { + m_fipackfile_open_archive = ptr.sub(0x18).as(); + }); + + // fiPackfile mount + main_batch.add("FPFM", "84 C0 74 1D 48 85 DB 74 0F 48", [this](memory::handle ptr) + { + m_fipackfile_mount = ptr.sub(0x1E).as(); + }); + + // fiPackfile unmount + main_batch.add("FPFUM", "E8 ? ? ? ? 84 C0 74 37 80 3D", [this](memory::handle ptr) + { + m_fipackfile_unmount = ptr.add(1).rip().as(); + }); + + // fidevice unmount + main_batch.add("FPFUM", "E8 ? ? ? ? 84 C0 74 37 80 3D", [this](memory::handle ptr) + { + m_fipackfile_unmount = ptr.add(1).rip().as(); + }); + + // game version + online version + main_batch.add("GVOV", "8B C3 33 D2 C6 44 24 20", [this](memory::handle ptr) + { + m_game_version = ptr.add(0x24).rip().as(); + m_online_version = ptr.add(0x24).rip().add(0x20).as(); + }); + auto mem_region = memory::module(nullptr); main_batch.run(mem_region); diff --git a/BigBaseV2/src/pointers.hpp b/BigBaseV2/src/pointers.hpp index ce56f65c..79ab99ae 100644 --- a/BigBaseV2/src/pointers.hpp +++ b/BigBaseV2/src/pointers.hpp @@ -106,6 +106,18 @@ namespace big Network** m_network; functions::reset_network_complaints m_reset_network_complaints{}; + + functions::fidevice_get_device m_fidevice_get_device{}; + uintptr_t m_fidevices{}; + uint16_t* m_fidevices_len{}; + functions::fipackfile_ctor m_fipackfile_ctor{}; + rage::fiPackfile** m_fipackfile_instances{}; + functions::fipackfile_open_archive m_fipackfile_open_archive{}; + functions::fipackfile_mount m_fipackfile_mount{}; + functions::fipackfile_unmount m_fipackfile_unmount{}; + + const char* m_game_version; + const char* m_online_version; }; inline pointers* g_pointers{}; diff --git a/BigBaseV2/src/services/gta_data/cache_file.cpp b/BigBaseV2/src/services/gta_data/cache_file.cpp new file mode 100644 index 00000000..6b791df2 --- /dev/null +++ b/BigBaseV2/src/services/gta_data/cache_file.cpp @@ -0,0 +1,84 @@ +#include "cache_file.hpp" + +namespace big +{ + cache_file::cache_file(file cache_file, std::uint32_t cache_version) : + m_cache_file(cache_file), + m_data(nullptr), + m_cache_version(cache_version), + m_cache_header() + { + + } + + void cache_file::free() + { + m_data.reset(); + } + + bool cache_file::load() + { + if (!m_cache_file.exists()) + return false; + if (m_data) + return true; + + auto file = std::ifstream(m_cache_file.get_path(), std::ios::binary); + + file.read(reinterpret_cast(&m_cache_header), sizeof(m_cache_header)); + + m_data = std::make_unique(m_cache_header.m_data_size); + file.read(reinterpret_cast(m_data.get()), m_cache_header.m_data_size); + + file.close(); + return true; + } + + bool cache_file::write() const + { + if (!m_data) + return false; + + auto file = std::ofstream(m_cache_file.get_path(), std::ios::binary); + + file.write(reinterpret_cast(&m_cache_header), sizeof(m_cache_header)); + file.write(reinterpret_cast(m_data.get()), m_cache_header.m_data_size); + file.close(); + + return true; + } + + std::uint8_t* cache_file::data() const + { + return m_data.get(); + } + + std::uint64_t cache_file::data_size() const + { + return m_cache_header.m_data_size; + } + + bool cache_file::up_to_date(std::uint32_t game_version, float online_version) const + { + if (!m_data) + return false; + + return + m_cache_version == m_cache_header.m_cache_version && + game_version == m_cache_header.m_game_version && + online_version == m_cache_header.m_online_version; + } + + void cache_file::set_data(cache_data&& data, std::uint64_t data_size) + { + m_data.swap(data); + m_cache_header.m_data_size = data_size; + } + + void cache_file::set_header_version(std::uint32_t game_version, float online_version) + { + m_cache_header.m_cache_version = m_cache_version; + m_cache_header.m_game_version = game_version; + m_cache_header.m_online_version = online_version; + } +} \ No newline at end of file diff --git a/BigBaseV2/src/services/gta_data/cache_file.hpp b/BigBaseV2/src/services/gta_data/cache_file.hpp new file mode 100644 index 00000000..c80fe108 --- /dev/null +++ b/BigBaseV2/src/services/gta_data/cache_file.hpp @@ -0,0 +1,71 @@ +#pragma once +#include "file_manager/file.hpp" + +namespace big +{ + class cache_header final + { + public: + std::uint32_t m_cache_version; + std::uint32_t m_game_version; + float m_online_version; + std::uint64_t m_data_size; + }; + + using cache_data = std::unique_ptr; + class cache_file final + { + public: + /// + /// + /// + /// FileMgr file object + /// Internal version, use this to invalidate the cache when changing the structure of the data + cache_file(file cache_file, std::uint32_t cache_version); + + /// + /// Frees any memory used to hold the cached data. + /// + void free(); + + /// + /// Attempts to load the cache from disk + /// + /// True after successfully loading the data, false if the file didn't exist. + bool load(); + + /// + /// Writes the cache to disk + /// + /// + bool write() const; + + std::uint8_t* data() const; + std::uint64_t data_size() const; + + /// + /// Check if the cache file is up to date with the expected versions + /// + /// Current Game version + /// Current Online version + /// True if cache is up to date, false otherwise. + bool up_to_date(std::uint32_t game_version, float online_version) const; + + + void set_data(cache_data&& data, std::uint64_t data_size); + /// + /// Sets the version information of the cache header. + /// + /// Game Build + /// Online Version + void set_header_version(std::uint32_t game_version, float online_version); + + private: + file m_cache_file; + + std::uint32_t m_cache_version; + + cache_header m_cache_header; + cache_data m_data; + }; +} \ No newline at end of file diff --git a/BigBaseV2/src/services/gta_data/gta_data_service.cpp b/BigBaseV2/src/services/gta_data/gta_data_service.cpp index a2d49d3c..a1d88940 100644 --- a/BigBaseV2/src/services/gta_data/gta_data_service.cpp +++ b/BigBaseV2/src/services/gta_data/gta_data_service.cpp @@ -1,48 +1,35 @@ -#include "api/remote.hpp" -#include "file_manager.hpp" -#include "thread_pool.hpp" #include "gta_data_service.hpp" - -#define EXIST_IN_ARRAY(arr, val) (std::find(std::begin(arr), std::end(arr), val) != std::end(arr)) +#include "file_manager.hpp" +#include "fiber_pool.hpp" +#include "natives.hpp" +#include "pointers.hpp" +#include "pugixml.hpp" +#include "script.hpp" +#include "thread_pool.hpp" +#include "util/session.hpp" +#include "yim_fipackfile.hpp" namespace big { - gta_data_service::gta_data_service() + bool add_if_not_exists(string_vec& vec, std::string str) { - g_thread_pool->push([this] { - while (!g_running) - std::this_thread::sleep_for(1s); + if (std::find(vec.begin(), vec.end(), str) != vec.end()) + return true; - const std::string url_prefix = "http://github-proxy.damon.sh/DurtyFree/gta-v-data-dumps/master/"; + vec.emplace_back(std::move(str)); + return false; + } - this->load_from_file( - "./lib/vehicles.json", - "./lib/vehicles_etag.txt", - url_prefix + "vehicles.json", - >a_data_service::load_vehicles, - "Vehicle" - ); - - std::this_thread::sleep_for(1s); - - this->load_from_file( - "./lib/peds.json", - "./lib/peds_etag.txt", - url_prefix + "peds.json", - >a_data_service::load_peds, - "Ped" - ); - - std::this_thread::sleep_for(1s); - - this->load_from_file( - "./lib/weapons.json", - "./lib/weapons_etag.txt", - url_prefix + "weapons.json", - >a_data_service::load_weapons, - "Weapon" - ); - }); + gta_data_service::gta_data_service() : + m_peds_cache(g_file_manager->get_project_file("./cache/peds.bin"), 1), + m_vehicles_cache(g_file_manager->get_project_file("./cache/vehicles.bin"), 1), + m_weapons_cache(g_file_manager->get_project_file("./cache/weapons.bin"), 1), + m_update_state(eGtaDataUpdateState::IDLE) + { + if (!is_cache_up_to_date()) + m_update_state = eGtaDataUpdateState::NEEDS_UPDATE; + else + load_data(); g_gta_data_service = this; } @@ -52,343 +39,391 @@ namespace big g_gta_data_service = nullptr; } - const vehicle_item& gta_data_service::find_vehicle_by_hash(Hash hash) + bool gta_data_service::cache_needs_update() const { - int idx = -1; + return m_update_state == eGtaDataUpdateState::NEEDS_UPDATE; + } - if (m_vehicle_hash_idx_map.count(hash)) + eGtaDataUpdateState gta_data_service::state() const + { + return m_update_state; + } + + void gta_data_service::update_in_online() + { + m_update_state = eGtaDataUpdateState::WAITING_FOR_ONLINE; + + g_fiber_pool->queue_job([this] { - idx = m_vehicle_hash_idx_map[hash]; - } + session::join_type(eSessionType::SOLO); - if (idx == -1) - { - return empty_vehicle_item; - } - - return m_vehicle_item_arr[idx]; - } - - const std::vector& gta_data_service::get_vehicle_class_arr() - { - return m_vehicle_class_arr; - } - - const std::vector& gta_data_service::get_vehicle_arr() - { - return m_vehicle_item_arr; - } - - - const ped_item& gta_data_service::find_ped_by_hash(Hash hash) - { - int idx = -1; - - if (m_ped_hash_idx_map.count(hash)) - { - idx = m_ped_hash_idx_map[hash]; - } - - if (idx == -1) - { - return empty_ped_item; - } - - return m_ped_item_arr[idx]; - } - - const std::vector& gta_data_service::get_ped_type_arr() - { - return m_ped_type_arr; - } - - const std::vector& gta_data_service::get_ped_arr() - { - return m_ped_item_arr; - } - - - const weapon_item& gta_data_service::find_weapon_by_hash(Hash hash) - { - int idx = -1; - - if (m_weapon_hash_idx_map.count(hash)) - { - idx = m_weapon_hash_idx_map[hash]; - } - - if (idx == -1) - { - return empty_weapon_item; - } - - return m_weapon_item_arr[idx]; - } - - const std::vector& gta_data_service::get_weapon_type_arr() - { - return m_weapon_type_arr; - } - - const std::vector& gta_data_service::get_weapon_arr() - { - return m_weapon_item_arr; - } - - - void gta_data_service::load_from_file( - std::string file_path, std::string etag_path, std::string url, - bool(gta_data_service::* load_func)(std::filesystem::path), std::string data_name - ) { - file file_to_load(g_file_manager->get_project_file(file_path)); - file file_etag(g_file_manager->get_project_file(etag_path)); - auto file_to_load_path = file_to_load.get_path(); - auto file_etag_path = file_etag.get_path(); - - bool up_to_date = false; - - for (int retry = 0; retry < 3 && g_running; retry++) - { - LOG(INFO) << "Checking update (attempt: " << (retry + 1) << "/3): " << data_name; - - try + while (!*g_pointers->m_is_session_started) { - bool ret = remote::update_binary( - url, - file_to_load_path, - file_etag_path - ); + script::get_current()->yield(100ms); + } - if (ret) + rebuild_cache(); + }); + } + + void gta_data_service::update_now() + { + g_fiber_pool->queue_job([this] + { + rebuild_cache(); + }); + } + + // innefficient getters, don't care to fix right now + const ped_item& gta_data_service::ped_by_hash(std::uint32_t hash) + { + for (const auto& [name, ped] : m_peds) + if (rage::joaat(name) == hash) + return ped; + return gta_data_service::empty_ped; + } + + const vehicle_item& gta_data_service::vehicle_by_hash(std::uint32_t hash) + { + for (const auto& [name, veh] : m_vehicles) + if (rage::joaat(name) == hash) + return veh; + return gta_data_service::empty_vehicle; + } + + const weapon_item& gta_data_service::weapon_by_hash(std::uint32_t hash) + { + for (const auto& [name, weapon] : m_weapons) + if (rage::joaat(name) == hash) + return weapon; + return gta_data_service::empty_weapon; + } + + string_vec& gta_data_service::ped_types() + { + return m_ped_types; + } + + string_vec& gta_data_service::vehicle_classes() + { + return m_vehicle_classes; + } + + string_vec& gta_data_service::weapon_types() + { + return m_weapon_types; + } + + bool gta_data_service::is_cache_up_to_date() + { + m_peds_cache.load(); + m_vehicles_cache.load(); + m_weapons_cache.load(); + + const auto game_version = std::strtoul(g_pointers->m_game_version, nullptr, 10); + const auto online_version = std::strtof(g_pointers->m_online_version, nullptr); + + return + m_peds_cache.up_to_date(game_version, online_version) && + m_vehicles_cache.up_to_date(game_version, online_version) && + m_weapons_cache.up_to_date(game_version, online_version); + } + + void gta_data_service::load_data() + { + LOG(G3LOG_DEBUG) << "Loading data from cache."; + + load_peds(); + load_vehicles(); + load_weapons(); + + LOG(G3LOG_DEBUG) << "Loaded all data from cache."; + } + + void gta_data_service::load_peds() + { + const auto ped_count = m_peds_cache.data_size() / sizeof(ped_item); + LOG(INFO) << "Loading " << ped_count << " peds from cache."; + + auto cached_peds = reinterpret_cast(m_peds_cache.data()); + for (size_t i = 0; i < ped_count; i++) + { + const auto ped = cached_peds[i]; + + add_if_not_exists(m_ped_types, ped.m_ped_type); + m_peds.insert({ ped.m_name, ped }); + } + + std::sort(m_ped_types.begin(), m_ped_types.end()); + m_peds_cache.free(); + } + + void gta_data_service::load_vehicles() + { + const auto vehicle_count = m_vehicles_cache.data_size() / sizeof(vehicle_item); + LOG(INFO) << "Loading " << vehicle_count << " vehicles from cache."; + + auto cached_vehicles = reinterpret_cast(m_vehicles_cache.data()); + for (size_t i = 0; i < vehicle_count; i++) + { + const auto vehicle = cached_vehicles[i]; + + add_if_not_exists(m_vehicle_classes, vehicle.m_vehicle_class); + m_vehicles.insert({ vehicle.m_name, vehicle }); + } + + std::sort(m_vehicle_classes.begin(), m_vehicle_classes.end()); + m_vehicles_cache.free(); + } + + void gta_data_service::load_weapons() + { + const auto weapon_count = m_weapons_cache.data_size() / sizeof(weapon_item); + LOG(INFO) << "Loading " << weapon_count << " weapons from cache."; + + auto cached_weapons = reinterpret_cast(m_weapons_cache.data()); + for (size_t i = 0; i < weapon_count; i++) + { + const auto weapon = cached_weapons[i]; + + add_if_not_exists(m_weapon_types, weapon.m_weapon_type); + m_weapons.insert({ weapon.m_name, weapon }); + } + + std::sort(m_weapon_types.begin(), m_weapon_types.end()); + m_weapons_cache.free(); + } + + void gta_data_service::rebuild_cache() + { + m_update_state = eGtaDataUpdateState::UPDATING; + + using hash_array = std::vector; + hash_array mapped_peds; + hash_array mapped_vehicles; + hash_array mapped_weapons; + + std::vector peds; + std::vector vehicles; + std::vector weapons; + + constexpr auto exists = [](const hash_array& arr, std::uint32_t val) -> bool + { + return std::find(arr.begin(), arr.end(), val) != arr.end(); + }; + + LOG(INFO) << "Rebuilding cache started..."; + yim_fipackfile::for_each_fipackfile([&](yim_fipackfile& rpf_wrapper) + { + const auto files = rpf_wrapper.get_file_paths(); + for (const auto& file : files) + { + if (file.filename() == "setup2.xml") { - up_to_date = true; - - LOG(INFO) << "Data updated: " << data_name; - - if ((this->*load_func)(file_to_load_path)) + std::string dlc_name; + rpf_wrapper.read_xml_file(file, [&dlc_name](pugi::xml_document& doc) { - LOG(INFO) << "Data loaded: " + data_name; - break; - } - else + const auto item = doc.select_node("/SSetupData/nameHash"); + dlc_name = item.node().text().as_string(); + }); + + if (dlc_name == "mpG9EC") { - std::filesystem::remove(file_to_load_path); - std::filesystem::remove(file_etag_path); + LOG(G3LOG_DEBUG) << "Bad DLC, skipping..."; + + return std::size_t(0); } } - } - catch (...) - { - LOG(WARNING) << "Data invalid: " + data_name; - } - - std::this_thread::sleep_for(2s); - } - - if (!up_to_date) - { - LOG(WARNING) << "Data not updated: " + data_name; - - try - { - if (file_to_load.exists()) + else if (file.filename() == "vehicles.meta") { - if ((this->*load_func)(file_to_load_path)) + rpf_wrapper.read_xml_file(file, [&exists, &vehicles, &mapped_vehicles](pugi::xml_document& doc) { - LOG(INFO) << "Cache loaded: " + data_name; - } - else + const auto& items = doc.select_nodes("/CVehicleModelInfo__InitDataList/InitDatas/Item"); + for (const auto& item_node : items) + { + const auto item = item_node.node(); + + const auto name = item.child("modelName").text().as_string(); + const auto hash = rage::joaat(name); + + if (exists(mapped_vehicles, hash)) + continue; + mapped_vehicles.emplace_back(hash); + + auto veh = vehicle_item{}; + std::strncpy(veh.m_name, name, sizeof(veh.m_name)); + + const auto manufacturer_display = item.child("vehicleMakeName").text().as_string(); + std::strncpy( + veh.m_display_manufacturer, + HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(manufacturer_display), + sizeof(veh.m_display_manufacturer)); + + const auto game_name = item.child("gameName").text().as_string(); + std::strncpy( + veh.m_display_name, + HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(game_name), + sizeof(veh.m_display_name)); + + const auto vehicle_class = item.child("vehicleClass").text().as_string(); + constexpr auto enum_prefix_len = 3; + if (std::strlen(vehicle_class) > enum_prefix_len) + std::strncpy(veh.m_vehicle_class, vehicle_class + enum_prefix_len, sizeof(veh.m_vehicle_class)); + + veh.m_hash = hash; + + vehicles.emplace_back(std::move(veh)); + } + }); + } + else if (const auto file_str = file.string(); file_str.find("weapon") != std::string::npos && file.extension() == ".meta") + { + rpf_wrapper.read_xml_file(file, [&exists, &weapons, &mapped_weapons](pugi::xml_document& doc) { - std::filesystem::remove(file_to_load_path); - std::filesystem::remove(file_etag_path); - throw std::exception(""); - } + const auto& items = doc.select_nodes("/CWeaponInfoBlob/Infos/Item/Infos/Item[@type='CWeaponInfo']"); + for (const auto& item_node : items) + { + const auto item = item_node.node(); + const auto name = item.child("Name").text().as_string(); + const auto hash = rage::joaat(name); + + if (exists(mapped_weapons, hash)) + continue; + mapped_weapons.emplace_back(hash); + + const auto human_name_hash = item.child("HumanNameHash").text().as_string(); + if (std::strcmp(human_name_hash, "WT_INVALID") == 0) + continue; + + auto weapon = weapon_item{}; + + std::strncpy(weapon.m_name, name, sizeof(weapon.m_name)); + + const auto display_name = HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(human_name_hash); + std::strncpy(weapon.m_display_name, display_name, sizeof(weapon.m_name)); + + auto weapon_flags = std::string( + item.child("WeaponFlags").text().as_string() + ); + + bool is_gun = false; + bool is_rechargable = false; + + std::size_t pos; + while ((pos = weapon_flags.find(' ')) != std::string::npos) { + const auto flag = weapon_flags.substr(0, pos); + if (flag == "Thrown") + { + weapon.m_throwable = true; + } + else if (flag == "Gun") + { + is_gun = true; + } + else if (flag == "DisplayRechargeTimeHUD") + { + is_rechargable = true; + } + + weapon_flags.erase(0, pos + 1); + } + + const auto category = item.child("Group").text().as_string(); + if (std::strlen(category) > 6) + { + std::strncpy(weapon.m_weapon_type, category + 6, sizeof(weapon.m_weapon_type)); + } + + if (is_gun || !std::strcmp(weapon.m_weapon_type, "MELEE") || !std::strcmp(weapon.m_weapon_type, "UNARMED")) + { + const std::string reward_prefix = "REWARD_"; + weapon.m_reward_hash = rage::joaat(reward_prefix + name); + + if (is_gun && !is_rechargable) + { + std::string weapon_id = name + 7; + weapon.m_reward_ammo_hash = rage::joaat(reward_prefix + "AMMO_" + weapon_id); + } + } + + weapon.m_hash = hash; + + weapons.emplace_back(std::move(weapon)); + } + }); } - } - catch (...) - { - LOG(WARNING) << "Cache invalid: " + data_name; - } - } - } - - bool gta_data_service::load_vehicles(std::filesystem::path path) - { - std::ifstream file(path); - nlohmann::json all_vehicles; - - try - { - file >> all_vehicles; - - if (!all_vehicles.is_array()) - { - throw std::exception("Invalid json format."); - } - - m_vehicle_class_arr.clear(); - m_vehicle_hash_idx_map.clear(); - m_vehicle_item_arr.clear(); - - for (auto& item_json : all_vehicles) - { - if ( - item_json["Hash"].is_null() || - item_json["Name"].is_null() || - item_json["MonetaryValue"] == 0 || - !item_json["Bones"].is_array() || - item_json["Bones"][0] == "stub" - ) { - continue; - } - - auto item = vehicle_item(item_json); - - m_vehicle_hash_idx_map[item_json["Hash"]] = (int)m_vehicle_item_arr.size(); - - m_vehicle_item_arr.push_back(item); - - if (std::find(m_vehicle_class_arr.begin(), m_vehicle_class_arr.end(), item.clazz) == m_vehicle_class_arr.end()) + else if (file.filename() == "peds.meta" || file.filename() == "peds.ymt") { - m_vehicle_class_arr.push_back(item.clazz); + rpf_wrapper.read_xml_file(file, [&exists, &peds, &mapped_peds](pugi::xml_document& doc) + { + const auto& items = doc.select_nodes("/CPedModelInfo__InitDataList/InitDatas/Item"); + for (const auto& item_node : items) + { + const auto& item = item_node.node(); + const auto name = item.child("Name").text().as_string(); + const auto hash = rage::joaat(name); + + if (exists(mapped_peds, hash)) + continue; + mapped_peds.emplace_back(hash); + + auto ped = ped_item{}; + + std::strncpy(ped.m_name, name, sizeof(ped.m_name)); + + const auto ped_type = item.child("Pedtype").text().as_string(); + std::strncpy(ped.m_ped_type, ped_type, sizeof(ped.m_ped_type)); + + ped.m_hash = hash; + + peds.emplace_back(std::move(ped)); + } + }); } - - std::sort(m_vehicle_class_arr.begin(), m_vehicle_class_arr.end()); } - } - catch (const std::exception& ex) + + return files.size(); + }); + + m_update_state = eGtaDataUpdateState::IDLE; + LOG(INFO) << "Cache has been rebuilt.\n\tPeds: " << peds.size() << "\n\tVehicles: " << vehicles.size() << "\n\tWeapons: " << weapons.size(); + + LOG(G3LOG_DEBUG) << "Starting cache saving procedure..."; + g_thread_pool->push([this, peds = std::move(peds), vehicles = std::move(vehicles), weapons = std::move(weapons)] { - LOG(WARNING) << "Failed to load vehicles.json:\n" << ex.what(); - return false; - } + const auto game_version = std::strtoul(g_pointers->m_game_version, nullptr, 10); + const auto online_version = std::strtof(g_pointers->m_online_version, nullptr); - return true; - } - - - bool gta_data_service::load_peds(std::filesystem::path path) - { - std::ifstream file(path); - nlohmann::json all_peds; - - try - { - file >> all_peds; - - if (!all_peds.is_array()) { - throw std::exception("Invalid json format."); + const auto data_size = sizeof(ped_item) * peds.size(); + m_peds_cache.set_data(std::make_unique(data_size), data_size); + std::memcpy(m_peds_cache.data(), peds.data(), data_size); + + m_peds_cache.set_header_version(game_version, online_version); + m_peds_cache.write(); } - m_ped_type_arr.clear(); - m_ped_hash_idx_map.clear(); - m_ped_item_arr.clear(); - - for (auto& item_json : all_peds) { - if ( - item_json["Hash"].is_null() || - item_json["Name"].is_null() - ) { - continue; - } + const auto data_size = sizeof(vehicle_item) * vehicles.size(); + m_vehicles_cache.set_data(std::make_unique(data_size), data_size); + std::memcpy(m_vehicles_cache.data(), vehicles.data(), data_size); - auto item = ped_item(item_json); - - m_ped_hash_idx_map[item_json["Hash"]] = (int)m_ped_item_arr.size(); - - m_ped_item_arr.push_back(item); - - if (std::find(m_ped_type_arr.begin(), m_ped_type_arr.end(), item.ped_type) == m_ped_type_arr.end()) - { - m_ped_type_arr.push_back(item.ped_type); - } - - std::sort(m_ped_type_arr.begin(), m_ped_type_arr.end()); + m_vehicles_cache.set_header_version(game_version, online_version); + m_vehicles_cache.write(); } - } - catch (const std::exception& ex) - { - LOG(WARNING) << "Failed to load peds.json:\n" << ex.what(); - return false; - } - return true; - } - - - bool gta_data_service::load_weapons(std::filesystem::path path) - { - std::ifstream file(path); - nlohmann::json all_weapons; - - try - { - file >> all_weapons; - - if (!all_weapons.is_array()) { - throw std::exception("Invalid json format."); + const auto data_size = sizeof(weapon_item) * weapons.size(); + m_weapons_cache.set_data(std::make_unique(data_size), data_size); + std::memcpy(m_weapons_cache.data(), weapons.data(), data_size); + + m_weapons_cache.set_header_version(game_version, online_version); + m_weapons_cache.write(); } - m_weapon_type_arr.clear(); - m_weapon_hash_idx_map.clear(); - m_weapon_item_arr.clear(); + LOG(INFO) << "Finished writing cache to disk."; - constexpr Hash hash_blacklist_arr[] = { - RAGE_JOAAT("WEAPON_BIRD_CRAP"), - RAGE_JOAAT("WEAPON_DIGISCANNER"), - RAGE_JOAAT("WEAPON_GARBAGEBAG"), - RAGE_JOAAT("WEAPON_GRENADELAUNCHER_SMOKE"), - RAGE_JOAAT("WEAPON_HANDCUFFS"), - RAGE_JOAAT("WEAPON_METALDETECTOR"), - RAGE_JOAAT("GADGET_NIGHTVISION"), - RAGE_JOAAT("GADGET_PARACHUTE"), - RAGE_JOAAT("WEAPON_TRANQUILIZER"), - RAGE_JOAAT("WEAPON_STINGER") - }; - - for (auto& item_json : all_weapons) - { - if ( - item_json["Hash"].is_null() || - item_json["Name"].is_null() || - item_json["IsVehicleWeapon"] - ) { - continue; - } - - if (EXIST_IN_ARRAY(hash_blacklist_arr, item_json["Hash"])) - { - continue; - } - - auto item = weapon_item(item_json); - - if (item.name == "Invalid" || item.name == "Unarmed" || item.weapon_type == "NULL") - { - continue; - } - - m_weapon_hash_idx_map[item_json["Hash"]] = (int)m_weapon_item_arr.size(); - - m_weapon_item_arr.push_back(item); - - if (std::find(m_weapon_type_arr.begin(), m_weapon_type_arr.end(), item.weapon_type) == m_weapon_type_arr.end()) - { - m_weapon_type_arr.push_back(item.weapon_type); - } - - std::sort(m_weapon_type_arr.begin(), m_weapon_type_arr.end()); - } - - } - catch (const std::exception& ex) - { - LOG(WARNING) << "Failed to load weapons.json:\n" << ex.what(); - return false; - } - - return true; + load_data(); + }); } } diff --git a/BigBaseV2/src/services/gta_data/gta_data_service.hpp b/BigBaseV2/src/services/gta_data/gta_data_service.hpp index 1abc4c9a..116ee959 100644 --- a/BigBaseV2/src/services/gta_data/gta_data_service.hpp +++ b/BigBaseV2/src/services/gta_data/gta_data_service.hpp @@ -1,55 +1,82 @@ #pragma once -#include "file_manager/file.hpp" -#include "vehicle_item.hpp" +#include "cache_file.hpp" #include "ped_item.hpp" +#include "vehicle_item.hpp" #include "weapon_item.hpp" -#include "gta/joaat.hpp" namespace big { - class gta_data_service + enum class eGtaDataUpdateState { - std::vector m_vehicle_class_arr; - std::map m_vehicle_hash_idx_map; - std::vector m_vehicle_item_arr; - const vehicle_item empty_vehicle_item = vehicle_item(); + IDLE, + NEEDS_UPDATE, + WAITING_FOR_ONLINE, + UPDATING + }; - std::vector m_ped_type_arr; - std::map m_ped_hash_idx_map; - std::vector m_ped_item_arr; - const ped_item empty_ped_item = ped_item(); - - std::vector m_weapon_type_arr; - std::map m_weapon_hash_idx_map; - std::vector m_weapon_item_arr; - const weapon_item empty_weapon_item = weapon_item(); + using ped_map = std::map; + using vehicle_map = std::map; + using weapon_map = std::map; + using string_vec = std::vector; + class gta_data_service final + { public: gta_data_service(); ~gta_data_service(); - const vehicle_item& find_vehicle_by_hash(Hash hash); - const std::vector& get_vehicle_class_arr(); - const std::vector& get_vehicle_arr(); + bool cache_needs_update() const; + eGtaDataUpdateState state() const; + void update_in_online(); + void update_now(); - const ped_item& find_ped_by_hash(Hash hash); - const std::vector& get_ped_type_arr(); - const std::vector& get_ped_arr(); + const ped_item& ped_by_hash(std::uint32_t hash); + const vehicle_item& vehicle_by_hash(std::uint32_t hash); + const weapon_item& weapon_by_hash(std::uint32_t hash); - const weapon_item& find_weapon_by_hash(Hash hash); - const std::vector& get_weapon_type_arr(); - const std::vector& get_weapon_arr(); + string_vec& ped_types(); + string_vec& vehicle_classes(); + string_vec& weapon_types(); + + ped_map& peds() + { return m_peds; } + vehicle_map& vehicles() + { return m_vehicles; } + weapon_map& weapons() + { return m_weapons; } private: - void load_from_file( - std::string file_path, std::string etag_path, std::string url, - bool(gta_data_service::* load_func)(std::filesystem::path), std::string data_name - ); + bool is_cache_up_to_date(); + + void load_data(); + void load_peds(); + void load_vehicles(); + void load_weapons(); + + void rebuild_cache(); + + private: + cache_file m_peds_cache; + cache_file m_vehicles_cache; + cache_file m_weapons_cache; + + // std::map is free sorting algo + ped_map m_peds; + vehicle_map m_vehicles; + weapon_map m_weapons; + + string_vec m_ped_types; + string_vec m_vehicle_classes; + string_vec m_weapon_types; + + eGtaDataUpdateState m_update_state; + + private: + static constexpr ped_item empty_ped {}; + static constexpr vehicle_item empty_vehicle {}; + static constexpr weapon_item empty_weapon {}; - bool load_vehicles(std::filesystem::path path); - bool load_peds(std::filesystem::path path); - bool load_weapons(std::filesystem::path path); }; inline gta_data_service* g_gta_data_service{}; -} \ No newline at end of file +} diff --git a/BigBaseV2/src/services/gta_data/ped_item.cpp b/BigBaseV2/src/services/gta_data/ped_item.cpp deleted file mode 100644 index 604e67b2..00000000 --- a/BigBaseV2/src/services/gta_data/ped_item.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "ped_item.hpp" - -namespace big -{ - ped_item::ped_item() - { - this->name = ""; - this->ped_type = ""; - this->hash = 0; - } - ped_item::ped_item(nlohmann::json& item_json) - { - this->name = item_json["Name"]; - - this->ped_type = item_json["Pedtype"]; - std::transform(this->ped_type.begin(), this->ped_type.end(), this->ped_type.begin(), ::toupper); - - this->hash = item_json["Hash"]; - } -} diff --git a/BigBaseV2/src/services/gta_data/ped_item.hpp b/BigBaseV2/src/services/gta_data/ped_item.hpp index 2033c817..97efb198 100644 --- a/BigBaseV2/src/services/gta_data/ped_item.hpp +++ b/BigBaseV2/src/services/gta_data/ped_item.hpp @@ -1,15 +1,14 @@ #pragma once -#include "file_manager/file.hpp" namespace big { - class ped_item { +#pragma pack(push, 4) + class ped_item final + { public: - ped_item(); - ped_item(nlohmann::json& item_json); - - std::string name; - std::string ped_type; - Hash hash; + char m_name[32]; + char m_ped_type[16]; + std::uint32_t m_hash; }; -} +#pragma pack(pop) +} \ No newline at end of file diff --git a/BigBaseV2/src/services/gta_data/vehicle_item.cpp b/BigBaseV2/src/services/gta_data/vehicle_item.cpp deleted file mode 100644 index 7c824b84..00000000 --- a/BigBaseV2/src/services/gta_data/vehicle_item.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "vehicle_item.hpp" - -namespace big -{ - vehicle_item::vehicle_item() - { - this->name = ""; - this->display_name = ""; - this->display_manufacturer = ""; - this->clazz = ""; - this->hash = 0; - } - vehicle_item::vehicle_item(nlohmann::json& item_json) - { - this->name = item_json["Name"]; - this->display_name = item_json["Name"]; - this->display_manufacturer = ""; - this->clazz = ""; - this->hash = item_json["Hash"]; - - if (!item_json["DisplayName"].is_null()) - { - this->display_name = item_json["DisplayName"]; - } - - if (!item_json["ManufacturerDisplayName"].is_null()) - { - this->display_manufacturer = item_json["ManufacturerDisplayName"]; - } - else if (!item_json["Manufacturer"].is_null()) - { - this->display_manufacturer = item_json["Manufacturer"]; - } - - if (!item_json["Class"].is_null()) - { - this->clazz = item_json["Class"]; - std::transform(this->clazz.begin(), this->clazz.end(), this->clazz.begin(), ::toupper); - - if (this->clazz == "COMPACTS") - { - this->clazz = "COMPACT"; - } - } - } -} diff --git a/BigBaseV2/src/services/gta_data/vehicle_item.hpp b/BigBaseV2/src/services/gta_data/vehicle_item.hpp index b8fe86f4..3ce2387f 100644 --- a/BigBaseV2/src/services/gta_data/vehicle_item.hpp +++ b/BigBaseV2/src/services/gta_data/vehicle_item.hpp @@ -1,17 +1,16 @@ #pragma once -#include "file_manager/file.hpp" namespace big { - class vehicle_item { +#pragma pack(push, 4) + class vehicle_item final + { public: - vehicle_item(); - vehicle_item(nlohmann::json& item_json); - - std::string name; - std::string display_name; - std::string display_manufacturer; - std::string clazz; - Hash hash; + char m_name[16]; + char m_display_name[32]; + char m_display_manufacturer[32]; + char m_vehicle_class[32]; + std::uint32_t m_hash; }; -} +#pragma pack(pop) +} \ No newline at end of file diff --git a/BigBaseV2/src/services/gta_data/weapon_item.cpp b/BigBaseV2/src/services/gta_data/weapon_item.cpp deleted file mode 100644 index 8b1b16b5..00000000 --- a/BigBaseV2/src/services/gta_data/weapon_item.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "weapon_item.hpp" -#include "gta/joaat.hpp" - -namespace big -{ - weapon_item::weapon_item() - { - this->name = ""; - this->throwable = false; - this->weapon_type = ""; - - this->hash = 0; - this->reward_hash = 0; - this->reward_ammo_hash = 0; - } - weapon_item::weapon_item(nlohmann::json& item_json) - { - this->name = item_json["Name"]; - this->throwable = false; - this->weapon_type = "NULL"; - - if ( - item_json.contains("TranslatedLabel") && - item_json["TranslatedLabel"].contains("English") && - !item_json["TranslatedLabel"]["English"].is_null() - ) { - this->name = item_json["TranslatedLabel"]["English"]; - } - - if (!item_json["Category"].is_null()) - { - this->weapon_type = item_json["Category"]; - this->weapon_type = this->weapon_type.substr(6); - } - - this->hash = item_json["Hash"]; - this->reward_hash = 0; - this->reward_ammo_hash = 0; - - const std::string reward_prefix = "REWARD_"; - bool is_gun = false; - bool is_recharge = false; - - if (item_json.contains("Flags")) - { - auto flags = item_json["Flags"]; - - for (auto& flag : flags) - { - if (flag == "Gun") - { - is_gun = true; - } - else if (flag == "DisplayRechargeTimeHUD") - { - is_recharge = true; - } - else if (flag == "Thrown") - { - this->throwable = true; - } - } - } - - if (this->weapon_type == "MELEE" || this->weapon_type == "UNARMED" || is_gun) - { - this->reward_hash = rage::joaat((reward_prefix + std::string(item_json["Name"])).c_str()); - - if (is_gun && !is_recharge) - { - std::string weapon_id = std::string(item_json["Name"]).substr(7); - this->reward_ammo_hash = rage::joaat((reward_prefix + "AMMO_" + weapon_id).c_str()); - } - } - } -} diff --git a/BigBaseV2/src/services/gta_data/weapon_item.hpp b/BigBaseV2/src/services/gta_data/weapon_item.hpp index db4d9247..7be5bee8 100644 --- a/BigBaseV2/src/services/gta_data/weapon_item.hpp +++ b/BigBaseV2/src/services/gta_data/weapon_item.hpp @@ -1,19 +1,18 @@ #pragma once -#include "file_manager/file.hpp" namespace big { - class weapon_item { +#pragma pack(push, 4) + class weapon_item final + { public: - weapon_item(); - weapon_item(nlohmann::json& item_json); - - std::string name; - bool throwable; - std::string weapon_type; - - Hash hash; - Hash reward_hash; - Hash reward_ammo_hash; + char m_name[32]; + char m_display_name[32]; + char m_weapon_type[16]; + std::uint32_t m_hash; + std::uint32_t m_reward_hash; + std::uint32_t m_reward_ammo_hash; + bool m_throwable; }; -} +#pragma pack(pop) +} \ No newline at end of file diff --git a/BigBaseV2/src/services/gta_data/yim_fipackfile.cpp b/BigBaseV2/src/services/gta_data/yim_fipackfile.cpp new file mode 100644 index 00000000..c9402784 --- /dev/null +++ b/BigBaseV2/src/services/gta_data/yim_fipackfile.cpp @@ -0,0 +1,216 @@ +#include "yim_fipackfile.hpp" +#include "pointers.hpp" +#include "gta/fidevice.hpp" +#include "script.hpp" + +namespace big +{ + yim_fipackfile::yim_fipackfile(rage::fiPackfile* rpf, const std::string& mount_name) + { + this->rpf = rpf; + this->mount_name = mount_name; + } + + static std::vector get_non_dlc_mounted_devices_names() + { + std::vector non_dlc_mounted_devices_names; + + uint16_t mounted_devices_len = *g_pointers->m_fidevices_len; + if (mounted_devices_len) + { + auto devices_arr = *(uint64_t*)g_pointers->m_fidevices; + uint8_t** current_device_mount_name_ptr = *(unsigned __int8***)g_pointers->m_fidevices; + auto device_i = 0; + + while (true) + { + non_dlc_mounted_devices_names.push_back(*(const char**)current_device_mount_name_ptr); + + ++device_i; + current_device_mount_name_ptr += 4; + if (device_i >= mounted_devices_len) + break; + } + } + + return non_dlc_mounted_devices_names; + } + + static int ends_with(const char* str, const char* suffix) + { + if (!str || !suffix) + return 0; + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + if (lensuffix > lenstr) + return 0; + return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; + } + + void yim_fipackfile::for_each_fipackfile(std::function cb) + { + // the idea is to reuse existing mount points as much as possible because + // even when mounting / unmounting properly you'll get file errors + // and crashes if the rpf file was already mounted + + // iterate the fidevice array which contains devices that are currently mounted + // the dlc devices are in another array + const auto non_dlc_mounted_devices_names = get_non_dlc_mounted_devices_names(); + + // for not hanging the game too much + constexpr auto yield_increment = 80; + + auto i = 1; + while (g_pointers->m_fipackfile_instances[i]) + { + auto* rpf = g_pointers->m_fipackfile_instances[i]; + + // its hard coded in the binary? + if (++i >= 3672) + { + break; + } + + yim_fipackfile rpf_wrapper = yim_fipackfile(rpf, default_mount_name); + + auto already_mounted = false; + for (const auto& non_dlc_mounted_device_name : non_dlc_mounted_devices_names) + { + auto* non_dlc_mounted_device = rage::fiDevice::GetDevice(non_dlc_mounted_device_name.c_str(), true); + + if (rpf == non_dlc_mounted_device) + { + rpf_wrapper.mount_name = non_dlc_mounted_device_name; + already_mounted = true; + } + } + + if (!already_mounted) + { + size_t acc = 0; + + rpf_wrapper.mount_name = "memory:/"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "memory:"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "dlc"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "dlc:"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "dlc:/"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "dlcpacks:/"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "common:/"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "commoncrc:/"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "update:/"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "update2:/"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "platform:/"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "platformcrc:/"; + acc += cb(rpf_wrapper); + + rpf_wrapper.mount_name = "gamecache:/"; + acc += cb(rpf_wrapper); + + // if we got nothing with those mount points for this rpf, mount it + if (!acc) + { + rpf_wrapper.mount_name = default_mount_name; + rpf->Mount(default_mount_name); + + cb(rpf_wrapper); + + g_pointers->m_fipackfile_unmount(default_mount_name); + } + } + else + { + cb(rpf_wrapper); + } + + if (i % yield_increment == 0) + script::get_current()->yield(); + } + } + + std::vector yim_fipackfile::get_file_paths(std::string parent) + { + std::vector file_paths; + if (parent.empty()) + parent = mount_name; + + std::vector directories; + + rage::fiFindData findData = { 0 }; + auto handlef = rpf->FindFirst(parent.c_str(), &findData); + if (handlef != -1) + { + do + { + std::string fn = std::string(parent.c_str()) + std::string("/") + std::string(findData.fileName); + + if (findData.fileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + directories.push_back(fn); + } + else + { + file_paths.push_back(fn); + } + } while (rpf->FindNext(handlef, &findData)); + + rpf->FindClose(handlef); + } + + for (auto& directory : directories) + { + auto files = get_file_paths(directory); + + file_paths.insert(file_paths.end(), files.begin(), files.end()); + } + + return file_paths; + } + + void yim_fipackfile::read_file(const std::filesystem::path& path, file_contents_callback&& cb) + { + if (const auto handle = rpf->Open(path.string().c_str(), true); handle != -1) + { + const auto data_length = rpf->GetFileLength(handle); + const auto file_content = std::make_unique(data_length); + + rpf->ReadFull(handle, file_content.get(), data_length); + + cb(file_content, data_length); + + rpf->Close(handle); + } + } + + void yim_fipackfile::read_xml_file(const std::filesystem::path& path, std::function cb) + { + read_file(path, [&cb](const std::unique_ptr& file_content, const int data_size) + { + if (pugi::xml_document doc; doc.load_buffer(file_content.get(), data_size).status == pugi::xml_parse_status::status_ok) + { + cb(doc); + } + }); + } +} diff --git a/BigBaseV2/src/services/gta_data/yim_fipackfile.hpp b/BigBaseV2/src/services/gta_data/yim_fipackfile.hpp new file mode 100644 index 00000000..bb10a52f --- /dev/null +++ b/BigBaseV2/src/services/gta_data/yim_fipackfile.hpp @@ -0,0 +1,27 @@ +#pragma once +#include + +namespace big +{ + using file_contents_callback = std::function& file_content, const int data_size)>; + class yim_fipackfile + { + static constexpr auto default_mount_name = "yimM:/"; + + rage::fiPackfile* rpf; + std::string mount_name; + + public: + explicit yim_fipackfile(rage::fiPackfile* rpf, const std::string& mount_name); + + static void for_each_fipackfile(std::function cb); + std::vector get_file_paths(std::string parent = {}); + + void read_file(const std::filesystem::path& path, file_contents_callback&& cb); + + void read_xml_file(const std::filesystem::path& path, std::function cb); + + private: + + }; +} diff --git a/BigBaseV2/src/services/pickups/pickup_service.cpp b/BigBaseV2/src/services/pickups/pickup_service.cpp index 10d82fbb..3afaddf2 100644 --- a/BigBaseV2/src/services/pickups/pickup_service.cpp +++ b/BigBaseV2/src/services/pickups/pickup_service.cpp @@ -1,4 +1,5 @@ #include "pickup_service.hpp" +#include "gta/joaat.hpp" #include "pointers.hpp" #include "script.hpp" #include "services/gta_data/gta_data_service.hpp" @@ -37,11 +38,11 @@ namespace big void pickup_service::give_ammo(const int targets) const { - for (const auto& weapon : g_gta_data_service->get_weapon_arr()) + for (const auto& [_, weapon] : g_gta_data_service->weapons()) { - if (weapon.reward_ammo_hash != 0 || weapon.throwable) + if (weapon.m_reward_ammo_hash != 0 || weapon.m_throwable) { - g_pointers->m_give_pickup_rewards(targets, weapon.reward_ammo_hash); + g_pointers->m_give_pickup_rewards(targets, weapon.m_reward_ammo_hash); script::get_current()->yield(20ms); } } @@ -63,11 +64,11 @@ namespace big void pickup_service::give_weapons(const int targets) const { - for (const auto& weapon : g_gta_data_service->get_weapon_arr()) + for (const auto& [_, weapon] : g_gta_data_service->weapons()) { - if (weapon.reward_hash != 0) + if (weapon.m_reward_hash != 0) { - g_pointers->m_give_pickup_rewards(targets, weapon.reward_hash); + g_pointers->m_give_pickup_rewards(targets, weapon.m_reward_hash); script::get_current()->yield(20ms); } } diff --git a/BigBaseV2/src/util/session.hpp b/BigBaseV2/src/util/session.hpp index a28b89d2..b7124205 100644 --- a/BigBaseV2/src/util/session.hpp +++ b/BigBaseV2/src/util/session.hpp @@ -6,23 +6,26 @@ namespace big::session { - void join_type(SessionType session) + inline void join_type(eSessionType session) { - *script_global(2726795).as() = (session.id == eSessionType::SC_TV ? 1 : 0); // If SC TV Then Enable Spectator Mode + *script_global(2726795).as() = (session == eSessionType::SC_TV ? 1 : 0); // If SC TV Then Enable Spectator Mode - if (session.id == eSessionType::LEAVE_ONLINE) + if (session == eSessionType::LEAVE_ONLINE) *script_global(1574589).at(2).as() = -1; else - *script_global(1575015).as() = (int)session.id; + *script_global(1575015).as() = (int)session; *script_global(1574589).as() = 1; script::get_current()->yield(200ms); *script_global(1574589).as() = 0; } - static constexpr char const* weathers[] = { "EXTRASUNNY", "CLEAR", "CLOUDS", "SMOG", "FOGGY", "OVERCAST", "RAIN", "THUNDER", "CLEARING", "NEUTRAL", "SNOW", "BLIZZARD", "SNOWLIGHT", "XMAS", "HALLOWEEN" }; - - void local_weather() + static constexpr char const* weathers[] = { + "EXTRASUNNY", "CLEAR", "CLOUDS", "SMOG", + "FOGGY", "OVERCAST", "RAIN", "THUNDER", + "CLEARING", "NEUTRAL", "SNOW", "BLIZZARD", + "SNOWLIGHT", "XMAS", "HALLOWEEN" }; + inline void local_weather() { MISC::CLEAR_OVERRIDE_WEATHER(); diff --git a/BigBaseV2/src/views/network/view_session.cpp b/BigBaseV2/src/views/network/view_session.cpp index 880ebd6a..b9d737c2 100644 --- a/BigBaseV2/src/views/network/view_session.cpp +++ b/BigBaseV2/src/views/network/view_session.cpp @@ -11,8 +11,9 @@ namespace big { for (const auto& session_type : sessions) { - components::selectable(session_type.name, false, [session_type] { - session::join_type(session_type); + components::selectable(session_type.name, false, [&session_type] + { + session::join_type(session_type.id); }); } ImGui::EndListBox(); @@ -32,7 +33,8 @@ namespace big } if (ImGui::TreeNode("Local Weather")) { - components::button("Clear Override", [] { + components::button("Clear Override", [] + { MISC::CLEAR_OVERRIDE_WEATHER(); }); diff --git a/BigBaseV2/src/views/self/view_weapons.cpp b/BigBaseV2/src/views/self/view_weapons.cpp index 9af76858..956882ca 100644 --- a/BigBaseV2/src/views/self/view_weapons.cpp +++ b/BigBaseV2/src/views/self/view_weapons.cpp @@ -72,24 +72,26 @@ namespace big ImGui::Checkbox("No Spread", &g->weapons.no_spread); - components::button("Get All Weapons", [] { - for (auto const& weapon : g_gta_data_service->get_weapon_arr()) + components::button("Get All Weapons", [] + { + for (const auto& [_, weapon] : g_gta_data_service->weapons()) { - WEAPON::GIVE_DELAYED_WEAPON_TO_PED(self::ped, weapon.hash, 9999, false); + WEAPON::GIVE_DELAYED_WEAPON_TO_PED(self::ped, weapon.m_hash, 9999, false); } constexpr auto parachute_hash = RAGE_JOAAT("GADGET_PARACHUTE"); WEAPON::GIVE_DELAYED_WEAPON_TO_PED(self::ped, parachute_hash, 0, true); - }); + }); ImGui::SameLine(); - components::button("Remove Current Weapon", [] { + components::button("Remove Current Weapon", [] + { Hash weaponHash; WEAPON::GET_CURRENT_PED_WEAPON(self::ped, &weaponHash, 1); if (weaponHash != RAGE_JOAAT("WEAPON_UNARMED")) { WEAPON::REMOVE_WEAPON_FROM_PED(self::ped, weaponHash); } - }); + }); ImGui::SliderFloat("Damage Multiplier", &g->weapons.increased_damage, 1.f, 10.f, "%.1f"); diff --git a/BigBaseV2/src/views/vehicle/view_pv.cpp b/BigBaseV2/src/views/vehicle/view_pv.cpp index 31994114..89305adf 100644 --- a/BigBaseV2/src/views/vehicle/view_pv.cpp +++ b/BigBaseV2/src/views/vehicle/view_pv.cpp @@ -51,7 +51,7 @@ namespace big static int selected_class = -1; - auto class_arr = g_gta_data_service->get_vehicle_class_arr(); + const auto& class_arr = g_gta_data_service->vehicle_classes(); ImGui::SetNextItemWidth(300.f); if (ImGui::BeginCombo("Vehicle Class", selected_class == -1 ? "ALL" : class_arr[selected_class].c_str())) @@ -99,16 +99,16 @@ namespace big { const auto& label = it.first; const auto& personal_veh = it.second; - auto item = g_gta_data_service->find_vehicle_by_hash(personal_veh->get_hash()); + const auto& item = g_gta_data_service->vehicle_by_hash(personal_veh->get_hash()); - std::string clazz = item.clazz; + std::string vehicle_class = item.m_vehicle_class; std::string display_name = label; - std::string display_manufacturer = item.display_manufacturer; + std::string display_manufacturer = item.m_display_manufacturer; std::transform(display_name.begin(), display_name.end(), display_name.begin(), ::tolower); std::transform(display_manufacturer.begin(), display_manufacturer.end(), display_manufacturer.begin(), ::tolower); if (( - selected_class == -1 || class_arr[selected_class] == clazz + selected_class == -1 || class_arr[selected_class] == vehicle_class ) && ( display_name.find(lower_search) != std::string::npos || display_manufacturer.find(lower_search) != std::string::npos diff --git a/BigBaseV2/src/views/vehicle/view_spawn_vehicle.cpp b/BigBaseV2/src/views/vehicle/view_spawn_vehicle.cpp index ba2afc24..12e399d8 100644 --- a/BigBaseV2/src/views/vehicle/view_spawn_vehicle.cpp +++ b/BigBaseV2/src/views/vehicle/view_spawn_vehicle.cpp @@ -33,7 +33,7 @@ namespace big static int selected_class = -1; - auto class_arr = g_gta_data_service->get_vehicle_class_arr(); + const auto& class_arr = g_gta_data_service->vehicle_classes(); ImGui::SetNextItemWidth(300.f); if (ImGui::BeginCombo("Vehicle Class", selected_class == -1 ? "ALL" : class_arr[selected_class].c_str())) @@ -78,9 +78,9 @@ namespace big if (veh_hash) { - auto item = g_gta_data_service->find_vehicle_by_hash(veh_hash); + const auto& item = g_gta_data_service->vehicle_by_hash(veh_hash); - components::selectable("Current Vehicle [" + item.display_name + "]", false, [] { + components::selectable(fmt::format("Current Vehicle [{}]", item.m_display_name), false, [] { if (self::veh) { Vector3 spawn_location = vehicle::get_spawn_location(g->spawn_vehicle.spawn_inside); @@ -129,17 +129,19 @@ namespace big } } - auto item_arr = g_gta_data_service->get_vehicle_arr(); - + const auto& item_arr = g_gta_data_service->vehicles(); if (item_arr.size() > 0) { std::string lower_search = search; std::transform(lower_search.begin(), lower_search.end(), lower_search.begin(), tolower); - for (auto& item : item_arr) { - std::string display_name = item.display_name; - std::string display_manufacturer = item.display_manufacturer; - std::string clazz = item.clazz; + for (auto& item : item_arr) + { + const auto& vehicle = item.second; + + std::string display_name = vehicle.m_display_name; + std::string display_manufacturer = vehicle.m_display_manufacturer; + std::string clazz = vehicle.m_vehicle_class; std::transform(display_name.begin(), display_name.end(), display_name.begin(), ::tolower); std::transform(display_manufacturer.begin(), display_manufacturer.end(), display_manufacturer.begin(), ::tolower); @@ -150,13 +152,13 @@ namespace big display_name.find(lower_search) != std::string::npos || display_manufacturer.find(lower_search) != std::string::npos )) { - ImGui::PushID(item.hash); - components::selectable(item.display_name, false, [item] { + ImGui::PushID(vehicle.m_hash); + components::selectable(vehicle.m_display_name, false, [&vehicle] + { + const auto spawn_location = vehicle::get_spawn_location(g->spawn_vehicle.spawn_inside); + const auto spawn_heading = ENTITY::GET_ENTITY_HEADING(self::ped); - Vector3 spawn_location = vehicle::get_spawn_location(g->spawn_vehicle.spawn_inside); - float spawn_heading = ENTITY::GET_ENTITY_HEADING(self::ped); - - const Vehicle veh = vehicle::spawn(item.hash, spawn_location, spawn_heading); + const auto veh = vehicle::spawn(vehicle.m_hash, spawn_location, spawn_heading); if (veh == 0) { @@ -187,7 +189,7 @@ namespace big } else if (ImGui::IsItemHovered()) { - g_model_preview_service->show_vehicle(item.hash, g->spawn_vehicle.spawn_maxed); + g_model_preview_service->show_vehicle(vehicle.m_hash, g->spawn_vehicle.spawn_maxed); } } } diff --git a/BigBaseV2/src/views/view.hpp b/BigBaseV2/src/views/view.hpp index 3cb606ee..ad17abdf 100644 --- a/BigBaseV2/src/views/view.hpp +++ b/BigBaseV2/src/views/view.hpp @@ -44,11 +44,16 @@ namespace big static void players(); static void weapons(); static void context_menu(); + static void gta_data(); + // later calls will be drawn over earlier calls static void always() { esp::draw(); context_menu(); + + gta_data(); + notifications(); } }; diff --git a/BigBaseV2/src/views/view_gta_data.cpp b/BigBaseV2/src/views/view_gta_data.cpp new file mode 100644 index 00000000..e9fa3dcd --- /dev/null +++ b/BigBaseV2/src/views/view_gta_data.cpp @@ -0,0 +1,75 @@ +#include "view.hpp" +#include "gui.hpp" +#include "pointers.hpp" +#include "services/gta_data/gta_data_service.hpp" + +namespace big +{ + void view::gta_data() + { + if (!g_gta_data_service) + return; + + if (g_gta_data_service->cache_needs_update()) + { + g_gui.m_opened = true; + ImGui::OpenPopup("Game Cache"); + } + + ImGui::SetNextWindowSize(ImVec2(800, 0), ImGuiCond_FirstUseEver); + if (ImGui::BeginPopupModal("Game Cache")) + { + switch (g_gta_data_service->state()) + { + case eGtaDataUpdateState::NEEDS_UPDATE: + { + ImGui::Text("YimMenu requires a rebuild of the game cache. This may take up to one minute to generate."); + + if (*g_pointers->m_is_session_started) + { + if (ImGui::Button("Update Cache")) + { + g_gta_data_service->update_now(); + } + } + else + { + ImGui::TextWrapped("You are currently in single player, you can force build the cache in single player but risk crashing when going into multiplayer or load online and cache."); + + if (ImGui::Button("I don't care, update in single player!")) + { + g_gta_data_service->update_now(); + } + + if (ImGui::Button("Update cache in online.")) + { + g_gta_data_service->update_in_online(); + } + } + + break; + } + case eGtaDataUpdateState::WAITING_FOR_ONLINE: + { + ImGui::Text("Waiting for online to start cache update..."); + + break; + } + case eGtaDataUpdateState::UPDATING: + { + ImGui::Text("Updating cache, please wait..."); + + break; + } + case eGtaDataUpdateState::IDLE: + { + ImGui::CloseCurrentPopup(); + + break; + } + } + + ImGui::EndPopup(); + } + } +} diff --git a/BigBaseV2/src/views/world/view_spawn_ped.cpp b/BigBaseV2/src/views/world/view_spawn_ped.cpp index f9998993..67d1509a 100644 --- a/BigBaseV2/src/views/world/view_spawn_ped.cpp +++ b/BigBaseV2/src/views/world/view_spawn_ped.cpp @@ -17,9 +17,9 @@ namespace big { Ped spawn_ped_at_location( - const int selected_ped_type, - const char* ped_model_buf, - const Player selected_ped_player_id, + const int selected_ped_type, + const char* ped_model_buf, + const Player selected_ped_player_id, const Player selected_ped_for_player_id, const bool is_bodyguard ) { @@ -114,8 +114,8 @@ namespace big void spawn_ped_give_weapon( - const Ped ped, - const int selected_ped_weapon_type, + const Ped ped, + const int selected_ped_weapon_type, const Hash selected_ped_weapon_hash ) { if (selected_ped_weapon_type == SPAWN_PED_NO_WEAPONS) @@ -123,18 +123,18 @@ namespace big return; } - auto weapon_type_arr = g_gta_data_service->get_weapon_type_arr(); - for (auto& weapon : g_gta_data_service->get_weapon_arr()) + const auto& weapon_type_arr = g_gta_data_service->weapon_types(); + for (auto& [_, weapon] : g_gta_data_service->weapons()) { if ( selected_ped_weapon_type == SPAWN_PED_ALL_WEAPONS || - weapon.weapon_type == weapon_type_arr[selected_ped_weapon_type] + weapon.m_weapon_type == weapon_type_arr[selected_ped_weapon_type] ) { if ( selected_ped_weapon_hash == 0 || - weapon.hash == selected_ped_weapon_hash + weapon.m_hash == selected_ped_weapon_hash ) { - WEAPON::GIVE_WEAPON_TO_PED(ped, weapon.hash, 9999, false, selected_ped_weapon_hash != 0); + WEAPON::GIVE_WEAPON_TO_PED(ped, weapon.m_hash, 9999, false, selected_ped_weapon_hash != 0); } } } @@ -147,14 +147,14 @@ namespace big static char ped_model_buf[64]; static Player selected_ped_player_id = -1; - auto ped_type_arr = g_gta_data_service->get_ped_type_arr(); - auto ped_arr = g_gta_data_service->get_ped_arr(); + auto ped_type_arr = g_gta_data_service->ped_types(); + auto ped_arr = g_gta_data_service->peds(); static int selected_ped_weapon_type = SPAWN_PED_ALL_WEAPONS; static Hash selected_ped_weapon_hash = 0; - auto weapon_type_arr = g_gta_data_service->get_weapon_type_arr(); - auto weapon_arr = g_gta_data_service->get_weapon_arr(); + auto weapon_type_arr = g_gta_data_service->weapon_types(); + auto weapon_arr = g_gta_data_service->weapons(); static Player selected_ped_for_player_id = -1; auto player_arr = g_player_service->players(); @@ -186,9 +186,9 @@ namespace big ImGui::SetNextItemWidth(160.f); if (ImGui::BeginCombo( - "##ped_type", - selected_ped_type == -1 ? "ALL" : - selected_ped_type == -2 ? "ONLINE PLAYER" : + "##ped_type", + selected_ped_type == -1 ? "ALL" : + selected_ped_type == -2 ? "ONLINE PLAYER" : ped_type_arr[selected_ped_type].c_str() )) { @@ -312,8 +312,8 @@ namespace big ImGui::SetNextItemWidth(240.f); components::input_text_with_hint( - "##ped_model_name", "Model Name", - ped_model_buf, sizeof(ped_model_buf), ImGuiInputTextFlags_EnterReturnsTrue, + "##ped_model_name", "Model Name", + ped_model_buf, sizeof(ped_model_buf), ImGuiInputTextFlags_EnterReturnsTrue, [] { ped_model_dropdown_open = false; } @@ -345,10 +345,10 @@ namespace big ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); ped_model_dropdown_focused |= ImGui::IsWindowFocused(); - for (auto& item : ped_arr) + for (const auto& [_, item] : ped_arr) { - std::string ped_type = item.ped_type; - std::string name = item.name; + std::string ped_type = item.m_ped_type; + std::string name = item.m_name; std::transform(name.begin(), name.end(), name.begin(), tolower); @@ -359,12 +359,12 @@ namespace big )) { bool selectable_highlighted = lower_search == name; - bool selectable_clicked = ImGui::Selectable(item.name.c_str(), selectable_highlighted); + bool selectable_clicked = ImGui::Selectable(item.m_name, selectable_highlighted); ped_model_dropdown_focused |= ImGui::IsItemFocused(); if (selectable_clicked) { - strncpy(ped_model_buf, item.name.c_str(), 64); + strncpy(ped_model_buf, item.m_name, 64); ped_model_dropdown_open = false; ped_model_dropdown_focused = false; } @@ -377,7 +377,7 @@ namespace big if (ImGui::IsItemHovered()) { item_hovered = true; - g_model_preview_service->show_ped(item.hash); + g_model_preview_service->show_ped(item.m_hash); } } } @@ -405,9 +405,9 @@ namespace big ImGui::SetNextItemWidth(160.f); if (ImGui::BeginCombo( - "##ped_weapon_type", - selected_ped_weapon_type == SPAWN_PED_ALL_WEAPONS ? - "ALL" : + "##ped_weapon_type", + selected_ped_weapon_type == SPAWN_PED_ALL_WEAPONS ? + "ALL" : selected_ped_weapon_type == SPAWN_PED_NO_WEAPONS ? "NO WEAPONS" : weapon_type_arr[selected_ped_weapon_type].c_str() @@ -459,12 +459,12 @@ namespace big ImGui::SetNextItemWidth(240.f); if (ImGui::BeginCombo( - "##ped_weapon", + "##ped_weapon", selected_ped_weapon_type == SPAWN_PED_NO_WEAPONS ? "NO WEAPONS" : - selected_ped_weapon_hash == 0 ? - "ALL" : - g_gta_data_service->find_weapon_by_hash(selected_ped_weapon_hash).name.c_str() + selected_ped_weapon_hash == 0 ? + "ALL" : + g_gta_data_service->weapon_by_hash(selected_ped_weapon_hash).m_display_name )) { if (selected_ped_weapon_type != SPAWN_PED_NO_WEAPONS) { @@ -478,19 +478,19 @@ namespace big ImGui::SetItemDefaultFocus(); } - for (auto& weapon : weapon_arr) + for (const auto& [_, weapon] : weapon_arr) { if ( selected_ped_weapon_type == SPAWN_PED_ALL_WEAPONS || - weapon.weapon_type == weapon_type_arr[selected_ped_weapon_type] + weapon.m_weapon_type == weapon_type_arr[selected_ped_weapon_type] ) { - if (ImGui::Selectable(weapon.name.c_str(), weapon.hash == selected_ped_weapon_hash)) + if (ImGui::Selectable(weapon.m_display_name, weapon.m_hash == selected_ped_weapon_hash)) { - selected_ped_weapon_hash = weapon.hash; + selected_ped_weapon_hash = weapon.m_hash; } } - if (selected_ped_weapon_hash == weapon.hash) + if (selected_ped_weapon_hash == weapon.m_hash) { ImGui::SetItemDefaultFocus(); } diff --git a/premake5.lua b/premake5.lua index 9655028d..8b85731f 100644 --- a/premake5.lua +++ b/premake5.lua @@ -179,6 +179,28 @@ workspace "BigBaseV2" DeclareMSVCOptions() DeclareDebugOptions() + + project "pugixml" + location "vendor/%{prj.name}" + kind "StaticLib" + language "C++" + + targetdir ("bin/lib/" .. outputdir) + objdir ("bin/lib/int/" .. outputdir .. "/%{prj.name}") + + files + { + "vendor/%{prj.name}/src/**.cpp", + "vendor/%{prj.name}/src/**.hpp" + } + + includedirs + { + "vendor/%{prj.name}/src/" + } + + DeclareMSVCOptions() + DeclareDebugOptions() project "BigBaseV2" location "BigBaseV2" @@ -207,7 +229,8 @@ workspace "BigBaseV2" "vendor/GTAV-Classes", "vendor/ImGui", "vendor/json/single_include", - "vendor/MinHook/include" + "vendor/MinHook/include", + "vendor/pugixml/src" } libdirs @@ -220,7 +243,8 @@ workspace "BigBaseV2" "fmtlib", "g3log", "ImGui", - "MinHook" + "MinHook", + "pugixml" } pchheader "common.hpp" diff --git a/vendor/pugixml b/vendor/pugixml new file mode 160000 index 00000000..86c91051 --- /dev/null +++ b/vendor/pugixml @@ -0,0 +1 @@ +Subproject commit 86c91051541d18a0d24b837a866cf0306fc8db1a