1#![cfg_attr(feature = "abi_thiscall", feature(abi_thiscall))]
2#![allow(non_snake_case, non_camel_case_types, unused_variables)]
3use 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#[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
48pub 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
66pub 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
127impl<'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#[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
196pub 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 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 pub unsafe fn add_natives(&self, myself: &IExtension, natives: *const NativeInfo) {
595 virtual_call!(AddNatives, self.0, myself.0, natives)
596 }
597}
598
599#[repr(C)]
601#[derive(Debug)]
602pub enum SPError {
603 None = 0,
605 FileFormat = 1,
607 Decompressor = 2,
609 HeapLow = 3,
611 Param = 4,
613 InvalidAddress = 5,
615 NotFound = 6,
617 Index = 7,
619 StackLow = 8,
621 NotDebugging = 9,
623 InvalidInstruction = 10,
625 MemAccess = 11,
627 StackMin = 12,
629 HeapMin = 13,
631 DivideByZero = 14,
633 ArrayBounds = 15,
635 InstructionParam = 16,
637 StackLeak = 17,
639 HeapLeak = 18,
641 ArrayTooBig = 19,
643 TrackerBounds = 20,
645 InvalidNative = 21,
647 ParamsMax = 22,
649 Native = 23,
651 NotRunnable = 24,
653 Aborted = 25,
655 CodeTooOld = 26,
657 CodeTooNew = 27,
659 OutOfMemory = 28,
661 IntegerOverflow = 29,
663 Timeout = 30,
665 User = 31,
667 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 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 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#[repr(C)]
893pub enum ExecType {
894 Ignore = 0,
896 Single = 1,
898 Event = 2,
900 Hook = 3,
902 LowEvent = 4,
904}
905
906#[repr(C)]
908pub enum ParamType {
909 Any = 0,
911 Cell = (1 << 1),
913 Float = (2 << 1),
915 String = (3 << 1) | 1,
917 Array = (4 << 1) | 1,
919 VarArgs = (5 << 1),
921 CellByRef = (1 << 1) | 1,
923 FloatByRef = (2 << 1) | 1,
925}
926
927pub type IForwardPtr = *mut *mut IForwardVtable;
928
929#[vtable(IForwardPtr)]
930pub struct IForwardVtable {
931 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 _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 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 _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 #[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
1033pub 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 pub GetInterfaceVersion: fn() -> c_uint,
1122 pub GetInterfaceName: fn() -> *const c_char,
1123 pub IsVersionCompatible: fn(version: c_uint) -> bool,
1124
1125 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#[repr(C)]
1264#[derive(Debug)]
1265pub enum HandleError {
1266 None = 0,
1268 Changed = 1,
1270 Type = 2,
1272 Freed = 3,
1274 Index = 4,
1276 Access = 5,
1278 Limit = 6,
1280 Identity = 7,
1282 Owner = 8,
1284 Version = 9,
1286 Parameter = 10,
1288 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 let object = object as *mut T;
1368 *size = std::mem::size_of_val(&*object) as u32;
1369
1370 *size != 0
1371 }
1372}
1373
1374#[repr(C)]
1376#[derive(Debug)]
1377pub struct HandleSecurity {
1378 pub owner: IdentityTokenPtr,
1380 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 pub GetInterfaceVersion: fn() -> c_uint,
1396 pub GetInterfaceName: fn() -> *const c_char,
1397 pub IsVersionCompatible: fn(version: c_uint) -> bool,
1398
1399 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
1431pub trait HasHandleType: Sized {
1433 fn handle_type<'ty>() -> &'ty HandleType<Self>;
1434
1435 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
1457pub 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
1494impl<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#[repr(C)]
1634#[derive(Debug)]
1635pub enum PathType {
1636 Path_None = 0,
1638 Path_Game = 1,
1640 Path_SM = 2,
1642 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 pub GetInterfaceVersion: fn() -> c_uint,
1654 pub GetInterfaceName: fn() -> *const c_char,
1655 pub IsVersionCompatible: fn(version: c_uint) -> bool,
1656
1657 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 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 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#[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#[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 let boxed = vec.into_boxed_slice();
1805 $sys.add_natives($myself, Box::leak(boxed).as_ptr());
1806 }
1807 };
1808}
1809
1810pub trait NativeResult {
1812 type Ok;
1813 type Err;
1814
1815 fn into_result(self) -> Result<Self::Ok, Self::Err>;
1816}
1817
1818#[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
1874pub 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}