wow_wmo/
chunk.rs

1use crate::error::{Result, WmoError};
2use crate::types::ChunkId;
3use std::io::{Read, Seek, SeekFrom, Write};
4
5/// Represents a chunk header in a WMO file
6#[derive(Debug, Clone, Copy)]
7pub struct ChunkHeader {
8    /// 4-byte chunk identifier (magic)
9    pub id: ChunkId,
10    /// Size of the chunk data in bytes (not including this header)
11    pub size: u32,
12}
13
14impl ChunkHeader {
15    /// Size of a chunk header in bytes
16    pub const SIZE: usize = 8;
17
18    /// Read a chunk header from a reader
19    pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
20        let mut id_bytes = [0u8; 4];
21        match reader.read_exact(&mut id_bytes) {
22            Ok(_) => {}
23            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
24                return Err(WmoError::UnexpectedEof);
25            }
26            Err(e) => return Err(WmoError::Io(e)),
27        }
28
29        // WMO files store chunk IDs in little-endian order, so we need to reverse
30        // the bytes to get the correct ASCII representation
31        id_bytes.reverse();
32
33        let mut size_bytes = [0u8; 4];
34        match reader.read_exact(&mut size_bytes) {
35            Ok(_) => {}
36            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
37                return Err(WmoError::UnexpectedEof);
38            }
39            Err(e) => return Err(WmoError::Io(e)),
40        }
41        let size = u32::from_le_bytes(size_bytes);
42
43        Ok(Self {
44            id: ChunkId(id_bytes),
45            size,
46        })
47    }
48
49    /// Write a chunk header to a writer
50    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
51        // WMO files store chunk IDs in little-endian order, so we need to reverse
52        // the bytes when writing
53        let mut id_bytes = self.id.0;
54        id_bytes.reverse();
55        writer.write_all(&id_bytes)?;
56        writer.write_all(&self.size.to_le_bytes())?;
57        Ok(())
58    }
59}
60
61/// Represents a chunk in a WMO file
62#[derive(Debug)]
63pub struct Chunk {
64    /// Chunk header
65    pub header: ChunkHeader,
66    /// Position of the chunk data in the file
67    pub data_position: u64,
68}
69
70impl Chunk {
71    /// Read a chunk from a reader
72    pub fn read<R: Read + Seek>(reader: &mut R) -> Result<Self> {
73        let header = ChunkHeader::read(reader)?;
74        let data_position = reader.stream_position()?;
75
76        // Skip the chunk data for now
77        reader.seek(SeekFrom::Current(header.size as i64))?;
78
79        Ok(Self {
80            header,
81            data_position,
82        })
83    }
84
85    /// Read a specific chunk from a reader, checking the expected ID
86    pub fn read_expected<R: Read + Seek>(reader: &mut R, expected_id: ChunkId) -> Result<Self> {
87        let chunk = Self::read(reader)?;
88
89        if chunk.header.id != expected_id {
90            return Err(WmoError::InvalidMagic {
91                expected: *expected_id.as_bytes(),
92                found: chunk.header.id.0,
93            });
94        }
95
96        Ok(chunk)
97    }
98
99    /// Seek to the data portion of this chunk
100    pub fn seek_to_data<S: Seek>(&self, seeker: &mut S) -> Result<()> {
101        seeker.seek(SeekFrom::Start(self.data_position))?;
102        Ok(())
103    }
104
105    /// Read the data of this chunk into a buffer
106    pub fn read_data<R: Read + Seek>(&self, reader: &mut R) -> Result<Vec<u8>> {
107        self.seek_to_data(reader)?;
108
109        let mut data = vec![0; self.header.size as usize];
110        reader.read_exact(&mut data)?;
111
112        Ok(data)
113    }
114}