use crate::error::{Result, WmoError};
use crate::types::ChunkId;
use std::io::{Read, Seek, SeekFrom, Write};
fn read_exact_or_eof<R: Read>(reader: &mut R, buf: &mut [u8]) -> Result<()> {
match reader.read_exact(buf) {
Ok(()) => Ok(()),
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => Err(WmoError::UnexpectedEof),
Err(e) => Err(WmoError::Io(e)),
}
}
#[derive(Debug, Clone, Copy)]
pub struct ChunkHeader {
pub id: ChunkId,
pub size: u32,
}
impl ChunkHeader {
pub const SIZE: usize = 8;
pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
let mut id_bytes = [0u8; 4];
read_exact_or_eof(reader, &mut id_bytes)?;
id_bytes.reverse();
let mut size_bytes = [0u8; 4];
read_exact_or_eof(reader, &mut size_bytes)?;
let size = u32::from_le_bytes(size_bytes);
Ok(Self {
id: ChunkId(id_bytes),
size,
})
}
pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
let mut id_bytes = self.id.0;
id_bytes.reverse();
writer.write_all(&id_bytes)?;
writer.write_all(&self.size.to_le_bytes())?;
Ok(())
}
}
#[derive(Debug)]
pub struct Chunk {
pub header: ChunkHeader,
pub data_position: u64,
}
impl Chunk {
pub fn read<R: Read + Seek>(reader: &mut R) -> Result<Self> {
let header = ChunkHeader::read(reader)?;
let data_position = reader.stream_position()?;
reader.seek(SeekFrom::Current(header.size as i64))?;
Ok(Self {
header,
data_position,
})
}
pub fn read_expected<R: Read + Seek>(reader: &mut R, expected_id: ChunkId) -> Result<Self> {
let chunk = Self::read(reader)?;
if chunk.header.id != expected_id {
return Err(WmoError::InvalidMagic {
expected: *expected_id.as_bytes(),
found: chunk.header.id.0,
});
}
Ok(chunk)
}
pub fn seek_to_data<S: Seek>(&self, seeker: &mut S) -> Result<()> {
seeker.seek(SeekFrom::Start(self.data_position))?;
Ok(())
}
pub fn read_data<R: Read + Seek>(&self, reader: &mut R) -> Result<Vec<u8>> {
self.seek_to_data(reader)?;
let mut data = vec![0; self.header.size as usize];
reader.read_exact(&mut data)?;
Ok(data)
}
}