aboutsummaryrefslogtreecommitdiff
path: root/src/object/read.rs
blob: c41163a7797819f6e0f151bba1b0beac8ad4a808 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use super::Value;
use crate::helper::{AlignExt, Endianness, ReadExt};
use crate::serialized_file::TypeTreeNode;
use anyhow::{Result, bail};
use log::trace;
use std::io::Seek;
use std::{collections::BTreeMap, io::Read};

impl Value {
    pub fn read(
        ty: &TypeTreeNode,
        e: Endianness,
        file: usize,
        data: &mut (impl Read + Seek),
    ) -> Result<Value> {
        let mut align = false;
        let pos_before = data.stream_position()?;
        let r = match ty.type_string.as_str() {
            "char" => {
                assert_eq!(ty.byte_size, 1);
                Ok(Value::U8(data.read_u8()?))
            }
            "Type*" => Ok(Value::U32(data.read_u32(e)?)),
            "int" => Ok(Value::I32(data.read_i32(e)?)),
            "unsigned int" => Ok(Value::U32(data.read_u32(e)?)),
            "UInt8" => Ok(Value::U8(data.read_u8()?)),
            "UInt16" => Ok(Value::U16(data.read_u16(e)?)),
            "UInt32" => Ok(Value::U32(data.read_u32(e)?)),
            "UInt64" => Ok(Value::U64(data.read_u64(e)?)),
            "SInt8" => Ok(Value::I8(data.read_i8()?)),
            "SInt16" => Ok(Value::I16(data.read_i16(e)?)),
            "SInt32" => Ok(Value::I32(data.read_i32(e)?)),
            "SInt64" => Ok(Value::I64(data.read_i64(e)?)),
            "bool" => Ok(Value::Bool(data.read_u8()? != 0)),
            "float" => {
                data.align(4)?;
                Ok(Value::F32(data.read_f32(e)?))
            }
            "double" => {
                data.align(4)?;
                Ok(Value::F64(data.read_f64(e)?))
            }
            "string" => {
                let Value::Array(arr) = Value::read(&ty.children[0], e, file, data)? else {
                    unreachable!()
                };
                let bytes = arr
                    .into_iter()
                    .map(|e| match e {
                        Value::U8(x) => x,
                        _ => unreachable!(),
                    })
                    .collect::<Vec<_>>();
                Ok(Value::String(String::from_utf8(bytes)?))
            }
            "Array" => {
                align |= ty.children[0].post_align();
                assert_eq!(ty.byte_size, -1);
                let Value::I32(size) = Value::read(&ty.children[0], e, file, data)? else {
                    unreachable!()
                };
                trace!("array of size {size}");
                let mut elems = Vec::new();
                for _ in 0..size {
                    elems.push(Value::read(&ty.children[1], e, file, data)?);
                }
                Ok(Value::Array(elems))
            }
            "TypelessData" => {
                let len = data.read_u32(e)?;
                let mut buf = vec![0u8; len as usize];
                data.read_exact(&mut buf)?;
                Ok(Value::Typeless(buf))
            }
            _ => {
                if ty.children.is_empty() && ty.byte_size != 0 {
                    todo!("need type {:?}", ty.type_string);
                }
                let mut fields = BTreeMap::new();
                for c in &ty.children {
                    fields.insert(c.name_string.clone(), Value::read(&c, e, file, data)?);
                }
                Ok(Value::Object {
                    fields,
                    file,
                    class: ty.type_string.clone(),
                })
            }
        };
        let pos_after = data.stream_position()?;
        if ty.byte_size != -1 && pos_after - pos_before < ty.byte_size as u64 {
            bail!(
                "did not read enough data ({} expected, {} actual)",
                ty.byte_size,
                pos_after - pos_before
            );
        }
        if align || ty.post_align() {
            trace!("post align");
            data.align(4)?;
        }
        r
    }
}