Skip to main content

me3_coalesced_parser/
de.rs

1use crate::{
2    crc32::hash_crc32,
3    error::{DecodeError, DecodeResult},
4    huffman::Huffman,
5    invert_huffman_tree,
6    shared::{CoalFile, Coalesced, Property, Section, Value, ValueType, ME3_MAGIC},
7    Tlk, TlkString, TLK_MAGIC,
8};
9use std::borrow::Cow;
10
11/// Seekable read buffer
12pub struct ReadBuffer<'de> {
13    /// Buffer storing the bytes to be deserialized
14    buffer: &'de [u8],
15    /// Cursor representing the current offset within the buffer
16    cursor: usize,
17}
18
19impl<'de> ReadBuffer<'de> {
20    /// Creates a new [Deserializer] from the provided buffer
21    pub fn new(buffer: &'de [u8]) -> Self {
22        Self { buffer, cursor: 0 }
23    }
24
25    /// Obtains the remaining length in bytes left of
26    /// the buffer after the cursor
27    pub fn remaining(&self) -> usize {
28        self.buffer.len() - self.cursor
29    }
30
31    /// Internal function used to read a slice of bytes from the buffer
32    pub(crate) fn read_bytes(&mut self, length: usize) -> DecodeResult<&'de [u8]> {
33        if self.cursor + length > self.buffer.len() {
34            return Err(DecodeError::UnexpectedEof {
35                cursor: self.cursor,
36                wanted: length,
37                remaining: self.remaining(),
38            });
39        }
40
41        let slice: &[u8] = &self.buffer[self.cursor..self.cursor + length];
42        self.cursor += length;
43        Ok(slice)
44    }
45
46    pub(crate) fn seek(&mut self, cursor: usize) -> DecodeResult<()> {
47        if cursor >= self.buffer.len() {
48            return Err(DecodeError::UnexpectedEof {
49                cursor: self.cursor,
50                wanted: cursor,
51                remaining: self.remaining(),
52            });
53        }
54
55        self.cursor = cursor;
56
57        Ok(())
58    }
59
60    /// Internal function for reading a fixed length array from the buffer
61    pub(crate) fn read_fixed<const S: usize>(&mut self) -> DecodeResult<[u8; S]> {
62        let slice = self.read_bytes(S)?;
63
64        // Copy the bytes into the new fixed size array
65        let mut bytes: [u8; S] = [0u8; S];
66        bytes.copy_from_slice(slice);
67
68        Ok(bytes)
69    }
70
71    pub fn take_slice(&mut self, length: usize) -> DecodeResult<ReadBuffer<'de>> {
72        Ok(Self::new(self.read_bytes(length)?))
73    }
74
75    pub fn read_u32(&mut self) -> DecodeResult<u32> {
76        let bytes = self.read_fixed::<4>()?;
77        Ok(u32::from_le_bytes(bytes))
78    }
79
80    pub fn read_u16(&mut self) -> DecodeResult<u16> {
81        let bytes = self.read_fixed::<2>()?;
82        Ok(u16::from_le_bytes(bytes))
83    }
84
85    pub fn read_i32(&mut self) -> DecodeResult<i32> {
86        let bytes = self.read_fixed::<4>()?;
87        Ok(i32::from_le_bytes(bytes))
88    }
89}
90
91pub fn deserialize_coalesced(input: &[u8]) -> DecodeResult<Coalesced> {
92    let mut r = ReadBuffer::new(input);
93    // Read the file header
94    let magic = r.read_u32()?;
95
96    if magic != ME3_MAGIC {
97        return Err(DecodeError::UnknownFileMagic);
98    }
99
100    let version = r.read_u32()?;
101    let _max_field_name_length = r.read_u32()?;
102    let max_value_length = r.read_u32()?;
103    let string_table_size = r.read_u32()?;
104    let huffman_size = r.read_u32()?;
105    let index_size = r.read_u32()?;
106    let data_size = r.read_u32()?;
107
108    // Read the string lookup table
109    let string_table: Vec<String> = {
110        let mut string_table_block = r.take_slice(string_table_size as usize)?;
111
112        let local_size = string_table_block.read_u32()?;
113
114        if local_size != string_table_size {
115            return Err(DecodeError::StringTableSizeMismatch);
116        }
117
118        let count = string_table_block.read_u32()?;
119
120        let mut offsets: Vec<(u32, u32)> = Vec::new();
121
122        for _ in 0..count {
123            let hash = string_table_block.read_u32()?;
124            let offset = string_table_block.read_u32()?;
125            offsets.push((offset, hash))
126        }
127
128        let mut values = Vec::new();
129        for (offset, hash) in offsets {
130            string_table_block.seek((8 + offset) as usize)?;
131
132            let length = string_table_block.read_u16()?;
133            let bytes = string_table_block.read_bytes(length as usize)?;
134            let text: Cow<str> = String::from_utf8_lossy(bytes);
135            let text: String = text.to_string();
136
137            if hash_crc32(text.as_bytes()) != hash {
138                return Err(DecodeError::StringTableHashMismatch);
139            }
140
141            values.push(text);
142        }
143
144        values
145    };
146
147    // Read the huffman tree
148    let huffman_tree: Vec<(i32, i32)> = {
149        let mut huffman_tree_block = r.take_slice(huffman_size as usize)?;
150
151        // Read the length of the tree
152        let count = huffman_tree_block.read_u16()?;
153
154        let mut values = Vec::with_capacity(count as usize);
155
156        for _ in 0..count {
157            let left = huffman_tree_block.read_i32()?;
158            let right = huffman_tree_block.read_i32()?;
159            values.push((left, right))
160        }
161
162        values
163    };
164
165    // Read the index block
166    let mut index_block: ReadBuffer = r.take_slice(index_size as usize)?;
167
168    let data_block: &[u8] = {
169        // Read the total bits count
170        let _total_bits = r.read_u32()?;
171
172        // Read the data block
173        let block = r.take_slice(data_size as usize)?;
174        block.buffer
175    };
176
177    // Read the number of files
178    let files_count = index_block.read_u16()?;
179
180    let mut files: Vec<CoalFile> = Vec::with_capacity(files_count as usize);
181
182    // Read the file offsets
183    let mut file_offsets: Vec<(String, usize)> = Vec::with_capacity(files_count as usize);
184
185    for _ in 0..files_count {
186        // Read the file name and get it from the string table
187        let file_name_index = index_block.read_u16()?;
188        let file_name = string_table
189            .get(file_name_index as usize)
190            .ok_or(DecodeError::InvalidNameOffset)?;
191
192        // Read the file offset
193        let file_offset = index_block.read_u32()?;
194
195        file_offsets.push((file_name.to_string(), file_offset as usize));
196    }
197
198    for (file_name, file_offset) in file_offsets {
199        // Seek the index to the file
200        index_block.seek(file_offset)?;
201
202        // Read the number of sections
203        let sections_count = index_block.read_u16()?;
204
205        let mut sections: Vec<Section> = Vec::with_capacity(sections_count as usize);
206        let mut section_offsets: Vec<(String, usize)> = Vec::with_capacity(sections_count as usize);
207
208        for _ in 0..sections_count {
209            // Read the section name and get it from the string table
210            let section_name_index = index_block.read_u16()?;
211            let section_name = string_table
212                .get(section_name_index as usize)
213                .ok_or(DecodeError::InvalidNameOffset)?;
214
215            // Read the section offset
216            let section_offset = index_block.read_u32()?;
217
218            section_offsets.push((section_name.to_string(), section_offset as usize));
219        }
220
221        for (section_name, section_offset) in section_offsets {
222            // Seek the index to the section
223            index_block.seek(file_offset + section_offset)?;
224
225            let values_count = index_block.read_u16()? as usize;
226            let mut properties: Vec<Property> = Vec::with_capacity(values_count);
227            let mut value_offsets: Vec<(String, usize)> = Vec::with_capacity(values_count);
228
229            for _ in 0..values_count {
230                // Read the value name and get it from the string table
231                let value_name_index = index_block.read_u16()?;
232                let value_name = string_table
233                    .get(value_name_index as usize)
234                    .ok_or(DecodeError::InvalidNameOffset)?;
235
236                // Read the value offset
237                let value_offset = index_block.read_u32()?;
238                value_offsets.push((value_name.to_string(), value_offset as usize));
239            }
240
241            for (property_name, value_offset) in value_offsets {
242                // Seek the index to the value
243                index_block.seek(file_offset + section_offset + value_offset)?;
244
245                let item_count = index_block.read_u16()? as usize;
246                let mut items: Vec<Value> = Vec::with_capacity(values_count);
247
248                for _ in 0..item_count {
249                    // Read the item offset
250                    let item_offset = index_block.read_u32()?;
251
252                    // Split the type and offset
253                    let ty = (item_offset & 0xE0000000) >> 29;
254                    let item_offset = item_offset & 0x1fffffff;
255
256                    let ty =
257                        ValueType::try_from(ty as u8).map_err(|_| DecodeError::UnknownValueType)?;
258
259                    let text = match ty {
260                        ValueType::RemoveProperty => None,
261                        _ => {
262                            let text = Huffman::decode(
263                                data_block,
264                                &huffman_tree,
265                                item_offset as usize,
266                                max_value_length as usize,
267                            )?;
268
269                            Some(text)
270                        }
271                    };
272
273                    items.push(Value { ty, text });
274                }
275
276                properties.push(Property {
277                    name: property_name,
278                    values: items,
279                });
280            }
281
282            sections.push(Section {
283                name: section_name,
284                properties,
285            });
286        }
287
288        files.push(CoalFile {
289            path: file_name,
290            sections,
291        })
292    }
293
294    let coalesced = Coalesced { version, files };
295
296    Ok(coalesced)
297}
298
299pub fn deserialize_tlk(input: &[u8]) -> DecodeResult<Tlk> {
300    let mut r = ReadBuffer::new(input);
301
302    let magic = r.read_u32()?;
303
304    if magic != TLK_MAGIC {
305        return Err(DecodeError::UnknownFileMagic);
306    }
307
308    // Header block
309    let version = r.read_u32()?;
310    let min_version = r.read_u32()?;
311    let male_entry_count = r.read_u32()?;
312    let female_entry_count = r.read_u32()?;
313    let tree_node_count = r.read_u32()?;
314    let data_length = r.read_u32()?;
315
316    let mut male_refs = Vec::<(u32, u32)>::with_capacity(male_entry_count as usize);
317    let mut female_refs = Vec::<(u32, u32)>::with_capacity(female_entry_count as usize);
318
319    // Read the male refs
320    for _ in 0..male_entry_count {
321        let left = r.read_u32()?;
322        let right = r.read_u32()?;
323
324        male_refs.push((left, right));
325    }
326
327    // Read the female refs
328    for _ in 0..female_entry_count {
329        let left = r.read_u32()?;
330        let right = r.read_u32()?;
331
332        female_refs.push((left, right));
333    }
334
335    let mut huffman_tree: Vec<(i32, i32)> = Vec::with_capacity(tree_node_count as usize);
336
337    // Read the huffman tree
338    for _ in 0..tree_node_count {
339        let left = r.read_i32()?;
340        let right = r.read_i32()?;
341        huffman_tree.push((left, right))
342    }
343
344    invert_huffman_tree(&mut huffman_tree);
345
346    // Read the data block
347    let data_block: &[u8] = r.take_slice(data_length as usize)?.buffer;
348
349    let mut male_values: Vec<TlkString> = Vec::with_capacity(male_refs.len());
350    let mut female_values: Vec<TlkString> = Vec::with_capacity(female_refs.len());
351
352    // Decode the male ref values
353    for (key, offset) in male_refs {
354        let text = Huffman::decode(data_block, &huffman_tree, offset as usize, usize::MAX)?;
355        male_values.push(TlkString {
356            id: key,
357            value: text,
358        })
359    }
360
361    // Decode the female ref values
362    for (key, offset) in female_refs {
363        let text = Huffman::decode(data_block, &huffman_tree, offset as usize, usize::MAX)?;
364        female_values.push(TlkString {
365            id: key,
366            value: text,
367        })
368    }
369
370    Ok(Tlk {
371        version,
372        min_version,
373        male_values,
374        female_values,
375    })
376}