1use crate::block::{EntryBlock, FileExtBlock, OfsDataBlock};
4use crate::constants::*;
5use crate::error::{AffsError, Result};
6use crate::types::{BlockDevice, FsType};
7
8pub struct FileReader<'a, D: BlockDevice> {
27 device: &'a D,
28 fs_type: FsType,
29 header_block: u32,
31 file_size: u32,
33 remaining: u32,
35 block_index: u32,
37 initial_blocks_in_header: u32,
39 blocks_in_current: u32,
41 index_in_current: u32,
43 initial_data_blocks: [u32; MAX_DATABLK],
45 data_blocks: [u32; MAX_DATABLK],
47 initial_extension: u32,
49 next_extension: u32,
51 initial_first_data: u32,
53 current_data_block: u32,
55 offset_in_block: usize,
57 buf: [u8; BLOCK_SIZE],
59}
60
61impl<'a, D: BlockDevice> FileReader<'a, D> {
62 pub fn new(device: &'a D, fs_type: FsType, header_block: u32) -> Result<Self> {
69 let mut buf = [0u8; BLOCK_SIZE];
70 device
71 .read_block(header_block, &mut buf)
72 .map_err(|()| AffsError::BlockReadError)?;
73
74 let entry = EntryBlock::parse(&buf)?;
75
76 if !entry.is_file() {
77 return Err(AffsError::NotAFile);
78 }
79
80 let file_size = entry.byte_size;
81 let blocks_in_current = entry.high_seq as u32;
82
83 let mut data_blocks = [0u32; MAX_DATABLK];
85 data_blocks.copy_from_slice(&entry.hash_table);
86
87 Ok(Self {
88 device,
89 fs_type,
90 header_block,
91 file_size,
92 remaining: file_size,
93 block_index: 0,
94 initial_blocks_in_header: blocks_in_current,
95 blocks_in_current,
96 index_in_current: 0,
97 initial_data_blocks: data_blocks,
98 data_blocks,
99 initial_extension: entry.extension,
100 next_extension: entry.extension,
101 initial_first_data: entry.first_data,
102 current_data_block: entry.first_data,
103 offset_in_block: 0,
104 buf,
105 })
106 }
107
108 pub fn from_entry(
118 device: &'a D,
119 fs_type: FsType,
120 header_block: u32,
121 entry: &EntryBlock,
122 ) -> Result<Self> {
123 if !entry.is_file() {
124 return Err(AffsError::NotAFile);
125 }
126
127 let file_size = entry.byte_size;
128 let blocks_in_current = entry.high_seq as u32;
129
130 let mut data_blocks = [0u32; MAX_DATABLK];
131 data_blocks.copy_from_slice(&entry.hash_table);
132
133 Ok(Self {
134 device,
135 fs_type,
136 header_block,
137 file_size,
138 remaining: file_size,
139 block_index: 0,
140 initial_blocks_in_header: blocks_in_current,
141 blocks_in_current,
142 index_in_current: 0,
143 initial_data_blocks: data_blocks,
144 data_blocks,
145 initial_extension: entry.extension,
146 next_extension: entry.extension,
147 initial_first_data: entry.first_data,
148 current_data_block: entry.first_data,
149 offset_in_block: 0,
150 buf: [0u8; BLOCK_SIZE],
151 })
152 }
153
154 #[inline]
156 pub const fn size(&self) -> u32 {
157 self.file_size
158 }
159
160 #[inline]
162 pub const fn header_block(&self) -> u32 {
163 self.header_block
164 }
165
166 #[inline]
168 pub const fn remaining(&self) -> u32 {
169 self.remaining
170 }
171
172 #[inline]
174 pub const fn is_eof(&self) -> bool {
175 self.remaining == 0
176 }
177
178 #[inline]
180 pub const fn position(&self) -> u32 {
181 self.file_size - self.remaining
182 }
183
184 pub fn reset(&mut self) {
188 self.remaining = self.file_size;
189 self.block_index = 0;
190 self.blocks_in_current = self.initial_blocks_in_header;
191 self.index_in_current = 0;
192 self.data_blocks = self.initial_data_blocks;
193 self.next_extension = self.initial_extension;
194 self.current_data_block = self.initial_first_data;
195 self.offset_in_block = 0;
196 }
197
198 pub fn read(&mut self, out: &mut [u8]) -> Result<usize> {
202 if self.remaining == 0 || out.is_empty() {
203 return Ok(0);
204 }
205
206 let mut total_read = 0;
207
208 while total_read < out.len() && self.remaining > 0 {
209 if self.offset_in_block == 0 || self.offset_in_block >= self.data_block_size() {
211 self.read_next_data_block()?;
212 }
213
214 let data_size = self.current_block_data_size();
216 let available = data_size.saturating_sub(self.offset_in_block);
217 let to_read = available
218 .min(out.len() - total_read)
219 .min(self.remaining as usize);
220
221 if to_read == 0 {
222 break;
223 }
224
225 let data_start = self.data_offset();
227 let src = &self.buf
228 [data_start + self.offset_in_block..data_start + self.offset_in_block + to_read];
229 out[total_read..total_read + to_read].copy_from_slice(src);
230
231 total_read += to_read;
232 self.offset_in_block += to_read;
233 self.remaining -= to_read as u32;
234 }
235
236 Ok(total_read)
237 }
238
239 pub fn read_all(&mut self, out: &mut [u8]) -> Result<usize> {
244 if out.len() < self.remaining as usize {
245 return Err(AffsError::BufferTooSmall);
246 }
247
248 let mut total = 0;
249 while self.remaining > 0 {
250 let n = self.read(&mut out[total..])?;
251 if n == 0 {
252 break;
253 }
254 total += n;
255 }
256 Ok(total)
257 }
258
259 #[inline]
261 const fn data_block_size(&self) -> usize {
262 match self.fs_type {
263 FsType::Ofs => OFS_DATA_SIZE,
264 FsType::Ffs => FFS_DATA_SIZE,
265 }
266 }
267
268 #[inline]
270 const fn data_offset(&self) -> usize {
271 match self.fs_type {
272 FsType::Ofs => OfsDataBlock::HEADER_SIZE,
273 FsType::Ffs => 0,
274 }
275 }
276
277 fn current_block_data_size(&self) -> usize {
279 match self.fs_type {
280 FsType::Ofs => {
281 let header = OfsDataBlock::parse(&self.buf).ok();
284 header.map(|h| h.data_size as usize).unwrap_or(0)
285 }
286 FsType::Ffs => {
287 let block_size = FFS_DATA_SIZE;
289 let remaining = self.remaining as usize + self.offset_in_block;
290 remaining.min(block_size)
291 }
292 }
293 }
294
295 fn read_next_data_block(&mut self) -> Result<()> {
297 let block = self.get_next_data_block()?;
298 if block == 0 {
299 return Err(AffsError::EndOfFile);
300 }
301
302 self.device
303 .read_block(block, &mut self.buf)
304 .map_err(|()| AffsError::BlockReadError)?;
305
306 if matches!(self.fs_type, FsType::Ofs) {
308 let _ = OfsDataBlock::parse(&self.buf)?;
309 }
310
311 self.offset_in_block = 0;
312 self.block_index += 1;
313 Ok(())
314 }
315
316 fn get_next_data_block(&mut self) -> Result<u32> {
318 match self.fs_type {
319 FsType::Ofs => self.get_next_ofs_block(),
320 FsType::Ffs => self.get_next_ffs_block(),
321 }
322 }
323
324 fn get_next_ofs_block(&mut self) -> Result<u32> {
326 if self.block_index == 0 {
327 return Ok(self.current_data_block);
330 }
331
332 let header = OfsDataBlock::parse(&self.buf)?;
335 self.current_data_block = header.next_data;
336 Ok(self.current_data_block)
337 }
338
339 fn get_next_ffs_block(&mut self) -> Result<u32> {
341 if self.index_in_current >= self.blocks_in_current {
343 if self.next_extension == 0 {
344 return Ok(0); }
346
347 self.device
349 .read_block(self.next_extension, &mut self.buf)
350 .map_err(|()| AffsError::BlockReadError)?;
351
352 let ext = FileExtBlock::parse(&self.buf)?;
353
354 self.data_blocks.copy_from_slice(&ext.data_blocks);
356 self.blocks_in_current = ext.high_seq as u32;
357 self.next_extension = ext.extension;
358 self.index_in_current = 0;
359 }
360
361 if self.index_in_current >= self.blocks_in_current {
362 return Ok(0);
363 }
364
365 let idx = self.index_in_current as usize;
367 let block = if idx < MAX_DATABLK {
368 self.data_blocks[MAX_DATABLK - 1 - idx]
369 } else {
370 0
371 };
372
373 self.index_in_current += 1;
374 Ok(block)
375 }
376
377 pub fn seek(&mut self, position: u32) -> Result<()> {
382 if position > self.file_size {
383 return Err(AffsError::EndOfFile);
384 }
385
386 if position == self.position() {
387 return Ok(());
388 }
389
390 if position < self.position() {
392 self.reset();
393 }
394
395 let mut discard = [0u8; 512];
397 let mut to_skip = position - self.position();
398 while to_skip > 0 {
399 let n = self.read(&mut discard[..to_skip.min(512) as usize])?;
400 if n == 0 {
401 return Err(AffsError::EndOfFile);
402 }
403 to_skip -= n as u32;
404 }
405
406 Ok(())
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413
414 struct DummyDevice;
415
416 impl BlockDevice for DummyDevice {
417 fn read_block(&self, _block: u32, _buf: &mut [u8; 512]) -> core::result::Result<(), ()> {
418 Err(())
419 }
420 }
421
422 #[test]
423 fn test_file_reader_error_on_bad_device() {
424 let device = DummyDevice;
425 let result = FileReader::new(&device, FsType::Ffs, 100);
426 assert!(result.is_err());
427 }
428}