1#![doc = include_str!("../README.md")]
2
3use std::{
4 any,
5 borrow::Cow,
6 ptr::{self, NonNull},
7 sync::{LazyLock, OnceLock},
8};
9
10use find::{FD4SingletonMap, FD4SingletonPartialResult};
11use fxhash::FxHashMap;
12use windows_sys::Win32::System::LibraryLoader::GetModuleHandleW;
13
14pub mod find;
15
16pub trait FromSingleton {
21 #[inline]
23 fn name() -> Cow<'static, str> {
24 let type_name = any::type_name::<Self>();
25
26 let end = type_name.find('<').unwrap_or(type_name.len());
27
28 let start = type_name[..end]
29 .rfind(':')
30 .unwrap_or(usize::MAX)
31 .wrapping_add(1);
32
33 Cow::Borrowed(&type_name[start..end])
34 }
35}
36
37pub fn map() -> &'static FxHashMap<String, NonNull<*mut u8>> {
43 if let Some(map) = ALL_SINGLETON_MAP.get() {
44 return map;
45 }
46
47 let derived = &*DERIVED_SINGLETON_MAP;
48 let partial = &*PARTIAL_SINGLETON_MAP;
49
50 if !partial.all_null() || !derived.all_null() {
51 ALL_SINGLETON_MAP.get_or_init(|| {
52 let mut new_map = unsafe { partial.clone().finish() };
54
55 new_map.extend(derived.iter().map(|(k, v)| (k.clone(), *v)));
56
57 new_map
58 })
59 } else {
60 derived
61 }
62}
63
64#[inline]
73pub fn address_of<T>() -> Option<NonNull<T>>
74where
75 T: FromSingleton + Sized,
76{
77 let static_ptr = static_of::<T>()?;
78
79 unsafe { NonNull::new(static_ptr.read()) }
81}
82
83#[inline]
92pub fn static_of<T>() -> Option<NonNull<*mut T>>
93where
94 T: FromSingleton + Sized,
95{
96 let name = <T as FromSingleton>::name();
97 map().get(&*name).cloned().map(NonNull::cast)
98}
99
100static DERIVED_SINGLETON_MAP: LazyLock<FD4SingletonMap> = LazyLock::new(|| unsafe {
101 let image_base = GetModuleHandleW(ptr::null());
102 assert!(!image_base.is_null(), "GetModuleHandleW failed");
103 let pe = pelite::pe::PeView::module(image_base as _);
104 find::derived_singletons(pe)
105});
106
107static PARTIAL_SINGLETON_MAP: LazyLock<FD4SingletonPartialResult> = LazyLock::new(|| unsafe {
108 let image_base = GetModuleHandleW(ptr::null());
109 assert!(!image_base.is_null(), "GetModuleHandleW failed");
110 let pe = pelite::pe::PeView::module(image_base as _);
111 find::fd4_singletons(pe)
112});
113
114static ALL_SINGLETON_MAP: OnceLock<FD4SingletonMap> = OnceLock::new();
115
116#[cfg(test)]
117mod tests {
118 use crate::FromSingleton;
119
120 mod fd4 {
121 use std::{borrow::Cow, marker::PhantomData};
122
123 use crate::FromSingleton;
124
125 pub struct FD4PadManager;
126 pub struct FD4HkEzDrawRigidBodyDispBufferManager<T>(PhantomData<T>);
127 pub struct FD4FileManager;
128
129 impl FromSingleton for FD4PadManager {}
130
131 impl<T> FromSingleton for FD4HkEzDrawRigidBodyDispBufferManager<T> {}
132
133 impl FromSingleton for FD4FileManager {
134 fn name() -> Cow<'static, str> {
135 Cow::Borrowed("CSFile")
136 }
137 }
138 }
139
140 mod cs {
141 use crate::FromSingleton;
142
143 pub struct CSFile;
144
145 impl FromSingleton for CSFile {}
146 }
147
148 impl<T> FromSingleton for Option<T> {}
149
150 #[test]
151 fn correct_names() {
152 type LongType = fd4::FD4HkEzDrawRigidBodyDispBufferManager<Option<Option<i32>>>;
153
154 assert_eq!(fd4::FD4PadManager::name(), "FD4PadManager");
155
156 assert_eq!(LongType::name(), "FD4HkEzDrawRigidBodyDispBufferManager");
157
158 assert_eq!(fd4::FD4FileManager::name(), cs::CSFile::name());
159
160 assert_eq!(Option::<Result<LongType, LongType>>::name(), "Option");
161 }
162}