Refactored weapons.bin into weapons.json for extensibility and readability. (#1632)
* Refactored weapons.bin into weapons.json for extensibility and human readability. Added weapon attachments scraping from the meta files (currently is missing a lot of attachments, more than half, requires RPF reading refactoring to fix.) Added Ammunation to Self -> Weapons, because it's vital you protect yourself, the patriotic way. * Fixed weapons.xml not properly populating all the components. Refactored buttons to use components::button. * Refactored the Attachments code to implicitly trust that the attachments will be there now. Added proper versioning to the weapons.json file. Removed debug logging from gta_data_service.cpp. * Fixed Ammunation buttons. Added loading message for the new weapons.json system. Fixed a bug where two components shared the same name, the user could not select the 2nd component. Fixed Attachments displaying an attachment from a previous weapon if the user changed weapons. * Fixed Tint Apply button not using the components::button template.
This commit is contained in:
@ -32,7 +32,6 @@ namespace big
|
||||
gta_data_service::gta_data_service() :
|
||||
m_peds_cache(g_file_manager->get_project_file("./cache/peds.bin"), 5),
|
||||
m_vehicles_cache(g_file_manager->get_project_file("./cache/vehicles.bin"), 4),
|
||||
m_weapons_cache(g_file_manager->get_project_file("./cache/weapons.bin"), 5),
|
||||
m_update_state(eGtaDataUpdateState::IDLE)
|
||||
{
|
||||
if (!is_cache_up_to_date())
|
||||
@ -120,12 +119,28 @@ namespace big
|
||||
|
||||
const weapon_item& gta_data_service::weapon_by_hash(std::uint32_t hash)
|
||||
{
|
||||
for (const auto& [name, weapon] : m_weapons)
|
||||
for (const auto& [name, weapon] : m_weapons_cache.weapon_map)
|
||||
if (rage::joaat(name) == hash)
|
||||
return weapon;
|
||||
return gta_data_service::empty_weapon;
|
||||
}
|
||||
|
||||
const weapon_component& gta_data_service::weapon_component_by_hash(std::uint32_t hash)
|
||||
{
|
||||
for (const auto& component : m_weapons_cache.weapon_components)
|
||||
if (component.second.m_hash == hash)
|
||||
return component.second;
|
||||
return gta_data_service::empty_component;
|
||||
}
|
||||
|
||||
const weapon_component& gta_data_service::weapon_component_by_name(std::string name)
|
||||
{
|
||||
for (const auto& component : m_weapons_cache.weapon_components)
|
||||
if (component.first == name)
|
||||
return component.second;
|
||||
return gta_data_service::empty_component;
|
||||
}
|
||||
|
||||
string_vec& gta_data_service::ped_types()
|
||||
{
|
||||
return m_ped_types;
|
||||
@ -145,7 +160,26 @@ namespace big
|
||||
{
|
||||
m_peds_cache.load();
|
||||
m_vehicles_cache.load();
|
||||
m_weapons_cache.load();
|
||||
|
||||
auto weapons_file = g_file_manager->get_project_file("./cache/weapons.json");
|
||||
if (weapons_file.exists())
|
||||
{
|
||||
std::ifstream file(weapons_file.get_path());
|
||||
file.open(weapons_file.get_path());
|
||||
|
||||
try
|
||||
{
|
||||
nlohmann::json weapons_file_json;
|
||||
file >> weapons_file_json;
|
||||
m_weapons_cache = weapons_file_json["weapons_cache"];
|
||||
file.close();
|
||||
}
|
||||
catch (const std::exception& exception)
|
||||
{
|
||||
file.close();
|
||||
LOG(WARNING) << "Detected corrupt weapons: " << exception.what();
|
||||
}
|
||||
}
|
||||
|
||||
const auto file_version = memory::module("GTA5.exe").size();
|
||||
|
||||
@ -158,7 +192,8 @@ namespace big
|
||||
|
||||
load_peds();
|
||||
load_vehicles();
|
||||
load_weapons();
|
||||
LOG(INFO) << "Loading " << m_weapons_cache.weapon_map.size() << " weapons from cache.";
|
||||
LOG(INFO) << "Loading " << m_weapons_cache.weapon_components.size() << " weapon components from cache.";
|
||||
|
||||
LOG(VERBOSE) << "Loaded all data from cache.";
|
||||
}
|
||||
@ -199,24 +234,6 @@ namespace big
|
||||
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();
|
||||
}
|
||||
|
||||
inline void parse_ped(std::vector<ped_item>& peds, std::vector<std::uint32_t>& mapped_peds, pugi::xml_document& doc)
|
||||
{
|
||||
const auto& items = doc.select_nodes("/CPedModelInfo__InitDataList/InitDatas/Item");
|
||||
@ -253,10 +270,12 @@ namespace big
|
||||
hash_array mapped_peds;
|
||||
hash_array mapped_vehicles;
|
||||
hash_array mapped_weapons;
|
||||
hash_array mapped_components;
|
||||
|
||||
std::vector<ped_item> peds;
|
||||
std::vector<vehicle_item> vehicles;
|
||||
std::vector<weapon_item> weapons;
|
||||
std::vector<weapon_component> weapon_components;
|
||||
|
||||
constexpr auto exists = [](const hash_array& arr, std::uint32_t val) -> bool {
|
||||
return std::find(arr.begin(), arr.end(), val) != arr.end();
|
||||
@ -313,6 +332,46 @@ namespace big
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (const auto file_str = path.string(); file_str.find("weaponcomponents") != std::string::npos && path.extension() == ".meta")
|
||||
{
|
||||
rpf_wrapper.read_xml_file(path, [&exists, &weapon_components, &mapped_components](pugi::xml_document& doc) {
|
||||
const auto& items = doc.select_nodes("/CWeaponComponentInfoBlob/Infos/*[self::Item[@type='CWeaponComponentInfo'] or self::Item[@type='CWeaponComponentFlashLightInfo'] or self::Item[@type='CWeaponComponentScopeInfo'] or self::Item[@type='CWeaponComponentSuppressorInfo'] or self::Item[@type='CWeaponComponentVariantModelInfo'] or self::Item[@type='CWeaponComponentClipInfo']]");
|
||||
for (const auto& item_node : items)
|
||||
{
|
||||
const auto item = item_node.node();
|
||||
const std::string name = item.child("Name").text().as_string();
|
||||
const auto hash = rage::joaat(name);
|
||||
|
||||
if (!name.starts_with("COMPONENT"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exists(mapped_components, hash))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
mapped_components.emplace_back(hash);
|
||||
|
||||
std::string LocName = item.child("LocName").text().as_string();
|
||||
std::string LocDesc = item.child("LocDesc").text().as_string();
|
||||
|
||||
if (LocName.ends_with("INVALID") || LocName.ends_with("RAIL"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
weapon_component component;
|
||||
|
||||
component.m_name = name;
|
||||
component.m_hash = hash;
|
||||
component.m_display_name = LocName;
|
||||
component.m_display_desc = LocDesc;
|
||||
|
||||
weapon_components.push_back(component);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (const auto file_str = path.string(); file_str.find("weapon") != std::string::npos && path.extension() == ".meta")
|
||||
{
|
||||
rpf_wrapper.read_xml_file(path, [&exists, &weapons, &mapped_weapons](pugi::xml_document& doc) {
|
||||
@ -336,9 +395,9 @@ namespace big
|
||||
|
||||
auto weapon = weapon_item{};
|
||||
|
||||
std::strncpy(weapon.m_name, name, sizeof(weapon.m_name));
|
||||
weapon.m_name = name;
|
||||
|
||||
std::strncpy(weapon.m_display_name, human_name_hash, sizeof(weapon.m_display_name));
|
||||
weapon.m_display_name = human_name_hash;
|
||||
|
||||
auto weapon_flags = std::string(item.child("WeaponFlags").text().as_string());
|
||||
|
||||
@ -378,10 +437,10 @@ namespace big
|
||||
|
||||
if (std::strlen(category) > 6)
|
||||
{
|
||||
std::strncpy(weapon.m_weapon_type, category + 6, sizeof(weapon.m_weapon_type));
|
||||
weapon.m_weapon_type = category + 6;
|
||||
}
|
||||
|
||||
if (is_gun || !std::strcmp(weapon.m_weapon_type, "MELEE") || !std::strcmp(weapon.m_weapon_type, "UNARMED"))
|
||||
if (is_gun || weapon.m_weapon_type == "MELEE" || weapon.m_weapon_type == "UNARMED")
|
||||
{
|
||||
const std::string reward_prefix = "REWARD_";
|
||||
weapon.m_reward_hash = rage::joaat(reward_prefix + name);
|
||||
@ -393,6 +452,14 @@ namespace big
|
||||
}
|
||||
}
|
||||
|
||||
for (pugi::xml_node attach_point : item.child("AttachPoints").children("Item"))
|
||||
{
|
||||
for (pugi::xml_node component : attach_point.child("Components").children("Item"))
|
||||
{
|
||||
weapon.m_attachments.push_back(component.child_value("Name"));
|
||||
}
|
||||
}
|
||||
|
||||
weapon.m_hash = hash;
|
||||
|
||||
weapons.emplace_back(std::move(weapon));
|
||||
@ -453,7 +520,12 @@ namespace big
|
||||
}
|
||||
for (auto& item : weapons)
|
||||
{
|
||||
std::strncpy(item.m_display_name, HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(item.m_display_name), sizeof(item.m_display_name));
|
||||
item.m_display_name = HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(item.m_display_name.c_str());
|
||||
}
|
||||
for (auto& item : weapon_components)
|
||||
{
|
||||
item.m_display_name = HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(item.m_display_name.c_str());
|
||||
item.m_display_desc = HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(item.m_display_desc.c_str());
|
||||
}
|
||||
for (auto it = peds.begin(); it != peds.end();)
|
||||
{
|
||||
@ -481,10 +553,10 @@ namespace big
|
||||
|
||||
m_update_state = eGtaDataUpdateState::IDLE;
|
||||
LOG(INFO) << "Cache has been rebuilt.\n\tPeds: " << peds.size() << "\n\tVehicles: " << vehicles.size()
|
||||
<< "\n\tWeapons: " << weapons.size();
|
||||
<< "\n\tWeapons: " << weapons.size() << "\n\tWeaponComponents: " << weapon_components.size();
|
||||
|
||||
LOG(VERBOSE) << "Starting cache saving procedure...";
|
||||
g_thread_pool->push([this, peds = std::move(peds), vehicles = std::move(vehicles), weapons = std::move(weapons)] {
|
||||
g_thread_pool->push([this, peds = std::move(peds), vehicles = std::move(vehicles), weapons = std::move(weapons), weapon_components = std::move(weapon_components)] {
|
||||
const auto file_version = memory::module("GTA5.exe").size();
|
||||
|
||||
{
|
||||
@ -506,12 +578,34 @@ namespace big
|
||||
}
|
||||
|
||||
{
|
||||
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.version_info.m_game_build = g_pointers->m_gta.m_game_version;
|
||||
m_weapons_cache.version_info.m_online_version = g_pointers->m_gta.m_online_version;
|
||||
m_weapons_cache.version_info.m_file_version = file_version;
|
||||
|
||||
m_weapons_cache.set_header_version(file_version);
|
||||
m_weapons_cache.write();
|
||||
for (auto weapon : weapons)
|
||||
{
|
||||
add_if_not_exists(m_weapon_types, weapon.m_weapon_type);
|
||||
m_weapons_cache.weapon_map.insert({weapon.m_name, weapon});
|
||||
}
|
||||
|
||||
for (auto weapon_component : weapon_components)
|
||||
{
|
||||
m_weapons_cache.weapon_components.insert({weapon_component.m_name, weapon_component});
|
||||
}
|
||||
|
||||
auto weapons_file = big::g_file_manager->get_project_file("./cache/weapons.json");
|
||||
std::ofstream file(weapons_file.get_path());
|
||||
try
|
||||
{
|
||||
nlohmann::json weapons_file_json;
|
||||
weapons_file_json["weapons_cache"] = m_weapons_cache;
|
||||
file << weapons_file_json;
|
||||
file.flush();
|
||||
}
|
||||
catch (const std::exception& exception)
|
||||
{
|
||||
LOG(WARNING) << "Failed to write weapons JSON: " << exception.what();
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << "Finished writing cache to disk.";
|
||||
@ -519,4 +613,4 @@ namespace big
|
||||
load_data();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
#include "cache_file.hpp"
|
||||
#include "ped_item.hpp"
|
||||
#include "vehicle_item.hpp"
|
||||
#include "weapon_item.hpp"
|
||||
#include "weapon_file.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
@ -20,7 +20,6 @@ namespace big
|
||||
|
||||
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
|
||||
@ -39,6 +38,8 @@ namespace big
|
||||
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_component& weapon_component_by_hash(std::uint32_t hash);
|
||||
const weapon_component& weapon_component_by_name(std::string name);
|
||||
|
||||
string_vec& ped_types();
|
||||
string_vec& vehicle_classes();
|
||||
@ -52,9 +53,13 @@ namespace big
|
||||
{
|
||||
return m_vehicles;
|
||||
}
|
||||
weapon_map& weapons()
|
||||
std::map<std::string, weapon_item>& weapons()
|
||||
{
|
||||
return m_weapons;
|
||||
return m_weapons_cache.weapon_map;
|
||||
}
|
||||
std::map<std::string, weapon_component>& weapon_components()
|
||||
{
|
||||
return m_weapons_cache.weapon_components;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -63,19 +68,17 @@ namespace big
|
||||
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;
|
||||
weapon_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;
|
||||
@ -87,6 +90,7 @@ namespace big
|
||||
static constexpr ped_item empty_ped{};
|
||||
static constexpr vehicle_item empty_vehicle{};
|
||||
static constexpr weapon_item empty_weapon{};
|
||||
static constexpr weapon_component empty_component{};
|
||||
};
|
||||
|
||||
inline gta_data_service* g_gta_data_service{};
|
||||
|
15
src/services/gta_data/weapon_component.hpp
Normal file
15
src/services/gta_data/weapon_component.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
class weapon_component final
|
||||
{
|
||||
public:
|
||||
std::string m_name;
|
||||
Hash m_hash;
|
||||
std::string m_display_name;
|
||||
std::string m_display_desc;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(weapon_component, m_name, m_hash, m_display_name, m_display_desc)
|
||||
};
|
||||
}
|
30
src/services/gta_data/weapon_file.hpp
Normal file
30
src/services/gta_data/weapon_file.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "weapon_item.hpp"
|
||||
#include "weapon_component.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
|
||||
class weapon_file
|
||||
{
|
||||
public:
|
||||
struct version_info
|
||||
{
|
||||
std::string m_game_build;
|
||||
std::string m_online_version;
|
||||
std::uint32_t m_file_version;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(version_info, m_game_build, m_online_version, m_file_version)
|
||||
} version_info{};
|
||||
|
||||
std::map<std::string, weapon_item> weapon_map;
|
||||
std::map<std::string, weapon_component> weapon_components;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(weapon_file, version_info, weapon_map, weapon_components)
|
||||
|
||||
bool up_to_date(std::uint32_t file_version) const
|
||||
{
|
||||
return file_version == version_info.m_file_version;
|
||||
}
|
||||
};
|
||||
}
|
@ -2,17 +2,18 @@
|
||||
|
||||
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::string m_name;
|
||||
std::string m_display_name;
|
||||
std::string m_weapon_type;
|
||||
std::uint32_t m_hash;
|
||||
std::uint32_t m_reward_hash;
|
||||
std::uint32_t m_reward_ammo_hash;
|
||||
std::vector<std::string> m_attachments;
|
||||
bool m_throwable;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(weapon_item, m_name, m_display_name, m_weapon_type, m_hash, m_reward_hash, m_reward_ammo_hash, m_attachments, m_throwable)
|
||||
};
|
||||
#pragma pack(pop)
|
||||
}
|
@ -181,4 +181,4 @@ namespace big
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user