ffxii-tza-auto-notes/src/game_state.rs

99 lines
3.7 KiB
Rust

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<u8>,
proc_mem: ProcessMemory,
story_pattern: Vec<u8>,
location_pattern: Vec<u8>,
}
impl GameState {
pub fn new() -> Result<Self> {
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)
}
}