1use std::any::Any;
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::ffi::{c_char, CString};
5use once_cell::unsync::Lazy;
6use paste::paste;
7
8macro_rules! typed_ptr {
9 ( $name:ident ) => {
10 #[repr(C)]
11 #[derive(Copy, Clone)]
12 struct $name {
13 ptr: *mut c_char
14 }
15 };
16}
17
18typed_ptr!(_Color);
19typed_ptr!(_ColorNote);
20typed_ptr!(_BombNote);
21typed_ptr!(_ChainHeadNote);
22typed_ptr!(_ChainLinkNote);
23typed_ptr!(_ChainNote);
24typed_ptr!(_Arc);
25typed_ptr!(_Wall);
26typed_ptr!(_Saber);
27typed_ptr!(_Vec2);
29typed_ptr!(_Vec3);
30typed_ptr!(_Vec4);
31typed_ptr!(_Quat);
32
33macro_rules! color_methods {
34 ( $tp:ty, $name:tt ) => {
35 paste! {
36 extern "C" {
37 fn [<_ $name _set_color>]($name: $tp, color: _Color);
38 fn [<_ $name _get_color>]($name: $tp) -> _Color;
39 }
40 }
41 }
42}
43
44macro_rules! position_methods {
45 ( $tp:ty, $name:tt ) => {
46 paste! {
47 extern "C" {
48 fn [<_ $name _set_position>]($name: $tp, pos: _Vec3);
49 fn [<_ $name _get_position>]($name: $tp) -> _Vec3;
50 fn [<_ $name _set_orientation>]($name: $tp, orientation: _Quat);
51 fn [<_ $name _get_orientation>]($name: $tp) -> _Quat;
52 }
53 }
54 }
55}
56
57macro_rules! gameplay_object_methods {
58 ( $tp:ty, $name:tt ) => {
59 paste! {
60 extern "C" {
61 fn [<_create_ $name>](beat: f32) -> $tp;
62 fn [<_beatmap_add_ $name>]($name: $tp);
63 fn [<_beatmap_remove_ $name>]($name: $tp);
64 }
65 }
66 position_methods! { $tp, $name }
67 color_methods! { $tp, $name }
68 };
69}
70
71macro_rules! attrs {
72 ( $tp:ty, $name:tt, $( $attr:ident: $attr_tp:ty ),* $(,)? ) => {
73 extern "C" {
74 paste! {
75 $(
76 fn [<_ $name _get_attr_ $attr>]($name: [<_ $tp>]) -> $attr_tp;
77 fn [<_ $name _set_attr_ $attr>]($name: [<_ $tp>], $attr: $attr_tp);
78 )*
79 }
80 }
81
82 impl $tp {
83 $(
84
85 paste! {
86 pub fn [<get_ $attr>](&self) -> $attr_tp {
87 unsafe { [<_ $name _get_attr_ $attr>](self._inner) }
88 }
89 pub fn [<set_ $attr>](&self, $attr: $attr_tp) {
90 unsafe { [<_ $name _set_attr_ $attr>](self._inner, $attr) }
91 }
92 }
93
94 )*
95 }
96 };
97}
98
99gameplay_object_methods! { _ColorNote, color_note }
100gameplay_object_methods! { _BombNote, bomb_note }
101gameplay_object_methods! { _Arc, arc }
102gameplay_object_methods! { _Wall, wall }
103gameplay_object_methods! { _ChainHeadNote, chain_head_note }
104gameplay_object_methods! { _ChainLinkNote, chain_link_note }
105gameplay_object_methods! { _ChainNote, chain_note }
106
107color_methods! { _Saber, saber }
108
109extern "C" {
110 fn _get_left_saber() -> _Saber;
111 fn _get_right_saber() -> _Saber;
112
113 fn _log(message: *const c_char);
114
115 fn _drop_reference(ptr: *mut c_char);
117
118
119 fn _vec2_from_native(x: f32, y: f32) -> _Vec2;
120 fn _vec3_from_native(x: f32, y: f32, z: f32) -> _Vec3;
121 fn _vec4_from_native(x: f32, y: f32, z: f32, w: f32) -> _Vec4;
122 fn _quat_from_native(x: f32, y: f32, z: f32, w: f32) -> _Quat;
123
124 fn _color_set_rgb(color: _Color, r: f32, g: f32, b: f32);
125 fn _color_set_rgba(color: _Color, r: f32, g: f32, b: f32, a: f32);
126
127}
128
129macro_rules! cstr {
130 ( $str:expr ) => {
131 CString::new($str).unwrap()
132 };
133}
134
135pub struct Log {}
138impl Log {
139 pub fn info(message: &str) {
140 let s = format!("info: {}", message);
141 let c = cstr!(s);
142 unsafe { _log(c.as_ptr()) };
143 }
144
145 pub fn warning(message: &str) {
146 let s = format!("warning: {}", message);
147 let c = cstr!(s);
148 unsafe { _log(c.as_ptr()) };
149 }
150
151 pub fn error(message: &str) {
152 let s = format!("error: {}", message);
153 let c = cstr!(s);
154 unsafe { _log(c.as_ptr()) };
155 }
156
157 pub fn debug(message: &str) {
158 let s = format!("debug: {}", message);
159 let c = cstr!(s);
160 unsafe { _log(c.as_ptr()) };
161 }
162
163}
164
165macro_rules! wrapped {
166 ( $name:tt ) => {
167 pub struct $name {
168 _inner: paste! { [<_ $name>] }
169 }
170 };
171}
172
173macro_rules! instantiable_obj {
174 ( $ty:tt, $name:tt ) => {
175 wrapped! { $ty }
176 paste! {
177
178 pub fn [<create_ $name>](beat: f32) -> $ty {
179 unsafe { $ty { _inner: [<_create_ $name>](beat) } }
180 }
181
182 impl Beatmap {
183 pub fn [<add_ $name>]($name: $ty) {
184 unsafe {
185 [<_beatmap_add_ $name>]($name._inner);
186 }
187 }
188 }
189 }
190 };
191}
192
193pub struct Beatmap;
194
195thread_local! {
196 static GLOBAL_MAP: Lazy<RefCell<HashMap<String, Box<dyn Any>>>> = Lazy::new(|| {
197 RefCell::new(HashMap::new())
198 });
199}
200
201pub struct Data;
202
203impl Data {
204
205 pub fn set_temp_value<T: Clone + 'static>(key: &str, v: T) {
206 GLOBAL_MAP.with(|map| {
207 map.borrow_mut().insert(key.to_string(), Box::new(v));
208 })
209 }
210
211 pub fn get_temp_value<T: Clone + 'static>(key: &str) -> Option<T> {
212 GLOBAL_MAP.with(|map| {
213 map.borrow().get(key).and_then(|v| v.downcast_ref::<T>()).cloned()
214 })
215 }
216
217 pub fn remove_temp_value(key: &str) {
218 GLOBAL_MAP.with(|map| {
219 map.borrow_mut().remove(key);
220 })
221 }
222
223}
224
225instantiable_obj! { ColorNote, color_note }
226instantiable_obj! { BombNote, bomb_note }
227instantiable_obj! { ChainHeadNote, chain_head_note }
228instantiable_obj! { ChainLinkNote, chain_link_note }
229instantiable_obj! { ChainNote, chain_note }
230instantiable_obj! { Arc, arc }
231instantiable_obj! { Wall, wall }
232
233wrapped! { Vec2 }
234
235
236wrapped! { Vec3 }
237wrapped! { Vec4 }
238wrapped! { Quat }
239wrapped! { Color }
240
241attrs! { Vec2, vec2, x: f32, y: f32 }
242attrs! { Vec3, vec3, x: f32, y: f32, z: f32 }
243attrs! { Vec4, vec4, x: f32, y: f32, z: f32, w: f32 }
244attrs! { Quat, quat, x: f32, y: f32, z: f32, w: f32 }
245attrs! { Color, color, r: f32, g: f32, b: f32, a: f32 }
246
247pub trait UnityConvertible {
248 type UnityType;
249 fn to_unity_type(self) -> Self::UnityType;
250 fn from_unity_type(t: Self::UnityType) -> Self;
251}
252
253impl UnityConvertible for glam::Vec2 {
254 type UnityType = Vec2;
255 fn to_unity_type(self) -> Self::UnityType {
256 Vec2 { _inner: unsafe { _vec2_from_native(self.x, self.y) } }
257 }
258 fn from_unity_type(t: Self::UnityType) -> Self {
259 glam::Vec2::new(t.get_x(), t.get_y())
260 }
261}
262
263impl UnityConvertible for glam::Vec3 {
264 type UnityType = Vec3;
265 fn to_unity_type(self) -> Self::UnityType {
266 Vec3 { _inner: unsafe { _vec3_from_native(self.x, self.y, self.z) } }
267 }
268 fn from_unity_type(t: Self::UnityType) -> Self {
269 glam::Vec3::new(t.get_x(), t.get_y(), t.get_z())
270 }
271}
272
273impl UnityConvertible for glam::Vec4 {
274 type UnityType = Vec4;
275 fn to_unity_type(self) -> Self::UnityType {
276 Vec4 { _inner: unsafe { _vec4_from_native(self.x, self.y, self.z, self.w) } }
277 }
278 fn from_unity_type(t: Self::UnityType) -> Self {
279 glam::Vec4::new(t.get_x(), t.get_y(), t.get_z(), t.get_w())
280 }
281}
282
283impl UnityConvertible for glam::Quat {
284 type UnityType = Quat;
285 fn to_unity_type(self) -> Self::UnityType {
286 Quat { _inner: unsafe { _quat_from_native(self.x, self.y, self.z, self.w) } }
287 }
288 fn from_unity_type(t: Self::UnityType) -> Self {
289 glam::Quat::from_xyzw(t.get_x(), t.get_y(), t.get_z(), t.get_w())
290 }
291}
292
293
294impl Color {
295 pub fn set_rgb(&self, r: f32, g: f32, b: f32) {
296 unsafe { _color_set_rgb(self._inner, r, g, b) };
297 }
298
299 pub fn set_rgba(&self, r: f32, g: f32, b: f32, a: f32) {
300 unsafe { _color_set_rgba(self._inner, r, g, b, a) };
301 }
302}
303