larian_formats/
lsf.rs

1use crate::{error::Result, format::Parser, reader::ReadSeek};
2use std::io::{Read, Seek};
3
4/// Reads a .lsf file with "magic number" LSOF.
5pub type Reader<R> = crate::reader::Reader<Lsf, R>;
6
7/// A successfully parsed .lsf file.
8pub struct LsfData {
9    names: Vec<Vec<String>>,
10    nodes: Vec<NodeInfo>,
11    attribute_entries: Vec<AttributeEntry>,
12    value_bytes: Vec<u8>,
13    #[allow(dead_code)]
14    key_entries: Option<Vec<KeyEntry>>,
15}
16
17#[derive(Debug, Clone, Copy)]
18enum ValueType {
19    String,
20    Int64,
21    Bool,
22}
23
24impl ValueType {
25    const fn from_u32(value: u32) -> Option<Self> {
26        match value & 0b111_111 {
27            19 => Some(Self::Bool),
28            22 | 23 => Some(Self::String),
29            32 => Some(Self::Int64),
30            _anything_else => None,
31        }
32    }
33}
34
35impl LsfData {
36    /// Prints a summary the data contained by the file to stdout.
37    pub fn print_summary(&self) {
38        let Some(mut current_node) = self.get_first() else {
39            return;
40        };
41
42        loop {
43            let NodeInfo {
44                name_index_outer,
45                name_index_inner,
46                next_sibling_index,
47                first_attribute_index,
48                ..
49            } = current_node;
50
51            let name = &self.names[*name_index_outer][*name_index_inner];
52
53            println!("{name}:");
54
55            self.print_attribute_summary(*first_attribute_index);
56
57            match u32::try_from(*next_sibling_index) {
58                Ok(value) => {
59                    current_node = &self.nodes[usize_from_u32!(value)];
60                }
61                Err(_) => {
62                    return;
63                }
64            }
65        }
66    }
67
68    fn get_first(&self) -> Option<&NodeInfo> {
69        self.nodes.iter().find(|node| node.parent_index == -1)
70    }
71
72    fn print_attribute_summary(&self, first_attribute_index: usize) {
73        let mut current_attribute = &self.attribute_entries[first_attribute_index];
74
75        loop {
76            let AttributeEntry {
77                name_index_outer,
78                name_index_inner,
79                next_index,
80                type_info,
81                offset,
82            } = current_attribute;
83
84            let name = &self.names[*name_index_outer][*name_index_inner];
85
86            print!("\t{name}: ");
87
88            if let Some(value_type) = ValueType::from_u32(*type_info) {
89                self.print_value(value_type, *offset, usize_from_u32!(type_info >> 6));
90            }
91
92            println!();
93
94            match u32::try_from(*next_index) {
95                Ok(value) => {
96                    current_attribute = &self.attribute_entries[usize_from_u32!(value)];
97                }
98                Err(_) => {
99                    return;
100                }
101            }
102        }
103    }
104
105    fn print_value(&self, value_type: ValueType, offset: usize, length: usize) {
106        let value_bytes = &self.value_bytes[offset..offset + length];
107
108        match value_type {
109            ValueType::String => {
110                let end = value_bytes
111                    .iter()
112                    .copied()
113                    .enumerate()
114                    .find_map(|(i, byte)| (byte == 0).then_some(i))
115                    .unwrap_or(value_bytes.len());
116
117                print!("\"{}\"", String::from_utf8_lossy(&value_bytes[..end]));
118            }
119            ValueType::Int64 => match value_bytes.try_into().map(i64::from_le_bytes) {
120                Ok(i) => print!("{i}"),
121                Err(_) => print!("(i64 bytes) {value_bytes:?}"),
122            },
123            ValueType::Bool => match value_bytes {
124                [0] => print!("false"),
125                [1] => print!("true"),
126                other => print!("(bool bytes) {other:?}"),
127            },
128        }
129    }
130}
131
132/// Wrapper type describing the length of data both compressed and decompressed.
133#[derive(Debug, Default, Clone)]
134pub struct CompressionMetadata<T> {
135    pub num_bytes_compressed: T,
136    pub num_bytes_decompressed: T,
137}
138
139/// Describes a given "node" of the .lsf file.
140#[derive(Debug, Clone)]
141pub struct NodeInfo {
142    /// The index corresponding to the list of names that contains the name of this node..
143    pub name_index_outer: usize,
144
145    /// The index of the name within the inner list.
146    pub name_index_inner: usize,
147
148    /// The index of the previous node in the .lsf file. The initial node will have a parent of -1.
149    pub parent_index: i32,
150
151    /// The index of the next node in the .lsf file. The final node will have -1 as the next index.
152    pub next_sibling_index: i32,
153
154    /// The index of the first attribute of the node.
155    pub first_attribute_index: usize,
156}
157
158/// Describes an "attribute" of node within an .lsf file.
159#[derive(Debug, Clone)]
160pub struct AttributeEntry {
161    /// The index corresponding to the list of names that contains the name of this node..
162    pub name_index_outer: usize,
163
164    /// The index of the name within the inner list.
165    pub name_index_inner: usize,
166
167    /// Identifies the type of data the attribute contains.
168    pub type_info: u32,
169
170    /// The index of the next attribute in the node. The final attribute will have -1 as the next
171    /// index.
172    pub next_index: i32,
173
174    /// The offset into the packed value bytes that contains the start of this attribute's value.
175    pub offset: usize,
176}
177
178/// Describes the metadata of a "key" in the .lsf file.
179#[derive(Debug, Clone)]
180pub struct KeyEntry {
181    /// The index of the node this key corresponds to.
182    pub node_index: usize,
183
184    /// The index corresponding to the list of names that contains the name of this node..
185    pub name_index_outer: usize,
186
187    /// The index of the name within the inner list.
188    pub name_index_inner: usize,
189}
190
191/// Contains the logic to parse an .lsf file.
192#[derive(Default)]
193pub struct Lsf {
194    engine_version: u64,
195    names_metadata: CompressionMetadata<u64>,
196    keys_metadata: CompressionMetadata<u64>,
197    nodes_metadata: CompressionMetadata<u64>,
198    attributes_metadata: CompressionMetadata<u64>,
199    values_metadata: CompressionMetadata<usize>,
200    compression_flag_bytes: u32,
201    extended_format: u32,
202}
203
204impl Parser for Lsf {
205    const ID_BYTES: [u8; 4] = [b'L', b'S', b'O', b'F'];
206
207    const MIN_SUPPORTED_VERSION: u32 = 7;
208
209    type Output = LsfData;
210
211    fn read(&mut self, reader: &mut ReadSeek<impl Read + Seek>) -> Result<Self::Output> {
212        self.engine_version = reader.read_u64_from_le_bytes()?;
213        self.names_metadata.num_bytes_decompressed = reader.read_u32_from_le_bytes()?.into();
214        self.names_metadata.num_bytes_compressed = reader.read_u32_from_le_bytes()?.into();
215        self.keys_metadata.num_bytes_decompressed = reader.read_u32_from_le_bytes()?.into();
216        self.keys_metadata.num_bytes_compressed = reader.read_u32_from_le_bytes()?.into();
217        self.nodes_metadata.num_bytes_decompressed = reader.read_u32_from_le_bytes()?.into();
218        self.nodes_metadata.num_bytes_compressed = reader.read_u32_from_le_bytes()?.into();
219        self.attributes_metadata.num_bytes_decompressed = reader.read_u32_from_le_bytes()?.into();
220        self.attributes_metadata.num_bytes_compressed = reader.read_u32_from_le_bytes()?.into();
221        self.values_metadata.num_bytes_decompressed = reader.read_usize_from_u32_le_bytes()?;
222        self.values_metadata.num_bytes_compressed = reader.read_usize_from_u32_le_bytes()?;
223
224        self.compression_flag_bytes = reader.read_u32_from_le_bytes()?;
225        self.extended_format = reader.read_u32_from_le_bytes()?;
226
227        let names = parse_names(reader)?;
228        let nodes = self.parse_nodes(reader)?;
229        let attribute_entries = self.parse_attribute_entries(reader)?;
230        let value_bytes = self.parse_value_bytes(reader)?;
231        let key_entries = self.parse_key_entries(reader)?;
232
233        Ok(LsfData {
234            names,
235            nodes,
236            attribute_entries,
237            value_bytes,
238            key_entries,
239        })
240    }
241}
242
243impl Lsf {
244    fn parse_nodes(&self, reader: &mut ReadSeek<impl Read + Seek>) -> Result<Vec<NodeInfo>> {
245        let starting_position = reader.stream_position()?;
246        let mut nodes = Vec::new();
247
248        while reader.stream_position()? <
249            starting_position + self.nodes_metadata.num_bytes_decompressed
250        {
251            let name_index_inner = reader.read_u16_from_le_bytes()?.into();
252            let name_index_outer = reader.read_u16_from_le_bytes()?.into();
253            let parent_index = reader.read_i32_from_le_bytes()?;
254            let next_sibling_index = reader.read_i32_from_le_bytes()?;
255            let first_attribute_index = reader.read_usize_from_u32_le_bytes()?;
256
257            nodes.push(NodeInfo {
258                name_index_outer,
259                name_index_inner,
260                parent_index,
261                next_sibling_index,
262                first_attribute_index,
263            });
264        }
265
266        Ok(nodes)
267    }
268
269    fn parse_attribute_entries(
270        &self,
271        reader: &mut ReadSeek<impl Read + Seek>,
272    ) -> Result<Vec<AttributeEntry>> {
273        let starting_position = reader.stream_position()?;
274        let mut attributes = Vec::new();
275
276        while reader.stream_position()? <
277            starting_position + self.attributes_metadata.num_bytes_decompressed
278        {
279            let name_index_inner = reader.read_u16_from_le_bytes()?.into();
280            let name_index_outer = reader.read_u16_from_le_bytes()?.into();
281            let type_info = reader.read_u32_from_le_bytes()?;
282            let next_index = reader.read_i32_from_le_bytes()?;
283            let offset = reader.read_usize_from_u32_le_bytes()?;
284
285            attributes.push(AttributeEntry {
286                name_index_outer,
287                name_index_inner,
288                type_info,
289                next_index,
290                offset,
291            });
292        }
293
294        Ok(attributes)
295    }
296
297    fn parse_value_bytes(&self, reader: &mut ReadSeek<impl Read + Seek>) -> Result<Vec<u8>> {
298        let mut bytes = vec![0_u8; self.values_metadata.num_bytes_decompressed];
299        reader.read_exact(&mut bytes)?;
300
301        Ok(bytes)
302    }
303
304    fn parse_key_entries(
305        &self,
306        reader: &mut ReadSeek<impl Read + Seek>,
307    ) -> Result<Option<Vec<KeyEntry>>> {
308        let starting_position = reader.stream_position()?;
309        let mut key_entries = None;
310
311        while reader.stream_position()? <
312            starting_position + self.keys_metadata.num_bytes_decompressed
313        {
314            let node_index = reader.read_usize_from_u32_le_bytes()?;
315            let name_index_inner = reader.read_u16_from_le_bytes()?.into();
316            let name_index_outer = reader.read_u16_from_le_bytes()?.into();
317
318            key_entries.get_or_insert_with(Vec::new).push(KeyEntry {
319                node_index,
320                name_index_outer,
321                name_index_inner,
322            });
323        }
324
325        Ok(key_entries)
326    }
327}
328
329fn parse_names(reader: &mut ReadSeek<impl Read + Seek>) -> Result<Vec<Vec<String>>> {
330    let num_entries = reader.read_usize_from_u32_le_bytes()?;
331
332    (0..num_entries)
333        .map(|_| {
334            let num_names = reader.read_u16_from_le_bytes()?;
335            (0..num_names)
336                .map(|_| {
337                    let num_bytes_in_name = usize::from(reader.read_u16_from_le_bytes()?);
338                    let mut bytes = vec![0_u8; num_bytes_in_name];
339                    reader.read_exact(&mut bytes)?;
340
341                    Ok(String::from_utf8_lossy(&bytes).into_owned())
342                })
343                .collect()
344        })
345        .collect()
346}