use crate::commands::CommandFlags;
use crate::ffi::{NotThreadSafe, borrow_cstr, copy_cstr, vcall};
use std::ffi::{CStr, CString, c_int};
use std::marker::PhantomData;
use std::ptr::NonNull;
interface! {
#[doc(alias = "ICvar")]
pub struct Cvar(sys::ICvar) = Engine c"VEngineCvar004";
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConVar<'s> {
raw: NonNull<sys::ConVar>,
_scope: PhantomData<&'s ()>,
_not_thread_safe: NotThreadSafe,
}
impl<'s> ConVar<'s> {
pub(crate) const unsafe fn from_raw(raw: NonNull<sys::ConVar>) -> Self {
Self {
raw,
_scope: PhantomData,
_not_thread_safe: PhantomData,
}
}
pub const fn as_ptr(self) -> *mut sys::ConVar {
self.raw.as_ptr()
}
#[doc(alias = "GetDefault")]
pub fn default_string(self) -> CString {
unsafe { copy_cstr((&raw const (*self.parent()).m_pszDefaultValue).read()) }
.unwrap_or_default()
}
#[doc(alias = "GetFloat")]
pub fn float(self) -> f32 {
unsafe { (&raw const (*self.parent()).m_fValue).read() }
}
#[doc(alias = "GetInt")]
pub fn int(self) -> c_int {
unsafe { (&raw const (*self.parent()).m_nValue).read() }
}
fn interface(self) -> *mut sys::IConVar {
unsafe { &raw mut (*self.as_ptr())._base_1 }
}
#[doc(alias = "GetName")]
pub fn name(self) -> &'s CStr {
unsafe { borrow_cstr((&raw const (*self.as_ptr())._base.m_pszName).read()) }
.unwrap_or_default()
}
fn parent(self) -> *mut sys::ConVar {
let parent = unsafe { (&raw const (*self.as_ptr()).m_pParent).read() };
if parent.is_null() {
self.as_ptr()
} else {
parent
}
}
#[doc(alias = "SetValue")]
pub fn set_float(self, value: f32) {
unsafe { vcall!(self.interface() => IConVar_SetValue1(value)) };
}
#[doc(alias = "SetValue")]
pub fn set_int(self, value: c_int) {
unsafe { vcall!(self.interface() => IConVar_SetValue2(value)) };
}
#[doc(alias = "SetValue")]
pub fn set_string(self, value: &CStr) {
unsafe { vcall!(self.interface() => IConVar_SetValue(value.as_ptr())) };
}
pub fn set_string_quietly(self, value: &CStr) {
let notify = CommandFlags::NOTIFY.bits();
let flags = unsafe { &raw mut (*self.parent())._base.m_nFlags };
let announced = unsafe { flags.read() } & notify;
unsafe { flags.write(flags.read() & !notify) };
self.set_string(value);
unsafe { flags.write(flags.read() | announced) };
}
#[doc(alias = "GetString")]
pub fn string(self) -> CString {
unsafe { copy_cstr((&raw const (*self.parent()).m_pszString).read()) }.unwrap_or_default()
}
}
impl<'s> Cvar<'s> {
#[doc(alias = "AllocateDLLIdentifier")]
pub(crate) fn allocate_dll_identifier(self) -> sys::CVarDLLIdentifier_t {
unsafe { vcall!(self.as_ptr() => ICvar_AllocateDLLIdentifier()) }
}
#[doc(alias = "CallGlobalChangeCallbacks")]
pub(crate) unsafe fn call_global_change_callbacks(
self,
var: NonNull<sys::ConVar>,
old_value: &CStr,
old_float: f32,
) {
unsafe {
vcall!(self.as_ptr() => ICvar_CallGlobalChangeCallbacks(
var.as_ptr(),
old_value.as_ptr(),
old_float,
))
};
}
#[doc(alias = "ConsolePrintf")]
pub(crate) fn console_printf(self, message: &CStr) {
unsafe { vcall!(self.as_ptr() => ICvar_ConsolePrintf(c"%s".as_ptr(), message.as_ptr())) };
}
#[doc(alias = "FindCommandBase")]
pub(crate) fn find_command_base(self, name: &CStr) -> Option<NonNull<sys::ConCommandBase>> {
NonNull::new(unsafe { vcall!(self.as_ptr() => ICvar_FindCommandBase(name.as_ptr())) })
}
#[doc(alias = "FindVar")]
pub fn find_var(self, name: &CStr) -> Option<ConVar<'s>> {
let var = NonNull::new(unsafe { vcall!(self.as_ptr() => ICvar_FindVar(name.as_ptr())) })?;
Some(unsafe { ConVar::from_raw(var) })
}
#[doc(alias = "RegisterConCommand")]
pub(crate) unsafe fn register_con_command(self, command: NonNull<sys::ConCommandBase>) {
unsafe { vcall!(self.as_ptr() => ICvar_RegisterConCommand(command.as_ptr())) };
}
#[doc(alias = "UnregisterConCommand")]
pub(crate) unsafe fn unregister_con_command(self, command: NonNull<sys::ConCommandBase>) {
unsafe { vcall!(self.as_ptr() => ICvar_UnregisterConCommand(command.as_ptr())) };
}
}