qt_json/
lib.rs

1//!
2//! [![codecov](https://codecov.io/gh/TheDome/qt-json-rs/branch/develop/graph/badge.svg?token=7MIOMJ88B1)](https://codecov.io/gh/TheDome/qt-json-rs)
3//! ![crates.io](https://img.shields.io/crates/v/qt-json.svg)
4//!
5//! A simple parser for the Internal Qt Binary JSON data format.
6//!
7//! This parser will transform the popular
8//! [QTBinary JSON](https://doc.qt.io/qt-6.2/qbinaryjson.html#toBinaryData)
9//! format into usable format for rust applications.
10//!
11//! # Use
12//!
13//! Simply provide a binary encoded JSON Array to the function and it will parse it into an
14//! internal JSON structure:
15//!
16//! ```rust
17//! use qt_json::QJSONDocument;
18//!
19//! fn main(){
20//!         let json_data = b"qbjs\
21//!     \x01\x00\x00\x00\
22//!     \x10\x00\x00\x00\
23//!     \x02\x00\x00\x00\
24//!     \x0C\x00\x00\x00\
25//!     \x4A\x01\x00\x00";
26//!
27//!     let document = QJSONDocument::from_binary(json_data.to_vec()).unwrap();
28//!
29//!     println!("{:?}", document);
30//! }
31//! ```
32//!
33//! # Disclaimer
34//!
35//! This library has been created by looking at the Qt source code and performing reverse
36//! engineering.
37//! There is a possibility that the code will not work with other Version of Qt JSON documents.
38//! Any help with this library is welcome.
39
40use std::collections::HashMap;
41use std::io::{Cursor, Error, ErrorKind, Read};
42
43use byteorder::ReadBytesExt;
44use log::{debug, trace, warn};
45use num_derive::FromPrimitive;
46use num_traits::FromPrimitive;
47
48use elements::{JsonBaseValue, JsonValue, Object};
49
50pub mod elements;
51
52/// A QJSONDocument is the root of every parsed JSOn Document.
53/// It consists out of metadata and a base
54#[derive(Debug)]
55pub struct QJSONDocument {
56    /// This will be "qbjs" encoded in an u32
57    pub tag: u32,
58    /// The QBJS Version. This needs to be 1
59    pub version: u32,
60    /// The Base element of the document.
61    /// It muse either be an Array or an Object
62    pub base: JsonBaseValue,
63}
64
65/// This is every possible value type in the QBJS format.
66#[derive(Debug, Eq, PartialEq, FromPrimitive)]
67#[repr(u32)]
68enum QTValueType {
69    /// A null value
70    Null = 0x0,
71    /// A boolean value
72    Bool = 0x1,
73    /// A signed integer value represented in the javascript number format (i.e. i64)
74    Double = 0x2,
75    /// A normal array of character. Can be latin or unicode
76    String = 0x3,
77    /// A JavaScript Array
78    Array = 0x4,
79    /// An JavaScript Object
80    Object = 0x5,
81    /// An explicitly undefined value
82    Undefined = 0x80,
83}
84
85const QT_JSON_TAG: u32 =
86    (('s' as u32) << 24) | (('j' as u32) << 16) | (('b' as u32) << 8) | ('q' as u32);
87
88pub type Endianess = byteorder::LittleEndian;
89
90impl QJSONDocument {
91    /// Parses a binary VEC into a QJSONDocument
92    pub fn from_binary(data: Vec<u8>) -> Result<Self, Error> {
93        debug!("[QBJS] Loading data");
94
95        let mut reader = Cursor::new(&data);
96
97        let tag = reader.read_u32::<Endianess>()?;
98        let version = reader.read_u32::<Endianess>()?;
99
100        assert_eq!(tag, QT_JSON_TAG);
101
102        assert_eq!(version, 1);
103
104        debug!("QBJS Version: {}", version);
105
106        let elem = Self::load_element(data[8..].to_vec())?;
107
108        let base = match elem {
109            JsonValue::Object(o) => JsonBaseValue::Object(o),
110            JsonValue::Array(a) => JsonBaseValue::Array(a),
111            _ => {
112                return Err(Error::new(
113                    ErrorKind::InvalidData,
114                    "The Base must be either an Array or object",
115                ));
116            }
117        };
118
119        let doc = QJSONDocument { tag, version, base };
120
121        debug!("[QBJS] Parsing finished!");
122
123        Ok(doc)
124    }
125
126    /// Loads a single element from the binary data.
127    fn load_element(data: Vec<u8>) -> Result<JsonValue, Error> {
128        let mut reader = Cursor::new(&data);
129
130        let size = reader.read_u32::<Endianess>()?;
131        let header = reader.read_u32::<Endianess>()?;
132        let offset = reader.read_u32::<Endianess>()?;
133
134        let is_object = (header & 0x1) == 1;
135        let len = header >> 1;
136
137        trace!("Element Size is: {:#0X}", size);
138        trace!("Element Offset is: {:#0X}", offset);
139        trace!("Element is an object: {}", is_object);
140        trace!("Element elements: {}", len);
141
142        let table = data.split_at(offset as usize).1;
143
144        // u32 is 4 bytes
145        trace!("Table len is {}", table.len() / 4);
146
147        let base = match is_object {
148            true => Self::load_object(&data, table, len, size),
149            false => Self::load_array(&data, table, len, size),
150        };
151
152        trace!("{:?}", base);
153
154        base
155    }
156
157    /**
158     * loads an object from the stream
159     */
160    fn load_object(data: &[u8], offsets: &[u8], len: u32, size: u32) -> Result<JsonValue, Error> {
161        debug!("Loading object ..");
162        trace!("Expected len: {}", len);
163        trace!("Actual len: {}", offsets.len() / 4);
164
165        if offsets.len() / 4 < (len as usize) {
166            return Err(Error::new(
167                ErrorKind::InvalidData,
168                format!(
169                    "The object is not the expected size, expected: {}, provided: {}",
170                    len,
171                    offsets.len() / 4
172                ),
173            ));
174        }
175
176        let mut offsets = Cursor::new(offsets);
177        let mut values = HashMap::new();
178
179        for i in 0..len {
180            trace!("Iterating over entry {}", i);
181
182            let offset = offsets.read_u32::<Endianess>()?;
183            trace!("Entry at offset: {:0X?}", offset);
184
185            let element = data.split_at(offset as usize).1;
186            let mut reader = Cursor::new(element);
187
188            let value_header = reader.read_u32::<Endianess>()?;
189            trace!(" > Value header {:032b}", value_header);
190
191            let value_type_number: u32 = value_header & 0b111;
192            let latin_or_int = ((value_header & 0b1000) >> 3) == 1;
193            let latin_key = ((value_header & 0b10000) >> 4) == 1;
194            let orig_value: u32 = (value_header & 0xFFFFFFE0) >> 5;
195
196            let value_type: Option<QTValueType> = FromPrimitive::from_u32(value_type_number);
197
198            if value_type.is_none() {
199                warn!("Could not parse value at json entry {}\nContinuing. But this might have unacceptable impact", i);
200                debug!("Value type: {:#0X}", value_type_number);
201                debug!("Value value: {:#04X}", orig_value);
202            }
203
204            trace!(" > Value of type: {:?}", value_type);
205            trace!(" > Key is latin: {}", latin_key);
206            let key = Self::read_string(&mut reader, latin_key)?;
207
208            trace!(" > Key is: '{}'", key);
209            trace!(" > Reading value of type: {:?}", value_type);
210
211            let value = Self::decode_value(
212                value_type,
213                orig_value,
214                latin_or_int,
215                latin_key,
216                size as usize,
217                data,
218            )?;
219
220            trace!(" > Value is: {:?}", value);
221
222            values.insert(key, value);
223        }
224
225        let object = Object { size: len, values };
226
227        trace!("Using object {:?}", object);
228
229        Ok(JsonValue::Object(object))
230    }
231
232    fn load_array(data: &[u8], offsets: &[u8], len: u32, size: u32) -> Result<JsonValue, Error> {
233        debug!("Loading array ..");
234        trace!("Expected len: {}", len);
235        trace!("Actual len: {}", offsets.len() / 4);
236
237        if offsets.len() / 4 < (len as usize) {
238            return Err(Error::new(
239                ErrorKind::InvalidData,
240                format!(
241                    "The array is not the expected size, expected: {}, provided: {}",
242                    len,
243                    offsets.len() / 4
244                ),
245            ));
246        }
247
248        let mut offsets = Cursor::new(offsets);
249        let mut values = Vec::new();
250
251        for i in 0..len {
252            trace!("Iterating over entry {}", i);
253
254            let offset = offsets.read_u32::<Endianess>()?;
255            trace!("Entry at offset: 0x{:0X}", offset);
256
257            let value_header = offset;
258            trace!(" > Value header {:032b}b", value_header);
259
260            let value_type_number: u16 = (value_header & 0b111) as u16;
261            let latin_or_int = ((value_header & 0b1000) >> 3) == 1;
262            let latin_key = ((value_header & 0b10000) >> 4) == 1;
263            let orig_value: u32 = (value_header & 0xFFFFFFE0) >> 5;
264
265            let value_type: Option<QTValueType> = FromPrimitive::from_u16(value_type_number);
266
267            if value_type.is_none() {
268                warn!("Could not parse value at json entry {}\nContinuing. But this might have unacceptable impact", i);
269                debug!("Value type: {:#0X}", value_type_number);
270                debug!("Value value: {:#04X}", orig_value);
271            }
272
273            trace!(" > Reading value of type: {:?}", value_type);
274
275            let value = Self::decode_value(
276                value_type,
277                orig_value,
278                latin_or_int,
279                latin_key,
280                size as usize,
281                data,
282            )?;
283
284            trace!(" > Value is: {:?}", value);
285
286            values.push(value);
287        }
288
289        Ok(JsonValue::Array(values))
290    }
291
292    /// This function is responsible from decoding a value from the given data.
293    /// The value will be passed from the upper declaration function and will
294    /// then be extracted here.
295    ///
296    /// This code has been created using reverse engineering. But it should work for QTJSONv1
297    fn decode_value(
298        value_type: Option<QTValueType>,
299        orig_value: u32,
300        latin_or_int: bool,
301        latin_key: bool,
302        size: usize,
303        data: &[u8],
304    ) -> Result<JsonValue, std::io::Error> {
305        let value = match value_type {
306            Some(QTValueType::Double) => {
307                if latin_or_int {
308                    JsonValue::Number(orig_value.into())
309                } else {
310                    trace!(" > > Value is of type f64");
311                    trace!(" > > Value located at offset: {:0X?}", orig_value);
312
313                    let value_data = data.split_at(orig_value as usize).1;
314                    let mut reader = Cursor::new(value_data);
315                    JsonValue::Number(reader.read_f64::<Endianess>()?)
316                }
317            }
318            Some(QTValueType::String) => {
319                trace!(" > > Value located at offset: {:0X?}", orig_value);
320
321                let value_data = data.split_at(orig_value as usize).1;
322                let mut reader = Cursor::new(value_data);
323                JsonValue::String(Self::read_string(&mut reader, latin_key)?)
324            }
325            Some(QTValueType::Object) | Some(QTValueType::Array) => {
326                trace!(" > > Value located at offset: {:0X?}", orig_value);
327
328                trace!(
329                    " > > Trimming {} bytes from object",
330                    data.len() - size as usize
331                );
332                let value_data = data.split_at(size as usize).0;
333
334                trace!(" > > Trimming {} bytes from object top", orig_value);
335                let encapsulated = value_data.split_at(orig_value as usize).1;
336                Self::load_element(Vec::from(encapsulated))?
337            }
338            Some(QTValueType::Bool) => JsonValue::Bool(orig_value != 0),
339            Some(QTValueType::Null) => JsonValue::Null,
340            _ => JsonValue::Undefined,
341        };
342
343        Ok(value)
344    }
345
346    /**
347     * reads a string.
348     * This class is capable of reading a string in UTF16 and UTF8
349     */
350    fn read_string(reader: &mut dyn Read, latin: bool) -> Result<String, Error> {
351        let key_len = reader.read_u16::<Endianess>()?;
352
353        trace!(" --> Reading string, latin:{}, len:{}", latin, key_len);
354        // A latin string defined an ASCII encoded string array. So every character is 8 bits long.
355        if latin {
356            let mut buffer = Vec::new();
357            for _ in 0..key_len {
358                buffer.push(reader.read_u8()?);
359            }
360
361            Ok(String::from_utf8_lossy(buffer.as_slice()).parse().unwrap())
362        } else {
363            // By definition any string in JavaScript is UTF16 encoded else.
364            let mut buffer = Vec::new();
365            for _ in 0..key_len {
366                buffer.push(reader.read_u16::<Endianess>()?);
367            }
368            String::from_utf16(buffer.as_slice())
369                .map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid UTF16"))
370        }
371    }
372}
373
374#[cfg(test)]
375mod test {
376    use crate::elements::{JsonBaseValue, JsonValue};
377    use crate::QJSONDocument;
378
379    #[test]
380    fn read_object() {
381        let object_str = b"qbjs\x01\x00\x00\x00$\x00\x00\x00\x03\x00\x00\x00 \
382        \x00\x00\x00\x1B\x03\x00\x00\x04\x00test\x00\x00\x03\x00yes\x00\x00\x00\x0C\x00\x00\x00";
383
384        let parsed = QJSONDocument::from_binary(object_str.to_vec()).unwrap();
385
386        let val = match parsed.base {
387            JsonBaseValue::Object(ref object) => {
388                assert_eq!(object.size, 1);
389
390                object.values.get("test").unwrap()
391            }
392            _ => panic!("Expected object"),
393        };
394
395        match val {
396            JsonValue::String(ref s) => assert_eq!(s, "yes"),
397            _ => panic!("Expected string"),
398        }
399    }
400
401    #[test]
402    fn test_non_latin_number() {
403        let data = b"qbjs\x01\x00\x00\x00\x18\x00\x00\x00\x02\x00\x00\x00\x14\x00\x00\x00\
404        \x33\x33\x33\x33\x33\x33\x24\x40\x82\x01\x00\x00";
405
406        let parsed = QJSONDocument::from_binary(data.to_vec()).unwrap();
407
408        match parsed.base {
409            JsonBaseValue::Array(ref vals) => {
410                assert_eq!(vals.len(), 1);
411                let num = &vals[0];
412                match num {
413                    JsonValue::Number(n) => assert_eq!(*n, 10.1),
414                    _ => panic!("Expected number"),
415                }
416            }
417            _ => panic!("Expected array"),
418        };
419    }
420
421    #[test]
422    fn test_latin_string() {
423        let data = b"qbjs\x01\x00\x00\x00\x14\x00\x00\x00\x02\x00\x00\x00\x10\x00\x00\x00\x01\x00\xF6\x00\x8B\x01\x00\x00";
424
425        let parsed = QJSONDocument::from_binary(data.to_vec()).unwrap();
426
427        match parsed.base {
428            JsonBaseValue::Array(ref vals) => {
429                assert_eq!(vals.len(), 1);
430                let num = &vals[0];
431                match num {
432                    JsonValue::String(n) => assert_eq!(*n, "รถ"),
433                    _ => panic!("Expected string"),
434                }
435            }
436            _ => panic!("Expected array"),
437        };
438    }
439
440    #[test]
441    fn test_bool() {
442        let data = b"qbjs\x01\x00\x00\x00\x10\x00\x00\x00\x02\x00\x00\x00\x0C\x00\x00\x00!\x00\x00
443        \x00";
444
445        let parsed = QJSONDocument::from_binary(data.to_vec()).unwrap();
446
447        match parsed.base {
448            JsonBaseValue::Array(ref vals) => {
449                assert_eq!(vals.len(), 1);
450                let num = &vals[0];
451                match num {
452                    JsonValue::Bool(n) => assert_eq!(*n, true),
453                    _ => panic!("Expected string"),
454                }
455            }
456            _ => panic!("Expected array"),
457        };
458    }
459
460    #[test]
461    fn test_nested_object() {
462        let data = b"qbjs\x01\x00\x00\x00\x34\x00\x00\x00\x02\x00\x00\x00\x30\x00\x00\x00\x24\x00\
463                \x00\x00\x03\x00\x00\x00\x20\x00\x00\x00\x1B\x03\x00\x00\x04\x00test\x00\x00\x03\
464                \x00yes\x00\x00\x00\x0C\x00\x00\x00\x85\x01\x00\x00";
465
466        let parsed = QJSONDocument::from_binary(data.to_vec()).unwrap();
467
468        match parsed.base {
469            JsonBaseValue::Array(ref vals) => {
470                assert_eq!(vals.len(), 1);
471                let num = &vals[0];
472                let val = match num {
473                    JsonValue::Object(n) => {
474                        assert_eq!(n.size, 1);
475                        n.values.get("test").unwrap()
476                    }
477                    _ => panic!("Expected string"),
478                };
479
480                match val {
481                    JsonValue::String(n) => assert_eq!(*n, "yes"),
482                    _ => panic!("Expected string"),
483                }
484            }
485            _ => panic!("Expected array"),
486        };
487    }
488}