lotus_script_sys/
lib.rs

1use serde::{de::DeserializeOwned, Serialize};
2
3#[no_mangle]
4pub extern "C" fn allocate(size: u32) -> u32 {
5    unsafe {
6        std::alloc::alloc(std::alloc::Layout::from_size_align(size as usize, 8).unwrap()) as u32
7    }
8}
9
10#[no_mangle]
11pub extern "C" fn deallocate(ptr: u32, size: u32) {
12    unsafe {
13        std::alloc::dealloc(
14            ptr as *mut u8,
15            std::alloc::Layout::from_size_align(size as usize, 8).unwrap(),
16        )
17    }
18}
19
20pub mod env {
21    #[link(wasm_import_module = "env")]
22    extern "C" {
23        pub fn is_rc() -> bool;
24        pub fn module_slot_cockpit_index() -> i32;
25        pub fn module_slot_index_in_class_group() -> i32;
26        pub fn module_slot_index() -> i32;
27    }
28}
29
30pub mod assets {
31    #[link(wasm_import_module = "assets")]
32    extern "C" {
33        pub fn preload(id: u64);
34    }
35}
36
37pub mod time {
38    #[link(wasm_import_module = "time")]
39    extern "C" {
40        pub fn delta_f64() -> f64;
41        pub fn ticks_alive() -> u64;
42        pub fn game_time() -> i64;
43    }
44}
45
46pub mod log {
47    #[link(wasm_import_module = "log")]
48    extern "C" {
49        pub fn write(level: i32, message: u64);
50    }
51}
52
53pub mod messages {
54    #[link(wasm_import_module = "messages")]
55    extern "C" {
56        pub fn take() -> u64;
57        pub fn send(target: u64, message: u64);
58    }
59}
60
61pub mod textures {
62    #[link(wasm_import_module = "textures")]
63    extern "C" {
64        pub fn create(options: u64) -> u32;
65        pub fn add_action(texture: u32, options: u64);
66        pub fn get_pixel(texture: u32, x: u32, y: u32) -> u32;
67        pub fn apply_to(texture: u32, name: u64);
68        pub fn flush_actions(texture: u32) -> u32;
69        pub fn dispose(texture: u32);
70        pub fn fetch_drawable_texture_properties() -> u64;
71        pub fn expose(texture: u32, name: u64);
72    }
73}
74
75pub mod var {
76    #[link(wasm_import_module = "var")]
77    extern "C" {
78        pub fn get_i64(name: u64) -> i64;
79        pub fn set_i64(name: u64, value: i64);
80        pub fn get_f64(name: u64) -> f64;
81        pub fn set_f64(name: u64, value: f64);
82        pub fn get_string(name: u64) -> u64;
83        pub fn set_string(name: u64, value: u64);
84        pub fn get_bool(name: u64) -> i32;
85        pub fn set_bool(name: u64, value: i32);
86        pub fn get_content_id(name: u64) -> u64;
87        pub fn set_content_id(name: u64, value: u64);
88    }
89}
90
91pub mod rand {
92    #[link(wasm_import_module = "rand")]
93    extern "C" {
94        pub fn f64() -> f64;
95        /// Generate a random u64 in the range `min` to `max` inclusive.
96        pub fn u64(min: u64, max: u64) -> u64;
97        pub fn seed(seed: u64);
98        pub fn random_seed();
99    }
100}
101
102pub mod gizmo {
103    #[link(wasm_import_module = "gizmo")]
104    extern "C" {
105        pub fn draw(gizmo: u64);
106    }
107}
108
109pub mod action {
110    #[link(wasm_import_module = "action")]
111    extern "C" {
112        pub fn register(action: u64);
113        pub fn state(action: u64) -> u64;
114    }
115}
116
117pub mod input {
118    #[link(wasm_import_module = "input")]
119    extern "C" {
120        pub fn mouse_delta() -> u64;
121    }
122}
123
124pub mod font {
125    #[link(wasm_import_module = "font")]
126    extern "C" {
127        pub fn bitmap_font_properties(font: u64) -> u64;
128
129        /// Returns: -1 if the font is not loaded.
130        /// Returns: >0 is the width of the text.
131        pub fn text_len(font: u64, text: u64, letter_spacing: i32) -> i32;
132    }
133}
134
135pub mod vehicle {
136    #[link(wasm_import_module = "vehicle")]
137    extern "C" {
138        pub fn bogie_is_valid(bogie: u32) -> u32;
139        pub fn axle_is_valid(bogie: u32, axle: u32) -> u32;
140        pub fn pantograph_is_valid(end: u32) -> u32;
141        pub fn is_coupled(coupling: u32) -> u32;
142        pub fn open_bus(coupling: u32, bus: u64);
143        pub fn close_bus(coupling: u32, bus: u64);
144        pub fn is_bus_open(coupling: u32, bus: u64) -> u32;
145        pub fn rail_quality(bogie: u32, axle: u32) -> u32;
146        pub fn surface_type(bogie: u32, axle: u32) -> u32;
147        pub fn inverse_radius(bogie: u32, axle: u32) -> f32;
148        pub fn velocity_vs_ground() -> f32;
149        pub fn acceleration_vs_ground() -> f32;
150        pub fn pantograph_height(pantograph: u32) -> f32;
151        pub fn pantograph_voltage(pantograph: u32) -> f32;
152        pub fn set_traction_force_newton(bogie: u32, axle: u32, value: f32);
153        pub fn set_brake_force_newton(bogie: u32, axle: u32, value: f32);
154        pub fn set_rail_brake_force_newton(bogie: u32, value: f32);
155    }
156}
157
158pub trait FromFfi {
159    type FfiType;
160    fn from_ffi(ffi: Self::FfiType) -> Self;
161}
162
163impl FromFfi for String {
164    type FfiType = u64;
165    fn from_ffi(ffi: Self::FfiType) -> Self {
166        FfiObject::from_packed(ffi).deserialize()
167    }
168}
169
170enum FfiObjectData {
171    Boxed(Box<[u8]>),
172    Raw(*mut u8, usize),
173}
174
175impl FfiObjectData {
176    fn as_slice(&self) -> &[u8] {
177        match self {
178            Self::Boxed(data) => data,
179            Self::Raw(ptr, len) => unsafe { std::slice::from_raw_parts(*ptr, *len) },
180        }
181    }
182}
183
184impl Drop for FfiObject {
185    fn drop(&mut self) {
186        match self.data {
187            FfiObjectData::Boxed(_) => {}
188            FfiObjectData::Raw(ptr, len) => unsafe {
189                std::alloc::dealloc(ptr, std::alloc::Layout::from_size_align(len, 8).unwrap())
190            },
191        }
192    }
193}
194
195pub struct FfiObject {
196    data: FfiObjectData,
197}
198
199impl FfiObject {
200    pub fn new<T: Serialize>(value: &T) -> Self {
201        let data = rmp_serde::to_vec_named(value)
202            .expect("Failed to serialize value")
203            .into_boxed_slice();
204
205        Self {
206            data: FfiObjectData::Boxed(data),
207        }
208    }
209
210    pub fn deserialize<T: DeserializeOwned>(&self) -> T {
211        rmp_serde::from_slice(self.data.as_slice()).expect("Failed to deserialize value")
212    }
213
214    pub fn packed(&self) -> u64 {
215        let ptr = self.data.as_slice().as_ptr() as u32;
216        let len = self.data.as_slice().len() as u32;
217
218        let mut packed = [0u8; 8];
219        packed[..4].copy_from_slice(&ptr.to_be_bytes());
220        packed[4..].copy_from_slice(&len.to_be_bytes());
221
222        u64::from_be_bytes(packed)
223    }
224
225    pub fn from_packed(packed: u64) -> Self {
226        let packed = packed.to_be_bytes();
227        let ptr = u32::from_be_bytes(packed[..4].try_into().unwrap());
228        let len = u32::from_be_bytes(packed[4..].try_into().unwrap());
229
230        Self {
231            data: FfiObjectData::Raw(ptr as *mut u8, len as usize),
232        }
233    }
234}