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}