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