Skip to main content

forge/mt/
fs.rs

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