sm_ext/
lib.rs

1#![cfg_attr(feature = "abi_thiscall", feature(abi_thiscall))]
2#![allow(non_snake_case, non_camel_case_types, unused_variables)]
3//! This interface is extremely unstable, everything just lives in a soup at the top level for now.
4
5use std::convert::TryFrom;
6use std::error::Error;
7use std::ffi::{CStr, CString, NulError};
8use std::ops::{Deref, DerefMut};
9use std::os::raw::{c_char, c_int, c_uint, c_void};
10use std::ptr::{null, null_mut};
11use std::str::Utf8Error;
12
13pub use c_str_macro::c_str;
14pub use libc::size_t;
15
16pub use sm_ext_derive::{forwards, native, vtable, vtable_override, ICallableApi, SMExtension, SMInterfaceApi};
17
18#[repr(transparent)]
19pub struct IdentityType(c_uint);
20
21#[repr(C)]
22pub enum FeatureType {
23    Native = 0,
24    Capability = 1,
25}
26
27#[repr(C)]
28pub enum FeatureStatus {
29    Available = 0,
30    Unavailable = 1,
31    Unknown = 2,
32}
33
34// TODO: Investigate using a `union` for this instead.
35/// Wrapper type that represents a value from SourcePawn.
36///
37/// Could be a [`i32`], [`f32`], `&i32`, `&f32`, or `&i8` (for character strings).
38#[repr(transparent)]
39#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
40pub struct cell_t(i32);
41
42impl std::fmt::Display for cell_t {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
44        self.0.fmt(f)
45    }
46}
47
48/// Trait to support conversions to/from [`cell_t`] that require an [`IPluginContext`] for access to plugin memory.
49pub trait TryFromPlugin<'ctx, T = cell_t>: Sized {
50    type Error;
51
52    fn try_from_plugin(ctx: &'ctx crate::IPluginContext, value: T) -> Result<Self, Self::Error>;
53}
54
55impl<T, U> TryFromPlugin<'_, T> for U
56where
57    U: TryFrom<T>,
58{
59    type Error = U::Error;
60
61    fn try_from_plugin(ctx: &IPluginContext, value: T) -> Result<Self, Self::Error> {
62        TryFrom::try_from(value)
63    }
64}
65
66/// Trait to support conversions to/from [`cell_t`] that require an [`IPluginContext`] for access to plugin memory.
67///
68/// As with Rust's [`TryInto`](std::convert::TryInto) and [`TryFrom`](std::convert::TryFrom), this is implemented automatically
69/// for types that implement [`TryFromPlugin`] which you should prefer to implement instead.
70pub trait TryIntoPlugin<'ctx, T = cell_t>: Sized {
71    type Error;
72
73    fn try_into_plugin(self, ctx: &'ctx IPluginContext) -> Result<T, Self::Error>;
74}
75
76impl<'ctx, T, U> TryIntoPlugin<'ctx, U> for T
77where
78    U: TryFromPlugin<'ctx, T>,
79{
80    type Error = U::Error;
81
82    fn try_into_plugin(self, ctx: &'ctx IPluginContext) -> Result<U, U::Error> {
83        U::try_from_plugin(ctx, self)
84    }
85}
86
87impl From<i32> for cell_t {
88    fn from(x: i32) -> Self {
89        cell_t(x)
90    }
91}
92
93impl From<cell_t> for i32 {
94    fn from(x: cell_t) -> Self {
95        x.0
96    }
97}
98
99impl From<f32> for cell_t {
100    fn from(x: f32) -> Self {
101        cell_t(x.to_bits() as i32)
102    }
103}
104
105impl From<cell_t> for f32 {
106    fn from(x: cell_t) -> Self {
107        f32::from_bits(x.0 as u32)
108    }
109}
110
111impl<'ctx> TryFromPlugin<'ctx> for &'ctx CStr {
112    type Error = SPError;
113
114    fn try_from_plugin(ctx: &'ctx IPluginContext, value: cell_t) -> Result<Self, Self::Error> {
115        Ok(ctx.local_to_string(value)?)
116    }
117}
118
119impl<'ctx> TryFromPlugin<'ctx> for &'ctx str {
120    type Error = Box<dyn Error>;
121
122    fn try_from_plugin(ctx: &'ctx IPluginContext, value: cell_t) -> Result<Self, Self::Error> {
123        Ok(ctx.local_to_string(value)?.to_str()?)
124    }
125}
126
127// TODO: These &mut implementations seem risky, maybe a SPRef/SPString/SPArray wrapper object would be a better way to go...
128
129impl<'ctx> TryFromPlugin<'ctx> for &'ctx mut cell_t {
130    type Error = SPError;
131
132    fn try_from_plugin(ctx: &'ctx IPluginContext, value: cell_t) -> Result<Self, Self::Error> {
133        Ok(ctx.local_to_phys_addr(value)?)
134    }
135}
136
137impl<'ctx> TryFromPlugin<'ctx> for &'ctx mut i32 {
138    type Error = SPError;
139
140    fn try_from_plugin(ctx: &'ctx IPluginContext, value: cell_t) -> Result<Self, Self::Error> {
141        let cell: &mut cell_t = value.try_into_plugin(ctx)?;
142        unsafe { Ok(&mut *(cell as *mut cell_t as *mut i32)) }
143    }
144}
145
146impl<'ctx> TryFromPlugin<'ctx> for &'ctx mut f32 {
147    type Error = SPError;
148
149    fn try_from_plugin(ctx: &'ctx IPluginContext, value: cell_t) -> Result<Self, Self::Error> {
150        let cell: &mut cell_t = value.try_into_plugin(ctx)?;
151        unsafe { Ok(&mut *(cell as *mut cell_t as *mut f32)) }
152    }
153}
154
155/// Struct to contain name/fnptr pairs for native registration.
156///
157/// SourceMod has very strict lifetime requirements for this data and you should not construct
158/// instances of this type yourself - use the [`register_natives!`] macro instead.
159#[repr(C)]
160pub struct NativeInfo {
161    pub name: *const c_char,
162    pub func: Option<unsafe extern "C" fn(ctx: IPluginContextPtr, args: *const cell_t) -> cell_t>,
163}
164
165pub struct IdentityToken {
166    _private: [u8; 0],
167}
168
169pub type IdentityTokenPtr = *mut IdentityToken;
170
171pub type IExtensionInterfacePtr = *mut *mut IExtensionInterfaceVtable;
172
173#[vtable(IExtensionInterfacePtr)]
174pub struct IExtensionInterfaceVtable {
175    pub GetExtensionVersion: fn() -> i32,
176    pub OnExtensionLoad: fn(me: IExtensionPtr, sys: IShareSysPtr, error: *mut c_char, maxlength: size_t, late: bool) -> bool,
177    pub OnExtensionUnload: fn() -> (),
178    pub OnExtensionsAllLoaded: fn() -> (),
179    pub OnExtensionPauseChange: fn(pause: bool) -> (),
180    pub QueryInterfaceDrop: fn(interface: SMInterfacePtr) -> bool,
181    pub NotifyInterfaceDrop: fn(interface: SMInterfacePtr) -> (),
182    pub QueryRunning: fn(error: *mut c_char, maxlength: size_t) -> bool,
183    pub IsMetamodExtension: fn() -> bool,
184    pub GetExtensionName: fn() -> *const c_char,
185    pub GetExtensionURL: fn() -> *const c_char,
186    pub GetExtensionTag: fn() -> *const c_char,
187    pub GetExtensionAuthor: fn() -> *const c_char,
188    pub GetExtensionVerString: fn() -> *const c_char,
189    pub GetExtensionDescription: fn() -> *const c_char,
190    pub GetExtensionDateString: fn() -> *const c_char,
191    pub OnCoreMapStart: fn(edict_list: *mut c_void, edict_count: c_int, client_max: c_int) -> (),
192    pub OnDependenciesDropped: fn() -> (),
193    pub OnCoreMapEnd: fn() -> (),
194}
195
196// There appears to be a bug with the MSVC linker in release mode dropping these symbols when threaded
197// compilation is enabled - if you run into undefined symbol errors here try setting code-units to 1.
198pub trait IExtensionInterface {
199    fn on_extension_load(&mut self, me: IExtension, sys: IShareSys, late: bool) -> Result<(), Box<dyn Error>> {
200        Ok(())
201    }
202    fn on_extension_unload(&mut self) {}
203    fn on_extensions_all_loaded(&mut self) {}
204    fn on_extension_pause_change(&mut self, pause: bool) {}
205    fn on_core_map_start(&mut self, edict_list: *mut c_void, edict_count: i32, client_max: i32) {}
206    fn on_core_map_end(&mut self) {}
207    fn query_interface_drop(&mut self, interface: SMInterface) -> bool {
208        false
209    }
210    fn notify_interface_drop(&mut self, interface: SMInterface) {}
211    fn query_running(&mut self) -> Result<(), CString> {
212        Ok(())
213    }
214    fn on_dependencies_dropped(&mut self) {}
215}
216
217pub trait ExtensionMetadata {
218    fn get_extension_name(&self) -> &'static CStr;
219    fn get_extension_url(&self) -> &'static CStr;
220    fn get_extension_tag(&self) -> &'static CStr;
221    fn get_extension_author(&self) -> &'static CStr;
222    fn get_extension_ver_string(&self) -> &'static CStr;
223    fn get_extension_description(&self) -> &'static CStr;
224    fn get_extension_date_string(&self) -> &'static CStr;
225}
226
227#[repr(C)]
228pub struct IExtensionInterfaceAdapter<T: IExtensionInterface + ExtensionMetadata> {
229    vtable: *mut IExtensionInterfaceVtable,
230    pub delegate: T,
231}
232
233impl<T: IExtensionInterface + ExtensionMetadata> Drop for IExtensionInterfaceAdapter<T> {
234    fn drop(&mut self) {
235        unsafe {
236            drop(Box::from_raw(self.vtable));
237        }
238    }
239}
240
241impl<T: IExtensionInterface + ExtensionMetadata> IExtensionInterfaceAdapter<T> {
242    pub fn new(delegate: T) -> IExtensionInterfaceAdapter<T> {
243        let vtable = IExtensionInterfaceVtable {
244            GetExtensionVersion: IExtensionInterfaceAdapter::<T>::get_extension_version,
245            OnExtensionLoad: IExtensionInterfaceAdapter::<T>::on_extension_load,
246            OnExtensionUnload: IExtensionInterfaceAdapter::<T>::on_extension_unload,
247            OnExtensionsAllLoaded: IExtensionInterfaceAdapter::<T>::on_extensions_all_loaded,
248            OnExtensionPauseChange: IExtensionInterfaceAdapter::<T>::on_extension_pause_change,
249            QueryInterfaceDrop: IExtensionInterfaceAdapter::<T>::query_interface_drop,
250            NotifyInterfaceDrop: IExtensionInterfaceAdapter::<T>::notify_interface_drop,
251            QueryRunning: IExtensionInterfaceAdapter::<T>::query_running,
252            IsMetamodExtension: IExtensionInterfaceAdapter::<T>::is_metamod_extension,
253            GetExtensionName: IExtensionInterfaceAdapter::<T>::get_extension_name,
254            GetExtensionURL: IExtensionInterfaceAdapter::<T>::get_extension_url,
255            GetExtensionTag: IExtensionInterfaceAdapter::<T>::get_extension_tag,
256            GetExtensionAuthor: IExtensionInterfaceAdapter::<T>::get_extension_author,
257            GetExtensionVerString: IExtensionInterfaceAdapter::<T>::get_extension_ver_string,
258            GetExtensionDescription: IExtensionInterfaceAdapter::<T>::get_extension_description,
259            GetExtensionDateString: IExtensionInterfaceAdapter::<T>::get_extension_date_string,
260            OnCoreMapStart: IExtensionInterfaceAdapter::<T>::on_core_map_start,
261            OnDependenciesDropped: IExtensionInterfaceAdapter::<T>::on_dependencies_dropped,
262            OnCoreMapEnd: IExtensionInterfaceAdapter::<T>::on_core_map_end,
263        };
264
265        IExtensionInterfaceAdapter { vtable: Box::into_raw(Box::new(vtable)), delegate }
266    }
267
268    #[vtable_override]
269    unsafe fn get_extension_version(this: IExtensionInterfacePtr) -> i32 {
270        8
271    }
272
273    #[vtable_override]
274    unsafe fn on_extension_load(this: IExtensionInterfacePtr, me: IExtensionPtr, sys: IShareSysPtr, error: *mut c_char, maxlength: size_t, late: bool) -> bool {
275        let result = std::panic::catch_unwind(|| (*this.cast::<Self>()).delegate.on_extension_load(IExtension(me), IShareSys(sys), late));
276
277        match result {
278            Ok(result) => match result {
279                Ok(result) => true,
280                Err(err) => {
281                    let err = CString::new(err.to_string()).unwrap_or_else(|_| c_str!("load error message contained NUL byte").into());
282                    libc::strncpy(error, err.as_ptr(), maxlength);
283                    false
284                }
285            },
286            Err(err) => {
287                let msg = format!(
288                    "load panicked: {}",
289                    if let Some(str_slice) = err.downcast_ref::<&'static str>() {
290                        str_slice
291                    } else if let Some(string) = err.downcast_ref::<String>() {
292                        string
293                    } else {
294                        "unknown message"
295                    }
296                );
297
298                let msg = CString::new(msg).unwrap_or_else(|_| c_str!("load panic message contained NUL byte").into());
299                libc::strncpy(error, msg.as_ptr(), maxlength);
300                false
301            }
302        }
303    }
304
305    #[vtable_override]
306    unsafe fn on_extension_unload(this: IExtensionInterfacePtr) {
307        let _ = std::panic::catch_unwind(|| (*this.cast::<Self>()).delegate.on_extension_unload());
308    }
309
310    #[vtable_override]
311    unsafe fn on_extensions_all_loaded(this: IExtensionInterfacePtr) {
312        let _ = std::panic::catch_unwind(|| (*this.cast::<Self>()).delegate.on_extensions_all_loaded());
313    }
314
315    #[vtable_override]
316    unsafe fn on_extension_pause_change(this: IExtensionInterfacePtr, pause: bool) {
317        let _ = std::panic::catch_unwind(|| (*this.cast::<Self>()).delegate.on_extension_pause_change(pause));
318    }
319
320    #[vtable_override]
321    unsafe fn query_interface_drop(this: IExtensionInterfacePtr, interface: SMInterfacePtr) -> bool {
322        let result = std::panic::catch_unwind(|| (*this.cast::<Self>()).delegate.query_interface_drop(SMInterface(interface)));
323
324        match result {
325            Ok(result) => result,
326            Err(_) => false,
327        }
328    }
329
330    #[vtable_override]
331    unsafe fn notify_interface_drop(this: IExtensionInterfacePtr, interface: SMInterfacePtr) {
332        let _ = std::panic::catch_unwind(|| (*this.cast::<Self>()).delegate.notify_interface_drop(SMInterface(interface)));
333    }
334
335    #[vtable_override]
336    unsafe fn query_running(this: IExtensionInterfacePtr, error: *mut c_char, maxlength: size_t) -> bool {
337        let result = std::panic::catch_unwind(|| match (*this.cast::<Self>()).delegate.query_running() {
338            Ok(_) => true,
339            Err(str) => {
340                libc::strncpy(error, str.as_ptr(), maxlength);
341                false
342            }
343        });
344
345        match result {
346            Ok(result) => result,
347            Err(_) => {
348                libc::strncpy(error, c_str!("query running callback panicked").as_ptr(), maxlength);
349                false
350            }
351        }
352    }
353
354    #[vtable_override]
355    unsafe fn is_metamod_extension(this: IExtensionInterfacePtr) -> bool {
356        false
357    }
358
359    #[vtable_override]
360    unsafe fn get_extension_name(this: IExtensionInterfacePtr) -> *const c_char {
361        (*this.cast::<Self>()).delegate.get_extension_name().as_ptr()
362    }
363
364    #[vtable_override]
365    unsafe fn get_extension_url(this: IExtensionInterfacePtr) -> *const c_char {
366        (*this.cast::<Self>()).delegate.get_extension_url().as_ptr()
367    }
368
369    #[vtable_override]
370    unsafe fn get_extension_tag(this: IExtensionInterfacePtr) -> *const c_char {
371        (*this.cast::<Self>()).delegate.get_extension_tag().as_ptr()
372    }
373
374    #[vtable_override]
375    unsafe fn get_extension_author(this: IExtensionInterfacePtr) -> *const c_char {
376        (*this.cast::<Self>()).delegate.get_extension_author().as_ptr()
377    }
378
379    #[vtable_override]
380    unsafe fn get_extension_ver_string(this: IExtensionInterfacePtr) -> *const c_char {
381        (*this.cast::<Self>()).delegate.get_extension_ver_string().as_ptr()
382    }
383
384    #[vtable_override]
385    unsafe fn get_extension_description(this: IExtensionInterfacePtr) -> *const c_char {
386        (*this.cast::<Self>()).delegate.get_extension_description().as_ptr()
387    }
388
389    #[vtable_override]
390    unsafe fn get_extension_date_string(this: IExtensionInterfacePtr) -> *const c_char {
391        (*this.cast::<Self>()).delegate.get_extension_date_string().as_ptr()
392    }
393
394    #[vtable_override]
395    unsafe fn on_core_map_start(this: IExtensionInterfacePtr, edict_list: *mut c_void, edict_count: c_int, client_max: c_int) {
396        let _ = std::panic::catch_unwind(|| (*this.cast::<Self>()).delegate.on_core_map_start(edict_list, edict_count, client_max));
397    }
398
399    #[vtable_override]
400    unsafe fn on_dependencies_dropped(this: IExtensionInterfacePtr) {
401        let _ = std::panic::catch_unwind(|| (*this.cast::<Self>()).delegate.on_dependencies_dropped());
402    }
403
404    #[vtable_override]
405    unsafe fn on_core_map_end(this: IExtensionInterfacePtr) {
406        let _ = std::panic::catch_unwind(|| (*this.cast::<Self>()).delegate.on_core_map_end());
407    }
408}
409
410pub type IExtensionPtr = *mut *mut IExtensionVtable;
411
412#[vtable(IExtensionPtr)]
413pub struct IExtensionVtable {
414    pub IsLoaded: fn() -> bool,
415    pub GetAPI: fn() -> IExtensionInterfacePtr,
416    pub GetFilename: fn() -> *const c_char,
417    pub GetIdentity: fn() -> IdentityTokenPtr,
418    _FindFirstDependency: fn() -> *mut c_void,
419    _FindNextDependency: fn() -> *mut c_void,
420    _FreeDependencyIterator: fn() -> *mut c_void,
421    pub IsRunning: fn(error: *mut c_char, maxlength: size_t) -> bool,
422    pub IsExternal: fn() -> bool,
423}
424
425#[derive(Debug)]
426pub enum IsRunningError<'str> {
427    WithReason(&'str str),
428    InvalidReason(Utf8Error),
429}
430
431impl std::fmt::Display for IsRunningError<'_> {
432    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
433        std::fmt::Debug::fmt(self, f)
434    }
435}
436
437impl Error for IsRunningError<'_> {}
438
439#[derive(Debug)]
440pub struct IExtension(IExtensionPtr);
441
442impl IExtension {
443    pub fn is_loaded(&self) -> bool {
444        unsafe { virtual_call!(IsLoaded, self.0) }
445    }
446
447    pub fn get_api(&self) -> IExtensionInterfacePtr {
448        unsafe { virtual_call!(GetAPI, self.0) }
449    }
450
451    pub fn get_filename(&self) -> Result<&str, Utf8Error> {
452        unsafe {
453            let c_name = virtual_call!(GetFilename, self.0);
454
455            CStr::from_ptr(c_name).to_str()
456        }
457    }
458
459    pub fn get_identity(&self) -> IdentityTokenPtr {
460        unsafe { virtual_call!(GetIdentity, self.0) }
461    }
462
463    pub fn is_running(&self) -> Result<(), IsRunningError> {
464        unsafe {
465            let mut c_error = [0 as c_char; 256];
466            let result = virtual_call!(IsRunning, self.0, c_error.as_mut_ptr(), c_error.len());
467
468            if result {
469                Ok(())
470            } else {
471                match CStr::from_ptr(c_error.as_ptr()).to_str() {
472                    Ok(error) => Err(IsRunningError::WithReason(error)),
473                    Err(e) => Err(IsRunningError::InvalidReason(e)),
474                }
475            }
476        }
477    }
478
479    pub fn is_external(&self) -> bool {
480        unsafe { virtual_call!(IsExternal, self.0) }
481    }
482}
483
484pub type SMInterfacePtr = *mut *mut SMInterfaceVtable;
485
486#[vtable(SMInterfacePtr)]
487pub struct SMInterfaceVtable {
488    pub GetInterfaceVersion: fn() -> c_uint,
489    pub GetInterfaceName: fn() -> *const c_char,
490    pub IsVersionCompatible: fn(version: c_uint) -> bool,
491}
492
493pub trait RequestableInterface {
494    fn get_interface_name() -> &'static str;
495    fn get_interface_version() -> u32;
496
497    /// # Safety
498    ///
499    /// Only for use internally by [`IShareSys::request_interface`], which always knows the correct type.
500    unsafe fn from_raw_interface(iface: SMInterface) -> Self;
501}
502
503pub trait SMInterfaceApi {
504    fn get_interface_version(&self) -> u32;
505    fn get_interface_name(&self) -> &str;
506    fn is_version_compatible(&self, version: u32) -> bool;
507}
508
509#[derive(Debug, SMInterfaceApi)]
510pub struct SMInterface(SMInterfacePtr);
511
512pub type IFeatureProviderPtr = *mut *mut IFeatureProviderVtable;
513
514#[vtable(IFeatureProviderPtr)]
515pub struct IFeatureProviderVtable {}
516
517pub type IPluginRuntimePtr = *mut *mut IPluginRuntimeVtable;
518
519#[vtable(IPluginRuntimePtr)]
520pub struct IPluginRuntimeVtable {}
521
522pub type IShareSysPtr = *mut *mut IShareSysVtable;
523
524#[vtable(IShareSysPtr)]
525pub struct IShareSysVtable {
526    pub AddInterface: fn(myself: IExtensionPtr, iface: SMInterfacePtr) -> bool,
527    pub RequestInterface: fn(iface_name: *const c_char, iface_vers: c_uint, myself: IExtensionPtr, iface: *mut SMInterfacePtr) -> bool,
528    pub AddNatives: fn(myself: IExtensionPtr, natives: *const NativeInfo) -> (),
529    pub CreateIdentType: fn(name: *const c_char) -> IdentityType,
530    pub FindIdentType: fn(name: *const c_char) -> IdentityType,
531    pub CreateIdentity: fn(ident_type: IdentityType, ptr: *mut c_void) -> IdentityTokenPtr,
532    pub DestroyIdentType: fn(ident_type: IdentityType) -> (),
533    pub DestroyIdentity: fn(identity: IdentityTokenPtr) -> (),
534    pub AddDependency: fn(myself: IExtensionPtr, filename: *const c_char, require: bool, autoload: bool) -> (),
535    pub RegisterLibrary: fn(myself: IExtensionPtr, name: *const c_char) -> (),
536    _OverrideNatives: fn(myself: IExtensionPtr, natives: *const NativeInfo) -> (),
537    pub AddCapabilityProvider: fn(myself: IExtensionPtr, provider: IFeatureProviderPtr, name: *const c_char) -> (),
538    pub DropCapabilityProvider: fn(myself: IExtensionPtr, provider: IFeatureProviderPtr, name: *const c_char) -> (),
539    pub TestFeature: fn(rt: IPluginRuntimePtr, feature_type: FeatureType, name: *const c_char) -> FeatureStatus,
540}
541
542#[derive(Debug)]
543pub enum RequestInterfaceError {
544    InvalidName(NulError),
545    InvalidInterface(String, u32),
546}
547
548impl std::fmt::Display for RequestInterfaceError {
549    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
550        match self {
551            RequestInterfaceError::InvalidName(err) => write!(f, "invalid interface name: {}", err),
552            RequestInterfaceError::InvalidInterface(name, ver) => write!(f, "failed to get {} interface version {}", name, ver),
553        }
554    }
555}
556
557impl Error for RequestInterfaceError {
558    fn source(&self) -> Option<&(dyn Error + 'static)> {
559        match self {
560            RequestInterfaceError::InvalidName(err) => Some(err),
561            RequestInterfaceError::InvalidInterface(_, _) => None,
562        }
563    }
564}
565
566#[derive(Debug)]
567pub struct IShareSys(IShareSysPtr);
568
569impl IShareSys {
570    pub fn request_interface<I: RequestableInterface>(&self, myself: &IExtension) -> Result<I, RequestInterfaceError> {
571        let iface = self.request_raw_interface(myself, I::get_interface_name(), I::get_interface_version())?;
572
573        unsafe { Ok(I::from_raw_interface(iface)) }
574    }
575
576    pub fn request_raw_interface(&self, myself: &IExtension, name: &str, version: u32) -> Result<SMInterface, RequestInterfaceError> {
577        let c_name = CString::new(name).map_err(RequestInterfaceError::InvalidName)?;
578
579        unsafe {
580            let mut iface: SMInterfacePtr = null_mut();
581            let res = virtual_call!(RequestInterface, self.0, c_name.as_ptr(), version, myself.0, &mut iface);
582
583            if res {
584                Ok(SMInterface(iface))
585            } else {
586                Err(RequestInterfaceError::InvalidInterface(name.into(), version))
587            }
588        }
589    }
590
591    /// # Safety
592    ///
593    /// This should be be used via the [`register_natives!`] macro only.
594    pub unsafe fn add_natives(&self, myself: &IExtension, natives: *const NativeInfo) {
595        virtual_call!(AddNatives, self.0, myself.0, natives)
596    }
597}
598
599/// Error codes for SourcePawn routines.
600#[repr(C)]
601#[derive(Debug)]
602pub enum SPError {
603    /// No error occurred
604    None = 0,
605    /// File format unrecognized
606    FileFormat = 1,
607    /// A decompressor was not found
608    Decompressor = 2,
609    /// Not enough space left on the heap
610    HeapLow = 3,
611    /// Invalid parameter or parameter type
612    Param = 4,
613    /// A memory address was not valid
614    InvalidAddress = 5,
615    /// The object in question was not found
616    NotFound = 6,
617    /// Invalid index parameter
618    Index = 7,
619    /// Not enough space left on the stack
620    StackLow = 8,
621    /// Debug mode was not on or debug section not found
622    NotDebugging = 9,
623    /// Invalid instruction was encountered
624    InvalidInstruction = 10,
625    /// Invalid memory access
626    MemAccess = 11,
627    /// Stack went beyond its minimum value
628    StackMin = 12,
629    /// Heap went beyond its minimum value
630    HeapMin = 13,
631    /// Division by zero
632    DivideByZero = 14,
633    /// Array index is out of bounds
634    ArrayBounds = 15,
635    /// Instruction had an invalid parameter
636    InstructionParam = 16,
637    /// A native leaked an item on the stack
638    StackLeak = 17,
639    /// A native leaked an item on the heap
640    HeapLeak = 18,
641    /// A dynamic array is too big
642    ArrayTooBig = 19,
643    /// Tracker stack is out of bounds
644    TrackerBounds = 20,
645    /// Native was pending or invalid
646    InvalidNative = 21,
647    /// Maximum number of parameters reached
648    ParamsMax = 22,
649    /// Error originates from a native
650    Native = 23,
651    /// Function or plugin is not runnable
652    NotRunnable = 24,
653    /// Function call was aborted
654    Aborted = 25,
655    /// Code is too old for this VM
656    CodeTooOld = 26,
657    /// Code is too new for this VM
658    CodeTooNew = 27,
659    /// Out of memory
660    OutOfMemory = 28,
661    /// Integer overflow (-INT_MIN / -1)
662    IntegerOverflow = 29,
663    /// Timeout
664    Timeout = 30,
665    /// Custom message
666    User = 31,
667    /// Custom fatal message
668    Fatal = 32,
669}
670
671impl std::fmt::Display for SPError {
672    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
673        f.pad(match self {
674            SPError::None => "no error occurred",
675            SPError::FileFormat => "unrecognizable file format",
676            SPError::Decompressor => "decompressor was not found",
677            SPError::HeapLow => "not enough space on the heap",
678            SPError::Param => "invalid parameter or parameter type",
679            SPError::InvalidAddress => "invalid plugin address",
680            SPError::NotFound => "object or index not found",
681            SPError::Index => "invalid index or index not found",
682            SPError::StackLow => "not enough space on the stack",
683            SPError::NotDebugging => "debug section not found or debug not enabled",
684            SPError::InvalidInstruction => "invalid instruction",
685            SPError::MemAccess => "invalid memory access",
686            SPError::StackMin => "stack went below stack boundary",
687            SPError::HeapMin => "heap went below heap boundary",
688            SPError::DivideByZero => "divide by zero",
689            SPError::ArrayBounds => "array index is out of bounds",
690            SPError::InstructionParam => "instruction contained invalid parameter",
691            SPError::StackLeak => "stack memory leaked by native",
692            SPError::HeapLeak => "heap memory leaked by native",
693            SPError::ArrayTooBig => "dynamic array is too big",
694            SPError::TrackerBounds => "tracker stack is out of bounds",
695            SPError::InvalidNative => "native is not bound",
696            SPError::ParamsMax => "maximum number of parameters reached",
697            SPError::Native => "native detected error",
698            SPError::NotRunnable => "plugin not runnable",
699            SPError::Aborted => "call was aborted",
700            SPError::CodeTooOld => "plugin format is too old",
701            SPError::CodeTooNew => "plugin format is too new",
702            SPError::OutOfMemory => "out of memory",
703            SPError::IntegerOverflow => "integer overflow",
704            SPError::Timeout => "script execution timed out",
705            SPError::User => "custom error",
706            SPError::Fatal => "fatal error",
707        })
708    }
709}
710
711impl Error for SPError {}
712
713pub type IPluginContextPtr = *mut *mut IPluginContextVtable;
714
715#[vtable(IPluginContextPtr)]
716pub struct IPluginContextVtable {
717    _Destructor: fn() -> (),
718    #[cfg(not(windows))]
719    _Destructor2: fn() -> (),
720    _GetVirtualMachine: fn(),
721    _GetContext: fn(),
722    _IsDebugging: fn(),
723    _SetDebugBreak: fn(),
724    _GetDebugInfo: fn(),
725    _HeapAlloc: fn(),
726    _HeapPop: fn(),
727    _HeapRelease: fn(),
728    _FindNativeByName: fn(),
729    _GetNativeByIndex: fn(),
730    _GetNativesNum: fn(),
731    _FindPublicByName: fn(),
732    _GetPublicByIndex: fn(),
733    _GetPublicsNum: fn(),
734    _GetPubvarByIndex: fn(),
735    _FindPubvarByName: fn(),
736    _GetPubvarAddrs: fn(),
737    _GetPubVarsNum: fn(),
738    pub LocalToPhysAddr: fn(local_addr: cell_t, phys_addr: *mut *mut cell_t) -> SPError,
739    pub LocalToString: fn(local_addr: cell_t, addr: *mut *mut c_char) -> SPError,
740    _StringToLocal: fn(),
741    _StringToLocalUTF8: fn(),
742    _PushCell: fn(),
743    _PushCellArray: fn(),
744    _PushString: fn(),
745    _PushCellsFromArray: fn(),
746    _BindNatives: fn(),
747    _BindNative: fn(),
748    _BindNativeToAny: fn(),
749    _Execute: fn(),
750    _ThrowNativeErrorEx: fn(),
751    pub ThrowNativeError: fn(*const c_char, ...) -> cell_t,
752    pub GetFunctionByName: fn(public_name: *const c_char) -> IPluginFunctionPtr,
753    pub GetFunctionById: fn(func_id: u32) -> IPluginFunctionPtr,
754    pub GetIdentity: fn() -> IdentityTokenPtr,
755    _GetNullRef: fn(),
756    _LocalToStringNULL: fn(),
757    _BindNativeToIndex: fn(),
758    _IsInExec: fn(),
759    _GetRuntime: fn(),
760    _Execute2: fn(),
761    _GetLastNativeError: fn(),
762    _GetLocalParams: fn(),
763    _SetKey: fn(),
764    _GetKey: fn(),
765    _ClearLastNativeError: fn(),
766    _APIv2: fn(),
767    _ReportError: fn(),
768    _ReportErrorVA: fn(),
769    _ReportFatalError: fn(),
770    _ReportFatalErrorVA: fn(),
771    _ReportErrorNumber: fn(),
772    _BlamePluginError: fn(),
773    _CreateFrameIterator: fn(),
774    _DestroyFrameIterator: fn(),
775}
776
777#[derive(Debug)]
778pub struct IPluginContext(IPluginContextPtr);
779
780#[derive(Debug)]
781pub enum GetFunctionError {
782    UnknownFunction,
783}
784
785impl std::fmt::Display for GetFunctionError {
786    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
787        std::fmt::Debug::fmt(self, f)
788    }
789}
790
791impl Error for GetFunctionError {}
792
793impl IPluginContext {
794    pub fn local_to_phys_addr(&self, local: cell_t) -> Result<&mut cell_t, SPError> {
795        unsafe {
796            let mut addr: *mut cell_t = null_mut();
797            let res = virtual_call!(LocalToPhysAddr, self.0, local, &mut addr);
798
799            match res {
800                SPError::None => Ok(&mut *addr),
801                _ => Err(res),
802            }
803        }
804    }
805
806    pub fn local_to_string(&self, local: cell_t) -> Result<&CStr, SPError> {
807        unsafe {
808            let mut addr: *mut c_char = null_mut();
809            let res = virtual_call!(LocalToString, self.0, local, &mut addr);
810
811            match res {
812                SPError::None => Ok(CStr::from_ptr(addr)),
813                _ => Err(res),
814            }
815        }
816    }
817
818    pub fn throw_native_error(&self, err: String) -> cell_t {
819        let fmt = c_str!("%s");
820        let err = CString::new(err).unwrap_or_else(|_| c_str!("native error message contained NUL byte").into());
821        unsafe { virtual_call_varargs!(ThrowNativeError, self.0, fmt.as_ptr(), err.as_ptr()) }
822    }
823
824    pub fn get_function_by_id(&self, func_id: u32) -> Result<IPluginFunction, GetFunctionError> {
825        unsafe {
826            let function = virtual_call!(GetFunctionById, self.0, func_id);
827            if function.is_null() {
828                Err(GetFunctionError::UnknownFunction)
829            } else {
830                Ok(IPluginFunction(function, self))
831            }
832        }
833    }
834
835    pub fn get_identity(&self) -> IdentityTokenPtr {
836        unsafe { virtual_call!(GetIdentity, self.0) }
837    }
838}
839
840pub type IPluginFunctionPtr = *mut *mut IPluginFunctionVtable;
841
842#[vtable(IPluginFunctionPtr)]
843pub struct IPluginFunctionVtable {
844    // ICallable
845    pub PushCell: fn(cell: cell_t) -> SPError,
846    pub PushCellByRef: fn(cell: *mut cell_t, flags: c_int) -> SPError,
847    pub PushFloat: fn(number: f32) -> SPError,
848    pub PushFloatByRef: fn(number: *mut f32, flags: c_int) -> SPError,
849    pub PushArray: fn(cell: *mut cell_t, cells: c_uint, flags: c_int) -> SPError,
850    pub PushString: fn(string: *const c_char) -> SPError,
851    pub PushStringEx: fn(string: *const c_char, length: size_t, sz_flags: c_int, cp_flags: c_int) -> SPError,
852    pub Cancel: fn(),
853
854    // IPluginFunction
855    pub Execute: fn(result: *mut cell_t) -> SPError,
856    _CallFunction: fn(),
857    _GetParentContext: fn(),
858    pub IsRunnable: fn() -> bool,
859    pub GetFunctionID: fn() -> u32,
860    _Execute2: fn(),
861    _CallFunction2: fn(),
862    _GetParentRuntime: fn(),
863    pub Invoke: fn(rval: *mut cell_t) -> bool,
864    pub DebugName: fn() -> *const c_char,
865}
866
867#[derive(Debug, ICallableApi)]
868pub struct IPluginFunction<'ctx>(IPluginFunctionPtr, &'ctx IPluginContext);
869
870impl Executable for IPluginFunction<'_> {
871    fn execute(&mut self) -> Result<cell_t, SPError> {
872        unsafe {
873            let mut result: cell_t = 0.into();
874            let res = virtual_call!(Execute, self.0, &mut result);
875            match res {
876                SPError::None => Ok(result),
877                _ => Err(res),
878            }
879        }
880    }
881}
882
883impl<'ctx> TryFromPlugin<'ctx> for IPluginFunction<'ctx> {
884    type Error = GetFunctionError;
885
886    fn try_from_plugin(ctx: &'ctx IPluginContext, value: cell_t) -> Result<Self, Self::Error> {
887        ctx.get_function_by_id(value.0 as u32)
888    }
889}
890
891/// Defines how a forward iterates through plugin functions.
892#[repr(C)]
893pub enum ExecType {
894    /// Ignore all return values, return 0
895    Ignore = 0,
896    /// Only return the last exec, ignore all others
897    Single = 1,
898    /// Acts as an event with the ResultTypes above, no mid-Stops allowed, returns highest
899    Event = 2,
900    /// Acts as a hook with the ResultTypes above, mid-Stops allowed, returns highest
901    Hook = 3,
902    /// Same as Event except that it returns the lowest value
903    LowEvent = 4,
904}
905
906/// Describes the various ways to pass parameters to plugins.
907#[repr(C)]
908pub enum ParamType {
909    /// Any data type can be pushed
910    Any = 0,
911    /// Only basic cells can be pushed
912    Cell = (1 << 1),
913    /// Only floats can be pushed
914    Float = (2 << 1),
915    /// Only strings can be pushed
916    String = (3 << 1) | 1,
917    /// Only arrays can be pushed
918    Array = (4 << 1) | 1,
919    /// Same as "..." in plugins, anything can be pushed, but it will always be byref
920    VarArgs = (5 << 1),
921    /// Only a cell by reference can be pushed
922    CellByRef = (1 << 1) | 1,
923    /// Only a float by reference can be pushed
924    FloatByRef = (2 << 1) | 1,
925}
926
927pub type IForwardPtr = *mut *mut IForwardVtable;
928
929#[vtable(IForwardPtr)]
930pub struct IForwardVtable {
931    // ICallable
932    pub PushCell: fn(cell: cell_t) -> SPError,
933    pub PushCellByRef: fn(cell: *mut cell_t, flags: c_int) -> SPError,
934    pub PushFloat: fn(number: f32) -> SPError,
935    pub PushFloatByRef: fn(number: *mut f32, flags: c_int) -> SPError,
936    pub PushArray: fn(cell: *mut cell_t, cells: c_uint, flags: c_int) -> SPError,
937    pub PushString: fn(string: *const c_char) -> SPError,
938    pub PushStringEx: fn(string: *const c_char, length: size_t, sz_flags: c_int, cp_flags: c_int) -> SPError,
939    pub Cancel: fn(),
940
941    // IForward
942    _Destructor: fn() -> (),
943    #[cfg(not(windows))]
944    _Destructor2: fn() -> (),
945    pub GetForwardName: fn() -> *const c_char,
946    pub GetFunctionCount: fn() -> c_uint,
947    pub GetExecType: fn() -> ExecType,
948    pub Execute: fn(result: *mut cell_t, filter: *mut c_void) -> SPError,
949}
950
951pub type IChangeableForwardPtr = *mut *mut IChangeableForwardVtable;
952
953#[vtable(IChangeableForwardPtr)]
954pub struct IChangeableForwardVtable {
955    // ICallable
956    pub PushCell: fn(cell: cell_t) -> SPError,
957    pub PushCellByRef: fn(cell: *mut cell_t, flags: c_int) -> SPError,
958    pub PushFloat: fn(number: f32) -> SPError,
959    pub PushFloatByRef: fn(number: *mut f32, flags: c_int) -> SPError,
960    pub PushArray: fn(cell: *mut cell_t, cells: c_uint, flags: c_int) -> SPError,
961    pub PushString: fn(string: *const c_char) -> SPError,
962    pub PushStringEx: fn(string: *const c_char, length: size_t, sz_flags: c_int, cp_flags: c_int) -> SPError,
963    pub Cancel: fn(),
964
965    // IForward
966    _Destructor: fn() -> (),
967    #[cfg(not(windows))]
968    _Destructor2: fn() -> (),
969    pub GetForwardName: fn() -> *const c_char,
970    pub GetFunctionCount: fn() -> c_uint,
971    pub GetExecType: fn() -> ExecType,
972    pub Execute: fn(result: *mut cell_t, filter: *mut c_void) -> SPError,
973
974    // IChangeableForward
975    #[cfg(windows)]
976    pub RemoveFunctionById: fn(ctx: IPluginContextPtr, func: u32) -> bool,
977    pub RemoveFunction: fn(func: IPluginFunctionPtr) -> bool,
978    _RemoveFunctionsOfPlugin: fn(),
979    #[cfg(windows)]
980    pub AddFunctionById: fn(ctx: IPluginContextPtr, func: u32) -> bool,
981    pub AddFunction: fn(func: IPluginFunctionPtr) -> bool,
982    #[cfg(not(windows))]
983    pub AddFunctionById: fn(ctx: IPluginContextPtr, func: u32) -> bool,
984    #[cfg(not(windows))]
985    pub RemoveFunctionById: fn(ctx: IPluginContextPtr, func: u32) -> bool,
986}
987
988pub trait CallableParam {
989    fn push<T: ICallableApi>(&self, callable: &mut T) -> Result<(), SPError>;
990    fn param_type() -> ParamType;
991}
992
993impl CallableParam for cell_t {
994    fn push<T: ICallableApi>(&self, callable: &mut T) -> Result<(), SPError> {
995        callable.push_int(self.0)
996    }
997
998    fn param_type() -> ParamType {
999        ParamType::Cell
1000    }
1001}
1002
1003impl CallableParam for i32 {
1004    fn push<T: ICallableApi>(&self, callable: &mut T) -> Result<(), SPError> {
1005        callable.push_int(*self)
1006    }
1007
1008    fn param_type() -> ParamType {
1009        ParamType::Cell
1010    }
1011}
1012
1013impl CallableParam for f32 {
1014    fn push<T: ICallableApi>(&self, callable: &mut T) -> Result<(), SPError> {
1015        callable.push_float(*self)
1016    }
1017
1018    fn param_type() -> ParamType {
1019        ParamType::Float
1020    }
1021}
1022
1023impl CallableParam for &CStr {
1024    fn push<T: ICallableApi>(&self, callable: &mut T) -> Result<(), SPError> {
1025        callable.push_string(self)
1026    }
1027
1028    fn param_type() -> ParamType {
1029        ParamType::String
1030    }
1031}
1032
1033// TODO: This interface is very, very rough.
1034pub trait ICallableApi {
1035    fn push_int(&mut self, cell: i32) -> Result<(), SPError>;
1036    fn push_float(&mut self, number: f32) -> Result<(), SPError>;
1037    fn push_string(&mut self, string: &CStr) -> Result<(), SPError>;
1038}
1039
1040pub trait Executable: ICallableApi + Sized {
1041    fn execute(&mut self) -> Result<cell_t, SPError>;
1042
1043    fn push<T: CallableParam>(&mut self, param: T) -> Result<(), SPError> {
1044        param.push(self)
1045    }
1046}
1047
1048#[derive(Debug, ICallableApi)]
1049pub struct Forward(IForwardPtr, IForwardManagerPtr);
1050
1051impl Drop for Forward {
1052    fn drop(&mut self) {
1053        IForwardManager(self.1).release_forward(&mut self.0);
1054    }
1055}
1056
1057impl Executable for Forward {
1058    fn execute(&mut self) -> Result<cell_t, SPError> {
1059        unsafe {
1060            let mut result: cell_t = 0.into();
1061            let res = virtual_call!(Execute, self.0, &mut result, null_mut());
1062            match res {
1063                SPError::None => Ok(result),
1064                _ => Err(res),
1065            }
1066        }
1067    }
1068}
1069
1070impl Forward {
1071    pub fn get_function_count(&self) -> u32 {
1072        unsafe { virtual_call!(GetFunctionCount, self.0) }
1073    }
1074}
1075
1076#[derive(Debug, ICallableApi)]
1077pub struct ChangeableForward(IChangeableForwardPtr, IForwardManagerPtr);
1078
1079impl Drop for ChangeableForward {
1080    fn drop(&mut self) {
1081        IForwardManager(self.1).release_forward(&mut (self.0 as IForwardPtr));
1082    }
1083}
1084
1085impl Executable for ChangeableForward {
1086    fn execute(&mut self) -> Result<cell_t, SPError> {
1087        unsafe {
1088            let mut result: cell_t = 0.into();
1089            let res = virtual_call!(Execute, self.0, &mut result, null_mut());
1090            match res {
1091                SPError::None => Ok(result),
1092                _ => Err(res),
1093            }
1094        }
1095    }
1096}
1097
1098impl ChangeableForward {
1099    pub fn get_function_count(&self) -> u32 {
1100        unsafe { virtual_call!(GetFunctionCount, self.0) }
1101    }
1102
1103    pub fn add_function(&mut self, func: &mut IPluginFunction) {
1104        unsafe {
1105            virtual_call!(AddFunction, self.0, func.0);
1106        }
1107    }
1108
1109    pub fn remove_function(&mut self, func: &mut IPluginFunction) {
1110        unsafe {
1111            virtual_call!(RemoveFunction, self.0, func.0);
1112        }
1113    }
1114}
1115
1116pub type IForwardManagerPtr = *mut *mut IForwardManagerVtable;
1117
1118#[vtable(IForwardManagerPtr)]
1119pub struct IForwardManagerVtable {
1120    // SMInterface
1121    pub GetInterfaceVersion: fn() -> c_uint,
1122    pub GetInterfaceName: fn() -> *const c_char,
1123    pub IsVersionCompatible: fn(version: c_uint) -> bool,
1124
1125    // IForwardManager
1126    pub CreateForward: fn(name: *const c_char, et: ExecType, num_params: c_uint, types: *const ParamType, ...) -> IForwardPtr,
1127    pub CreateForwardEx: fn(name: *const c_char, et: ExecType, num_params: c_uint, types: *const ParamType, ...) -> IChangeableForwardPtr,
1128    pub FindForward: fn(name: *const c_char, *mut IChangeableForwardPtr) -> IForwardPtr,
1129    pub ReleaseForward: fn(forward: IForwardPtr) -> (),
1130}
1131
1132#[derive(Debug)]
1133pub enum CreateForwardError {
1134    InvalidName(NulError),
1135    InvalidParams(Option<String>),
1136}
1137
1138impl std::fmt::Display for CreateForwardError {
1139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
1140        match self {
1141            CreateForwardError::InvalidName(err) => write!(f, "invalid forward name: {}", err),
1142            CreateForwardError::InvalidParams(name) => match name {
1143                Some(name) => write!(f, "failed to create forward {}: invalid params", name),
1144                None => write!(f, "failed to create forward anonymous forward: invalid params"),
1145            },
1146        }
1147    }
1148}
1149
1150impl Error for CreateForwardError {
1151    fn source(&self) -> Option<&(dyn Error + 'static)> {
1152        match self {
1153            CreateForwardError::InvalidName(err) => Some(err),
1154            CreateForwardError::InvalidParams(_) => None,
1155        }
1156    }
1157}
1158
1159#[derive(Debug, SMInterfaceApi)]
1160#[interface("IForwardManager", 4)]
1161pub struct IForwardManager(IForwardManagerPtr);
1162
1163impl IForwardManager {
1164    pub fn create_global_forward(&self, name: &str, et: ExecType, params: &[ParamType]) -> Result<Forward, CreateForwardError> {
1165        let c_name = CString::new(name).map_err(CreateForwardError::InvalidName)?;
1166
1167        unsafe {
1168            let forward = virtual_call_varargs!(CreateForward, self.0, c_name.as_ptr(), et, params.len() as u32, params.as_ptr());
1169
1170            if forward.is_null() {
1171                Err(CreateForwardError::InvalidParams(Some(name.into())))
1172            } else {
1173                Ok(Forward(forward, self.0))
1174            }
1175        }
1176    }
1177
1178    pub fn create_private_forward(&self, name: Option<&str>, et: ExecType, params: &[ParamType]) -> Result<ChangeableForward, CreateForwardError> {
1179        let c_name = match name {
1180            Some(name) => Some(CString::new(name).map_err(CreateForwardError::InvalidName)?),
1181            None => None,
1182        };
1183
1184        let c_name = match c_name {
1185            Some(c_name) => c_name.as_ptr(),
1186            None => null(),
1187        };
1188
1189        unsafe {
1190            let forward = virtual_call_varargs!(CreateForwardEx, self.0, c_name, et, params.len() as u32, params.as_ptr());
1191
1192            if forward.is_null() {
1193                Err(CreateForwardError::InvalidParams(name.map(|name| name.into())))
1194            } else {
1195                Ok(ChangeableForward(forward, self.0))
1196            }
1197        }
1198    }
1199
1200    fn release_forward(&self, forward: &mut IForwardPtr) {
1201        if forward.is_null() {
1202            panic!("release_forward called on null forward ptr")
1203        }
1204
1205        unsafe {
1206            virtual_call!(ReleaseForward, self.0, *forward);
1207            *forward = null_mut();
1208        }
1209    }
1210}
1211
1212#[repr(transparent)]
1213#[derive(Debug, Copy, Clone, PartialEq)]
1214pub struct HandleTypeId(c_uint);
1215
1216impl HandleTypeId {
1217    pub fn is_valid(self) -> bool {
1218        self.0 != 0
1219    }
1220
1221    pub fn invalid() -> Self {
1222        Self(0)
1223    }
1224}
1225
1226#[repr(transparent)]
1227#[derive(Debug, Copy, Clone, PartialEq)]
1228pub struct HandleId(c_uint);
1229
1230impl HandleId {
1231    pub fn is_valid(self) -> bool {
1232        self.0 != 0
1233    }
1234
1235    pub fn invalid() -> Self {
1236        Self(0)
1237    }
1238}
1239
1240impl From<cell_t> for HandleId {
1241    fn from(x: cell_t) -> Self {
1242        Self(x.0 as u32)
1243    }
1244}
1245
1246impl From<HandleId> for cell_t {
1247    fn from(x: HandleId) -> Self {
1248        Self(x.0 as i32)
1249    }
1250}
1251
1252impl CallableParam for HandleId {
1253    fn push<T: ICallableApi>(&self, callable: &mut T) -> Result<(), SPError> {
1254        callable.push_int(self.0 as i32)
1255    }
1256
1257    fn param_type() -> ParamType {
1258        ParamType::Cell
1259    }
1260}
1261
1262/// Lists the possible handle error codes.
1263#[repr(C)]
1264#[derive(Debug)]
1265pub enum HandleError {
1266    /// No error
1267    None = 0,
1268    /// The handle has been freed and reassigned
1269    Changed = 1,
1270    /// The handle has a different type registered
1271    Type = 2,
1272    /// The handle has been freed
1273    Freed = 3,
1274    /// Generic internal indexing error
1275    Index = 4,
1276    /// No access permitted to free this handle
1277    Access = 5,
1278    /// The limited number of handles has been reached
1279    Limit = 6,
1280    /// The identity token was not usable
1281    Identity = 7,
1282    /// Owners do not match for this operation
1283    Owner = 8,
1284    /// Unrecognized security structure version
1285    Version = 9,
1286    /// An invalid parameter was passed
1287    Parameter = 10,
1288    /// This type cannot be inherited
1289    NoInherit = 11,
1290}
1291
1292impl std::fmt::Display for HandleError {
1293    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
1294        f.pad(match self {
1295            HandleError::None => "no error",
1296            HandleError::Changed => "the handle has been freed and reassigned",
1297            HandleError::Type => "the handle has a different type registered",
1298            HandleError::Freed => "the handle has been freed",
1299            HandleError::Index => "generic internal indexing error",
1300            HandleError::Access => "no access permitted to free this handle",
1301            HandleError::Limit => "the limited number of handles has been reached",
1302            HandleError::Identity => "the identity token was not usable",
1303            HandleError::Owner => "owners do not match for this operation",
1304            HandleError::Version => "unrecognized security structure version",
1305            HandleError::Parameter => "an invalid parameter was passed",
1306            HandleError::NoInherit => "this type cannot be inherited",
1307        })
1308    }
1309}
1310
1311impl Error for HandleError {}
1312
1313pub type IHandleTypeDispatchPtr = *mut *mut IHandleTypeDispatchVtable;
1314
1315#[vtable(IHandleTypeDispatchPtr)]
1316pub struct IHandleTypeDispatchVtable {
1317    pub GetDispatchVersion: fn() -> c_uint,
1318    pub OnHandleDestroy: fn(ty: HandleTypeId, object: *mut c_void) -> (),
1319    pub GetHandleApproxSize: fn(ty: HandleTypeId, object: *mut c_void, size: *mut c_uint) -> bool,
1320}
1321
1322#[repr(C)]
1323pub struct IHandleTypeDispatchAdapter<T> {
1324    vtable: *mut IHandleTypeDispatchVtable,
1325    phantom: std::marker::PhantomData<T>,
1326}
1327
1328impl<T> Drop for IHandleTypeDispatchAdapter<T> {
1329    fn drop(&mut self) {
1330        unsafe {
1331            drop(Box::from_raw(self.vtable));
1332        }
1333    }
1334}
1335
1336impl<T> Default for IHandleTypeDispatchAdapter<T> {
1337    fn default() -> Self {
1338        Self::new()
1339    }
1340}
1341
1342impl<T> IHandleTypeDispatchAdapter<T> {
1343    pub fn new() -> IHandleTypeDispatchAdapter<T> {
1344        let vtable = IHandleTypeDispatchVtable {
1345            GetDispatchVersion: IHandleTypeDispatchAdapter::<T>::get_dispatch_version,
1346            OnHandleDestroy: IHandleTypeDispatchAdapter::<T>::on_handle_destroy,
1347            GetHandleApproxSize: IHandleTypeDispatchAdapter::<T>::get_handle_approx_size,
1348        };
1349
1350        IHandleTypeDispatchAdapter { vtable: Box::into_raw(Box::new(vtable)), phantom: std::marker::PhantomData }
1351    }
1352
1353    #[vtable_override]
1354    unsafe fn get_dispatch_version(this: IHandleTypeDispatchPtr) -> u32 {
1355        <IHandleSys as RequestableInterface>::get_interface_version()
1356    }
1357
1358    #[vtable_override]
1359    unsafe fn on_handle_destroy(this: IHandleTypeDispatchPtr, ty: HandleTypeId, object: *mut c_void) {
1360        drop(Box::from_raw(object as *mut T));
1361    }
1362
1363    #[vtable_override]
1364    unsafe fn get_handle_approx_size(this: IHandleTypeDispatchPtr, ty: HandleTypeId, object: *mut c_void, size: *mut c_uint) -> bool {
1365        // This isn't ideal as it doesn't account for dynamic sizes, probably need to add a trait at some point
1366        // for people to implement this properly. See also: https://github.com/rust-lang/rust/issues/63073
1367        let object = object as *mut T;
1368        *size = std::mem::size_of_val(&*object) as u32;
1369
1370        *size != 0
1371    }
1372}
1373
1374/// This pair of tokens is used for identification.
1375#[repr(C)]
1376#[derive(Debug)]
1377pub struct HandleSecurity {
1378    /// Owner of the Handle
1379    pub owner: IdentityTokenPtr,
1380    /// Owner of the Type
1381    pub identity: IdentityTokenPtr,
1382}
1383
1384impl HandleSecurity {
1385    pub fn new(owner: IdentityTokenPtr, identity: IdentityTokenPtr) -> Self {
1386        Self { owner, identity }
1387    }
1388}
1389
1390pub type IHandleSysPtr = *mut *mut IHandleSysVtable;
1391
1392#[vtable(IHandleSysPtr)]
1393pub struct IHandleSysVtable {
1394    // SMInterface
1395    pub GetInterfaceVersion: fn() -> c_uint,
1396    pub GetInterfaceName: fn() -> *const c_char,
1397    pub IsVersionCompatible: fn(version: c_uint) -> bool,
1398
1399    // IHandleSys
1400    pub CreateType: fn(name: *const c_char, dispatch: IHandleTypeDispatchPtr, parent: HandleTypeId, typeAccess: *const c_void, handleAccess: *const c_void, ident: IdentityTokenPtr, err: *mut HandleError) -> HandleTypeId,
1401    pub RemoveType: fn(ty: HandleTypeId, ident: IdentityTokenPtr) -> bool,
1402    pub FindHandleType: fn(name: *const c_char, ty: *mut HandleTypeId) -> bool,
1403    pub CreateHandle: fn(ty: HandleTypeId, object: *mut c_void, owner: IdentityTokenPtr, ident: IdentityTokenPtr, err: *mut HandleError) -> HandleId,
1404    pub FreeHandle: fn(handle: HandleId, security: *const HandleSecurity) -> HandleError,
1405    pub CloneHandle: fn(handle: HandleId, newHandle: *mut HandleId, newOwner: IdentityTokenPtr, security: *const HandleSecurity) -> HandleError,
1406    pub ReadHandle: fn(handle: HandleId, ty: HandleTypeId, security: *const HandleSecurity, object: *mut *mut c_void) -> HandleError,
1407    pub InitAccessDefaults: fn(typeAccess: *mut c_void, handleAccess: *mut c_void) -> bool,
1408    pub CreateHandleEx: fn(ty: HandleTypeId, object: *mut c_void, security: *const HandleSecurity, access: *const c_void, err: *mut HandleError) -> HandleId,
1409    pub FastCloneHandle: fn(handle: HandleId) -> HandleId,
1410    pub TypeCheck: fn(given: HandleTypeId, actual: HandleTypeId) -> bool,
1411}
1412
1413#[derive(Debug)]
1414pub struct HandleType<T> {
1415    iface: IHandleSysPtr,
1416    id: HandleTypeId,
1417    dispatch: *mut IHandleTypeDispatchAdapter<T>,
1418    ident: IdentityTokenPtr,
1419}
1420
1421impl<T> Drop for HandleType<T> {
1422    fn drop(&mut self) {
1423        IHandleSys(self.iface).remove_type(self).unwrap();
1424
1425        unsafe {
1426            drop(Box::from_raw(self.dispatch));
1427        }
1428    }
1429}
1430
1431/// Implement this trait to allow automatic conversion to [`HandleRef`] from native arguments.
1432pub trait HasHandleType: Sized {
1433    fn handle_type<'ty>() -> &'ty HandleType<Self>;
1434
1435    // TODO: Try and avoid having to read the ptr back from the handle.
1436    fn into_handle<'ty>(self) -> Result<HandleRef<'ty, Self>, HandleError> {
1437        let ty = Self::handle_type();
1438        let handle = ty.create_handle(self, ty.ident)?;
1439        let ptr = ty.read_handle(handle, ty.ident)?;
1440
1441        Ok(HandleRef { ty, handle, ptr })
1442    }
1443}
1444
1445impl<'ctx, 'ty, T: HasHandleType> TryFromPlugin<'ctx> for HandleRef<'ty, T> {
1446    type Error = HandleError;
1447
1448    fn try_from_plugin(ctx: &'ctx IPluginContext, value: cell_t) -> Result<Self, Self::Error> {
1449        let ty = T::handle_type();
1450        let handle = HandleId::from(value);
1451        let owner = ctx.get_identity();
1452
1453        HandleRef::new(ty, handle, owner)
1454    }
1455}
1456
1457/// Wrapper for a [`HandleId`] that is [`Deref`] to the wrapping type.
1458pub struct HandleRef<'ty, T> {
1459    ty: &'ty HandleType<T>,
1460    handle: HandleId,
1461    ptr: *mut T,
1462}
1463
1464impl<'ty, T> HandleRef<'ty, T> {
1465    pub fn new(ty: &'ty HandleType<T>, handle: HandleId, owner: IdentityTokenPtr) -> Result<Self, HandleError> {
1466        let ptr = ty.read_handle(handle, owner)?;
1467        let owned = ty.clone_handle(handle, owner, ty.ident)?;
1468
1469        Ok(HandleRef { ty, handle: owned, ptr })
1470    }
1471
1472    pub fn clone_handle(&self, new_owner: IdentityTokenPtr) -> Result<HandleId, HandleError> {
1473        self.ty.clone_handle(self.handle, self.ty.ident, new_owner)
1474    }
1475}
1476
1477impl<T> Drop for HandleRef<'_, T> {
1478    fn drop(&mut self) {
1479        match self.ty.free_handle(self.handle, self.ty.ident) {
1480            Ok(_) => self.handle = HandleId::invalid(),
1481            Err(e) => panic!("invalid handle when dropping HandleRef: {}", e),
1482        }
1483    }
1484}
1485
1486impl<T> Deref for HandleRef<'_, T> {
1487    type Target = T;
1488
1489    fn deref(&self) -> &Self::Target {
1490        unsafe { &*self.ptr }
1491    }
1492}
1493
1494/// This is unsound if multiple [`HandleRef`]s are created wrapping the same Handle data.
1495///
1496/// To guarantee safety, do not use mutable HandleRefs but instead use a [`std::cell::RefCell`] wrapper inside your [`HandleType`].
1497impl<T> DerefMut for HandleRef<'_, T> {
1498    fn deref_mut(&mut self) -> &mut Self::Target {
1499        unsafe { &mut *self.ptr }
1500    }
1501}
1502
1503impl<T> std::fmt::Debug for HandleRef<'_, T> {
1504    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
1505        write!(f, "HandleRef({:08x}, {:?})", self.handle.0, self.ptr)
1506    }
1507}
1508
1509impl<T> HandleType<T> {
1510    pub fn create_handle(&self, object: T, owner: IdentityTokenPtr) -> Result<HandleId, HandleError> {
1511        IHandleSys(self.iface).create_handle(self, object, owner)
1512    }
1513
1514    pub fn clone_handle(&self, handle: HandleId, owner: IdentityTokenPtr, new_owner: IdentityTokenPtr) -> Result<HandleId, HandleError> {
1515        IHandleSys(self.iface).clone_handle(self, handle, owner, new_owner)
1516    }
1517
1518    pub fn free_handle(&self, handle: HandleId, owner: IdentityTokenPtr) -> Result<(), HandleError> {
1519        IHandleSys(self.iface).free_handle(self, handle, owner)
1520    }
1521
1522    pub fn read_handle(&self, handle: HandleId, owner: IdentityTokenPtr) -> Result<*mut T, HandleError> {
1523        IHandleSys(self.iface).read_handle(self, handle, owner)
1524    }
1525}
1526
1527#[derive(Debug)]
1528pub enum CreateHandleTypeError {
1529    InvalidName(NulError),
1530    HandleError(String, HandleError),
1531}
1532
1533impl std::fmt::Display for CreateHandleTypeError {
1534    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
1535        match self {
1536            CreateHandleTypeError::InvalidName(err) => write!(f, "invalid handle type name: {}", err),
1537            CreateHandleTypeError::HandleError(name, err) => write!(f, "failed to create handle type {}: {}", name, err),
1538        }
1539    }
1540}
1541
1542impl Error for CreateHandleTypeError {
1543    fn source(&self) -> Option<&(dyn Error + 'static)> {
1544        match self {
1545            CreateHandleTypeError::InvalidName(err) => Some(err),
1546            CreateHandleTypeError::HandleError(_, err) => Some(err),
1547        }
1548    }
1549}
1550
1551#[derive(Debug, SMInterfaceApi)]
1552#[interface("IHandleSys", 5)]
1553pub struct IHandleSys(IHandleSysPtr);
1554
1555impl IHandleSys {
1556    pub fn create_type<T>(&self, name: &str, ident: IdentityTokenPtr) -> Result<HandleType<T>, CreateHandleTypeError> {
1557        unsafe {
1558            let c_name = CString::new(name).map_err(CreateHandleTypeError::InvalidName)?;
1559            let dispatch = Box::into_raw(Box::new(IHandleTypeDispatchAdapter::<T>::new()));
1560
1561            let mut err: HandleError = HandleError::None;
1562            let id = virtual_call!(CreateType, self.0, c_name.as_ptr(), dispatch as IHandleTypeDispatchPtr, HandleTypeId::invalid(), null(), null(), ident, &mut err);
1563
1564            if id.is_valid() {
1565                Ok(HandleType { iface: self.0, id, dispatch, ident })
1566            } else {
1567                Err(CreateHandleTypeError::HandleError(name.into(), err))
1568            }
1569        }
1570    }
1571
1572    fn remove_type<T>(&self, ty: &mut HandleType<T>) -> Result<(), bool> {
1573        unsafe {
1574            if virtual_call!(RemoveType, self.0, ty.id, ty.ident) {
1575                Ok(())
1576            } else {
1577                Err(false)
1578            }
1579        }
1580    }
1581
1582    fn create_handle<T>(&self, ty: &HandleType<T>, object: T, owner: IdentityTokenPtr) -> Result<HandleId, HandleError> {
1583        unsafe {
1584            let object = Box::into_raw(Box::new(object)) as *mut c_void;
1585            let security = HandleSecurity::new(owner, ty.ident);
1586            let mut err: HandleError = HandleError::None;
1587            let id = virtual_call!(CreateHandleEx, self.0, ty.id, object, &security, null(), &mut err);
1588            if id.is_valid() {
1589                Ok(id)
1590            } else {
1591                Err(err)
1592            }
1593        }
1594    }
1595
1596    fn free_handle<T>(&self, ty: &HandleType<T>, handle: HandleId, owner: IdentityTokenPtr) -> Result<(), HandleError> {
1597        unsafe {
1598            let security = HandleSecurity::new(owner, ty.ident);
1599            let err = virtual_call!(FreeHandle, self.0, handle, &security);
1600            match err {
1601                HandleError::None => Ok(()),
1602                _ => Err(err),
1603            }
1604        }
1605    }
1606
1607    fn clone_handle<T>(&self, ty: &HandleType<T>, handle: HandleId, owner: IdentityTokenPtr, new_owner: IdentityTokenPtr) -> Result<HandleId, HandleError> {
1608        unsafe {
1609            let security = HandleSecurity::new(owner, ty.ident);
1610            let mut new_handle = HandleId::invalid();
1611            let err = virtual_call!(CloneHandle, self.0, handle, &mut new_handle, new_owner, &security);
1612            match err {
1613                HandleError::None => Ok(new_handle),
1614                _ => Err(err),
1615            }
1616        }
1617    }
1618
1619    fn read_handle<T>(&self, ty: &HandleType<T>, handle: HandleId, owner: IdentityTokenPtr) -> Result<*mut T, HandleError> {
1620        unsafe {
1621            let security = HandleSecurity::new(owner, ty.ident);
1622            let mut object: *mut c_void = null_mut();
1623            let err = virtual_call!(ReadHandle, self.0, handle, ty.id, &security, &mut object);
1624            match err {
1625                HandleError::None => Ok(object as *mut T),
1626                _ => Err(err),
1627            }
1628        }
1629    }
1630}
1631
1632/// Describes various ways of formatting a base path.
1633#[repr(C)]
1634#[derive(Debug)]
1635pub enum PathType {
1636    /// No base path
1637    Path_None = 0,
1638    /// Base path is absolute mod folder
1639    Path_Game = 1,
1640    /// Base path is absolute to SourceMod
1641    Path_SM = 2,
1642    /// Base path is relative to SourceMod
1643    Path_SM_Rel = 3,
1644}
1645
1646pub type GameFrameHookFunc = unsafe extern "C" fn(simulating: bool);
1647
1648pub type ISourceModPtr = *mut *mut ISourceModVtable;
1649
1650#[vtable(ISourceModPtr)]
1651pub struct ISourceModVtable {
1652    // SMInterface
1653    pub GetInterfaceVersion: fn() -> c_uint,
1654    pub GetInterfaceName: fn() -> *const c_char,
1655    pub IsVersionCompatible: fn(version: c_uint) -> bool,
1656
1657    // ISourceMod
1658    pub GetGamePath: fn() -> *const c_char,
1659    pub GetSourceModPath: fn() -> *const c_char,
1660    pub BuildPath: fn(ty: PathType, buffer: *mut c_char, maxlength: size_t, format: *const c_char, ...) -> size_t,
1661    pub LogMessage: fn(ext: IExtensionPtr, format: *const c_char, ...) -> (),
1662    pub LogError: fn(ext: IExtensionPtr, format: *const c_char, ...) -> (),
1663    pub FormatString: fn(buffer: *mut c_char, maxlength: size_t, context: IPluginContextPtr, params: *const cell_t, param: c_uint) -> size_t,
1664    _CreateDataPack: fn(),
1665    _FreeDataPack: fn(),
1666    _GetDataPackHandleType: fn(),
1667    _ReadKeyValuesHandle: fn(),
1668    pub GetGameFolderName: fn() -> *const c_char,
1669    pub GetScriptingEngine: fn() -> *mut c_void,
1670    pub GetScriptingVM: fn() -> *mut c_void,
1671    _GetAdjustedTime: fn(),
1672    pub SetGlobalTarget: fn(index: c_uint) -> c_uint,
1673    pub GetGlobalTarget: fn() -> c_uint,
1674    pub AddGameFrameHook: fn(hook: GameFrameHookFunc) -> (),
1675    pub RemoveGameFrameHook: fn(hook: GameFrameHookFunc) -> (),
1676    pub Format: fn(buffer: *mut c_char, maxlength: size_t, format: *const c_char, ...) -> size_t,
1677    _FormatArgs: fn(),
1678    pub AddFrameAction: fn(func: unsafe extern "C" fn(*mut c_void), data: *mut c_void) -> (),
1679    pub GetCoreConfigValue: fn(key: *const c_char) -> *const c_char,
1680    pub GetPluginId: fn() -> c_int,
1681    pub GetShApiVersion: fn() -> c_int,
1682    pub IsMapRunning: fn() -> bool,
1683    pub FromPseudoAddress: fn(pseudo: u32) -> *mut c_void,
1684    pub ToPseudoAddress: fn(addr: *mut c_void) -> u32,
1685}
1686
1687#[derive(Debug, SMInterfaceApi)]
1688#[interface("ISourceMod", 14)]
1689pub struct ISourceMod(ISourceModPtr);
1690
1691pub struct GameFrameHookId(GameFrameHookFunc, ISourceModPtr);
1692
1693impl Drop for GameFrameHookId {
1694    fn drop(&mut self) {
1695        ISourceMod(self.1).remove_game_frame_hook(self.0);
1696    }
1697}
1698
1699unsafe extern "C" fn frame_action_trampoline<F: FnMut() + 'static>(func: *mut c_void) {
1700    let mut func = Box::<F>::from_raw(func as *mut _);
1701    (*func)()
1702}
1703
1704impl ISourceMod {
1705    pub fn log_message(&self, myself: &IExtension, msg: String) {
1706        let fmt = c_str!("%s");
1707        let msg = CString::new(msg).expect("log message contained NUL byte");
1708        unsafe { virtual_call_varargs!(LogMessage, self.0, myself.0, fmt.as_ptr(), msg.as_ptr()) }
1709    }
1710
1711    pub fn log_error(&self, myself: &IExtension, msg: String) {
1712        let fmt = c_str!("%s");
1713        let msg = CString::new(msg).expect("log message contained NUL byte");
1714        unsafe { virtual_call_varargs!(LogError, self.0, myself.0, fmt.as_ptr(), msg.as_ptr()) }
1715    }
1716
1717    /// Add a function that will be called every game frame until the [`GameFrameHookId`] return value
1718    /// is dropped. This is a fairly low-level building block as the callback must be `extern "C"`.
1719    pub fn add_game_frame_hook(&self, hook: GameFrameHookFunc) -> GameFrameHookId {
1720        unsafe {
1721            virtual_call!(AddGameFrameHook, self.0, hook);
1722        }
1723
1724        GameFrameHookId(hook, self.0)
1725    }
1726
1727    fn remove_game_frame_hook(&self, hook: GameFrameHookFunc) {
1728        unsafe {
1729            virtual_call!(RemoveGameFrameHook, self.0, hook);
1730        }
1731    }
1732
1733    // TODO: If we implement a [`Send`] subset of [`ISourceMod`] this function should be included but the closure must also be [`Send`].
1734    /// Add a function that will be called on the next game frame. This has a runtime cost as this API
1735    /// is thread-safe on the SM side, but it supports a Rust closure so is more flexible than [`ISourceMod::add_game_frame_hook`].
1736    pub fn add_frame_action<F>(&self, func: F)
1737    where
1738        F: FnMut() + 'static,
1739    {
1740        unsafe {
1741            let func = Box::into_raw(Box::new(func));
1742            virtual_call!(AddFrameAction, self.0, frame_action_trampoline::<F>, func as *mut c_void);
1743        }
1744    }
1745}
1746
1747/// Helper for virtual function invocation that works with the `#[vtable]` attribute to support
1748/// virtual calls on Windows without compiler support for the `thiscall` calling convention.
1749#[macro_export]
1750macro_rules! virtual_call {
1751    ($name:ident, $this:expr, $($param:expr),* $(,)?) => {
1752        ((**$this).$name)(
1753            $this,
1754            #[cfg(all(windows, target_arch = "x86", not(feature = "abi_thiscall")))]
1755            std::ptr::null_mut(),
1756            $(
1757                $param,
1758            )*
1759        )
1760    };
1761    ($name:ident, $this:expr) => {
1762        virtual_call!($name, $this, )
1763    };
1764}
1765
1766// TODO: Figure out a way to make this type-safe (and hopefully avoid the need for it completely.)
1767/// Helper for varargs-using virtual function invocation that works with the `#[vtable]` attribute to
1768/// support virtual calls on Windows without compiler support for the `thiscall` calling convention.
1769#[macro_export]
1770macro_rules! virtual_call_varargs {
1771    ($name:ident, $this:expr, $($param:expr),* $(,)?) => {
1772        ((**$this).$name)(
1773            $this,
1774            $(
1775                $param,
1776            )*
1777        )
1778    };
1779    ($name:ident, $this:expr) => {
1780        virtual_call!($name, $this, )
1781    };
1782}
1783
1784#[macro_export]
1785macro_rules! register_natives {
1786    ($sys:expr, $myself:expr, [$(($name:expr, $func:expr)),* $(,)?]) => {
1787        unsafe {
1788            let mut vec = Vec::new();
1789            $(
1790                let name = concat!($name, "\0").as_ptr() as *const ::std::os::raw::c_char;
1791                vec.push($crate::NativeInfo {
1792                    name: name,
1793                    func: Some($func),
1794                });
1795            )*
1796            vec.push($crate::NativeInfo {
1797                name: ::std::ptr::null(),
1798                func: None,
1799            });
1800
1801            // This leaks vec so that it remains valid.
1802            // TODO: Look into making it static somewhere, it only has to live as long as the extension is loaded.
1803            // Would probably need some of the nightly macro features, which tbh would help the native callbacks anyway.
1804            let boxed = vec.into_boxed_slice();
1805            $sys.add_natives($myself, Box::leak(boxed).as_ptr());
1806        }
1807    };
1808}
1809
1810/// The return type for native callbacks.
1811pub trait NativeResult {
1812    type Ok;
1813    type Err;
1814
1815    fn into_result(self) -> Result<Self::Ok, Self::Err>;
1816}
1817
1818/// Dummy error used for [`NativeResult`] implementations that can never fail.
1819#[derive(Debug)]
1820pub struct DummyNativeError;
1821
1822impl std::fmt::Display for DummyNativeError {
1823    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
1824        std::fmt::Debug::fmt(self, f)
1825    }
1826}
1827
1828impl Error for DummyNativeError {}
1829
1830impl NativeResult for () {
1831    type Ok = i32;
1832    type Err = DummyNativeError;
1833
1834    fn into_result(self) -> Result<Self::Ok, Self::Err> {
1835        Ok(0)
1836    }
1837}
1838
1839impl<'ctx, T> NativeResult for T
1840where
1841    T: TryIntoPlugin<'ctx, cell_t>,
1842{
1843    type Ok = T;
1844    type Err = DummyNativeError;
1845
1846    fn into_result(self) -> Result<Self::Ok, Self::Err> {
1847        Ok(self)
1848    }
1849}
1850
1851impl<E> NativeResult for Result<(), E> {
1852    type Ok = i32;
1853    type Err = E;
1854
1855    #[allow(clippy::type_complexity)]
1856    fn into_result(self) -> Result<<Result<(), E> as NativeResult>::Ok, <Result<(), E> as NativeResult>::Err> {
1857        self.map(|_| 0)
1858    }
1859}
1860
1861impl<'ctx, T, E> NativeResult for Result<T, E>
1862where
1863    T: TryIntoPlugin<'ctx, cell_t>,
1864{
1865    type Ok = T;
1866    type Err = E;
1867
1868    #[allow(clippy::type_complexity)]
1869    fn into_result(self) -> Result<<Result<T, E> as NativeResult>::Ok, <Result<T, E> as NativeResult>::Err> {
1870        self
1871    }
1872}
1873
1874/// Wrapper to invoke a native callback and translate a [`panic!`] or [`Err`](std::result::Result::Err)
1875/// return into a SourceMod error using [`IPluginContext::throw_native_error`].
1876///
1877/// This is used internally by the `#[native]` attribute.
1878pub fn safe_native_invoke<F>(ctx: IPluginContextPtr, f: F) -> cell_t
1879where
1880    F: FnOnce(&IPluginContext) -> Result<cell_t, Box<dyn Error>> + std::panic::UnwindSafe,
1881{
1882    let ctx = IPluginContext(ctx);
1883    let result = std::panic::catch_unwind(|| f(&ctx));
1884
1885    match result {
1886        Ok(result) => match result {
1887            Ok(result) => result,
1888            Err(err) => ctx.throw_native_error(err.to_string()),
1889        },
1890        Err(err) => {
1891            let msg = format!(
1892                "native panicked: {}",
1893                if let Some(str_slice) = err.downcast_ref::<&'static str>() {
1894                    str_slice
1895                } else if let Some(string) = err.downcast_ref::<String>() {
1896                    string
1897                } else {
1898                    "unknown message"
1899                }
1900            );
1901
1902            ctx.throw_native_error(msg)
1903        }
1904    }
1905}