Skip to main content

hermes_tdata/qdatastream/
reader.rs

1//! `QDataStream` reader implementation
2
3use byteorder::{BigEndian, ReadBytesExt};
4use std::io::{Cursor, Read, Seek, SeekFrom};
5
6use crate::{Error, Result};
7
8use super::constants::{EXTENDED_LENGTH_MARKER, NULL_MARKER, QT_VERSION_5_1};
9
10/// `QDataStream` reader for parsing Qt binary serialization format
11pub struct QDataStream<'a> {
12    pub(super) cursor: Cursor<&'a [u8]>,
13    pub(super) version: u32,
14}
15
16impl std::fmt::Debug for QDataStream<'_> {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        f.debug_struct("QDataStream")
19            .field("position", &self.cursor.position())
20            .field("version", &self.version)
21            .finish()
22    }
23}
24
25impl<'a> QDataStream<'a> {
26    /// Create a new `QDataStream` reader with Qt 5.1 version
27    #[must_use]
28    pub const fn new(data: &'a [u8]) -> Self {
29        Self { cursor: Cursor::new(data), version: QT_VERSION_5_1 }
30    }
31
32    /// Create a new `QDataStream` reader with specified version
33    #[must_use]
34    pub const fn with_version(data: &'a [u8], version: u32) -> Self {
35        Self { cursor: Cursor::new(data), version }
36    }
37
38    /// Get the Qt version
39    #[must_use]
40    pub const fn version(&self) -> u32 {
41        self.version
42    }
43
44    /// Get current position in the stream
45    #[must_use]
46    pub const fn position(&self) -> u64 {
47        self.cursor.position()
48    }
49
50    /// Check if we've reached the end of the stream
51    #[must_use]
52    pub const fn at_end(&self) -> bool {
53        self.cursor.position() >= self.cursor.get_ref().len() as u64
54    }
55
56    /// Get remaining bytes count
57    #[must_use]
58    pub const fn remaining(&self) -> usize {
59        let pos = self.cursor.position() as usize;
60        let len = self.cursor.get_ref().len();
61        len.saturating_sub(pos)
62    }
63
64    /// Skip n bytes
65    pub fn skip(&mut self, n: usize) -> Result<()> {
66        let _ = self
67            .cursor
68            .seek(SeekFrom::Current(n as i64))
69            .map_err(|_| Error::UnexpectedEof { offset: self.position() })?;
70        Ok(())
71    }
72
73    /// Read a single byte (quint8)
74    pub fn read_u8(&mut self) -> Result<u8> {
75        self.cursor.read_u8().map_err(|_| Error::UnexpectedEof { offset: self.position() })
76    }
77
78    /// Read a signed 8-bit integer (qint8)
79    pub fn read_i8(&mut self) -> Result<i8> {
80        self.cursor.read_i8().map_err(|_| Error::UnexpectedEof { offset: self.position() })
81    }
82
83    /// Read an unsigned 16-bit integer (quint16) - Big Endian
84    pub fn read_u16(&mut self) -> Result<u16> {
85        self.cursor
86            .read_u16::<BigEndian>()
87            .map_err(|_| Error::UnexpectedEof { offset: self.position() })
88    }
89
90    /// Read a signed 16-bit integer (qint16) - Big Endian
91    pub fn read_i16(&mut self) -> Result<i16> {
92        self.cursor
93            .read_i16::<BigEndian>()
94            .map_err(|_| Error::UnexpectedEof { offset: self.position() })
95    }
96
97    /// Read an unsigned 32-bit integer (quint32) - Big Endian
98    pub fn read_u32(&mut self) -> Result<u32> {
99        self.cursor
100            .read_u32::<BigEndian>()
101            .map_err(|_| Error::UnexpectedEof { offset: self.position() })
102    }
103
104    /// Read a signed 32-bit integer (qint32) - Big Endian
105    pub fn read_i32(&mut self) -> Result<i32> {
106        self.cursor
107            .read_i32::<BigEndian>()
108            .map_err(|_| Error::UnexpectedEof { offset: self.position() })
109    }
110
111    /// Read an unsigned 64-bit integer (quint64) - Big Endian
112    pub fn read_u64(&mut self) -> Result<u64> {
113        self.cursor
114            .read_u64::<BigEndian>()
115            .map_err(|_| Error::UnexpectedEof { offset: self.position() })
116    }
117
118    /// Read a signed 64-bit integer (qint64) - Big Endian
119    pub fn read_i64(&mut self) -> Result<i64> {
120        self.cursor
121            .read_i64::<BigEndian>()
122            .map_err(|_| Error::UnexpectedEof { offset: self.position() })
123    }
124
125    /// Read a boolean value
126    pub fn read_bool(&mut self) -> Result<bool> {
127        Ok(self.read_u8()? != 0)
128    }
129
130    /// Read a 32-bit float - Big Endian
131    pub fn read_f32(&mut self) -> Result<f32> {
132        self.cursor
133            .read_f32::<BigEndian>()
134            .map_err(|_| Error::UnexpectedEof { offset: self.position() })
135    }
136
137    /// Read a 64-bit double - Big Endian
138    pub fn read_f64(&mut self) -> Result<f64> {
139        self.cursor
140            .read_f64::<BigEndian>()
141            .map_err(|_| Error::UnexpectedEof { offset: self.position() })
142    }
143
144    /// Read raw bytes of specified length
145    pub fn read_raw(&mut self, len: usize) -> Result<Vec<u8>> {
146        if self.remaining() < len {
147            return Err(Error::UnexpectedEof { offset: self.position() });
148        }
149
150        let mut buf = vec![0u8; len];
151        self.cursor
152            .read_exact(&mut buf)
153            .map_err(|_| Error::UnexpectedEof { offset: self.position() })?;
154        Ok(buf)
155    }
156
157    /// Read a `QByteArray`
158    ///
159    /// Wire format:
160    /// - 4 bytes: length (quint32 BE)
161    ///   - 0xFFFFFFFF = null `QByteArray` (returns empty vec)
162    ///   - 0xFFFFFFFE = extended 64-bit length (followed by quint64)
163    /// - N bytes: raw data
164    pub fn read_qbytearray(&mut self) -> Result<Vec<u8>> {
165        let len = self.read_u32()?;
166
167        match len {
168            NULL_MARKER => Ok(Vec::new()),
169            EXTENDED_LENGTH_MARKER => {
170                // Extended 64-bit length (Qt 6.7+)
171                let real_len = self.read_u64()? as usize;
172                self.read_raw(real_len)
173            },
174            _ => self.read_raw(len as usize),
175        }
176    }
177
178    /// Read a `QString`
179    ///
180    /// Wire format:
181    /// - 4 bytes: length in BYTES (not chars!) of UTF-16 data
182    ///   - 0xFFFFFFFF = null `QString` (returns empty string)
183    /// - N bytes: UTF-16 Big Endian encoded characters
184    pub fn read_qstring(&mut self) -> Result<String> {
185        let byte_len = self.read_u32()?;
186
187        if byte_len == NULL_MARKER {
188            return Ok(String::new());
189        }
190
191        if byte_len % 2 != 0 {
192            return Err(Error::qdatastream("QString byte length is not even"));
193        }
194
195        let char_count = (byte_len / 2) as usize;
196        let mut utf16: Vec<u16> = Vec::with_capacity(char_count);
197
198        for _ in 0..char_count {
199            utf16.push(self.read_u16()?);
200        }
201
202        String::from_utf16(&utf16).map_err(|_| Error::InvalidUtf16)
203    }
204
205    /// Read a length-prefixed C string (writeBytes format)
206    ///
207    /// Wire format:
208    /// - 4 bytes: length including null terminator
209    /// - N bytes: string data including null terminator
210    pub fn read_cstring(&mut self) -> Result<String> {
211        let data = self.read_qbytearray()?;
212
213        // Remove null terminator if present
214        let data = if data.last() == Some(&0) { &data[..data.len() - 1] } else { &*data };
215
216        String::from_utf8(data.to_vec())
217            .map_err(|_| Error::qdatastream("invalid UTF-8 in C string"))
218    }
219}