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 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 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}