From 8b08b0ee68ac2afa2e4252b49fe191a781019bc7 Mon Sep 17 00:00:00 2001 From: Jujstme Date: Sun, 15 Oct 2023 00:53:14 +0200 Subject: [PATCH] Porting DeepPointer over from classic LiveSplit (#64) Despite the runtime already allows to resolve pointer paths with `process.read_pointer_path32()` and `process.read_pointer_path64()`, sometimes it's more convenient to define pointer paths, store them in a struct and dereference them when needed inside the autosplitter logic. This provides an essentially equal implementation of `DeepPointer` from OG LiveSplit, allowing for easier retrieval of memory addresses or values via the defined `deref_offset()` or `deeref::()` functions. --- src/deep_pointer.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 82 insertions(+) create mode 100644 src/deep_pointer.rs diff --git a/src/deep_pointer.rs b/src/deep_pointer.rs new file mode 100644 index 0000000..253d2b2 --- /dev/null +++ b/src/deep_pointer.rs @@ -0,0 +1,81 @@ +//! Support for storing pointer paths for easy dereferencing inside the autosplitter logic. + +use arrayvec::ArrayVec; +use bytemuck::CheckedBitPattern; + +use crate::{Address, Address32, Address64, Error, Process}; + +/// An abstraction of a pointer path, usable for easy dereferencing inside an autosplitter logic. +/// +/// The maximum depth of the pointer path is given by the generic parameter `CAP`. +/// Of note, `CAP` must be higher or equal to the number of offsets provided in `path`, +/// otherwise calling `new()` on this struct will trigger a ***Panic***. +#[derive(Clone)] +pub struct DeepPointer { + base_address: Address, + path: ArrayVec, + deref_type: DerefType, +} + +impl Default for DeepPointer { + /// Creates a new empty DeepPointer. + #[inline] + fn default() -> Self { + Self { + base_address: Address::default(), + path: ArrayVec::default(), + deref_type: DerefType::default(), + } + } +} + +impl DeepPointer { + /// Creates a new DeepPointer and specify the pointer size dereferencing + #[inline] + pub fn new(base_address: Address, deref_type: DerefType, path: &[u64]) -> Self { + assert!(CAP != 0 && CAP >= path.len()); + Self { + base_address, + path: path.iter().cloned().collect(), + deref_type, + } + } + + /// Creates a new DeepPointer with 32bit pointer size dereferencing + pub fn new_32bit(base_address: Address, path: &[u64]) -> Self { + Self::new(base_address, DerefType::Bit32, path) + } + + /// Creates a new DeepPointer with 64bit pointer size dereferencing + pub fn new_64bit(base_address: Address, path: &[u64]) -> Self { + Self::new(base_address, DerefType::Bit64, path) + } + + /// Dereferences the pointer path, returning the memory address of the value of interest + pub fn deref_offsets(&self, process: &Process) -> Result { + let mut address = self.base_address; + let (&last, path) = self.path.split_last().ok_or(Error {})?; + for &offset in path { + address = match self.deref_type { + DerefType::Bit32 => process.read::(address + offset)?.into(), + DerefType::Bit64 => process.read::(address + offset)?.into(), + }; + } + Ok(address + last) + } + + /// Dereferences the pointer path, returning the value stored at the final memory address + pub fn deref(&self, process: &Process) -> Result { + process.read(self.deref_offsets(process)?) + } +} + +/// Describes the pointer size that should be used while deferecencing a pointer path +#[derive(Copy, Clone, Default)] +pub enum DerefType { + /// 4-byte pointer size, used in 32bit processes + Bit32, + /// 8-byte pointer size, used in 64bit processes + #[default] + Bit64, +} diff --git a/src/lib.rs b/src/lib.rs index cd479a1..a53754b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,6 +143,7 @@ pub mod time_util; #[cfg(all(feature = "wasi-no-std", target_os = "wasi"))] mod wasi_no_std; pub mod watcher; +pub mod deep_pointer; pub use self::{primitives::*, runtime::*}; pub use arrayvec;