Skip to main content

forge/mt/
fs.rs

1use core::fmt::Write;
2
3use macros::{CacheDti, Object, pure_virtual};
4
5use crate::mt::{
6    dti::CacheDti,
7    error::{MtError, MtResult},
8};
9
10/// MT Framework's file stream class (`MtFile`).
11///
12/// Wraps a game-side file object, exposing its I/O operations - open, read,
13/// write, seek and the various size/position queries - through the underlying
14/// vtable. Instances are created through the game's own factory (see
15/// [`open`](Self::open)), which is why the type derives
16/// [`CacheDti`](macros::CacheDti) to locate its [`MtDti`](crate::mt::dti::MtDti).
17///
18/// `MtFile` also implements [`core::fmt::Write`], so it can be used as the
19/// target of `write!` / `writeln!` to append UTF-8 text.
20///
21/// # Layout
22///
23/// `#[repr(C)]` (an opaque, vtable-only type via `#[derive(Object)]`), since it
24/// is only ever used by reference to call its virtual functions.
25#[derive(Object, CacheDti)]
26pub struct MtFile;
27
28/// How a file should be opened, passed to [`MtFile::open`].
29#[repr(i32)]
30pub enum OpenMode {
31    /// No/invalid mode.
32    Undefined = 0x10,
33    /// Open an existing file for reading.
34    Read,
35    /// Open an existing file for asynchronous reading.
36    ReadAsync,
37    /// Create or truncate a file for writing.
38    Write,
39    /// Open a file for writing, appending to the end.
40    Append,
41    /// Open a file for both reading and writing.
42    ReadWrite,
43    /// Open a file for reading and writing, appending to the end.
44    ReadWriteAppend,
45}
46
47/// Reference point for a [`seek`](MtFile::seek) operation.
48#[repr(i32)]
49pub enum Origin {
50    /// Seek relative to the start of the file.
51    Begin,
52    /// Seek relative to the current position.
53    Current,
54    /// Seek relative to the end of the file.
55    End,
56}
57
58impl MtFile {
59    /// Opens the file at `path` with the given [`OpenMode`].
60    ///
61    /// Locates the `MtFile` class, constructs a new instance through its
62    /// factory, and opens the file on it. The path is passed to the game as a
63    /// raw byte pointer.
64    ///
65    /// # Errors
66    ///
67    /// Returns [`MtError::DtiNotFound`] if the `MtFile` class is missing,
68    /// [`MtError::FailedToCreateInstance`] if construction fails, or
69    /// [`MtError::FailedToOpenFile`] if the open itself fails.
70    pub fn open(path: &str, mode: OpenMode) -> MtResult<&mut Self> {
71        let dti = Self::dti().ok_or(MtError::DtiNotFound("MtFile"))?;
72        let file: &mut MtFile = dti.new().ok_or(MtError::FailedToCreateInstance("MtFile"))?;
73
74        let success = file.open_impl(path.as_ptr(), mode as i32, false);
75        if success { Ok(file) } else { Err(MtError::FailedToOpenFile) }
76    }
77
78    /// Reads up to `buffer.len()` bytes into `buffer`, returning the number of
79    /// bytes actually read.
80    pub fn read(&mut self, buffer: &mut [u8]) -> usize {
81        self.read_impl(buffer.as_mut_ptr(), buffer.len())
82    }
83
84    /// Writes `data` to the file, returning the number of bytes actually
85    /// written.
86    pub fn write(&mut self, data: &[u8]) -> usize {
87        self.write_impl(data.as_ptr(), data.len())
88    }
89
90    /// Moves the file position by `offset` bytes relative to `origin`.
91    pub fn seek(&mut self, offset: isize, origin: Origin) {
92        self.seek_impl(offset, origin as i32)
93    }
94
95    /// Raw open virtual function (vtable slot 6); use [`open`](Self::open).
96    #[pure_virtual(6)]
97    fn open_impl(&mut self, path: *const u8, mode: i32, interrupt: bool) -> bool;
98
99    /// Closes the file (vtable slot 7).
100    #[pure_virtual(7)]
101    pub fn close(&mut self);
102
103    /// Raw read virtual function (vtable slot 8); use [`read`](Self::read).
104    #[pure_virtual(8)]
105    fn read_impl(&mut self, buffer: *mut u8, size: usize) -> usize;
106
107    /// Raw asynchronous-read virtual function (vtable slot 9).
108    #[pure_virtual(9)]
109    fn read_async_impl(&mut self, buffer: *mut u8, size: usize) -> usize;
110
111    /// Raw await-async-read virtual function (vtable slot 10).
112    #[pure_virtual(10)]
113    fn read_await_impl(&mut self, buffer: *mut u8, size: usize) -> usize;
114
115    /// Returns `true` while an asynchronous read is in progress (vtable slot
116    /// 11).
117    #[pure_virtual(11)]
118    pub fn is_async_reading(&self) -> bool;
119
120    /// Raw write virtual function (vtable slot 12); use [`write`](Self::write).
121    #[pure_virtual(12)]
122    fn write_impl(&mut self, buffer: *const u8, size: usize) -> usize;
123
124    /// Raw seek virtual function (vtable slot 13); use [`seek`](Self::seek).
125    #[pure_virtual(13)]
126    fn seek_impl(&mut self, offset: isize, origin: i32);
127
128    /// Returns the current file position, in bytes (vtable slot 14).
129    #[pure_virtual(14)]
130    pub fn tell(&self) -> usize;
131
132    /// Returns the file's size, in bytes (vtable slot 15).
133    #[pure_virtual(15)]
134    pub fn size(&self) -> usize;
135
136    /// Sets the file's size, in bytes (vtable slot 16).
137    #[pure_virtual(16)]
138    pub fn set_size(&mut self, size: usize);
139
140    /// Returns `true` if the file is open for reading (vtable slot 17).
141    #[pure_virtual(17)]
142    pub fn is_readable(&self) -> bool;
143
144    /// Returns `true` if the file is open for writing (vtable slot 18).
145    #[pure_virtual(18)]
146    pub fn is_writable(&self) -> bool;
147
148    /// Returns `true` if the file supports asynchronous reading (vtable slot
149    /// 19).
150    #[pure_virtual(19)]
151    pub fn is_async_readable(&self) -> bool;
152
153    /// Returns the size of the pending asynchronous read, in bytes (vtable slot
154    /// 20).
155    #[pure_virtual(20)]
156    pub fn get_async_read_size(&self) -> usize;
157}
158
159impl Write for MtFile {
160    fn write_str(&mut self, s: &str) -> core::fmt::Result {
161        self.write(s.as_bytes());
162        Ok(())
163    }
164}