use crate::{FmodResultExt, Result};
use fmod_sys::*;
use lanyard::Utf8CStr;
use std::ffi::{c_char, c_float, c_int, c_uint, c_void};
use crate::{
Guid, panic_wrapper,
studio::{Bank, CommandReplay, EventDescription, EventInstance, LoadBankFlags},
};
pub trait CreateInstanceCallback {
fn create_instance_callback(
replay: CommandReplay,
command_index: c_int,
description: EventDescription,
userdata: *mut c_void,
) -> Result<Option<EventInstance>>;
}
unsafe extern "C" fn create_instance_impl<C: CreateInstanceCallback>(
replay: *mut FMOD_STUDIO_COMMANDREPLAY,
command_index: c_int,
event_description: *mut FMOD_STUDIO_EVENTDESCRIPTION,
event_instance: *mut *mut FMOD_STUDIO_EVENTINSTANCE,
userdata: *mut c_void,
) -> FMOD_RESULT {
panic_wrapper(|| unsafe {
let replay = CommandReplay::from_ffi(replay);
let description = EventDescription::from_ffi(event_description);
let result = C::create_instance_callback(replay, command_index, description, userdata);
match result {
Ok(Some(instance)) => {
std::ptr::write(event_instance, instance.into());
FMOD_RESULT::FMOD_OK
}
Ok(None) => FMOD_RESULT::FMOD_OK,
Err(e) => e.into(),
}
})
}
pub trait FrameCallback {
fn frame_callback(
replay: CommandReplay,
command_index: c_int,
current_time: c_float,
userdata: *mut c_void,
) -> Result<()>;
}
unsafe extern "C" fn frame_impl<C: FrameCallback>(
replay: *mut FMOD_STUDIO_COMMANDREPLAY,
command_index: c_int,
current_time: c_float,
userdata: *mut c_void,
) -> FMOD_RESULT {
panic_wrapper(|| {
let replay = unsafe { CommandReplay::from_ffi(replay) };
let result = C::frame_callback(replay, command_index, current_time, userdata);
FMOD_RESULT::from_result(result)
})
}
pub trait LoadBankCallback {
fn load_bank_callback(
replay: CommandReplay,
command_index: c_int,
guid: Option<Guid>,
filename: Option<&Utf8CStr>,
flags: LoadBankFlags,
userdata: *mut c_void,
) -> Result<Option<Bank>>;
}
unsafe extern "C" fn load_bank_impl<C: LoadBankCallback>(
replay: *mut FMOD_STUDIO_COMMANDREPLAY,
command_index: c_int,
guid: *const FMOD_GUID,
filename: *const c_char,
flags: c_uint,
bank_ptr: *mut *mut FMOD_STUDIO_BANK,
userdata: *mut c_void,
) -> FMOD_RESULT {
panic_wrapper(|| {
let replay = unsafe { CommandReplay::from_ffi(replay) };
let flags = LoadBankFlags::from(flags);
let guid = if guid.is_null() {
None
} else {
Some(unsafe { std::ptr::read(guid.cast()) })
};
let filename = if filename.is_null() {
None
} else {
Some(unsafe { Utf8CStr::from_ptr_unchecked(filename) })
};
let result = C::load_bank_callback(replay, command_index, guid, filename, flags, userdata);
match result {
Ok(Some(bank)) => {
unsafe {
std::ptr::write(bank_ptr, bank.into());
}
FMOD_RESULT::FMOD_OK
}
Ok(None) => FMOD_RESULT::FMOD_OK,
Err(e) => e.into(),
}
})
}
impl CommandReplay {
#[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn set_userdata(&self, userdata: *mut c_void) -> Result<()> {
unsafe { FMOD_Studio_CommandReplay_SetUserData(self.inner.as_ptr(), userdata).to_result() }
}
pub fn get_userdata(&self) -> Result<*mut c_void> {
let mut userdata = std::ptr::null_mut();
unsafe {
FMOD_Studio_CommandReplay_GetUserData(self.inner.as_ptr(), &raw mut userdata)
.to_result()?;
}
Ok(userdata)
}
pub fn set_create_instance_callback<C: CreateInstanceCallback>(&self) -> Result<()> {
unsafe {
FMOD_Studio_CommandReplay_SetCreateInstanceCallback(
self.inner.as_ptr(),
Some(create_instance_impl::<C>),
)
.to_result()
}
}
pub fn set_frame_callback<C: FrameCallback>(&self) -> Result<()> {
unsafe {
FMOD_Studio_CommandReplay_SetFrameCallback(self.inner.as_ptr(), Some(frame_impl::<C>))
.to_result()
}
}
pub fn set_load_bank_callback<C: LoadBankCallback>(&self) -> Result<()> {
unsafe {
FMOD_Studio_CommandReplay_SetLoadBankCallback(
self.inner.as_ptr(),
Some(load_bank_impl::<C>),
)
.to_result()
}
}
}