firefly_rust/
fs.rs

1//! Access file system: the game ROM files and the data dir.
2
3use crate::graphics::*;
4#[cfg(feature = "alloc")]
5use alloc::boxed::Box;
6#[cfg(feature = "alloc")]
7use alloc::vec;
8
9/// Like [File] but owns the buffer.
10///
11/// Returned by [`rom::load_buf`] and [`data::load_buf`]. Requires a global allocator.
12/// For a file of statically-known size, you might want to use [`rom::load`]
13/// and [`data::load`] instead.
14#[cfg(feature = "alloc")]
15pub struct FileBuf {
16    pub(crate) raw: Box<[u8]>,
17}
18
19#[cfg(feature = "alloc")]
20impl FileBuf {
21    /// Access the raw data in the file.
22    #[must_use]
23    pub fn data(&self) -> &[u8] {
24        &self.raw
25    }
26
27    /// Interpret the file as a font.
28    #[must_use]
29    pub fn as_font(&self) -> Font {
30        Font { raw: &self.raw }
31    }
32
33    /// Interpret the file as an image.
34    #[must_use]
35    pub fn as_image(&self) -> Image {
36        Image { raw: &self.raw }
37    }
38}
39
40/// A file loaded from ROM or data dir into the memory.
41///
42/// Returned by [`rom::load`] and [`data::load`] which requires a pre-allocated buffer
43/// of the right size. If the file size is deterimed dynamically,
44/// you might want to use [`rom::load_buf`] and [`data::load_buf`] instead
45/// (which will take care of the dynamic allocation).
46pub struct File<'a> {
47    pub(crate) raw: &'a [u8],
48}
49
50impl File<'_> {
51    #[must_use]
52    pub const fn data(&self) -> &[u8] {
53        self.raw
54    }
55
56    #[must_use]
57    pub const fn as_font(&self) -> Font {
58        Font { raw: self.raw }
59    }
60
61    #[must_use]
62    pub const fn as_image(&self) -> Image {
63        Image { raw: self.raw }
64    }
65}
66
67/// Get a file size in the data dir.
68///
69/// If the file does not exist, 0 is returned.
70#[must_use]
71pub fn get_file_size(name: &str) -> usize {
72    let path_ptr = name.as_ptr();
73    let size = unsafe { bindings::get_file_size(path_ptr as u32, name.len() as u32) };
74    size as usize
75}
76
77/// Read the whole file with the given name into the given buffer.
78///
79/// If the file size is not known in advance (and so the buffer has to be allocated
80/// dynamically), consider using [`load_file_buf`] instead.
81pub fn load_file<'a>(name: &str, buf: &'a mut [u8]) -> File<'a> {
82    let path_ptr = name.as_ptr();
83    let buf_ptr = buf.as_mut_ptr();
84    unsafe {
85        bindings::load_file(
86            path_ptr as u32,
87            name.len() as u32,
88            buf_ptr as u32,
89            buf.len() as u32,
90        );
91    }
92    File { raw: buf }
93}
94
95/// Read the whole file with the given name.
96///
97/// If you have a pre-allocated buffer of the right size, use [`load_file`] instead.
98///
99/// `None` is returned if the file does not exist.
100#[cfg(feature = "alloc")]
101#[must_use]
102pub fn load_file_buf(name: &str) -> Option<FileBuf> {
103    let size = get_file_size(name);
104    if size == 0 {
105        return None;
106    }
107    let mut buf = vec![0; size];
108    load_file(name, &mut buf);
109    Some(FileBuf {
110        raw: buf.into_boxed_slice(),
111    })
112}
113
114/// Write the buffer into the given file in the data dir.
115///
116/// If the file exists, it will be overwritten.
117/// If it doesn't exist, it will be created.
118pub fn dump_file(name: &str, buf: &[u8]) {
119    let path_ptr = name.as_ptr();
120    let buf_ptr = buf.as_ptr();
121    unsafe {
122        bindings::dump_file(
123            path_ptr as u32,
124            name.len() as u32,
125            buf_ptr as u32,
126            buf.len() as u32,
127        );
128    }
129}
130
131/// Remove file (if exists) with the given name from the data dir.
132pub fn remove_file(name: &str) {
133    let path_ptr = name.as_ptr();
134    unsafe {
135        bindings::remove_file(path_ptr as u32, name.len() as u32);
136    }
137}
138
139/// A loaded font file.
140///
141/// Can be loaded as [`FileBuf`] from ROM with [`rom::load_buf`]
142/// and then cast using [Into].
143pub struct Font<'a> {
144    pub(crate) raw: &'a [u8],
145}
146
147impl<'a> From<File<'a>> for Font<'a> {
148    fn from(value: File<'a>) -> Self {
149        Self { raw: value.raw }
150    }
151}
152
153#[cfg(feature = "alloc")]
154impl<'a> From<&'a FileBuf> for Font<'a> {
155    fn from(value: &'a FileBuf) -> Self {
156        Self { raw: &value.raw }
157    }
158}
159
160mod bindings {
161    #[link(wasm_import_module = "fs")]
162    extern {
163        pub(crate) fn get_file_size(path_ptr: u32, path_len: u32) -> u32;
164        pub(crate) fn load_file(path_ptr: u32, path_len: u32, buf_ptr: u32, buf_len: u32) -> u32;
165        pub(crate) fn dump_file(path_ptr: u32, path_len: u32, buf_ptr: u32, buf_len: u32) -> u32;
166        pub(crate) fn remove_file(path_ptr: u32, path_len: u32);
167    }
168}