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}