use super::super::binary::scan_pattern;
use super::super::source::MemorySource;
use super::super::ue5::GNamesPool;
use anyhow::{bail, Result};
pub fn discover_gnames(source: &dyn MemorySource) -> Result<GNamesPool> {
let pattern = b"\x1e\x01None\x10\x03ByteProperty";
let mask = vec![1u8; pattern.len()];
let results = scan_pattern(source, pattern, &mask)?;
if results.is_empty() {
let alt_pattern: &[u8] = b"None";
let alt_mask = vec![1u8; alt_pattern.len()];
let alt_results = scan_pattern(source, alt_pattern, &alt_mask)?;
for addr in alt_results {
if addr < 2 {
continue;
}
if let Ok(data) = source.read_bytes(addr.saturating_sub(2), 64) {
if let Some(_pos) = data.windows(12).position(|w| w == b"ByteProperty") {
let gnames_addr = addr - 2;
let mut sample_names = Vec::new();
sample_names.push((0, "None".to_string()));
sample_names.push((1, "ByteProperty".to_string()));
if let Ok(pool_data) = source.read_bytes(gnames_addr, 4096) {
let mut offset = 0;
let mut index = 0u32;
while offset < pool_data.len() - 2 && sample_names.len() < 20 {
let len_byte = pool_data[offset];
let string_len = (len_byte >> 1) & 0x3F;
if string_len == 0 || string_len > 60 {
offset += 1;
continue;
}
let start = offset + 2; let end = start + string_len as usize;
if end <= pool_data.len() {
if let Ok(name) = String::from_utf8(pool_data[start..end].to_vec())
{
if name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
sample_names.push((index, name));
}
}
}
offset = end;
index += 1;
}
}
return Ok(GNamesPool {
address: gnames_addr,
sample_names,
});
}
}
}
bail!("GNames pool not found. The game may use a different FName format.");
}
let gnames_addr = results[0];
let sample_names = vec![(0, "None".to_string()), (1, "ByteProperty".to_string())];
Ok(GNamesPool {
address: gnames_addr,
sample_names,
})
}