arcdps/
globals.rs

1//! Global instance with ArcDPS information.
2
3use crate::{
4    exports::{
5        has_e3_log_file, has_e8_log_window, log_to_file, log_to_window,
6        raw::{
7            Export0, Export10, Export3, Export5, Export6, Export7, Export8, Export9,
8            ExportAddExtension, ExportFreeExtension, ExportListExtension,
9        },
10    },
11    imgui,
12    util::{exported_proc, Share},
13};
14use std::{
15    ffi::c_void,
16    mem::transmute,
17    ptr::{self, NonNull},
18    sync::{
19        atomic::{AtomicU32, Ordering},
20        OnceLock,
21    },
22};
23use windows::{
24    core::Interface,
25    Win32::{
26        Foundation::HMODULE,
27        Graphics::{Direct3D11::ID3D11Device, Dxgi::IDXGISwapChain},
28    },
29};
30
31/// Global instance of ArcDPS handle & exported functions.
32pub static ARC_GLOBALS: OnceLock<ArcGlobals> = OnceLock::new();
33
34/// ArcDPS handle & exported functions.
35// TODO: should we move other globals from codegen here? or move this to codegen?
36#[derive(Debug)]
37pub struct ArcGlobals {
38    /// Handle to ArcDPS dll.
39    pub handle: HMODULE,
40
41    /// ArcDPS version as string.
42    pub version: Option<&'static str>,
43
44    /// Config path export.
45    pub e0: Option<Export0>,
46
47    /// Log file export.
48    pub e3: Option<Export3>,
49
50    /// Colors export.
51    pub e5: Option<Export5>,
52
53    /// Ui settings export.
54    pub e6: Option<Export6>,
55
56    /// Modifiers export.
57    pub e7: Option<Export7>,
58
59    /// Log window export.
60    pub e8: Option<Export8>,
61
62    /// Add event export.
63    pub e9: Option<Export9>,
64
65    /// Add event combat/skill export.
66    pub e10: Option<Export10>,
67
68    /// Add extension export.
69    pub add_extension: Option<ExportAddExtension>,
70
71    /// Free extension export.
72    pub free_extension: Option<ExportFreeExtension>,
73
74    /// List extension export.
75    pub list_extension: Option<ExportListExtension>,
76}
77
78impl ArcGlobals {
79    /// Creates new ArcDPS globals.
80    pub unsafe fn new(handle: HMODULE, version: Option<&'static str>) -> Self {
81        #![allow(clippy::missing_transmute_annotations)]
82        Self {
83            handle,
84            version,
85            e0: transmute(exported_proc(handle, "e0\0")),
86            e3: transmute(exported_proc(handle, "e3\0")),
87            e5: transmute(exported_proc(handle, "e5\0")),
88            e6: transmute(exported_proc(handle, "e6\0")),
89            e7: transmute(exported_proc(handle, "e7\0")),
90            e8: transmute(exported_proc(handle, "e8\0")),
91            e9: transmute(exported_proc(handle, "e9\0")),
92            e10: transmute(exported_proc(handle, "e10\0")),
93            add_extension: transmute(exported_proc(handle, "addextension2\0")),
94            free_extension: transmute(exported_proc(handle, "freeextension2\0")),
95            list_extension: transmute(exported_proc(handle, "listextension\0")),
96        }
97    }
98
99    /// Initializes the ArcDPS globals.
100    pub unsafe fn init(handle: HMODULE, version: Option<&'static str>) -> &'static Self {
101        ARC_GLOBALS.get_or_init(|| Self::new(handle, version))
102    }
103
104    /// Returns the ArcDPS globals.
105    #[inline]
106    pub fn get() -> &'static Self {
107        Self::try_get().expect("arcdps globals not initialized")
108    }
109
110    /// Tries to retrieve the ArcDPS globals.
111    #[inline]
112    pub fn try_get() -> Option<&'static Self> {
113        ARC_GLOBALS.get()
114    }
115}
116
117unsafe impl Send for ArcGlobals {}
118
119unsafe impl Sync for ArcGlobals {}
120
121pub type MallocFn = unsafe extern "C" fn(size: usize, user_data: *mut c_void) -> *mut c_void;
122
123pub type FreeFn = unsafe extern "C" fn(ptr: *mut c_void, user_data: *mut c_void);
124
125/// ImGui context.
126pub static IG_CONTEXT: OnceLock<Share<imgui::Context>> = OnceLock::new();
127
128/// Helper to initialize ImGui.
129pub unsafe fn init_imgui(
130    ctx: *mut imgui::sys::ImGuiContext,
131    malloc: Option<MallocFn>,
132    free: Option<FreeFn>,
133) {
134    imgui::sys::igSetCurrentContext(ctx);
135    imgui::sys::igSetAllocatorFunctions(malloc, free, ptr::null_mut());
136    IG_CONTEXT.get_or_init(|| Share(imgui::Context::current()));
137}
138
139/// Current DirectX version.
140pub static D3D_VERSION: AtomicU32 = AtomicU32::new(0);
141
142/// Returns the current DirectX version.
143///
144/// `11` for DirectX 11 and `9` for legacy DirectX 9 mode.
145#[inline]
146pub fn d3d_version() -> u32 {
147    D3D_VERSION.load(Ordering::Relaxed)
148}
149
150/// DirectX 11 swap chain.
151pub static DXGI_SWAP_CHAIN: OnceLock<Share<NonNull<c_void>>> = OnceLock::new();
152
153/// Returns the DirectX swap chain, if available.
154#[inline]
155pub fn dxgi_swap_chain() -> Option<IDXGISwapChain> {
156    DXGI_SWAP_CHAIN.get().map(|share| {
157        unsafe { IDXGISwapChain::from_raw_borrowed(&share.0.as_ptr()) }
158            .expect("invalid swap chain")
159            .clone()
160    })
161}
162
163/// Returns the DirectX 11 device, if available.
164#[inline]
165pub fn d3d11_device() -> Option<ID3D11Device> {
166    let swap_chain = dxgi_swap_chain()?;
167    unsafe { swap_chain.GetDevice() }.ok()
168}
169
170/// Helper to initialize DirectX information.
171pub unsafe fn init_dxgi(id3d: *const c_void, d3d_version: u32, name: &'static str) {
172    D3D_VERSION.store(d3d_version, Ordering::Relaxed);
173    if d3d_version == 11 {
174        if let Some(id3d) = NonNull::new(id3d.cast_mut()) {
175            let ptr = id3d.as_ptr();
176            let swap_chain =
177                unsafe { IDXGISwapChain::from_raw_borrowed(&ptr) }.expect("invalid swap chain");
178
179            if let Err(err) = swap_chain.GetDevice::<ID3D11Device>() {
180                let msg = &format!("{name} error: failed to get d3d11 device: {err}");
181                if has_e3_log_file() {
182                    let _ = log_to_file(msg);
183                }
184                if has_e8_log_window() {
185                    let _ = log_to_window(msg);
186                }
187            }
188
189            DXGI_SWAP_CHAIN.get_or_init(|| Share(id3d));
190        }
191    }
192}