diff --git a/hook/src/ue/map.rs b/hook/src/ue/map.rs new file mode 100644 index 0000000..16b10af --- /dev/null +++ b/hook/src/ue/map.rs @@ -0,0 +1,241 @@ +use std::fmt::Debug; + +use super::TArray; + +pub trait UEHash { + fn ue_hash(&self) -> u32; +} + +#[derive(Default, Debug)] +#[repr(C)] +struct TSetElement { + value: V, + hash_next_id: FSetElementId, + hash_index: i32, +} +impl UEHash for TSetElement> { + fn ue_hash(&self) -> u32 { + self.value.a.ue_hash() + } +} + +#[derive(Default, Clone, Copy)] +#[repr(C)] +pub struct FSetElementId { + index: i32, +} +impl FSetElementId { + pub fn is_valid(self) -> bool { + self.index != -1 + } +} +impl Debug for FSetElementId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "FSetElementId({:?})", self.index) + } +} + +#[derive(Default, Debug)] +#[repr(C)] +struct TTuple { + a: A, + b: B, +} + +#[repr(C)] +union TSparseArrayElementOrFreeListLink { + element: std::mem::ManuallyDrop, + list_link: ListLink, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +struct ListLink { + next_free_index: i32, + prev_free_index: i32, +} + +#[derive(Debug)] +#[repr(C)] +struct TInlineAllocator { + inline_data: [V; N], + secondary_data: *const V, // TSizedHeapAllocator<32>::ForElementType, +} +impl Default for TInlineAllocator { + fn default() -> Self { + Self { + inline_data: [Default::default(); N], + secondary_data: std::ptr::null(), + } + } +} + +impl TInlineAllocator { + fn get_allocation(&self) -> *const V { + if !self.secondary_data.is_null() { + self.secondary_data + } else { + self.inline_data.as_ptr() + } + } +} + +#[derive(Default, Debug)] +#[repr(C)] +struct TBitArray { + allocator_instance: TInlineAllocator<4, u32>, + num_bits: i32, + max_bits: i32, +} +impl TBitArray { + fn get_data(&self) -> *const u32 { + self.allocator_instance.get_allocation() + } + + fn index(&self, index: usize) -> FBitReference<'_> { + assert!(index < self.num_bits as usize); + let num_bits_per_dword = 32; + FBitReference { + data: unsafe { &*self.get_data().add(index / num_bits_per_dword) }, + mask: 1 << (index & (num_bits_per_dword - 1)), + } + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +struct FBitReference<'data> { + data: &'data u32, + mask: u32, +} +impl FBitReference<'_> { + fn bool(self) -> bool { + (self.data & self.mask) != 0 + } +} + +#[repr(C)] +struct TSparseArray { + data: TArray>, + allocation_flags: TBitArray, + first_free_index: i32, + num_free_indices: i32, +} +impl Default for TSparseArray { + fn default() -> Self { + Self { + data: Default::default(), + allocation_flags: Default::default(), + first_free_index: 0, + num_free_indices: 0, + } + } +} +impl TSparseArray { + fn index(&self, index: usize) -> &E { + assert!(index < self.data.len() && index < self.allocation_flags.num_bits as usize); + assert!(self.allocation_flags.index(index).bool()); + unsafe { &self.data.as_slice()[index].element } + } +} + +struct DbgTSparseArrayData<'a, E>(&'a TSparseArray); +impl Debug for DbgTSparseArrayData<'_, E> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut dbg = f.debug_list(); + for i in 0..(self.0.allocation_flags.num_bits as usize) { + if self.0.allocation_flags.index(i).bool() { + dbg.entry(self.0.index(i)); + } + } + dbg.finish() + } +} + +impl Debug for TSparseArray { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TSparseArray") + .field("data", &DbgTSparseArrayData(self)) + .field("allocation_flags", &self.allocation_flags) + .field("first_free_index", &self.first_free_index) + .field("num_free_indices", &self.num_free_indices) + .finish() + } +} + +#[repr(C)] +pub struct TMap { + elements: TSparseArray>>, + hash: TInlineAllocator<1, FSetElementId>, + hash_size: i32, +} +impl TMap { + fn hash(&self) -> &[FSetElementId] { + unsafe { std::slice::from_raw_parts(self.hash.get_allocation(), self.hash_size as usize) } + } +} +impl Default for TMap { + fn default() -> Self { + Self { + elements: Default::default(), + hash: Default::default(), + hash_size: 0, + } + } +} +impl Debug for TMap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TMap") + .field("elements", &self.elements) + .field("hash", &self.hash()) + .finish() + } +} + +impl TMap { + pub fn find(&self, key: K) -> Option<&V> { + let id = self.find_id(key); + if id.is_valid() { + Some(&self.elements.index(id.index as usize).value.b) + } else { + None + } + } + pub fn find_id(&self, key: K) -> FSetElementId { + if self.elements.data.len() != self.elements.num_free_indices as usize { + let key_hash = key.ue_hash(); + let hash = &self.hash(); + + let mut i: FSetElementId = + hash[(((self.hash_size as i64) - 1) & (key_hash as i64)) as usize]; + + if i.is_valid() { + loop { + let elm = self.elements.index(i.index as usize); + + if elm.value.a == key { + return i; + } + + i = elm.hash_next_id; + if !i.is_valid() { + break; + } + } + } + } + + FSetElementId { index: -1 } + } +} + +#[cfg(test)] +mod test { + use crate::ue::FName; + + use super::*; + const _: [u8; 0x50] = [0; std::mem::size_of::>()]; + const _: [u8; 0x38] = + [0; std::mem::size_of::>>>()]; + const _: [u8; 0x10] = [0; std::mem::size_of::>()]; +} diff --git a/hook/src/ue/mod.rs b/hook/src/ue/mod.rs index b9af7d5..121bed8 100644 --- a/hook/src/ue/mod.rs +++ b/hook/src/ue/mod.rs @@ -1,12 +1,14 @@ mod array; pub mod kismet; mod malloc; +mod map; mod name; mod object; mod string; pub use array::*; pub use malloc::*; +pub use map::*; pub use name::*; pub use object::*; pub use string::*; diff --git a/hook/src/ue/name.rs b/hook/src/ue/name.rs index dc3dddb..9179784 100644 --- a/hook/src/ue/name.rs +++ b/hook/src/ue/name.rs @@ -1,5 +1,7 @@ use crate::{globals, ue::FString}; +use super::UEHash; + #[derive(Debug, Clone, Copy)] #[repr(u32)] pub enum EFindName { @@ -8,7 +10,7 @@ pub enum EFindName { ReplaceNotSafeForThreading, } -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)] #[repr(C)] pub struct FName { pub comparison_index: FNameEntryId, @@ -32,7 +34,7 @@ impl std::fmt::Debug for FName { } } -#[derive(Default, Debug, Clone, Copy)] +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)] #[repr(C)] pub struct FNameEntryId { pub value: u32, @@ -47,3 +49,15 @@ impl std::fmt::Display for FName { write!(f, "{string}") } } +impl UEHash for FNameEntryId { + fn ue_hash(&self) -> u32 { + let value = self.value; + (value >> 4) + value.wrapping_mul(0x10001) + (value >> 0x10).wrapping_mul(0x80001) + } +} + +impl UEHash for FName { + fn ue_hash(&self) -> u32 { + self.comparison_index.ue_hash() + self.number + } +}