1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
//! Access file system: the game ROM files and the data dir.

use crate::graphics::{Point, Size};
#[cfg(feature = "alloc")]
use alloc::vec;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;

/// Like [File] but owns the buffer.
///
/// Returned by [`rom::load_buf`] and [`data::load_buf`]. Requires a global allocator.
/// For a file of statically-known size, you might want to use [`rom::load`]
/// and [`data::load`] instead.
#[cfg(feature = "alloc")]
pub struct FileBuf {
    pub(crate) raw: Vec<u8>,
}

#[cfg(feature = "alloc")]
impl FileBuf {
    /// Access the raw data in the file.
    #[must_use]
    pub fn data(&self) -> &[u8] {
        &self.raw
    }

    /// Interpret the file as a font.
    #[must_use]
    pub fn as_font(&self) -> Font {
        Font { raw: &self.raw }
    }

    /// Interpret the file as an image.
    #[must_use]
    pub fn as_image(&self) -> Image {
        Image { raw: &self.raw }
    }
}

/// A file loaded from ROM or data dir into the memory.
///
/// Returned by [`rom::load`] and [`data::load`] which requires a pre-allocated buffer
/// of the right size. If the file size is deterimed dynamically,
/// you might want to use [`rom::load_buf`] and [`data::load_buf`] instead
/// (which will take care of the dynamic allocation).
pub struct File<'a> {
    pub(crate) raw: &'a [u8],
}

impl<'a> File<'a> {
    #[must_use]
    pub fn data(&self) -> &[u8] {
        self.raw
    }

    #[must_use]
    pub fn as_font(&self) -> Font {
        Font { raw: self.raw }
    }

    #[must_use]
    pub fn as_image(&self) -> Image {
        Image { raw: self.raw }
    }
}

/// Functions for accessing files in the app ROM.
pub mod rom {
    use super::*;
    use crate::bindings as b;

    /// Determine the required size (in bytes) to store the given file.
    ///
    /// If the file does not exist, 0 is returned.
    #[must_use]
    pub fn get_size(name: &str) -> usize {
        let path_ptr = name.as_ptr();
        let size = unsafe { b::get_rom_file_size(path_ptr as u32, name.len() as u32) };
        size as usize
    }

    /// Read the whole file with the given name into the given buffer.
    ///
    /// If the file size is not known in advance (and so the buffer has to be allocated
    /// dynamically), consider using [`load_buf`] instead.
    pub fn load<'a>(name: &str, buf: &'a mut [u8]) -> File<'a> {
        let path_ptr = name.as_ptr();
        let buf_ptr = buf.as_mut_ptr();
        unsafe {
            b::load_rom_file(
                path_ptr as u32,
                name.len() as u32,
                buf_ptr as u32,
                buf.len() as u32,
            );
        }
        File { raw: buf }
    }

    /// Read the whole file with the given name from ROM.
    ///
    /// If you have a pre-allocated buffer of the right size, use [load] instead.
    ///
    /// If the file does not exist, the returned buffer will be empty.
    /// This, however, should not happen in normal operation because
    /// the contents of the ROM directory are statically known.
    #[cfg(feature = "alloc")]
    #[must_use]
    pub fn load_buf(name: &str) -> FileBuf {
        let size = rom::get_size(name);
        let mut buf = vec![0; size];
        rom::load(name, &mut buf);
        FileBuf { raw: buf }
    }
}

/// Functions for accessing files in the app data dir.
///
/// Each app has an its own data dir. That directory is empty by default,
/// writable by the app, and not accessible by other apps.
/// Typically, it is used to store game save data.
///
/// The device owner may empty this dir if they wish to remove the app data.
pub mod data {
    use super::*;
    use crate::bindings as b;

    /// Get a file size in the data dir.
    ///
    /// If the file does not exist, 0 is returned.
    #[must_use]
    pub fn get_size(name: &str) -> usize {
        let path_ptr = name.as_ptr();
        let size = unsafe { b::get_file_size(path_ptr as u32, name.len() as u32) };
        size as usize
    }

    /// Read the whole file with the given name into the given buffer.
    ///
    /// If the file size is not known in advance (and so the buffer has to be allocated
    /// dynamically), consider using [`load_buf`] instead.
    pub fn load<'a>(name: &str, buf: &'a mut [u8]) -> File<'a> {
        let path_ptr = name.as_ptr();
        let buf_ptr = buf.as_mut_ptr();
        unsafe {
            b::load_file(
                path_ptr as u32,
                name.len() as u32,
                buf_ptr as u32,
                buf.len() as u32,
            );
        }
        File { raw: buf }
    }

    /// Read the whole file with the given name from the data dir.
    ///
    /// If you have a pre-allocated buffer of the right size, use [load] instead.
    ///
    /// `None` is returned if the file does not exist.
    #[cfg(feature = "alloc")]
    #[must_use]
    pub fn load_buf(name: &str) -> Option<FileBuf> {
        let size = data::get_size(name);
        if size == 0 {
            return None;
        }
        let mut buf = vec![0; size];
        data::load(name, &mut buf);
        Some(FileBuf { raw: buf })
    }

    /// Write the buffer into the given file in the data dir.
    ///
    /// If the file exists, it will be overwritten.
    /// If it doesn't exist, it will be created.
    pub fn dump(name: &str, buf: &[u8]) {
        let path_ptr = name.as_ptr();
        let buf_ptr = buf.as_ptr();
        unsafe {
            b::dump_file(
                path_ptr as u32,
                name.len() as u32,
                buf_ptr as u32,
                buf.len() as u32,
            );
        }
    }

    /// Remove file (if exists) with the given name from the data dir.
    pub fn remove(name: &str) {
        let path_ptr = name.as_ptr();
        unsafe {
            b::remove_file(path_ptr as u32, name.len() as u32);
        }
    }
}

/// A loaded font file.
///
/// Can be loaded as [`FileBuf`] from ROM with [`rom::load_buf`]
/// and then cast using [Into].
pub struct Font<'a> {
    pub(crate) raw: &'a [u8],
}

impl<'a> From<File<'a>> for Font<'a> {
    fn from(value: File<'a>) -> Self {
        Self { raw: value.raw }
    }
}

#[cfg(feature = "alloc")]
impl<'a> From<&'a FileBuf> for Font<'a> {
    fn from(value: &'a FileBuf) -> Self {
        Self { raw: &value.raw }
    }
}

/// A loaded image file.
///
/// Can be loaded as [`FileBuf`] from ROM with [`rom::load_buf`]
/// and then cast using [Into].
pub struct Image<'a> {
    pub(crate) raw: &'a [u8],
}

impl<'a> From<File<'a>> for Image<'a> {
    fn from(value: File<'a>) -> Self {
        Self { raw: value.raw }
    }
}

#[cfg(feature = "alloc")]
impl<'a> From<&'a FileBuf> for Image<'a> {
    fn from(value: &'a FileBuf) -> Self {
        Self { raw: &value.raw }
    }
}

impl<'a> Image<'a> {
    /// Get a rectangle subregion of the image.
    #[must_use]
    pub fn sub(&self, p: Point, s: Size) -> SubImage<'a> {
        SubImage {
            point: p,
            size:  s,
            raw:   self.raw,
        }
    }
}

/// A subregion of an image. Constructed using [`Image::sub`].
pub struct SubImage<'a> {
    pub(crate) point: Point,
    pub(crate) size:  Size,
    pub(crate) raw:   &'a [u8],
}