commonlibsse_ng\re\t/
TESForm.rs1mod flags;
2mod vtable;
3
4pub use self::flags::{InGameFormFlag, RecordFlag};
5pub use self::vtable::TESFormVtbl;
6
7use crate::re::BSAtomic::BSReadWriteLock;
8use crate::re::BSCoreTypes::FormID;
9use crate::re::BSFixedString::BSFixedString;
10use crate::re::BSTArray::BSStaticArray;
11use crate::re::BSTHashMap::BSTHashMap;
12use crate::re::BaseFormComponent::BaseFormComponent;
13use crate::re::FormTypes::{FormType, FormType_CEnum};
14use crate::re::TESFile::TESFile;
15use crate::re::offsets_rtti::RTTI_TESForm;
16use crate::re::offsets_vtable::VTABLE_TESForm;
17use crate::rel::ResolvableAddress as _;
18use crate::rel::id::RelocationID;
19use crate::rel::id::{DataBaseError, VariantID};
20use core::ffi::CStr;
21use core::ptr::NonNull;
22use std::sync::LazyLock;
23
24#[repr(C)]
25#[derive(Debug, PartialEq)]
26struct TESFileArray {
27 _base: BSStaticArray<TESFile>,
28}
29const_assert_eq!(core::mem::size_of::<TESFileArray>(), 0x10);
30
31#[repr(C)]
32#[derive(Debug, PartialEq, Eq)]
33pub struct TESFileContainer {
34 array: *mut TESFileArray,
35}
36const_assert_eq!(core::mem::size_of::<TESFileContainer>(), 0x8);
37
38#[repr(C)]
39#[derive(Debug, PartialEq, Eq)]
40pub struct TESForm {
41 pub __base: BaseFormComponent,
42 pub sourceFiles: TESFileContainer,
43 pub formFlags: u32,
44 pub formID: FormID,
45 pub inGameFormFlags: InGameFormFlag,
46 pub formType: FormType_CEnum,
47 pub pad1B: u8,
48 pub pad1C: u32,
49}
50const_assert_eq!(core::mem::size_of::<TESForm>(), 0x20);
51
52struct RawFormMapLock<K>
53where
54 K: core::hash::Hash + Eq,
55{
56 map_ptr: NonNull<*mut BSTHashMap<K, Option<NonNull<TESForm>>>>,
57 lock_ptr: NonNull<BSReadWriteLock>,
58}
59
60impl<K> RawFormMapLock<K>
61where
62 K: core::hash::Hash + Eq,
63{
64 #[inline]
65 pub fn get(&self, key: &K) -> Option<NonNull<TESForm>> {
66 let _guard = unsafe { self.lock_ptr.as_ref().read() };
67
68 let map = unsafe { self.map_ptr.as_ref().as_ref()? };
69 match map.get(key) {
70 Some(form) => *form,
71 None => None,
72 }
73 }
74
75 #[inline]
76 pub fn get_mut(&mut self, key: &K) -> Option<NonNull<TESForm>> {
77 let _guard = unsafe { self.lock_ptr.as_mut().write() };
78
79 let map = unsafe { self.map_ptr.as_mut().as_mut()? };
80 match map.get_mut(key) {
81 Some(form) => *form,
82 None => None,
83 }
84 }
85}
86
87unsafe impl<K> Send for RawFormMapLock<K> where K: core::hash::Hash + Eq {}
88unsafe impl<K> Sync for RawFormMapLock<K> where K: core::hash::Hash + Eq {}
89
90pub struct FormIDMap(RawFormMapLock<FormID>);
92impl FormIDMap {
93 #[inline]
94 pub fn get(&self, form_id: FormID) -> Option<NonNull<TESForm>> {
95 self.0.get(&form_id)
96 }
97
98 #[inline]
99 pub fn get_mut(&mut self, form_id: FormID) -> Option<NonNull<TESForm>> {
100 self.0.get_mut(&form_id)
101 }
102}
103
104pub struct EditorIDMap(RawFormMapLock<BSFixedString>);
106impl EditorIDMap {
107 #[inline]
108 pub fn get(&self, editor_id: &CStr) -> Option<NonNull<TESForm>> {
109 self.0.get(&editor_id.into())
110 }
111
112 #[inline]
113 pub fn get_mut(&mut self, editor_id: &CStr) -> Option<NonNull<TESForm>> {
114 self.0.get_mut(&editor_id.into())
115 }
116}
117
118impl TESForm {
119 pub const RTTI: VariantID = RTTI_TESForm;
120 pub const VTABLE: [VariantID; 1] = VTABLE_TESForm;
121 pub const FORM_TYPE: FormType = FormType::None;
122
123 #[inline]
124 pub const fn vtable(&self) -> &TESFormVtbl {
125 debug_assert!(!self.__base.vtable.is_null(), "TESFormVtbl ptr must not be null ptr");
126 unsafe { &*self.__base.vtable.cast() }
127 }
128
129 #[inline]
130 pub fn vtable_from_addr(&self) -> Option<&'static TESFormVtbl> {
131 Some(unsafe { Self::VTABLE[0].address().ok()?.cast().as_ref() })
132 }
133
134 #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 14509, ae_id = 14667)]
135 pub unsafe fn add_compile_index(id: FormID, file: TESFile) {}
136
137 pub fn get_all_forms() -> &'static Result<FormIDMap, DataBaseError> {
138 static PTR_LOCK: LazyLock<Result<FormIDMap, DataBaseError>> = LazyLock::new(|| {
139 let map_ptr = RelocationID::from_se_ae_id(514351, 400507).address()?.cast();
140 let lock_ptr = RelocationID::from_se_ae_id(514360, 400517).address()?.cast();
141 Ok(FormIDMap(RawFormMapLock { map_ptr, lock_ptr }))
142 });
143 &PTR_LOCK
144 }
145
146 pub fn get_all_forms_by_editor_id() -> &'static Result<EditorIDMap, DataBaseError> {
147 static PTR_LOCK: LazyLock<Result<EditorIDMap, DataBaseError>> = LazyLock::new(|| {
148 Ok(EditorIDMap(RawFormMapLock {
149 map_ptr: RelocationID::from_se_ae_id(514352, 400509).address()?.cast(),
150 lock_ptr: RelocationID::from_se_ae_id(514361, 400518).address()?.cast(),
151 }))
152 });
153 &PTR_LOCK
154 }
155
156 #[inline]
158 pub fn lookup_by_id(form_id: FormID) -> Option<NonNull<Self>> {
159 let all_form_map = Self::get_all_forms().as_ref().ok()?;
160 all_form_map.get(form_id)
161 }
162
163 #[inline]
165 pub fn lookup_by_editor_id(editor_id: &CStr) -> Option<NonNull<Self>> {
166 let all_form_map = Self::get_all_forms_by_editor_id().as_ref().ok()?;
167 all_form_map.get(editor_id)
168 }
169
170 #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 14809, ae_id = 14988)]
179 pub fn get_weight(&self) -> f32 {}
180
181 #[allow(clippy::missing_const_for_fn)]
183 pub fn is_deleted(&self) -> bool {
184 false
185 }
186
187 #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 14482, ae_id = 14639)]
188 pub fn set_player_knowns(&self, known: bool) {}
189}
190
191pub trait DerivedTESForm {
192 const FORM_TYPE: FormType;
193
194 fn get_form(&self) -> &TESForm;
195}
196
197impl DerivedTESForm for TESForm {
198 const FORM_TYPE: FormType = FormType::None;
199
200 #[inline]
201 fn get_form(&self) -> &TESForm {
202 self
203 }
204}