1use russimp_sys_ng::{aiFile, aiFileIO, aiOrigin, aiReturn};
6use std::ffi::CStr;
7use std::io::SeekFrom;
8use std::os::raw::c_char;
9
10pub trait FileSystem {
14 fn open(&self, file_path: &str, mode: &str) -> Option<Box<dyn FileOperations>>;
15}
16
17pub trait FileOperations {
21 fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()>;
23 fn write(&mut self, buf: &[u8]) -> Result<usize, ()>;
25 fn tell(&mut self) -> usize;
26 fn size(&mut self) -> usize;
27 fn seek(&mut self, seek_from: SeekFrom) -> Result<(), ()>;
28 fn flush(&mut self);
29 fn close(&mut self);
30}
31
32pub(crate) struct FileOperationsWrapper<T: FileSystem> {
34 ai_file: aiFileIO,
35 _phantom: std::marker::PhantomData<T>,
36}
37
38impl<T: FileSystem> FileOperationsWrapper<T> {
39 pub fn new(file_system: &T) -> FileOperationsWrapper<T> {
41 let trait_obj: &dyn FileSystem = file_system;
42 let managed_box = Box::new(trait_obj);
43 let user_data = Box::into_raw(managed_box);
44 let user_data = user_data as *mut c_char;
45 FileOperationsWrapper {
46 ai_file: aiFileIO {
47 OpenProc: Some(FileOperationsWrapper::<T>::io_open),
48 CloseProc: Some(FileOperationsWrapper::<T>::io_close),
49 UserData: user_data,
50 },
51 _phantom: Default::default(),
52 }
53 }
54 pub fn ai_file(&mut self) -> &mut aiFileIO {
56 &mut self.ai_file
57 }
58 unsafe extern "C" fn io_open(
60 ai_file_io: *mut aiFileIO,
61 file_path: *const ::std::os::raw::c_char,
62 mode: *const ::std::os::raw::c_char,
63 ) -> *mut aiFile {
64 let file_system = Box::leak(Box::from_raw(
65 (*ai_file_io).UserData as *mut &dyn FileSystem,
66 ));
67
68 let file_path = CStr::from_ptr(file_path)
69 .to_str()
70 .unwrap_or("Invalid UTF-8 Filename");
71 let mode = CStr::from_ptr(mode)
72 .to_str()
73 .unwrap_or("Invalid UTF-8 Mode");
74 let file = match file_system.open(file_path, mode) {
75 None => return std::ptr::null_mut(),
76 Some(file) => file,
77 };
78
79 let double_box = Box::new(file);
82 let managed_box = Box::into_raw(double_box); let user_data = managed_box as *mut c_char;
84 let ai_file = aiFile {
85 ReadProc: Some(Self::io_read),
86 WriteProc: Some(Self::io_write),
87 TellProc: Some(Self::io_tell),
88 FileSizeProc: Some(Self::io_size),
89 SeekProc: Some(Self::io_seek),
90 FlushProc: Some(Self::io_flush),
91 UserData: user_data,
92 };
93 Box::into_raw(Box::new(ai_file))
95 }
96
97 unsafe extern "C" fn io_close(_ai_file_io: *mut aiFileIO, ai_file: *mut aiFile) {
99 let ai_file = Box::from_raw(ai_file);
102 let mut file: Box<Box<dyn FileOperations>> =
103 Box::from_raw(ai_file.UserData as *mut Box<dyn FileOperations>);
104 file.close();
105 }
106 unsafe fn get_file<'a>(ai_file: *mut aiFile) -> &'a mut Box<dyn FileOperations> {
113 Box::leak(Box::from_raw(
117 (*ai_file).UserData as *mut Box<dyn FileOperations>,
118 ))
119 }
120 unsafe extern "C" fn io_read(
122 ai_file: *mut aiFile,
123 buffer: *mut c_char,
124 size: usize,
125 count: usize,
126 ) -> usize {
127 let file = Self::get_file(ai_file);
128 let mut buffer =
129 std::slice::from_raw_parts_mut(buffer as *mut u8, size * count);
130 if size == 0 {
131 panic!("Size 0 is invalid");
132 }
133 if count == 0 {
134 panic!("Count 0 is invalid");
135 }
136 if size == usize::MAX {
137 panic!("huge read size not supported");
138 }
139 if size == 1 {
140 if count == usize::MAX {
142 panic!("huge read not supported");
143 }
144
145 let (buffer, _) = buffer.split_at_mut(count);
146 match file.read(buffer) {
147 Ok(size) => size,
148 Err(_) => usize::MAX,
149 }
150 } else {
151 let mut total: usize = 0;
154 for _ in 0..count {
155 let split = buffer.split_at_mut(size);
156 buffer = split.1;
157 let bytes_read = match file.read(split.0) {
158 Err(_) => break,
159 Ok(bytes_read) => bytes_read,
160 };
161 if bytes_read != size {
162 break;
163 }
164 total += 1;
165 }
166 total
167 }
168 }
169 unsafe extern "C" fn io_write(
171 ai_file: *mut aiFile,
172 buffer: *const std::os::raw::c_char,
173 size: usize,
174 count: usize,
175 ) -> usize {
176 let file = Self::get_file(ai_file);
177 let mut buffer =
178 std::slice::from_raw_parts(buffer as *mut u8, size * count);
179 if size == 0 {
180 panic!("Write of size 0");
181 }
182 if count == 0 {
183 panic!("Write of count 0");
184 }
185 if size == usize::MAX {
186 panic!("huge write size not supported");
187 }
188 if size == 1 {
189 if count == usize::MAX {
190 panic!("huge write not supported");
191 }
192 let (buffer, _) = buffer.split_at(count);
193 match file.write(buffer) {
194 Ok(size) => size,
195 Err(_) => usize::MAX,
196 }
197 } else {
198 let mut total: usize = 0;
201 for _ in 0..count {
202 let split = buffer.split_at(size);
203 buffer = split.1;
204 let bytes_written = match file.write(split.0) {
205 Err(_) => break,
206 Ok(bytes_written) => bytes_written,
207 };
208 if bytes_written != size {
209 break;
210 }
211 total += 1;
212 }
213 total
214 }
215 }
216 unsafe extern "C" fn io_tell(ai_file: *mut aiFile) -> usize {
218 let file = Self::get_file(ai_file);
219 file.tell()
220 }
221 unsafe extern "C" fn io_size(ai_file: *mut aiFile) -> usize {
223 let file = Self::get_file(ai_file);
224 file.size()
225 }
226 unsafe extern "C" fn io_seek(ai_file: *mut aiFile, pos: usize, origin: aiOrigin) -> aiReturn {
228 let file = Self::get_file(ai_file);
229 let seek_from = match origin {
230 russimp_sys_ng::aiOrigin_aiOrigin_SET => SeekFrom::Start(pos as u64),
231 russimp_sys_ng::aiOrigin_aiOrigin_CUR => SeekFrom::Current(pos as i64),
232 russimp_sys_ng::aiOrigin_aiOrigin_END => SeekFrom::End(pos as i64),
233 _ => panic!("Assimp passed invalid origin"),
234 };
235 match file.seek(seek_from) {
236 Ok(()) => 0,
237 Err(()) => russimp_sys_ng::aiReturn_aiReturn_FAILURE,
238 }
239 }
240 unsafe extern "C" fn io_flush(ai_file: *mut aiFile) {
242 let file = Self::get_file(ai_file);
243 file.flush();
244 }
245}
246
247impl<T: FileSystem> Drop for FileOperationsWrapper<T> {
248 fn drop(&mut self) {
249 let _managed_box: Box<&dyn FileSystem> =
251 unsafe { Box::from_raw(self.ai_file.UserData as *mut &dyn FileSystem) };
252 }
253}
254
255#[cfg(test)]
256mod test {
257 use crate::scene::PostProcess;
258 use crate::scene::Scene;
259 use crate::utils;
260 use std::fs::File;
261 use std::io::{prelude::*, SeekFrom};
262
263 struct MyFileOperations {
264 file: File,
265 }
266
267 impl super::FileOperations for MyFileOperations {
268 fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> {
269 self.file.read(buf).map_err(|_| ())
270 }
271
272 fn write(&mut self, _buf: &[u8]) -> Result<usize, ()> {
273 unimplemented!("write support");
274 }
275
276 fn tell(&mut self) -> usize {
277 self.file
278 .stream_position()
279 .unwrap_or(0)
280 .try_into()
281 .unwrap_or(0)
282 }
283
284 fn size(&mut self) -> usize {
285 self.file
286 .metadata()
287 .expect("Missing metadata")
288 .len()
289 .try_into()
290 .unwrap_or(0)
291 }
292
293 fn seek(&mut self, seek_from: SeekFrom) -> Result<(), ()> {
294 match self.file.seek(seek_from) {
295 Ok(_) => Ok(()),
296 Err(_) => Err(()),
297 }
298 }
299
300 fn flush(&mut self) {
301 }
303
304 fn close(&mut self) {
305 }
307 }
308
309 struct MyFS {}
310
311 impl super::FileSystem for MyFS {
312 fn open(&self, file_path: &str, mode: &str) -> Option<Box<dyn super::FileOperations>> {
313 assert_eq!(mode, "rb");
315 let file = File::open(file_path).expect("Couldn't open {file_path}");
316 Some(Box::new(MyFileOperations { file }))
317 }
318 }
319
320 #[test]
321 fn test_file_operations() {
322 let model_path = utils::get_model("models/OBJ/cube.obj");
325 let mut myfs = MyFS {};
326 let scene = Scene::from_file_system(
327 model_path.as_str(),
328 vec![
329 PostProcess::CalculateTangentSpace,
330 PostProcess::Triangulate,
331 PostProcess::JoinIdenticalVertices,
332 PostProcess::SortByPrimitiveType,
333 ],
334 &mut myfs,
335 )
336 .unwrap();
337
338 assert_eq!(scene.meshes[0].texture_coords.len(), 8);
339 assert_eq!(scene.materials.len(), 2);
340 }
341}