Skip to main content

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: {0}")]
16    NotFound(Cow<'static, str>),
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: {0}")]
22    Null(Cow<'static, str>),
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 as a reference.
40    ///
41    /// This function is safe because it's always already unsafe to dereference
42    /// a pointer. Most callers should use [FromStatic::instance] or
43    /// [FromStatic::instance_mut] instead, which have more explicit safety
44    /// requirements but provide access to Rust references in exchange.
45    fn instance_ptr() -> InstanceResult<*mut Self>;
46
47    /// Looks up the single global instance of this object as a mutable
48    /// reference.
49    ///
50    /// ## Safety
51    ///
52    /// The caller must ensure that access to the static object is exclusive,
53    /// both with Rust and the game's code. This means that this should only
54    /// ever be called from the game's main thread (typically either from the
55    /// task system or from hooked functions that run in the main thread).
56    ///
57    /// Individual implementations may add additional safety requirements.
58    unsafe fn instance_mut() -> InstanceResult<&'static mut Self> {
59        Self::instance_ptr()
60            .and_then(|p| unsafe { p.as_mut() }.ok_or(InstanceError::Null(Self::name())))
61    }
62
63    /// Looks up the single global instance of this object as a reference.
64    ///
65    /// ## Safety
66    ///
67    /// The caller must ensure that no mutable references exist to the static
68    /// object and that no fields outside of [UnsafeCell]s are mutated by the
69    /// game while this reference exists. This is generally safe to use on the
70    /// main thread. It's safe to use on other threads as long as the object is
71    /// thread-safe, which should be noted in its documentation.
72    ///
73    /// [UnsafeCell]: std::cell::UnsafeCell
74    ///
75    /// Individual implementations may add additional safety requirements.
76    unsafe fn instance() -> InstanceResult<&'static Self> {
77        Self::instance_ptr()
78            .and_then(|p| unsafe { p.as_ref() }.ok_or(InstanceError::Null(Self::name())))
79    }
80}
81
82/// Looks up instances of singleton instances by their name. Some singletons
83/// aren't necessarily always instanciated and available. Discovered singletons
84/// are cached so invokes after the first will be much faster.
85///
86/// Note: currently this never returns [InstanceError::NotFound], but callers
87/// shouldn't rely on that being true into the future.
88impl<T: FromSingleton> FromStatic for T {
89    fn name() -> Cow<'static, str> {
90        <Self as FromSingleton>::name()
91    }
92
93    /// ## Safety
94    ///
95    /// In addition to the standard [FromStatic::instance] safety requirements, the
96    /// caller must ensure that the main module (the exe) is a From Software title
97    /// with DLRF reflection data, and that the DLRF reflection metadata has been
98    /// populated (usually by calling the current game's `wait_for_system_init`
99    /// function).
100    fn instance_ptr() -> InstanceResult<*mut T> {
101        address_of::<T>()
102            .map(|nn| nn.as_ptr())
103            .ok_or(InstanceError::NotFound(Self::name()))
104    }
105}
106
107/// Loads a static reference to `T` from an [Rva] that points directly to its
108/// memory. Because this always assumes that the underlying object is
109/// initialized, it can only return [InstanceError::Null] if `rva` itself is 0.
110pub fn load_static_direct<T: FromStatic>(rva: Rva) -> InstanceResult<*mut T> {
111    Program::current()
112        .rva_to_va(rva)
113        .map_err(|_| InstanceError::NotFound(T::name()))
114        .map(|a| a as *mut T)
115}
116
117/// Loads a static reference to `T` from an [Rva] that points to a pointer to
118/// its memory.
119///
120/// ## Safety
121///
122/// The caller must ensure that `rva` points to a pointer.
123pub unsafe fn load_static_indirect<T: FromStatic>(rva: Rva) -> InstanceResult<*mut T> {
124    let target = Program::current()
125        .rva_to_va(rva)
126        .map_err(|_| InstanceError::NotFound(T::name()))?
127        as *mut Option<NonNull<T>>;
128
129    unsafe {
130        target
131            .as_mut()
132            .and_then(|opt| opt.as_mut())
133            .map(|nn| nn.as_ptr())
134            .ok_or(InstanceError::Null(T::name()))
135    }
136}