fromsoftware_shared/static.rs
1use std::{borrow::Cow, ptr::NonNull};
2
3use from_singleton::*;
4use pelite::pe64::{Pe, Rva};
5use thiserror::Error;
6
7use crate::Program;
8
9/// An error type returned by [FromStatic::instance].
10#[derive(Error, Debug)]
11pub enum InstanceError {
12 /// The object's location wasn't found in the executable. This usually means
13 /// something is wrong with the logic of how the object is being loaded in
14 /// the first place.
15 #[error("Static object not found")]
16 NotFound,
17
18 /// The static object is defined, but it's currently set to null. For many
19 /// objects, this is a normal occurrence, and just means that the caller
20 /// should wait until it's defined to start using it.
21 #[error("Static object not initialized")]
22 Null,
23}
24
25/// A [Result] whose error type is [InstanceError].
26pub type InstanceResult<T> = Result<T, InstanceError>;
27
28/// A trait for all objects that are instantiated a single time at a fixed point
29/// in memory.
30///
31/// This is automatically implemented for [FromSingleton]s generated using the
32/// [singleton](crate::singleton) attribute macro, and may be manually
33/// implemented for other types that have different ways of looking up their
34/// locations in-memory.
35pub trait FromStatic {
36 /// The name of this object. Used for debugging purposes.
37 fn name() -> Cow<'static, str>;
38
39 /// Looks up the single global instance of this object.
40 ///
41 /// Implementations may cache information about the object's location to
42 /// make this more efficient in future calls.
43 ///
44 /// ## Safety
45 ///
46 /// The caller must ensure that access to the static object is exclusive,
47 /// both with Rust and the game's code. For single-threaded objects, this
48 /// means ensuring that this is only called from the task system or from
49 /// hooked functions running in the game's main thread. For multi-threaded
50 /// objects, it's sufficient to ensure you have mutex ownership before
51 /// accessing any locked fields.
52 ///
53 /// Individual implementations may add additional safety requirements.
54 unsafe fn instance() -> InstanceResult<&'static mut Self>;
55}
56
57/// Looks up instances of singleton instances by their name. Some singletons
58/// aren't necessarily always instanciated and available. Discovered singletons
59/// are cached so invokes after the first will be much faster.
60///
61/// Note: currently this never returns [InstanceError::NotFound], but callers
62/// shouldn't rely on that being true into the future.
63impl<T: FromSingleton> FromStatic for T {
64 fn name() -> Cow<'static, str> {
65 <Self as FromSingleton>::name()
66 }
67
68 /// ## Safety
69 ///
70 /// In addition to the standard [FromStatic::instance] safety requirements, the
71 /// caller must ensure that the main module (the exe) is a From Software title
72 /// with DLRF reflection data, and that the DLRF reflection metadata has been
73 /// populated (usually by calling the current game's `wait_for_system_init`
74 /// function).
75 unsafe fn instance() -> InstanceResult<&'static mut T> {
76 address_of::<T>()
77 .map(|mut ptr| unsafe { ptr.as_mut() })
78 .ok_or(InstanceError::NotFound)
79 }
80}
81
82/// Loads a static reference to `T` from an [Rva] that points directly to its
83/// memory. Because this always assumes that the underlying object is
84/// initialized, it can only return [InstanceError::Null] if `rva` itself is 0.
85///
86/// ## Safety
87///
88/// This has all the same safety requirements as [FromStatic::instance]. In
89/// addition, the caller must ensure that `rva` points to a valid, initialized
90/// instance of `T`.
91pub unsafe fn load_static_direct<T: FromStatic>(rva: Rva) -> InstanceResult<&'static mut T> {
92 let target = Program::current()
93 .rva_to_va(rva)
94 .map_err(|_| InstanceError::NotFound)? as *mut T;
95
96 unsafe { target.as_mut().ok_or(InstanceError::Null) }
97}
98
99/// Loads a static reference to `T` from an [Rva] that points to a pointer to
100/// its memory.
101///
102/// ## Safety
103///
104/// This has all the same safety requirements as [FromStatic::instance]. In
105/// addition, the caller must ensure that `rva` points to a pointer that is
106/// either null or points to a valid, initialized instance of `T`.
107pub unsafe fn load_static_indirect<T: FromStatic>(rva: Rva) -> InstanceResult<&'static mut T> {
108 let target = Program::current()
109 .rva_to_va(rva)
110 .map_err(|_| InstanceError::NotFound)? as *mut Option<NonNull<T>>;
111
112 unsafe {
113 target
114 .as_mut()
115 .and_then(|opt| opt.as_mut())
116 .map(|nn| nn.as_mut())
117 .ok_or(InstanceError::Null)
118 }
119}