Skip to main content

mlv/
block_reader.rs

1use crate::BlockHeader;
2use crate::blocks::{block_get_type, block_get_timestamp, block_get_size};
3
4#[derive(Debug,Copy,Clone)]
5pub enum ReadBlocksError<ReadErrorType> {
6    BlockCutoff,
7    TrailingBytes,
8    ImpossiblySmallBlockSize,
9    FileTooSmall,
10    ReadError(ReadErrorType),
11}
12
13#[cfg(feature = "std")]
14use std::io::{self, Read, Seek, SeekFrom};
15
16#[cfg(feature = "std")]
17pub fn read_wrapper<File>(mut file: File) -> impl FnMut(u64, &mut [u8]) -> io::Result<()>
18where
19    File: Read + Seek
20{
21    let mut pos = 0u64;
22    file.rewind();
23
24    return move |read_pos: u64, out: &mut [u8]| {
25        if read_pos != pos {
26            file.seek_relative(read_pos as i64 - pos as i64)?;
27        }
28        pos = read_pos + out.len() as u64;
29        file.read_exact(out).map(|_| ())
30    }
31}
32
33pub fn read_blocks<const MAX_BLOCK_BYTES: usize, ReadError>(
34    file_length: u64,
35    /* Pos, out buffer. If this returns error, the function returns an error and exits */
36    mut read_exact: impl FnMut(u64, &mut [u8]) -> Result<(), ReadError>,
37    /* Return true to continue, false to exit early, args: data, block offset */
38    mut block_data_callback: impl FnMut(&[u8], u64) -> bool,
39) -> Result<(), ReadBlocksError<ReadError>> {
40    let mut buf = [0u8; MAX_BLOCK_BYTES];
41    let mut pos = 0u64;
42
43    if file_length < 16 {
44        return Err(ReadBlocksError::FileTooSmall)
45    }
46
47    loop {
48        /* Due to checks, this should always succeed */
49        if let Err(e) = read_exact(pos, &mut buf[0..16]) {
50            return Err(ReadBlocksError::ReadError(e))
51        }
52        let block_size = u32::from_le_bytes([buf[4],buf[5],buf[6],buf[7]]);
53
54        if block_size < 16 {
55            return Err(ReadBlocksError::ImpossiblySmallBlockSize)
56        }
57
58        let next_block_pos = pos + block_size as u64;
59
60        if next_block_pos > file_length {
61            println!("File length = {file_length}, nextpos = {next_block_pos}");
62            // TODO: Add mechanism for leniency to cut-off blocks? Eg slightly cut off final frame
63            return Err(ReadBlocksError::BlockCutoff)
64        } else {
65            let read_end = (block_size as usize).min(MAX_BLOCK_BYTES);
66            if let Err(e) = read_exact(pos+16, &mut buf[16..read_end]) {
67                return Err(ReadBlocksError::ReadError(e))
68            }
69            block_data_callback(&buf[0..read_end], pos);
70            if next_block_pos == file_length {
71                /* End of file! */
72                return Ok(())
73            } else if file_length - next_block_pos < 16 {
74                /* Data after next block is less than 16 bytes which is the minimum size for a block */
75                return Err(ReadBlocksError::TrailingBytes)
76            } else { /* Fine */ }
77
78            pos = next_block_pos;
79        }
80    }
81}