mirror of
https://github.com/Mr-X-GTA/YimMenu.git
synced 2025-06-24 01:22:23 +08:00
refactor!: Replace premake5 with CMake. (#551)
Co-authored-by: tupoy-ya <tupoy-ya@users.noreply.github.com>
This commit is contained in:
84
src/services/gta_data/cache_file.cpp
Normal file
84
src/services/gta_data/cache_file.cpp
Normal 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;
|
||||
}
|
||||
}
|
71
src/services/gta_data/cache_file.hpp
Normal file
71
src/services/gta_data/cache_file.hpp
Normal 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;
|
||||
};
|
||||
}
|
429
src/services/gta_data/gta_data_service.cpp
Normal file
429
src/services/gta_data/gta_data_service.cpp
Normal file
@ -0,0 +1,429 @@
|
||||
#include "gta_data_service.hpp"
|
||||
#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
|
||||
{
|
||||
bool add_if_not_exists(string_vec& vec, std::string str)
|
||||
{
|
||||
if (std::find(vec.begin(), vec.end(), str) != vec.end())
|
||||
return true;
|
||||
|
||||
vec.emplace_back(std::move(str));
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
gta_data_service::~gta_data_service()
|
||||
{
|
||||
g_gta_data_service = nullptr;
|
||||
}
|
||||
|
||||
bool gta_data_service::cache_needs_update() const
|
||||
{
|
||||
return m_update_state == eGtaDataUpdateState::NEEDS_UPDATE;
|
||||
}
|
||||
|
||||
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]
|
||||
{
|
||||
session::join_type(eSessionType::SOLO);
|
||||
|
||||
while (!*g_pointers->m_is_session_started)
|
||||
{
|
||||
script::get_current()->yield(100ms);
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
std::string dlc_name;
|
||||
rpf_wrapper.read_xml_file(file, [&dlc_name](pugi::xml_document& doc)
|
||||
{
|
||||
const auto item = doc.select_node("/SSetupData/nameHash");
|
||||
dlc_name = item.node().text().as_string();
|
||||
});
|
||||
|
||||
if (dlc_name == "mpG9EC")
|
||||
{
|
||||
LOG(G3LOG_DEBUG) << "Bad DLC, skipping...";
|
||||
|
||||
return std::size_t(0);
|
||||
}
|
||||
}
|
||||
else if (file.filename() == "vehicles.meta")
|
||||
{
|
||||
rpf_wrapper.read_xml_file(file, [&exists, &vehicles, &mapped_vehicles](pugi::xml_document& doc)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (file.filename() == "peds.meta" || file.filename() == "peds.ymt")
|
||||
{
|
||||
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));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
{
|
||||
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);
|
||||
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
{
|
||||
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);
|
||||
|
||||
m_vehicles_cache.set_header_version(game_version, online_version);
|
||||
m_vehicles_cache.write();
|
||||
}
|
||||
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
LOG(INFO) << "Finished writing cache to disk.";
|
||||
|
||||
load_data();
|
||||
});
|
||||
}
|
||||
}
|
82
src/services/gta_data/gta_data_service.hpp
Normal file
82
src/services/gta_data/gta_data_service.hpp
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "cache_file.hpp"
|
||||
#include "ped_item.hpp"
|
||||
#include "vehicle_item.hpp"
|
||||
#include "weapon_item.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
enum class eGtaDataUpdateState
|
||||
{
|
||||
IDLE,
|
||||
NEEDS_UPDATE,
|
||||
WAITING_FOR_ONLINE,
|
||||
UPDATING
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
bool cache_needs_update() const;
|
||||
eGtaDataUpdateState state() const;
|
||||
void update_in_online();
|
||||
void update_now();
|
||||
|
||||
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);
|
||||
|
||||
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:
|
||||
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 {};
|
||||
|
||||
};
|
||||
|
||||
inline gta_data_service* g_gta_data_service{};
|
||||
}
|
14
src/services/gta_data/ped_item.hpp
Normal file
14
src/services/gta_data/ped_item.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
#pragma pack(push, 4)
|
||||
class ped_item final
|
||||
{
|
||||
public:
|
||||
char m_name[32];
|
||||
char m_ped_type[16];
|
||||
std::uint32_t m_hash;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
}
|
16
src/services/gta_data/vehicle_item.hpp
Normal file
16
src/services/gta_data/vehicle_item.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
#pragma pack(push, 4)
|
||||
class vehicle_item final
|
||||
{
|
||||
public:
|
||||
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)
|
||||
}
|
18
src/services/gta_data/weapon_item.hpp
Normal file
18
src/services/gta_data/weapon_item.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
#pragma pack(push, 4)
|
||||
class weapon_item final
|
||||
{
|
||||
public:
|
||||
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)
|
||||
}
|
216
src/services/gta_data/yim_fipackfile.cpp
Normal file
216
src/services/gta_data/yim_fipackfile.cpp
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
27
src/services/gta_data/yim_fipackfile.hpp
Normal file
27
src/services/gta_data/yim_fipackfile.hpp
Normal 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:
|
||||
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user