use crate::{
ParameterInfo,
util::{read_i16_le, read_i32_le, read_u32_le},
};
#[derive(Clone, Copy, Debug)]
pub struct FuncRecord<'a> {
bytes: &'a [u8],
}
impl<'a> FuncRecord<'a> {
pub(crate) fn new(bytes: &'a [u8]) -> Self {
Self { bytes }
}
pub const BASE_SIZE: usize = 0x18;
#[inline]
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
#[inline]
pub fn info(&self) -> u32 {
read_u32_le(self.bytes, 0x00).unwrap_or(0)
}
#[inline]
pub fn record_size(&self) -> usize {
(self.info() & 0xFFFF) as usize
}
#[inline]
pub fn datatype(&self) -> i32 {
read_i32_le(self.bytes, 0x04).unwrap_or(-1)
}
#[inline]
pub fn flags(&self) -> u32 {
read_u32_le(self.bytes, 0x08).unwrap_or(0)
}
#[inline]
pub fn vtable_offset(&self) -> i16 {
read_i16_le(self.bytes, 0x0C).unwrap_or(0)
}
#[inline]
pub fn funcdesc_size(&self) -> i16 {
read_i16_le(self.bytes, 0x0E).unwrap_or(0)
}
#[inline]
pub fn fkccic(&self) -> u32 {
read_u32_le(self.bytes, 0x10).unwrap_or(0)
}
#[inline]
pub fn func_kind(&self) -> u8 {
(self.fkccic() & 0x07) as u8
}
#[inline]
pub fn invoke_kind(&self) -> u8 {
((self.fkccic() >> 3) & 0x0F) as u8
}
#[inline]
pub fn callconv(&self) -> u8 {
((self.fkccic() >> 8) & 0x0F) as u8
}
#[inline]
pub fn nrargs(&self) -> i16 {
read_i16_le(self.bytes, 0x14).unwrap_or(0)
}
#[inline]
pub fn nroargs(&self) -> i16 {
read_i16_le(self.bytes, 0x16).unwrap_or(0)
}
fn nrattribs(&self) -> usize {
let size = self.record_size();
let nrargs = self.nrargs().max(0) as usize;
let params_size = nrargs * ParameterInfo::SIZE;
let arg_cust_size = if self.has_arg_cust_data() {
nrargs * 4
} else {
0
};
size.saturating_sub(Self::BASE_SIZE)
.saturating_sub(params_size)
.saturating_sub(arg_cust_size)
/ 4
}
#[inline]
pub fn has_cust_data(&self) -> bool {
self.fkccic() & 0x80 != 0
}
#[inline]
pub fn has_arg_cust_data(&self) -> bool {
self.fkccic() & 0x1000 != 0
}
pub fn help_context(&self) -> Option<i32> {
if self.nrattribs() > 0 {
read_i32_le(self.bytes, 0x18)
} else {
None
}
}
pub fn help_string_offset(&self) -> Option<i32> {
if self.nrattribs() > 1 {
read_i32_le(self.bytes, 0x1C)
} else {
None
}
}
pub fn oentry(&self) -> Option<i32> {
if self.nrattribs() > 2 {
read_i32_le(self.bytes, 0x20)
} else {
None
}
}
pub fn name_offset(&self) -> Option<i32> {
if self.nrattribs() > 3 {
read_i32_le(self.bytes, 0x24)
} else {
None
}
}
pub fn helpstringcontext(&self) -> Option<i32> {
if self.nrattribs() > 4 {
read_i32_le(self.bytes, 0x28)
} else {
None
}
}
pub fn cust_data_offset(&self) -> Option<i32> {
if self.nrattribs() > 5 {
read_i32_le(self.bytes, 0x2C)
} else {
None
}
}
pub fn arg_cust_data_offset(&self, arg_index: usize) -> Option<i32> {
if !self.has_arg_cust_data() {
return None;
}
let nrargs = self.nrargs().max(0) as usize;
if arg_index >= nrargs {
return None;
}
let attrs = self.nrattribs();
let offset = Self::BASE_SIZE + attrs * 4 + arg_index * 4;
read_i32_le(self.bytes, offset)
}
}