commonlibsse_ng\re\e/
ExtraDataList.rs

1use core::ffi::CStr;
2use core::ptr::{self, NonNull};
3
4use crate::re::BGSEncounterZone::BGSEncounterZone;
5use crate::re::BSAtomic::BSReadWriteLock;
6use crate::re::BSExtraData::{
7    BSExtraData, BSExtraDataIter, BSExtraDataIterMut, DerivedBSExtraData, downcast_as,
8};
9use crate::re::BSPointerHandle::ObjectRefHandle;
10use crate::re::ExtraAshPileRef::ExtraAshPileRef;
11use crate::re::ExtraCount::ExtraCount;
12use crate::re::ExtraDataType::ExtraDataType;
13use crate::re::ExtraEncounterZone::ExtraEncounterZone;
14use crate::re::ExtraHealth::ExtraHealth;
15use crate::re::ExtraReferenceHandle::ExtraReferenceHandle;
16use crate::re::ExtraTextDisplayData::ExtraTextDisplayData;
17use crate::re::TESBoundObject::TESBoundObject;
18use crate::re::TESBox::TESBox;
19use crate::rel::relocation::PhantomMember;
20
21#[repr(C)]
22#[derive(Debug, Clone)]
23pub struct ExtraDataList {
24    extra_data: BaseExtraList,
25    /// Zero-size dummy member
26    lock: PhantomMember<BSReadWriteLock, 0x10, 0x18>,
27
28    /// # Attention when inheriting
29    /// Because it contains 1 byte of dummy size for zero-size classes,
30    /// this size disappears when inheriting, so it would be dangerous to use it as it is for inheritance.
31    pad: usize,
32}
33const _: () = assert!(core::mem::size_of::<ExtraDataList>() == 0x8);
34
35unsafe impl std_fork::zeroable::Zeroable for ExtraDataList {}
36
37impl ExtraDataList {
38    /// - calc cost: O(n)
39    #[inline]
40    pub fn has_type(&self, type_: ExtraDataType) -> bool {
41        let _lock = self.lock.get();
42        match self.extra_data.presence.get() {
43            Ok(presence) => {
44                unsafe { presence.as_ref() }.is_some_and(|presence| presence.has_type(type_.bits()))
45            }
46            Err(_err) => {
47                #[cfg(feature = "tracing")]
48                tracing::error!("Error getting presence address: {_err}");
49                false
50            }
51        }
52    }
53
54    /// The target `to_remove` is removed from the LinkedList, but the memory itself is not removed in this function.
55    ///
56    /// If `to_remove` is not removed by the user, it will remain a memory leak.
57    pub fn remove(&mut self, type_: ExtraDataType, to_remove: *mut BSExtraData) -> bool {
58        let _lock = match self.lock.get_mut() {
59            Ok(lock) => lock.write(),
60            Err(_err) => return false,
61        };
62
63        if to_remove.is_null() {
64            return false;
65        }
66
67        let mut removed = false;
68
69        let data = match self.extra_data.data.0.get_mut() {
70            Ok(data) => data,
71            Err(_) => return false,
72        };
73        if (*data) == to_remove {
74            *data = (unsafe { &**data }).next;
75            removed = true;
76        } else {
77            let mut iter = self.extra_data.data.iter();
78            while iter.next().is_some() {
79                if iter.remove_current().is_some() {
80                    removed = true;
81                };
82            }
83        }
84
85        if removed {
86            if let Ok(presence) = self.extra_data.presence.get_mut() {
87                match unsafe { presence.as_mut() } {
88                    Some(presence) => presence.mark_type(type_.bits(), true),
89                    None => return false,
90                };
91            };
92        }
93
94        removed
95    }
96
97    /// The type to be removed is removed from the LinkedList and the memory itself is `Drop::drop`.
98    pub fn remove_by_type(&mut self, type_: ExtraDataType) -> bool {
99        let _lock = match self.lock.get_mut() {
100            Ok(lock) => lock.write(),
101            Err(_err) => return false,
102        };
103
104        let mut removed = false;
105
106        let mut iter = self.extra_data.data.iter_mut();
107        while let Some(data) = iter.next() {
108            if (unsafe { &*data }).get_type() == type_ {
109                iter.remove_current();
110            }
111            removed = true;
112        }
113
114        if removed {
115            if let Ok(presence) = self.extra_data.presence.get_mut() {
116                match unsafe { presence.as_mut() } {
117                    Some(presence) => presence.mark_type(type_.bits(), true),
118                    None => return false,
119                };
120            };
121        }
122
123        removed
124    }
125
126    /// Add `item` to `Self`'s list. & Return prev?
127    #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 12176, ae_id = 12315)]
128    pub fn add(&mut self, item: *mut BSExtraData) -> *mut BSExtraData {}
129
130    pub fn get_ash_pile_ref(&mut self) -> ObjectRefHandle {
131        let ash_ref = self.get_by_type_as::<ExtraAshPileRef>();
132        ash_ref
133            .map(|ash_ref| unsafe { ash_ref.as_ref() })
134            .map_or_else(ObjectRefHandle::default, |ash_ref| ash_ref.ashPileRef.clone())
135    }
136
137    pub fn get_count(&self) -> i32 {
138        let x_count = self.get_by_type_as::<ExtraCount>();
139        x_count.map_or(1, |x_count| unsafe { x_count.as_ref().count as i32 })
140    }
141
142    #[allow(clippy::not_unsafe_ptr_arg_deref)]
143    pub fn get_display_name<'a>(&mut self, base_object: &'a TESBoundObject) -> Option<&'a CStr> {
144        let health = self
145            .get_by_type_as::<ExtraHealth>()
146            .map_or(1.0, |x_health| unsafe { x_health.as_ref().health });
147
148        let df_health = if health <= 1.0 { (1.0 - health) < 0.001 } else { (health - 1.0) < 0.001 };
149
150        let mut x_text = self.get_extra_text_display_data();
151        if x_text.is_none() && !df_health {
152            let x_text_ptr = TESBox::into_non_null(TESBox::new(ExtraTextDisplayData::new()));
153            x_text.get_or_insert(x_text_ptr);
154            self.add(x_text_ptr.as_ptr().cast());
155        }
156
157        let c_char_ptr =
158            unsafe { x_text?.as_mut().get_display_name(base_object, health).as_ref() }?;
159        unsafe { Some(CStr::from_ptr(c_char_ptr)) }
160    }
161
162    #[inline]
163    pub fn get_encounter_zone(&self) -> Option<NonNull<BGSEncounterZone>> {
164        self.get_by_type_as::<ExtraEncounterZone>()
165            .and_then(|x_ref| unsafe { NonNull::new(x_ref.as_ref().zone) })
166    }
167
168    pub fn get_extra_text_display_data(&self) -> Option<NonNull<ExtraTextDisplayData>> {
169        let tes_ref = self
170            .get_by_type_as::<ExtraReferenceHandle>()
171            .map(|x_ref| unsafe { x_ref.as_ref().get_original_reference() })?;
172
173        tes_ref.as_ref().map_or_else(
174            || self.get_by_type_as::<ExtraTextDisplayData>(),
175            |tes_ref| {
176                if tes_ref.__base.is_deleted() {
177                    tes_ref
178                        .extraList
179                        .get_by_type_as::<ExtraTextDisplayData>()
180                        .map_or_else(|| self.get_by_type_as::<ExtraTextDisplayData>(), Some)
181                } else {
182                    self.get_by_type_as::<ExtraTextDisplayData>()
183                }
184            },
185        )
186    }
187
188    /// Gets the pointer to the matched type of data from the linked list.
189    /// - calc cost: O(n)
190    #[inline]
191    pub fn get_by_type(&self, type_: ExtraDataType) -> Option<*mut BSExtraData> {
192        let _lock = self.lock.get();
193        self.extra_data
194            .data
195            .iter()
196            .find(|data| unsafe { data.as_ref() }.is_some_and(|data| data.get_type() == type_))
197    }
198
199    /// Gets the pointer to the matched type of data from the linked list.
200    ///
201    /// Then downcast to the specified one.
202    /// If the type T does not inherit from `BSExtraData`, it will be UB.
203    /// - calc cost: O(n)
204    #[inline]
205    pub fn get_by_type_as<T>(&self) -> Option<NonNull<T>>
206    where
207        T: DerivedBSExtraData,
208    {
209        downcast_as(self.get_by_type(T::get_extra_data_type())?)
210    }
211}
212
213#[repr(C)]
214#[derive(Debug, Clone)]
215pub struct BaseExtraList {
216    /// - SE: partial Data
217    /// - AE: dummy empty class size
218    data: Data,
219    presence: PhantomMember<*mut PresenceBitfield, 0x8, 0x10>,
220}
221const _: () = assert!(core::mem::size_of::<BaseExtraList>() == 0x0);
222
223/// Wrapper type for iterator
224#[derive(Debug, Clone)]
225pub struct Data(pub PhantomMember<*mut BSExtraData, 0x0, 0x8>);
226
227impl Data {
228    ///It has a `remove current` method, which only concatenates prev and next without `drop` the current pointer.
229    /// This means memory leaks.
230    #[inline]
231    pub fn iter(&self) -> BSExtraDataIter<'_> {
232        BSExtraDataIter::new(self.0.get().copied().unwrap_or(ptr::null_mut()))
233    }
234
235    /// It has a `remove current` method, which only `drops` the current pointer and concatenates prev and next.
236    /// In other words, it completely deletes the data itself.
237    #[inline]
238    pub fn iter_mut(&mut self) -> BSExtraDataIterMut<'_> {
239        BSExtraDataIterMut::new(self.0.get().copied().unwrap_or(ptr::null_mut()))
240    }
241}
242
243impl<'a> IntoIterator for &'a Data {
244    type Item = *mut BSExtraData;
245    type IntoIter = BSExtraDataIter<'a>;
246
247    fn into_iter(self) -> Self::IntoIter {
248        self.iter()
249    }
250}
251
252// `IntoIterator` for mutable references
253impl<'a> IntoIterator for &'a mut Data {
254    type Item = *mut BSExtraData;
255    type IntoIter = BSExtraDataIterMut<'a>;
256
257    fn into_iter(self) -> Self::IntoIter {
258        self.iter_mut()
259    }
260}
261
262#[repr(C)]
263#[derive(Debug, Clone, Default)]
264pub struct PresenceBitfield {
265    pub bits: [u8; 0x18], // size: 24
266}
267const _: () = assert!(core::mem::size_of::<PresenceBitfield>() == 0x18);
268
269impl PresenceBitfield {
270    pub const fn has_type(&self, type_: u32) -> bool {
271        let index = (type_ >> 3) as usize;
272        if index >= self.bits.len() {
273            return false;
274        }
275        let bit_mask = 1 << (type_ % 8);
276        (self.bits[index] & bit_mask) != 0
277    }
278
279    pub const fn mark_type(&mut self, type_: u32, cleared: bool) {
280        let index = (type_ >> 3) as usize;
281        if index >= self.bits.len() {
282            return;
283        }
284        let bit_mask = 1 << (type_ % 8);
285        let flag = &mut self.bits[index];
286        if cleared {
287            *flag &= !bit_mask;
288        } else {
289            *flag |= bit_mask;
290        }
291    }
292}