use crate::{addressmap::AddressMap, error::Error, util::read_u32_le};
#[derive(Debug, Clone, Copy)]
pub struct MethodLink {
pub thunk_va: u32,
pub code_va: u32,
pub this_adjust: Option<u32>,
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct MethodLinkIterator<'a, 'p> {
map: &'p AddressMap<'a>,
table_va: u32,
index: u32,
total: u32,
}
impl<'a, 'p> MethodLinkIterator<'a, 'p> {
pub fn new(map: &'p AddressMap<'a>, table_va: u32, total: u32) -> Self {
Self {
map,
table_va,
index: 0,
total,
}
}
}
impl<'a, 'p> Iterator for MethodLinkIterator<'a, 'p> {
type Item = Result<MethodLink, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.total || self.table_va == 0 {
return None;
}
let ptr_va = self.table_va.wrapping_add(self.index.saturating_mul(4));
self.index = self.index.saturating_add(1);
let ptr_data = match self.map.slice_from_va(ptr_va, 4) {
Ok(d) => d,
Err(e) => return Some(Err(e)),
};
let thunk_va = match read_u32_le(ptr_data, 0) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let thunk_data = match self.map.slice_from_va(thunk_va, 13) {
Ok(d) => d,
Err(_) => {
match self.map.slice_from_va(thunk_va, 5) {
Ok(d) => d,
Err(e) => return Some(Err(e)),
}
}
};
let code_va = if thunk_data.first().copied() == Some(0xE9) {
let rel_bytes: [u8; 4] = match thunk_data.get(1..5).and_then(|s| s.try_into().ok()) {
Some(b) => b,
None => {
return Some(Err(Error::Truncated {
needed: 5,
available: thunk_data.len(),
}));
}
};
let rel32 = i32::from_le_bytes(rel_bytes);
i64::from(thunk_va)
.wrapping_add(5)
.wrapping_add(i64::from(rel32)) as u32
} else {
thunk_va
};
let this_adjust = if thunk_data.len() >= 13
&& thunk_data.get(5).copied() == Some(0x81)
&& thunk_data.get(6).copied() == Some(0x6C)
&& thunk_data.get(7).copied() == Some(0x24)
&& thunk_data.get(8).copied() == Some(0x04)
{
match read_u32_le(thunk_data, 9) {
Ok(v) => Some(v),
Err(e) => return Some(Err(e)),
}
} else {
None
};
Some(Ok(MethodLink {
thunk_va,
code_va,
this_adjust,
}))
}
}