fmod/studio/command_replay/
callback.rs1use fmod_sys::*;
8use lanyard::Utf8CStr;
9use std::ffi::{c_char, c_float, c_int, c_uint, c_void};
10
11use crate::{
12 studio::{Bank, CommandReplay, EventDescription, EventInstance, LoadBankFlags},
13 Guid,
14};
15
16#[cfg(feature = "userdata-abstraction")]
17use crate::userdata::{get_userdata, insert_userdata, set_userdata, Userdata};
18
19#[cfg(feature = "userdata-abstraction")]
20pub trait CreateInstanceCallback {
21 fn create_instance_callback(
22 replay: CommandReplay,
23 command_index: c_int,
24 description: EventDescription,
25 userdata: Option<Userdata>,
26 ) -> Result<Option<EventInstance>>;
27}
28
29#[cfg(not(feature = "userdata-abstraction"))]
30pub trait CreateInstanceCallback {
31 fn create_instance_callback(
32 replay: CommandReplay,
33 command_index: c_int,
34 description: EventDescription,
35 userdata: *mut c_void,
36 ) -> Result<Option<EventInstance>>;
37}
38
39unsafe extern "C" fn create_instance_impl<C: CreateInstanceCallback>(
40 replay: *mut FMOD_STUDIO_COMMANDREPLAY,
41 command_index: c_int,
42 event_description: *mut FMOD_STUDIO_EVENTDESCRIPTION,
43 event_instance: *mut *mut FMOD_STUDIO_EVENTINSTANCE,
44 userdata: *mut c_void,
45) -> FMOD_RESULT {
46 #[cfg(feature = "userdata-abstraction")]
47 let userdata = get_userdata(userdata.into());
48
49 unsafe {
50 let replay = CommandReplay::from(replay);
51 let description = EventDescription::from(event_description);
52 let result = C::create_instance_callback(replay, command_index, description, userdata);
53 match result {
54 Ok(Some(instance)) => {
55 std::ptr::write(event_instance, instance.into());
56 FMOD_RESULT::FMOD_OK
57 }
58 Ok(None) => FMOD_RESULT::FMOD_OK,
59 Err(e) => e.into(),
60 }
61 }
62}
63
64#[cfg(feature = "userdata-abstraction")]
65pub trait FrameCallback {
66 fn frame_callback(
67 replay: CommandReplay,
68 command_index: c_int,
69 current_time: c_float,
70 userdata: Option<Userdata>,
71 ) -> Result<()>;
72}
73
74#[cfg(not(feature = "userdata-abstraction"))]
75pub trait FrameCallback {
76 fn frame_callback(
77 replay: CommandReplay,
78 command_index: c_int,
79 current_time: c_float,
80 userdata: *mut c_void,
81 ) -> Result<()>;
82}
83
84unsafe extern "C" fn frame_impl<C: FrameCallback>(
85 replay: *mut FMOD_STUDIO_COMMANDREPLAY,
86 command_index: c_int,
87 current_time: c_float,
88 userdata: *mut c_void,
89) -> FMOD_RESULT {
90 #[cfg(feature = "userdata-abstraction")]
91 let userdata = get_userdata(userdata.into());
92
93 let replay = CommandReplay::from(replay);
94 C::frame_callback(replay, command_index, current_time, userdata).into()
95}
96
97#[cfg(feature = "userdata-abstraction")]
98pub trait LoadBankCallback {
99 fn load_bank_callback(
100 replay: CommandReplay,
101 command_index: c_int,
102 guid: Option<Guid>,
103 filename: Option<&Utf8CStr>,
104 flags: LoadBankFlags,
105 userdata: Option<Userdata>,
106 ) -> Result<Option<Bank>>;
107}
108
109#[cfg(not(feature = "userdata-abstraction"))]
110pub trait LoadBankCallback {
111 fn load_bank_callback(
112 replay: CommandReplay,
113 command_index: c_int,
114 guid: Option<Guid>,
115 filename: Option<&Utf8CStr>,
116 flags: LoadBankFlags,
117 userdata: *mut c_void,
118 ) -> Result<Option<Bank>>;
119}
120
121unsafe extern "C" fn load_bank_impl<C: LoadBankCallback>(
122 replay: *mut FMOD_STUDIO_COMMANDREPLAY,
123 command_index: c_int,
124 guid: *const FMOD_GUID,
125 filename: *const c_char,
126 flags: c_uint,
127 bank_ptr: *mut *mut FMOD_STUDIO_BANK,
128 userdata: *mut c_void,
129) -> FMOD_RESULT {
130 #[cfg(feature = "userdata-abstraction")]
131 let userdata = get_userdata(userdata.into());
132
133 let replay = CommandReplay::from(replay);
134 let flags = LoadBankFlags::from(flags);
135 let guid = if guid.is_null() {
136 None
137 } else {
138 Some(unsafe { std::ptr::read(guid.cast()) })
139 };
140 let filename = if filename.is_null() {
141 None
142 } else {
143 Some(unsafe { Utf8CStr::from_ptr_unchecked(filename) })
144 };
145 let result = C::load_bank_callback(replay, command_index, guid, filename, flags, userdata);
146 match result {
147 Ok(Some(bank)) => {
148 unsafe {
149 std::ptr::write(bank_ptr, bank.into());
150 }
151 FMOD_RESULT::FMOD_OK
152 }
153 Ok(None) => FMOD_RESULT::FMOD_OK,
154 Err(e) => e.into(),
155 }
156}
157
158#[cfg(feature = "userdata-abstraction")]
159impl CommandReplay {
160 pub fn set_userdata(&self, userdata: Userdata) -> Result<()> {
161 let pointer = self.get_raw_userdata()?;
162 if pointer.is_null() {
163 let key = insert_userdata(userdata, *self);
164 self.set_raw_userdata(key.into())?;
165 } else {
166 set_userdata(pointer.into(), userdata);
167 }
168
169 Ok(())
170 }
171
172 pub fn get_userdata(&self) -> Result<Option<Userdata>> {
173 let pointer = self.get_raw_userdata()?;
174 Ok(get_userdata(pointer.into()))
175 }
176}
177
178impl CommandReplay {
179 #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn set_raw_userdata(&self, userdata: *mut c_void) -> Result<()> {
181 unsafe { FMOD_Studio_CommandReplay_SetUserData(self.inner, userdata).to_result() }
182 }
183
184 pub fn get_raw_userdata(&self) -> Result<*mut c_void> {
185 let mut userdata = std::ptr::null_mut();
186 unsafe {
187 FMOD_Studio_CommandReplay_GetUserData(self.inner, &mut userdata).to_result()?;
188 }
189 Ok(userdata)
190 }
191
192 pub fn set_create_instance_callback<C: CreateInstanceCallback>(&self) -> Result<()> {
193 unsafe {
194 FMOD_Studio_CommandReplay_SetCreateInstanceCallback(
195 self.inner,
196 Some(create_instance_impl::<C>),
197 )
198 .to_result()
199 }
200 }
201
202 pub fn set_frame_callback<C: FrameCallback>(&self) -> Result<()> {
203 unsafe {
204 FMOD_Studio_CommandReplay_SetFrameCallback(self.inner, Some(frame_impl::<C>))
205 .to_result()
206 }
207 }
208
209 pub fn set_load_bank_callback<C: LoadBankCallback>(&self) -> Result<()> {
210 unsafe {
211 FMOD_Studio_CommandReplay_SetLoadBankCallback(self.inner, Some(load_bank_impl::<C>))
212 .to_result()
213 }
214 }
215}