Fix GTA data service (#1635)
* fix(gta_data): service now parses unloaded RPF files
This commit is contained in:
@ -30,9 +30,9 @@ namespace big
|
||||
}
|
||||
|
||||
gta_data_service::gta_data_service() :
|
||||
m_peds_cache(g_file_manager->get_project_file("./cache/peds.bin"), 4),
|
||||
m_vehicles_cache(g_file_manager->get_project_file("./cache/vehicles.bin"), 3),
|
||||
m_weapons_cache(g_file_manager->get_project_file("./cache/weapons.bin"), 4),
|
||||
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())
|
||||
@ -264,178 +264,170 @@ namespace big
|
||||
|
||||
LOG(INFO) << "Rebuilding cache started...";
|
||||
|
||||
yim_fipackfile::add_wrapper_call_back([&](yim_fipackfile& rpf_wrapper) {
|
||||
const auto files = rpf_wrapper.get_file_paths();
|
||||
for (const auto& file : files)
|
||||
yim_fipackfile::add_wrapper_call_back([&](yim_fipackfile& rpf_wrapper, std::filesystem::path path) -> void {
|
||||
if (path.filename() == "setup2.xml")
|
||||
{
|
||||
if (file.filename() == "setup2.xml")
|
||||
std::string dlc_name;
|
||||
rpf_wrapper.read_xml_file(path, [&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")
|
||||
{
|
||||
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(VERBOSE) << "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, manufacturer_display, sizeof(veh.m_display_manufacturer));
|
||||
|
||||
const auto game_name = item.child("gameName").text().as_string();
|
||||
std::strncpy(veh.m_display_name, 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 (hash == RAGE_JOAAT("WEAPON_BIRD_CRAP"))
|
||||
continue;
|
||||
|
||||
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 || std::strcmp(human_name_hash, "WT_VEHMINE") == 0)
|
||||
continue;
|
||||
|
||||
auto weapon = weapon_item{};
|
||||
|
||||
std::strncpy(weapon.m_name, name, sizeof(weapon.m_name));
|
||||
|
||||
std::strncpy(weapon.m_display_name, human_name_hash, sizeof(weapon.m_display_name));
|
||||
|
||||
auto weapon_flags = std::string(item.child("WeaponFlags").text().as_string());
|
||||
|
||||
bool is_gun = false;
|
||||
bool is_rechargable = false;
|
||||
|
||||
const char* category = "";
|
||||
|
||||
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;
|
||||
}
|
||||
else if (flag == "Vehicle" || flag == "HiddenFromWeaponWheel" || flag == "NotAWeapon")
|
||||
{
|
||||
goto skip;
|
||||
}
|
||||
|
||||
weapon_flags.erase(0, pos + 1);
|
||||
}
|
||||
|
||||
category = item.child("Group").text().as_string();
|
||||
|
||||
if (std::strlen(category) == 0 || std::strcmp(category, "GROUP_DIGISCANNER") == 0)
|
||||
continue;
|
||||
|
||||
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));
|
||||
skip:
|
||||
continue;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (file.filename() == "peds.meta")
|
||||
{
|
||||
rpf_wrapper.read_xml_file(file, [&exists, &peds, &mapped_peds](pugi::xml_document& doc) {
|
||||
parse_ped(peds, mapped_peds, doc);
|
||||
});
|
||||
}
|
||||
else if (std::string str = rpf_wrapper.get_name(); (str.find("componentpeds") != std::string::npos || str.find("streamedpeds") != std::string::npos || str.find("mppatches") != std::string::npos || str.find("cutspeds") != std::string::npos) && file.extension() == ".yft")
|
||||
{
|
||||
const auto name = file.stem().string();
|
||||
const auto hash = rage::joaat(name);
|
||||
|
||||
if (is_crash_ped(hash))
|
||||
continue;
|
||||
|
||||
if (std::find(mapped_peds.begin(), mapped_peds.end(), hash) != mapped_peds.end())
|
||||
continue;
|
||||
|
||||
mapped_peds.emplace_back(hash);
|
||||
|
||||
auto ped = ped_item{};
|
||||
|
||||
std::strncpy(ped.m_name, name.c_str(), sizeof(ped.m_name));
|
||||
|
||||
ped.m_hash = hash;
|
||||
|
||||
peds.emplace_back(std::move(ped));
|
||||
LOG(VERBOSE) << "Bad DLC, skipping...";
|
||||
}
|
||||
}
|
||||
else if (path.filename() == "vehicles.meta")
|
||||
{
|
||||
rpf_wrapper.read_xml_file(path, [&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();
|
||||
|
||||
return files.size();
|
||||
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, manufacturer_display, sizeof(veh.m_display_manufacturer));
|
||||
|
||||
const auto game_name = item.child("gameName").text().as_string();
|
||||
std::strncpy(veh.m_display_name, 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 = 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) {
|
||||
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 (hash == RAGE_JOAAT("WEAPON_BIRD_CRAP"))
|
||||
continue;
|
||||
|
||||
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 || std::strcmp(human_name_hash, "WT_VEHMINE") == 0)
|
||||
continue;
|
||||
|
||||
auto weapon = weapon_item{};
|
||||
|
||||
std::strncpy(weapon.m_name, name, sizeof(weapon.m_name));
|
||||
|
||||
std::strncpy(weapon.m_display_name, human_name_hash, sizeof(weapon.m_display_name));
|
||||
|
||||
auto weapon_flags = std::string(item.child("WeaponFlags").text().as_string());
|
||||
|
||||
bool is_gun = false;
|
||||
bool is_rechargable = false;
|
||||
|
||||
const char* category = "";
|
||||
|
||||
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;
|
||||
}
|
||||
else if (flag == "Vehicle" || flag == "HiddenFromWeaponWheel" || flag == "NotAWeapon")
|
||||
{
|
||||
goto skip;
|
||||
}
|
||||
|
||||
weapon_flags.erase(0, pos + 1);
|
||||
}
|
||||
|
||||
category = item.child("Group").text().as_string();
|
||||
|
||||
if (std::strlen(category) == 0 || std::strcmp(category, "GROUP_DIGISCANNER") == 0)
|
||||
continue;
|
||||
|
||||
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));
|
||||
skip:
|
||||
continue;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (path.filename() == "peds.meta")
|
||||
{
|
||||
rpf_wrapper.read_xml_file(path, [&exists, &peds, &mapped_peds](pugi::xml_document& doc) {
|
||||
parse_ped(peds, mapped_peds, doc);
|
||||
});
|
||||
}
|
||||
else if (std::string str = rpf_wrapper.get_name(); (str.find("componentpeds") != std::string::npos || str.find("streamedpeds") != std::string::npos || str.find("mppatches") != std::string::npos || str.find("cutspeds") != std::string::npos) && path.extension() == ".yft")
|
||||
{
|
||||
const auto name = path.stem().string();
|
||||
const auto hash = rage::joaat(name);
|
||||
|
||||
if (is_crash_ped(hash))
|
||||
return;
|
||||
|
||||
if (std::find(mapped_peds.begin(), mapped_peds.end(), hash) != mapped_peds.end())
|
||||
return;
|
||||
|
||||
mapped_peds.emplace_back(hash);
|
||||
|
||||
auto ped = ped_item{};
|
||||
|
||||
std::strncpy(ped.m_name, name.c_str(), sizeof(ped.m_name));
|
||||
|
||||
ped.m_hash = hash;
|
||||
|
||||
peds.emplace_back(std::move(ped));
|
||||
}
|
||||
});
|
||||
|
||||
if (state() == eGtaDataUpdateState::UPDATING)
|
||||
|
@ -8,30 +8,67 @@ namespace big
|
||||
{
|
||||
yim_fipackfile::yim_fipackfile(rage::fiPackfile* rpf, const std::string& mount_name)
|
||||
{
|
||||
this->rpf = rpf;
|
||||
this->rpf = rpf;
|
||||
this->mount_name = mount_name;
|
||||
}
|
||||
|
||||
void yim_fipackfile::add_wrapper_call_back(std::function<size_t(yim_fipackfile& rpf_wrapper)> cb)
|
||||
void yim_fipackfile::add_wrapper_call_back(std::function<void(yim_fipackfile& rpf_wrapper, std::filesystem::path path)> cb)
|
||||
{
|
||||
m_wrapper_call_back.push_back(cb);
|
||||
}
|
||||
|
||||
void yim_fipackfile::for_each_fipackfile()
|
||||
void yim_fipackfile::traverse_rpf_file(const std::string& path, int depth)
|
||||
{
|
||||
for (int i = 1; i < 3672; i++) // fipackfile ctor start with 1
|
||||
std::string mount_path = std::format("temp{}:/", depth);
|
||||
|
||||
rage::fiPackfile packfile;
|
||||
packfile.OpenPackfile(path.c_str(), true, 0, 0);
|
||||
packfile.Mount(mount_path.c_str());
|
||||
|
||||
yim_fipackfile rpf_wrapper = yim_fipackfile(&packfile, mount_path);
|
||||
|
||||
const auto files = rpf_wrapper.get_file_paths();
|
||||
for (const auto& file : files)
|
||||
{
|
||||
auto* rpf = g_pointers->m_gta.m_fipackfile_instances[i];
|
||||
|
||||
if (rpf)
|
||||
if (file.extension() == ".rpf")
|
||||
{
|
||||
yim_fipackfile rpf_wrapper = yim_fipackfile(rpf);
|
||||
if (auto handle = ((rage::fiDevice*)&packfile)->Open(file.string().c_str(), true))
|
||||
{
|
||||
uint32_t encryption_type{};
|
||||
((rage::fiDevice*)&packfile)->Seek(handle, 12, 0);
|
||||
((rage::fiDevice*)&packfile)->Read(handle, &encryption_type, 4);
|
||||
((rage::fiDevice*)&packfile)->Close(handle);
|
||||
|
||||
std::for_each(yim_fipackfile::m_wrapper_call_back.begin(), yim_fipackfile::m_wrapper_call_back.end(), [&rpf_wrapper](std::function<size_t(yim_fipackfile & rpf_wrapper)> cb) {
|
||||
cb(rpf_wrapper);
|
||||
if (encryption_type == 0xFFFFFF9)
|
||||
continue; // skip AES encrypted RPFs
|
||||
|
||||
traverse_rpf_file(file.string(), depth + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::for_each(yim_fipackfile::m_wrapper_call_back.begin(), yim_fipackfile::m_wrapper_call_back.end(), [&rpf_wrapper, file](std::function<void(yim_fipackfile & rpf_wrapper, std::filesystem::path path)> cb) {
|
||||
cb(rpf_wrapper, file);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
packfile.Unmount(mount_path.c_str());
|
||||
packfile.ClosePackfile();
|
||||
}
|
||||
|
||||
void yim_fipackfile::for_each_fipackfile()
|
||||
{
|
||||
for (auto& entry : std::filesystem::recursive_directory_iterator(std::filesystem::current_path()))
|
||||
{
|
||||
auto rel_path = std::filesystem::relative(entry.path());
|
||||
|
||||
if (rel_path.string().contains("mods"))
|
||||
continue;
|
||||
|
||||
if (rel_path.extension() == ".rpf")
|
||||
traverse_rpf_file(rel_path.string());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> yim_fipackfile::get_file_paths(std::string parent)
|
||||
@ -50,7 +87,7 @@ namespace big
|
||||
{
|
||||
std::string fn;
|
||||
|
||||
if (parent == "/")
|
||||
if (parent == mount_name)
|
||||
fn = std::string(parent.c_str()) + std::string(findData.fileName);
|
||||
else
|
||||
fn = std::string(parent.c_str()) + std::string("/") + std::string(findData.fileName);
|
||||
|
@ -13,10 +13,11 @@ namespace big
|
||||
public:
|
||||
explicit yim_fipackfile(rage::fiPackfile* rpf, const std::string& mount_name = "/");
|
||||
|
||||
static inline std::vector<std::function<size_t(yim_fipackfile& rpf_wrapper)>> m_wrapper_call_back;
|
||||
static inline std::vector<std::function<void(yim_fipackfile& rpf_wrapper, std::filesystem::path path)>> m_wrapper_call_back;
|
||||
|
||||
static void add_wrapper_call_back(std::function<size_t(yim_fipackfile& rpf_wrapper)> cb);
|
||||
static void add_wrapper_call_back(std::function<void(yim_fipackfile& rpf_wrapper, std::filesystem::path path)> cb);
|
||||
|
||||
static void traverse_rpf_file(const std::string& path, int depth = 0);
|
||||
static void for_each_fipackfile();
|
||||
|
||||
std::vector<std::filesystem::path> get_file_paths(std::string parent = {});
|
||||
|
Reference in New Issue
Block a user