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}