Skip to main content

arcdps\globals/
arc.rs

1use crate::{
2    exports::raw::{
3        Export0, Export3, Export5, Export6, Export7, Export8, Export9, Export10,
4        ExportAddExtension, ExportFreeExtension, ExportListExtension,
5    },
6    util::str_from_cstr,
7};
8use std::{
9    ffi::c_char,
10    io,
11    mem::{self, transmute},
12    ptr,
13    sync::OnceLock,
14};
15use windows::{
16    Win32::{
17        Foundation::HMODULE,
18        System::{
19            LibraryLoader::GetProcAddress, ProcessStatus::EnumProcessModules,
20            Threading::GetCurrentProcess,
21        },
22    },
23    core::{PCSTR, s},
24};
25
26/// Manually initializes ArcDPS information.
27#[inline]
28pub unsafe fn init_arc(arc_handle: HMODULE, version: *const c_char) {
29    unsafe { ArcGlobals::init(arc_handle, str_from_cstr(version)) };
30}
31
32/// Attempts to initialize ArcDPS information by searching the current process for the ArcDPS module.
33///
34/// When initializing via this method, the ArcDPS version will always be [`None`].
35///
36/// This does **not** initialize ImGui information.
37#[inline]
38pub unsafe fn search_and_init_arc() -> Result<(), io::Error> {
39    let arc_handle = search_arc_handle()?;
40    unsafe { ArcGlobals::init(arc_handle, None) };
41    Ok(())
42}
43
44/// Searches the current process for the ArcDPS module.
45pub fn search_arc_handle() -> Result<HMODULE, io::Error> {
46    const EXPORT: PCSTR = s!("arcdps_identifier_export"); // TODO: is this guaranteed to stay?
47    const MAX_MODULES: usize = 1024;
48
49    let mut modules = const { [HMODULE(ptr::null_mut()); MAX_MODULES] };
50    let mut needed = 0;
51    unsafe {
52        EnumProcessModules(
53            GetCurrentProcess(),
54            modules.as_mut_ptr(),
55            mem::size_of_val(&modules) as _,
56            &mut needed,
57        )
58    }?;
59
60    let len = needed as usize / mem::size_of::<HMODULE>();
61    modules
62        .into_iter()
63        .take(len)
64        .find(|module| unsafe { GetProcAddress(*module, EXPORT) }.is_some())
65        .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "module not found"))
66}
67
68/// Global instance of ArcDPS handle & exported functions.
69static ARC_GLOBALS: OnceLock<ArcGlobals> = OnceLock::new();
70
71/// ArcDPS handle & exported functions.
72// TODO: should we move other globals from codegen here? or move this to codegen?
73#[derive(Debug)]
74pub struct ArcGlobals {
75    /// Handle to ArcDPS dll.
76    pub handle: HMODULE,
77
78    /// ArcDPS version as string.
79    pub version: Option<&'static str>,
80
81    /// Config path export.
82    pub e0: Option<Export0>,
83
84    /// Log file export.
85    pub e3: Option<Export3>,
86
87    /// Colors export.
88    pub e5: Option<Export5>,
89
90    /// Ui settings export.
91    pub e6: Option<Export6>,
92
93    /// Modifiers export.
94    pub e7: Option<Export7>,
95
96    /// Log window export.
97    pub e8: Option<Export8>,
98
99    /// Add event export.
100    pub e9: Option<Export9>,
101
102    /// Add event combat/skill export.
103    pub e10: Option<Export10>,
104
105    /// Add extension export.
106    pub add_extension: Option<ExportAddExtension>,
107
108    /// Free extension export.
109    pub free_extension: Option<ExportFreeExtension>,
110
111    /// List extension export.
112    pub list_extension: Option<ExportListExtension>,
113}
114
115impl ArcGlobals {
116    /// Creates new ArcDPS globals.
117    pub unsafe fn new(handle: HMODULE, version: Option<&'static str>) -> Self {
118        #![allow(clippy::missing_transmute_annotations)]
119        unsafe {
120            Self {
121                handle,
122                version,
123                e0: transmute(GetProcAddress(handle, s!("e0"))),
124                e3: transmute(GetProcAddress(handle, s!("e3"))),
125                e5: transmute(GetProcAddress(handle, s!("e5"))),
126                e6: transmute(GetProcAddress(handle, s!("e6"))),
127                e7: transmute(GetProcAddress(handle, s!("e7"))),
128                e8: transmute(GetProcAddress(handle, s!("e8"))),
129                e9: transmute(GetProcAddress(handle, s!("e9"))),
130                e10: transmute(GetProcAddress(handle, s!("e10"))),
131                add_extension: transmute(GetProcAddress(handle, s!("addextension2"))),
132                free_extension: transmute(GetProcAddress(handle, s!("freeextension2"))),
133                list_extension: transmute(GetProcAddress(handle, s!("listextension"))),
134            }
135        }
136    }
137
138    /// Initializes the ArcDPS globals.
139    pub unsafe fn init(handle: HMODULE, version: Option<&'static str>) -> &'static Self {
140        ARC_GLOBALS.get_or_init(|| unsafe { Self::new(handle, version) })
141    }
142
143    /// Returns the ArcDPS globals.
144    #[inline]
145    pub fn get() -> &'static Self {
146        Self::try_get().expect("arcdps globals not initialized")
147    }
148
149    /// Tries to retrieve the ArcDPS globals.
150    #[inline]
151    pub fn try_get() -> Option<&'static Self> {
152        ARC_GLOBALS.get()
153    }
154}
155
156unsafe impl Send for ArcGlobals {}
157
158unsafe impl Sync for ArcGlobals {}