use anyhow::{Context, Result}; use sysinfo::{PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt}; use vmemory::ProcessMemory; pub struct GameState { pub story: u16, pub location: u16, pid: u32, base: usize, base_size: usize, mem: Vec, proc_mem: ProcessMemory, story_pattern: Vec, location_pattern: Vec, } impl GameState { pub fn new() -> Result { let mut sys = System::new_with_specifics(RefreshKind::new().with_processes(ProcessRefreshKind::new())); let proc = sys.processes() .iter() .find(|(_, proc)| proc.name() == "FFXII_TZA.exe"); let (pid, _) = match proc { Some(p) => p, None => anyhow::bail!("could not find TZA process") }; let mem = ProcessMemory::attach_process(pid.as_u32()).unwrap(); let mut base = 0; let mut base_size = 0; process_list::for_each_module(pid.as_u32(), |(address, size), name| { let stem = name.file_stem().unwrap_or_default(); if stem == "FFXII_TZA" { base = address; base_size = size; } }).context("could not loop TZA modules")?; if base == 0 || base_size == 0 { anyhow::bail!("could not find TZA base address"); } let tza_mem = mem.read_memory(base, base_size, false); let story_ptr_pattern = crate::util::parse_pattern("48 8B 05 ?? ?? ?? ?? 48 85 C0 74 03 66 89 08 C3").unwrap(); let location_pattern = crate::util::parse_pattern("8B 0D ?? ?? ?? ?? FF D0 4C 89 3D ?? ?? ?? ?? 48 83 C4 30 41 5F C3").unwrap(); Ok(Self { story: 0, location: 0, pid: pid.as_u32(), base, base_size, mem: tza_mem, proc_mem: mem, story_pattern: story_ptr_pattern, location_pattern, }) } pub fn refresh(&mut self) -> Result<()> { let story_ptr_ptr = match crate::util::find_pattern(&self.mem, &self.story_pattern) { Some(ptr) => ptr, None => anyhow::bail!("could not find story pointer"), }; let location_ptr = match crate::util::find_pattern(&self.mem, &self.location_pattern) { Some(ptr) => ptr, None => anyhow::bail!("could not find location pointer"), }; let story_ptr_offset = match crate::util::get_static_address(&self.mem, story_ptr_ptr, self.base) { Some(addr) => addr, None => anyhow::bail!("could not find story pointer offset"), }; let location_ptr_offset = match crate::util::get_static_address(&self.mem, location_ptr, self.base) { Some(addr) => addr, None => anyhow::bail!("could not find location pointer offset"), }; let story_ptr_array: [u8; 8] = self.mem[story_ptr_offset..story_ptr_offset + 8].try_into().unwrap(); let story_ptr = u64::from_le_bytes(story_ptr_array) as usize - self.base; let story_vec = self.proc_mem.read_memory(self.base + story_ptr, 2, false); let story_array: [u8; 2] = story_vec.try_into().map_err(|_| anyhow::anyhow!("not enough story bytes"))?; self.story = u16::from_le_bytes(story_array); let location_vec = self.proc_mem.read_memory(self.base + location_ptr_offset, 2, false); let location_array: [u8; 2] = location_vec.try_into().map_err(|_| anyhow::anyhow!("not enough location bytes"))?; self.location = u16::from_le_bytes(location_array); Ok(()) } pub fn location_name(&self) -> Option<&'static str> { crate::util::LOCATIONS.get(&self.location).map(std::ops::Deref::deref) } }