Added local caching for peds, vehicles and weapons (#457)

This commit is contained in:
Quentin E. / iDeath 2022-10-19 00:30:32 +02:00 committed by GitHub
parent 04aad64439
commit 7f25ee41cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1714 additions and 638 deletions

4
.gitmodules vendored
View File

@ -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

View File

@ -53,4 +53,10 @@ namespace big::functions
//Sync signatures END
using reset_network_complaints = void(*)(CNetComplaintMgr* mgr);
}
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);
}

View File

@ -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);
}
}

View File

@ -0,0 +1,286 @@
#pragma once
#include "sysMemAllocator.hpp"
#include "pointers.hpp"
#include <array>
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<uint8_t, 32>& 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();
};
}

View File

@ -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;

View File

@ -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;
};

View File

@ -344,6 +344,57 @@ namespace big
m_reset_network_complaints = ptr.add(1).rip().as<functions::reset_network_complaints>();
});
// 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<functions::fidevice_get_device>();
});
// fiDevices
main_batch.add("FDS", "74 1B 48 8D 0D ? ? ? ? 41 8B D6", [this](memory::handle ptr)
{
m_fidevices = ptr.add(5).rip().as<uintptr_t>();
m_fidevices_len = ptr.add(5).rip().add(8).as<uint16_t*>();
});
// 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<functions::fipackfile_ctor>();
m_fipackfile_instances = ptr.add(26).rip().as<rage::fiPackfile**>();
});
// 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<functions::fipackfile_open_archive>();
});
// 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<functions::fipackfile_mount>();
});
// 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<functions::fipackfile_unmount>();
});
// 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<functions::fipackfile_unmount>();
});
// 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<const char*>();
m_online_version = ptr.add(0x24).rip().add(0x20).as<const char*>();
});
auto mem_region = memory::module(nullptr);
main_batch.run(mem_region);

View File

@ -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{};

View File

@ -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<char*>(&m_cache_header), sizeof(m_cache_header));
m_data = std::make_unique<std::uint8_t[]>(m_cache_header.m_data_size);
file.read(reinterpret_cast<char*>(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<const char*>(&m_cache_header), sizeof(m_cache_header));
file.write(reinterpret_cast<const char*>(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;
}
}

View File

@ -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<std::uint8_t[]>;
class cache_file final
{
public:
/// <summary>
///
/// </summary>
/// <param name="cache_file">FileMgr file object</param>
/// <param name="cache_version">Internal version, use this to invalidate the cache when changing the structure of the data</param>
cache_file(file cache_file, std::uint32_t cache_version);
/// <summary>
/// Frees any memory used to hold the cached data.
/// </summary>
void free();
/// <summary>
/// Attempts to load the cache from disk
/// </summary>
/// <returns>True after successfully loading the data, false if the file didn't exist.</returns>
bool load();
/// <summary>
/// Writes the cache to disk
/// </summary>
/// <returns></returns>
bool write() const;
std::uint8_t* data() const;
std::uint64_t data_size() const;
/// <summary>
/// Check if the cache file is up to date with the expected versions
/// </summary>
/// <param name="game_version">Current Game version</param>
/// <param name="online_version">Current Online version</param>
/// <returns>True if cache is up to date, false otherwise.</returns>
bool up_to_date(std::uint32_t game_version, float online_version) const;
void set_data(cache_data&& data, std::uint64_t data_size);
/// <summary>
/// Sets the version information of the cache header.
/// </summary>
/// <param name="game_version">Game Build</param>
/// <param name="online_version">Online Version</param>
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;
};
}

View File

@ -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",
&gta_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",
&gta_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",
&gta_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<std::string>& gta_data_service::get_vehicle_class_arr()
{
return m_vehicle_class_arr;
}
const std::vector<vehicle_item>& 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<std::string>& gta_data_service::get_ped_type_arr()
{
return m_ped_type_arr;
}
const std::vector<ped_item>& 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<std::string>& gta_data_service::get_weapon_type_arr()
{
return m_weapon_type_arr;
}
const std::vector<weapon_item>& 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<const ped_item*>(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<const vehicle_item*>(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<const weapon_item*>(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<std::uint32_t>;
hash_array mapped_peds;
hash_array mapped_vehicles;
hash_array mapped_weapons;
std::vector<ped_item> peds;
std::vector<vehicle_item> vehicles;
std::vector<weapon_item> 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<std::uint8_t[]>(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<std::uint8_t[]>(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<std::uint8_t[]>(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();
});
}
}

View File

@ -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<std::string> m_vehicle_class_arr;
std::map<Hash, int> m_vehicle_hash_idx_map;
std::vector<vehicle_item> m_vehicle_item_arr;
const vehicle_item empty_vehicle_item = vehicle_item();
IDLE,
NEEDS_UPDATE,
WAITING_FOR_ONLINE,
UPDATING
};
std::vector<std::string> m_ped_type_arr;
std::map<Hash, int> m_ped_hash_idx_map;
std::vector<ped_item> m_ped_item_arr;
const ped_item empty_ped_item = ped_item();
std::vector<std::string> m_weapon_type_arr;
std::map<Hash, int> m_weapon_hash_idx_map;
std::vector<weapon_item> m_weapon_item_arr;
const weapon_item empty_weapon_item = weapon_item();
using ped_map = std::map<std::string, ped_item>;
using vehicle_map = std::map<std::string, vehicle_item>;
using weapon_map = std::map<std::string, weapon_item>;
using string_vec = std::vector<std::string>;
class gta_data_service final
{
public:
gta_data_service();
~gta_data_service();
const vehicle_item& find_vehicle_by_hash(Hash hash);
const std::vector<std::string>& get_vehicle_class_arr();
const std::vector<vehicle_item>& 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<std::string>& get_ped_type_arr();
const std::vector<ped_item>& 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<std::string>& get_weapon_type_arr();
const std::vector<weapon_item>& 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{};
}
}

View File

@ -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"];
}
}

View File

@ -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)
}

View File

@ -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";
}
}
}
}

View File

@ -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)
}

View File

@ -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());
}
}
}
}

View File

@ -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)
}

View File

@ -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<std::string> get_non_dlc_mounted_devices_names()
{
std::vector<std::string> 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<size_t(yim_fipackfile& rpf_wrapper)> 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<std::filesystem::path> yim_fipackfile::get_file_paths(std::string parent)
{
std::vector<std::filesystem::path> file_paths;
if (parent.empty())
parent = mount_name;
std::vector<std::string> 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<std::uint8_t[]>(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<void(pugi::xml_document& doc)> cb)
{
read_file(path, [&cb](const std::unique_ptr<std::uint8_t[]>& 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);
}
});
}
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <pugixml.hpp>
namespace big
{
using file_contents_callback = std::function<void(const std::unique_ptr<std::uint8_t[]>& 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<size_t(yim_fipackfile& rpf_wrapper)> cb);
std::vector<std::filesystem::path> 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<void(pugi::xml_document& doc)> cb);
private:
};
}

View File

@ -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);
}
}

View File

@ -6,23 +6,26 @@
namespace big::session
{
void join_type(SessionType session)
inline void join_type(eSessionType session)
{
*script_global(2726795).as<int*>() = (session.id == eSessionType::SC_TV ? 1 : 0); // If SC TV Then Enable Spectator Mode
*script_global(2726795).as<int*>() = (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<int*>() = -1;
else
*script_global(1575015).as<int*>() = (int)session.id;
*script_global(1575015).as<int*>() = (int)session;
*script_global(1574589).as<int*>() = 1;
script::get_current()->yield(200ms);
*script_global(1574589).as<int*>() = 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();

View File

@ -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();
});

View File

@ -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");

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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();
}
};

View File

@ -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();
}
}
}

View File

@ -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();
}

View File

@ -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"

1
vendor/pugixml vendored Submodule

@ -0,0 +1 @@
Subproject commit 86c91051541d18a0d24b837a866cf0306fc8db1a