1#[cfg(not(target_pointer_width = "64"))]
8compile_error!("fmod-oxide's userdata system is currently only supported on 64-bit platforms.");
9
10use std::{
11 any::Any,
12 sync::{Arc, RwLock},
13};
14
15use once_cell::sync::Lazy;
16use slotmap::{HopSlotMap, Key, KeyData};
17
18use crate::{
19 studio::{Bank, CommandReplay, EventDescription, EventInstance, System as StudioSystem},
20 ChannelControl, Dsp, DspConnection, Geometry, Reverb3D, Sound, SoundGroup, System,
21};
22
23#[derive(Default)]
24struct UserdataStorage {
25 slotmap: HopSlotMap<UserdataKey, UserdataValue>,
28}
29
30slotmap::new_key_type! {
31 pub struct UserdataKey;
32}
33
34struct UserdataValue {
35 userdata: Userdata,
36 owner: HasUserdata,
37}
38
39#[derive(PartialEq)]
40pub(crate) enum HasUserdata {
41 StudioSystem(StudioSystem),
42 Bank(Bank),
43 EventDescription(EventDescription),
44 EventInstance(EventInstance),
45 CommandReplay(CommandReplay),
46 Reverb3D(Reverb3D),
48 SoundGroup(SoundGroup),
49 ChannelControl(ChannelControl),
50 Dsp(Dsp),
51 Sound(Sound),
52 Geometry(Geometry),
53 DspConnection(DspConnection),
54 System(System),
55}
56
57impl HasUserdata {
58 fn get_raw_userdata(&self) -> fmod_sys::Result<*mut std::ffi::c_void> {
59 match self {
60 HasUserdata::StudioSystem(s) => s.get_raw_userdata(),
61 HasUserdata::Bank(b) => b.get_raw_userdata(),
62 HasUserdata::EventDescription(e) => e.get_raw_userdata(),
63 HasUserdata::EventInstance(e) => e.get_raw_userdata(),
64 HasUserdata::CommandReplay(c) => c.get_raw_userdata(),
65 HasUserdata::Reverb3D(r) => r.get_raw_userdata(),
66 HasUserdata::SoundGroup(s) => s.get_raw_userdata(),
67 HasUserdata::ChannelControl(c) => c.get_raw_userdata(),
69 HasUserdata::Dsp(d) => d.get_raw_userdata(),
70 HasUserdata::Sound(s) => s.get_raw_userdata(),
71 HasUserdata::Geometry(g) => g.get_raw_userdata(),
72 HasUserdata::DspConnection(c) => c.get_raw_userdata(),
73 HasUserdata::System(s) => s.get_raw_userdata(),
74 }
75 }
76
77 fn is_valid(&self, key: UserdataKey) -> bool {
78 match self {
79 HasUserdata::StudioSystem(s) => s.is_valid(),
80 HasUserdata::Bank(b) => b.is_valid(),
81 HasUserdata::EventDescription(e) => e.is_valid(),
82 HasUserdata::EventInstance(e) => e.is_valid(),
83 HasUserdata::CommandReplay(c) => c.is_valid(),
84 HasUserdata::System(_) => true,
86 _ => {
90 let userdata = self.get_raw_userdata();
91 userdata.is_ok_and(|ptr| ptr == key.into())
92 }
93 }
94 }
95}
96
97pub type Userdata = Arc<dyn Any + Send + Sync + 'static>;
98
99static STORAGE: Lazy<RwLock<UserdataStorage>> = Lazy::new(Default::default);
100
101pub(crate) fn insert_userdata(userdata: Userdata, owner: impl Into<HasUserdata>) -> UserdataKey {
102 let mut storage = STORAGE.write().unwrap();
103 storage.slotmap.insert(UserdataValue {
104 userdata,
105 owner: owner.into(),
106 })
107}
108
109pub(crate) fn remove_userdata(key: UserdataKey) -> Option<Userdata> {
110 let mut storage = STORAGE.write().unwrap();
111 storage.slotmap.remove(key).map(|v| v.userdata)
112}
113
114pub(crate) fn get_userdata(key: UserdataKey) -> Option<Userdata> {
115 let storage = STORAGE.read().unwrap();
116 storage.slotmap.get(key).map(|v| v.userdata.clone())
117}
118
119pub(crate) fn set_userdata(key: UserdataKey, userdata: Userdata) {
120 let mut storage = STORAGE.write().unwrap();
121 match storage.slotmap.get_mut(key) {
122 Some(v) => v.userdata = userdata,
123 None => eprintln!("Warning: userdata key does not exist!"),
124 }
125}
126
127pub(crate) fn cleanup_userdata() {
128 let mut storage = STORAGE.write().unwrap();
129 storage.slotmap.retain(|k, v| v.owner.is_valid(k));
130}
131
132pub(crate) fn clear_userdata() {
133 let mut storage = STORAGE.write().unwrap();
134 storage.slotmap.clear();
135}
136
137impl From<UserdataKey> for *mut std::ffi::c_void {
138 fn from(key: UserdataKey) -> Self {
139 key.data().as_ffi() as *mut std::ffi::c_void
140 }
141}
142
143impl From<*mut std::ffi::c_void> for UserdataKey {
144 fn from(ptr: *mut std::ffi::c_void) -> Self {
145 UserdataKey::from(KeyData::from_ffi(ptr as u64))
146 }
147}
148
149impl From<StudioSystem> for HasUserdata {
150 fn from(s: StudioSystem) -> Self {
151 HasUserdata::StudioSystem(s)
152 }
153}
154
155impl From<Bank> for HasUserdata {
156 fn from(b: Bank) -> Self {
157 HasUserdata::Bank(b)
158 }
159}
160
161impl From<EventDescription> for HasUserdata {
162 fn from(e: EventDescription) -> Self {
163 HasUserdata::EventDescription(e)
164 }
165}
166
167impl From<EventInstance> for HasUserdata {
168 fn from(e: EventInstance) -> Self {
169 HasUserdata::EventInstance(e)
170 }
171}
172
173impl From<CommandReplay> for HasUserdata {
174 fn from(c: CommandReplay) -> Self {
175 HasUserdata::CommandReplay(c)
176 }
177}
178
179impl From<Reverb3D> for HasUserdata {
180 fn from(r: Reverb3D) -> Self {
181 HasUserdata::Reverb3D(r)
182 }
183}
184
185impl From<SoundGroup> for HasUserdata {
186 fn from(s: SoundGroup) -> Self {
187 HasUserdata::SoundGroup(s)
188 }
189}
190
191impl From<ChannelControl> for HasUserdata {
192 fn from(c: ChannelControl) -> Self {
193 HasUserdata::ChannelControl(c)
194 }
195}
196
197impl From<Dsp> for HasUserdata {
198 fn from(d: Dsp) -> Self {
199 HasUserdata::Dsp(d)
200 }
201}
202
203impl From<Sound> for HasUserdata {
204 fn from(s: Sound) -> Self {
205 HasUserdata::Sound(s)
206 }
207}
208
209impl From<Geometry> for HasUserdata {
210 fn from(g: Geometry) -> Self {
211 HasUserdata::Geometry(g)
212 }
213}
214
215impl From<DspConnection> for HasUserdata {
216 fn from(c: DspConnection) -> Self {
217 HasUserdata::DspConnection(c)
218 }
219}
220
221impl From<System> for HasUserdata {
222 fn from(s: System) -> Self {
223 HasUserdata::System(s)
224 }
225}