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}