unity_asset_binary/
reader.rs

1//! Binary data reader for Unity files
2
3use crate::error::{BinaryError, Result};
4use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
5use std::io::{Cursor, Read, Seek, SeekFrom};
6
7/// Byte order for reading binary data
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9pub enum ByteOrder {
10    /// Big endian (network byte order)
11    Big,
12    /// Little endian (most common on x86/x64)
13    #[default]
14    Little,
15}
16
17/// Binary reader for Unity file formats
18pub struct BinaryReader<'a> {
19    cursor: Cursor<&'a [u8]>,
20    byte_order: ByteOrder,
21}
22
23impl<'a> BinaryReader<'a> {
24    /// Create a new binary reader from byte slice
25    pub fn new(data: &'a [u8], byte_order: ByteOrder) -> Self {
26        Self {
27            cursor: Cursor::new(data),
28            byte_order,
29        }
30    }
31
32    /// Get current position in the stream
33    pub fn position(&self) -> u64 {
34        self.cursor.position()
35    }
36
37    /// Set position in the stream
38    pub fn set_position(&mut self, pos: u64) -> Result<()> {
39        self.cursor.set_position(pos);
40        Ok(())
41    }
42
43    /// Seek to a position relative to the current position
44    pub fn seek(&mut self, offset: i64) -> Result<u64> {
45        Ok(self.cursor.seek(SeekFrom::Current(offset))?)
46    }
47
48    /// Get the total length of the data
49    pub fn len(&self) -> usize {
50        self.cursor.get_ref().len()
51    }
52
53    /// Check if the reader is empty
54    pub fn is_empty(&self) -> bool {
55        self.len() == 0
56    }
57
58    /// Get remaining bytes from current position
59    pub fn remaining(&self) -> usize {
60        self.len().saturating_sub(self.position() as usize)
61    }
62
63    /// Check if we have at least `count` bytes remaining
64    pub fn has_bytes(&self, count: usize) -> bool {
65        self.remaining() >= count
66    }
67
68    /// Align to the next 4-byte boundary
69    pub fn align(&mut self) -> Result<()> {
70        self.align_to(4)
71    }
72
73    /// Align to the specified byte boundary
74    pub fn align_to(&mut self, alignment: u64) -> Result<()> {
75        let pos = self.position();
76        let aligned = (pos + alignment - 1) & !(alignment - 1);
77        if aligned != pos {
78            self.set_position(aligned)?;
79        }
80        Ok(())
81    }
82
83    /// Read a single byte
84    pub fn read_u8(&mut self) -> Result<u8> {
85        if !self.has_bytes(1) {
86            return Err(BinaryError::not_enough_data(1, self.remaining()));
87        }
88        Ok(self.cursor.read_u8()?)
89    }
90
91    /// Read a boolean (as u8, 0 = false, non-zero = true)
92    pub fn read_bool(&mut self) -> Result<bool> {
93        Ok(self.read_u8()? != 0)
94    }
95
96    /// Read a signed 8-bit integer
97    pub fn read_i8(&mut self) -> Result<i8> {
98        Ok(self.read_u8()? as i8)
99    }
100
101    /// Read an unsigned 16-bit integer
102    pub fn read_u16(&mut self) -> Result<u16> {
103        if !self.has_bytes(2) {
104            return Err(BinaryError::not_enough_data(2, self.remaining()));
105        }
106        match self.byte_order {
107            ByteOrder::Big => Ok(self.cursor.read_u16::<BigEndian>()?),
108            ByteOrder::Little => Ok(self.cursor.read_u16::<LittleEndian>()?),
109        }
110    }
111
112    /// Read a signed 16-bit integer
113    pub fn read_i16(&mut self) -> Result<i16> {
114        if !self.has_bytes(2) {
115            return Err(BinaryError::not_enough_data(2, self.remaining()));
116        }
117        match self.byte_order {
118            ByteOrder::Big => Ok(self.cursor.read_i16::<BigEndian>()?),
119            ByteOrder::Little => Ok(self.cursor.read_i16::<LittleEndian>()?),
120        }
121    }
122
123    /// Read an unsigned 32-bit integer
124    pub fn read_u32(&mut self) -> Result<u32> {
125        if !self.has_bytes(4) {
126            return Err(BinaryError::not_enough_data(4, self.remaining()));
127        }
128        match self.byte_order {
129            ByteOrder::Big => Ok(self.cursor.read_u32::<BigEndian>()?),
130            ByteOrder::Little => Ok(self.cursor.read_u32::<LittleEndian>()?),
131        }
132    }
133
134    /// Read a signed 32-bit integer
135    pub fn read_i32(&mut self) -> Result<i32> {
136        if !self.has_bytes(4) {
137            return Err(BinaryError::not_enough_data(4, self.remaining()));
138        }
139        match self.byte_order {
140            ByteOrder::Big => Ok(self.cursor.read_i32::<BigEndian>()?),
141            ByteOrder::Little => Ok(self.cursor.read_i32::<LittleEndian>()?),
142        }
143    }
144
145    /// Read an unsigned 64-bit integer
146    pub fn read_u64(&mut self) -> Result<u64> {
147        if !self.has_bytes(8) {
148            return Err(BinaryError::not_enough_data(8, self.remaining()));
149        }
150        match self.byte_order {
151            ByteOrder::Big => Ok(self.cursor.read_u64::<BigEndian>()?),
152            ByteOrder::Little => Ok(self.cursor.read_u64::<LittleEndian>()?),
153        }
154    }
155
156    /// Read a signed 64-bit integer
157    pub fn read_i64(&mut self) -> Result<i64> {
158        if !self.has_bytes(8) {
159            return Err(BinaryError::not_enough_data(8, self.remaining()));
160        }
161        match self.byte_order {
162            ByteOrder::Big => Ok(self.cursor.read_i64::<BigEndian>()?),
163            ByteOrder::Little => Ok(self.cursor.read_i64::<LittleEndian>()?),
164        }
165    }
166
167    /// Read a 32-bit floating point number
168    pub fn read_f32(&mut self) -> Result<f32> {
169        if !self.has_bytes(4) {
170            return Err(BinaryError::not_enough_data(4, self.remaining()));
171        }
172        match self.byte_order {
173            ByteOrder::Big => Ok(self.cursor.read_f32::<BigEndian>()?),
174            ByteOrder::Little => Ok(self.cursor.read_f32::<LittleEndian>()?),
175        }
176    }
177
178    /// Read a 64-bit floating point number
179    pub fn read_f64(&mut self) -> Result<f64> {
180        if !self.has_bytes(8) {
181            return Err(BinaryError::not_enough_data(8, self.remaining()));
182        }
183        match self.byte_order {
184            ByteOrder::Big => Ok(self.cursor.read_f64::<BigEndian>()?),
185            ByteOrder::Little => Ok(self.cursor.read_f64::<LittleEndian>()?),
186        }
187    }
188
189    /// Read a fixed number of bytes
190    pub fn read_bytes(&mut self, count: usize) -> Result<Vec<u8>> {
191        if !self.has_bytes(count) {
192            return Err(BinaryError::not_enough_data(count, self.remaining()));
193        }
194        let mut buffer = vec![0u8; count];
195        self.cursor.read_exact(&mut buffer)?;
196        Ok(buffer)
197    }
198
199    /// Read all remaining bytes
200    pub fn read_remaining(&mut self) -> &[u8] {
201        let pos = self.cursor.position() as usize;
202        let data = self.cursor.get_ref();
203        &data[pos..]
204    }
205
206    /// Read a null-terminated string
207    pub fn read_cstring(&mut self) -> Result<String> {
208        let mut bytes = Vec::new();
209        loop {
210            let byte = self.read_u8()?;
211            if byte == 0 {
212                break;
213            }
214            bytes.push(byte);
215        }
216        Ok(String::from_utf8(bytes)?)
217    }
218
219    /// Read a string with a length prefix (32-bit)
220    pub fn read_string(&mut self) -> Result<String> {
221        let length = self.read_u32()? as usize;
222        let bytes = self.read_bytes(length)?;
223        Ok(String::from_utf8(bytes)?)
224    }
225
226    /// Read a string with a specific length
227    pub fn read_string_fixed(&mut self, length: usize) -> Result<String> {
228        let bytes = self.read_bytes(length)?;
229        // Remove null terminators
230        let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
231        Ok(String::from_utf8(bytes[..end].to_vec())?)
232    }
233
234    /// Read an aligned string (Unity format)
235    pub fn read_aligned_string(&mut self) -> Result<String> {
236        let string = self.read_string()?;
237        // Align to 4-byte boundary
238        self.align()?;
239        Ok(string)
240    }
241
242    /// Get the current byte order
243    pub fn byte_order(&self) -> ByteOrder {
244        self.byte_order
245    }
246
247    /// Set the byte order
248    pub fn set_byte_order(&mut self, byte_order: ByteOrder) {
249        self.byte_order = byte_order;
250    }
251
252    /// Get a slice of the remaining data
253    pub fn remaining_slice(&self) -> &[u8] {
254        let pos = self.position() as usize;
255        &self.cursor.get_ref()[pos..]
256    }
257
258    /// Create a new reader for a subset of the data
259    pub fn sub_reader(&self, offset: usize, length: usize) -> Result<BinaryReader<'a>> {
260        let data = self.cursor.get_ref();
261        if offset + length > data.len() {
262            return Err(BinaryError::not_enough_data(offset + length, data.len()));
263        }
264        Ok(BinaryReader::new(
265            &data[offset..offset + length],
266            self.byte_order,
267        ))
268    }
269}
270
271#[cfg(test)]
272mod tests {
273    use super::*;
274
275    #[test]
276    fn test_basic_reading() {
277        let data = [0x01, 0x02, 0x03, 0x04];
278        let mut reader = BinaryReader::new(&data, ByteOrder::Little);
279
280        assert_eq!(reader.read_u8().unwrap(), 0x01);
281        assert_eq!(reader.read_u8().unwrap(), 0x02);
282        assert_eq!(reader.position(), 2);
283        assert_eq!(reader.remaining(), 2);
284    }
285
286    #[test]
287    fn test_endianness() {
288        let data = [0x01, 0x02, 0x03, 0x04];
289
290        let mut reader_le = BinaryReader::new(&data, ByteOrder::Little);
291        assert_eq!(reader_le.read_u32().unwrap(), 0x04030201);
292
293        let mut reader_be = BinaryReader::new(&data, ByteOrder::Big);
294        assert_eq!(reader_be.read_u32().unwrap(), 0x01020304);
295    }
296
297    #[test]
298    fn test_string_reading() {
299        let data = b"Hello\0World\0";
300        let mut reader = BinaryReader::new(data, ByteOrder::Little);
301
302        assert_eq!(reader.read_cstring().unwrap(), "Hello");
303        assert_eq!(reader.read_cstring().unwrap(), "World");
304    }
305
306    #[test]
307    fn test_alignment() {
308        let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
309        let mut reader = BinaryReader::new(&data, ByteOrder::Little);
310
311        reader.read_u8().unwrap(); // pos = 1
312        reader.align().unwrap(); // pos = 4
313        assert_eq!(reader.position(), 4);
314    }
315}