use super::FakeHost;
use super::Installed;
use super::address;
use super::table;
use crate::FnPtr;
use crate::Hook;
use crate::abi::CallerFrame;
use crate::abi::Fingerprint;
use crate::abi::HookFlags;
impl FakeHost {
#[must_use]
pub fn base(mut self, base: u64) -> Self {
self.base = base;
self
}
#[must_use]
pub fn fingerprint(mut self, fingerprint: Fingerprint) -> Self {
self.fingerprint = fingerprint;
self
}
#[must_use]
pub fn build_table(mut self, table: crate::abi::BuildTable) -> Self {
self.table = table;
self
}
#[must_use]
pub fn symbol(mut self, name: &str, address: u64) -> Self {
self.symbols.insert(name.to_owned(), address);
self
}
#[must_use]
pub fn system(mut self, name: &str, address: u64) -> Self {
self.systems.insert(name.to_owned(), address);
self
}
#[must_use]
pub fn constant(mut self, name: &str, value: u64) -> Self {
self.constants.insert(name.to_owned(), value);
self
}
#[must_use]
pub fn field_offset(mut self, type_name: &str, field: &str, offset: u64) -> Self {
self.field_offsets
.insert((type_name.to_owned(), field.to_owned()), offset);
self
}
#[must_use]
pub fn type_size(mut self, type_name: &str, size: u32) -> Self {
self.type_sizes.insert(type_name.to_owned(), size);
self
}
#[must_use]
pub fn type_hash(mut self, type_name: &str, hash: u32) -> Self {
self.type_hashes.insert(type_name.to_owned(), hash);
self
}
#[must_use]
pub fn caller_frame(mut self, frame: CallerFrame) -> Self {
self.caller_frame = frame;
self
}
#[must_use]
pub fn refuse_hook(mut self, target: u64) -> Self {
self.refused_hooks.insert(target);
self
}
#[must_use]
pub fn code(mut self, code: &'static [u8]) -> Self {
self.code = (code.as_ptr().addr() as u64, code.len() as u64);
self
}
#[must_use]
pub fn workshop_recipes(mut self, limit: u32) -> Self {
self.workshop_recipes = limit;
self
}
}
#[expect(
clippy::unused_self,
reason = "the fake's state is process-wide; `self` proves it is installed"
)]
impl Installed {
#[must_use]
pub fn hooks(&self) -> Vec<LinkedHook> {
table::with(Vec::new(), |s| s.hooks.clone())
}
#[must_use]
pub fn mid_hooks(&self) -> Vec<u64> {
table::with(Vec::new(), |s| s.mid_hooks.iter().map(|h| h.0).collect())
}
#[must_use = "the count says whether the mod hooks the address"]
pub fn run_at(&self, address: u64, registers: &mut crate::abi::Registers) -> usize {
let handlers: Vec<_> = table::with(Vec::new(), |s| {
s.mid_hooks
.iter()
.filter(|h| h.0 == address)
.map(|h| (h.1, h.2))
.collect()
});
for &(handler, user) in &handlers {
unsafe { handler(registers, core::ptr::with_exposed_provenance_mut(user)) };
}
handlers.len()
}
#[must_use]
pub fn detour<F: FnPtr>(&self, hook: &'static Hook<F>) -> Option<F> {
let entry = address(hook.entry());
self.hooks()
.iter()
.any(|linked| linked.detour == entry)
.then(|| hook.entry())
}
#[must_use]
pub fn code_patches(&self) -> Vec<(u64, Vec<u8>, Vec<u8>)> {
table::with(Vec::new(), |s| s.code_patches.clone())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct LinkedHook {
pub target: u64,
pub detour: u64,
pub priority: i32,
pub flags: HookFlags,
}