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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/// Tries to read X bytes from the cursor, if reading fails, captures position nicely.
macro_rules! try_read {
    ($cursor: ident, u8) => {
        $cursor.read_u8().context(err::FailedToRead {
            offset: $cursor.tell().unwrap(),
            t: "u8",
        })?;
    };

    ($cursor: ident, i8) => {
        $cursor.read_i8().context(err::FailedToRead {
            offset: $cursor.tell().unwrap(),
            t: "i8",
        })?;
    };

    ($cursor: ident, u16) => {
        $cursor
            .read_u16::<byteorder::LittleEndian>()
            .context(err::FailedToRead {
                offset: $cursor.tell().unwrap(),
                t: "u16",
            })?;
    };

    ($cursor: ident, i16) => {
        $cursor
            .read_i16::<byteorder::LittleEndian>()
            .context(err::FailedToRead {
                offset: $cursor.tell().unwrap(),
                t: "i16",
            })?;
    };

    ($cursor: ident, i32) => {
        $cursor
            .read_i32::<byteorder::LittleEndian>()
            .context(err::FailedToRead {
                offset: $cursor.tell().unwrap(),
                t: "i32",
            })?;
    };

    ($cursor: ident, u32) => {
        $cursor
            .read_u32::<byteorder::LittleEndian>()
            .context(err::FailedToRead {
                offset: $cursor.tell().unwrap(),
                t: "u32",
            })?;
    };

    ($cursor: ident, f32) => {
        $cursor
            .read_f32::<byteorder::LittleEndian>()
            .context(err::FailedToRead {
                offset: $cursor.tell().unwrap(),
                t: "f32",
            })?;
    };

    ($cursor: ident, i64) => {
        $cursor
            .read_i64::<byteorder::LittleEndian>()
            .context(err::FailedToRead {
                offset: $cursor.tell().unwrap(),
                t: "i64",
            })?;
    };

    ($cursor: ident, u64) => {
        $cursor
            .read_u64::<byteorder::LittleEndian>()
            .context(err::FailedToRead {
                offset: $cursor.tell().unwrap(),
                t: "u64",
            })?;
    };

    ($cursor: ident, f64) => {
        $cursor
            .read_f64::<byteorder::LittleEndian>()
            .context(err::FailedToRead {
                offset: $cursor.tell().unwrap(),
                t: "f64",
            })?;
    };

    ($cursor: ident, bool) => {{
        let bool_value = try_read!($cursor, i32);
        match bool_value {
            0 => false,
            1 => true,
            _ => {
                log::warn!(
                    "{:?} is an unknown value for bool, coercing to `true`",
                    bool_value
                );
                true
            }
        }
    }};

    ($cursor: ident, guid) => {
        Guid::from_stream($cursor).context(err::FailedToReadGUID {
            offset: $cursor.position(),
        })?
    };

    ($cursor: ident, utf_16_str) => {{
        let s = read_len_prefixed_utf16_string($cursor, false)
            .context(err::FailedToDecodeUTF16String {
                offset: $cursor.position(),
            })?
            .unwrap_or_else(|| "".to_owned());

        Cow::Owned(s)
    }};

    ($cursor: ident, null_terminated_utf_16_str) => {{
        let s =
            read_null_terminated_utf16_string($cursor).context(err::FailedToDecodeUTF16String {
                offset: $cursor.position(),
            })?;

        Cow::Owned(s)
    }};

    ($cursor: ident, sid) => {
        Sid::from_stream($cursor).context(err::FailedToReadNTSID {
            offset: $cursor.position(),
        })?
    };

    ($cursor: ident, hex32) => {
        Cow::Owned(format!("0x{:x}", try_read!($cursor, i32)))
    };

    ($cursor: ident, hex64) => {
        Cow::Owned(format!("0x{:x}", try_read!($cursor, i64)))
    };

    ($cursor: ident, filetime) => {
        datetime_from_filetime(try_read!($cursor, u64))
    };

    ($cursor: ident, systime) => {
        read_systemtime($cursor)?
    };
}

macro_rules! try_read_sized_array {
    ($cursor: ident, $unit: ident, $size: ident) => {{
        let mut array = vec![];
        let start_pos = $cursor.position();

        loop {
            if ($cursor.position() - start_pos) >= u64::from($size) {
                break;
            }

            let val = try_read!($cursor, $unit);
            array.push(val);
        }

        array
    }};
}