Skip to main content

steamkit/types/
key_value.rs

1//! `KeyValue`: Valve's key-value format, in both the binary (VDF1) form used
2//! for app info, depots and published files, and the text (VDF2) form.
3//!
4//! The binary reader matches SteamKit2's byte for byte, quirks included: only
5//! the first top-level entry is kept, and the text reader works the same way.
6
7use std::fmt;
8use std::io::{Cursor, Read, Write};
9use std::path::Path;
10use std::sync::OnceLock;
11
12use crate::base::generated::steam_language::EDepotFileFlag;
13use crate::error::SteamError;
14
15/// The value types a `KeyValue` can hold on the wire.
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17enum KeyValueType {
18    None = 0,
19    String = 1,
20    Int32 = 2,
21    Float32 = 3,
22    Pointer = 4,
23    WideString = 5,
24    Color = 6,
25    UInt64 = 7,
26    End = 8,
27    Int64 = 10,
28    AlternateEnd = 11,
29}
30
31impl KeyValueType {
32    fn from_byte(value: u8) -> Option<KeyValueType> {
33        match value {
34            0 => Some(KeyValueType::None),
35            1 => Some(KeyValueType::String),
36            2 => Some(KeyValueType::Int32),
37            3 => Some(KeyValueType::Float32),
38            4 => Some(KeyValueType::Pointer),
39            5 => Some(KeyValueType::WideString),
40            6 => Some(KeyValueType::Color),
41            7 => Some(KeyValueType::UInt64),
42            8 => Some(KeyValueType::End),
43            10 => Some(KeyValueType::Int64),
44            11 => Some(KeyValueType::AlternateEnd),
45            _ => None,
46        }
47    }
48}
49
50/// The escape sequences of the text format.
51const ESCAPED_MAPPING: [(char, char); 4] = [('\\', '\\'), ('n', '\n'), ('r', '\r'), ('t', '\t')];
52
53/// A tree of string keys to values or child nodes.
54#[derive(Clone, Debug, Default, PartialEq, Eq)]
55pub struct KeyValue {
56    name: Option<String>,
57    value: Option<String>,
58    children: Vec<KeyValue>,
59}
60
61/// The shared "invalid" node returned when a child doesn't exist.
62static INVALID_KEY_VALUE: OnceLock<KeyValue> = OnceLock::new();
63
64impl KeyValue {
65    /// Creates an empty node.
66    pub fn new() -> KeyValue {
67        KeyValue {
68            name: None,
69            value: None,
70            children: Vec::new(),
71        }
72    }
73
74    /// Creates a node with a name.
75    pub fn with_name(name: impl Into<String>) -> KeyValue {
76        KeyValue {
77            name: Some(name.into()),
78            value: None,
79            children: Vec::new(),
80        }
81    }
82
83    /// Creates a node with a name and a value.
84    pub fn with_name_and_value(name: impl Into<String>, value: impl Into<String>) -> KeyValue {
85        KeyValue {
86            name: Some(name.into()),
87            value: Some(value.into()),
88            children: Vec::new(),
89        }
90    }
91
92    /// The node returned when a looked-up child doesn't exist.
93    pub fn invalid() -> &'static KeyValue {
94        INVALID_KEY_VALUE.get_or_init(KeyValue::new)
95    }
96
97    /// The name of this node.
98    pub fn name(&self) -> Option<&str> {
99        self.name.as_deref()
100    }
101
102    /// Sets the name of this node.
103    pub fn set_name(&mut self, name: Option<String>) {
104        self.name = name;
105    }
106
107    /// The value of this node.
108    pub fn value(&self) -> Option<&str> {
109        self.value.as_deref()
110    }
111
112    /// Sets the value of this node.
113    pub fn set_value(&mut self, value: Option<String>) {
114        self.value = value;
115    }
116
117    /// The children of this node.
118    pub fn children(&self) -> &[KeyValue] {
119        &self.children
120    }
121
122    /// The children of this node, for modification.
123    pub fn children_mut(&mut self) -> &mut Vec<KeyValue> {
124        &mut self.children
125    }
126
127    /// The child with the given key (ordinal, case-insensitive), or
128    /// [`KeyValue::invalid`] if there's none.
129    pub fn get(&self, key: &str) -> &KeyValue {
130        self.children
131            .iter()
132            .find(|child| {
133                child
134                    .name
135                    .as_deref()
136                    .is_some_and(|name| ordinal_ignore_case_eq(name, key))
137            })
138            .unwrap_or_else(|| KeyValue::invalid())
139    }
140
141    /// Sets the child with the given key: removes any existing child with that
142    /// key, renames `value` to `key` and appends it.
143    pub fn set(&mut self, key: &str, value: KeyValue) {
144        if let Some(index) = self.children.iter().position(|child| {
145            child
146                .name
147                .as_deref()
148                .is_some_and(|name| ordinal_ignore_case_eq(name, key))
149        }) {
150            self.children.remove(index);
151        }
152
153        let mut value = value;
154        value.name = Some(key.to_string());
155        self.children.push(value);
156    }
157
158    /// The value as a string.
159    pub fn as_string(&self) -> Option<&str> {
160        self.value.as_deref()
161    }
162
163    /// The value as a `u8`, or `default_value` if it isn't one.
164    pub fn as_unsigned_byte(&self, default_value: u8) -> u8 {
165        parse_integer(self.value.as_deref().unwrap_or(""))
166            .and_then(|value| u8::try_from(value).ok())
167            .unwrap_or(default_value)
168    }
169
170    /// The value as a `u16`, or `default_value` if it isn't one.
171    pub fn as_unsigned_short(&self, default_value: u16) -> u16 {
172        parse_integer(self.value.as_deref().unwrap_or(""))
173            .and_then(|value| u16::try_from(value).ok())
174            .unwrap_or(default_value)
175    }
176
177    /// The value as an `i32`, or `default_value` if it isn't one.
178    pub fn as_integer(&self, default_value: i32) -> i32 {
179        parse_integer(self.value.as_deref().unwrap_or(""))
180            .and_then(|value| i32::try_from(value).ok())
181            .unwrap_or(default_value)
182    }
183
184    /// The value as a `u32`, or `default_value` if it isn't one.
185    pub fn as_unsigned_integer(&self, default_value: u32) -> u32 {
186        parse_integer(self.value.as_deref().unwrap_or(""))
187            .and_then(|value| u32::try_from(value).ok())
188            .unwrap_or(default_value)
189    }
190
191    /// The value as an `i64`, or `default_value` if it isn't one.
192    pub fn as_long(&self, default_value: i64) -> i64 {
193        parse_integer(self.value.as_deref().unwrap_or("")).unwrap_or(default_value)
194    }
195
196    /// The value as a `u64`, or `default_value` if it isn't one.
197    pub fn as_unsigned_long(&self, default_value: u64) -> u64 {
198        parse_unsigned_integer(self.value.as_deref().unwrap_or("")).unwrap_or(default_value)
199    }
200
201    /// The value as an `f32`, or `default_value` if it isn't one.
202    pub fn as_float(&self, default_value: f32) -> f32 {
203        parse_float(self.value.as_deref().unwrap_or("")).unwrap_or(default_value)
204    }
205
206    /// The value as a boolean, or `default_value` if it isn't one.
207    pub fn as_boolean(&self, default_value: bool) -> bool {
208        match parse_integer(self.value.as_deref().unwrap_or("")) {
209            Some(value) => value != 0,
210            None => default_value,
211        }
212    }
213
214    /// The value as an enum, or `default_value` if it isn't one.
215    pub fn as_enum<T: FromKeyValue>(&self, default_value: T) -> T {
216        self.value
217            .as_deref()
218            .and_then(T::from_key_value)
219            .unwrap_or(default_value)
220    }
221}
222
223impl fmt::Display for KeyValue {
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        write!(
226            f,
227            "{} = {}",
228            self.name.as_deref().unwrap_or(""),
229            self.value.as_deref().unwrap_or("")
230        )
231    }
232}
233
234/// Types that can be parsed from a `KeyValue` string. Enums accept the member
235/// name or its numeric value.
236pub trait FromKeyValue: Sized {
237    /// Parses `text`, or `None` if it isn't a valid value.
238    fn from_key_value(text: &str) -> Option<Self>;
239}
240
241macro_rules! impl_from_key_value_for_integer {
242    ($($type:ty),*) => {
243        $(
244            impl FromKeyValue for $type {
245                fn from_key_value(text: &str) -> Option<Self> {
246                    parse_unsigned_integer(text).and_then(|value| <$type>::try_from(value).ok())
247                }
248            }
249        )*
250    };
251}
252
253impl_from_key_value_for_integer!(u8, u16, u32, u64, usize);
254
255impl FromKeyValue for i32 {
256    fn from_key_value(text: &str) -> Option<Self> {
257        parse_integer(text).and_then(|value| i32::try_from(value).ok())
258    }
259}
260
261impl FromKeyValue for i64 {
262    fn from_key_value(text: &str) -> Option<Self> {
263        parse_integer(text)
264    }
265}
266
267impl FromKeyValue for f32 {
268    fn from_key_value(text: &str) -> Option<Self> {
269        parse_float(text)
270    }
271}
272
273impl FromKeyValue for bool {
274    fn from_key_value(text: &str) -> Option<Self> {
275        parse_integer(text).map(|value| value != 0)
276    }
277}
278
279impl FromKeyValue for EDepotFileFlag {
280    fn from_key_value(text: &str) -> Option<Self> {
281        let trimmed = text.trim();
282
283        for flag in EDepotFileFlag::ALL {
284            if flag_name(*flag).eq_ignore_ascii_case(trimmed) {
285                return Some(*flag);
286            }
287        }
288
289        let numeric = parse_unsigned_integer(trimmed)?;
290        if numeric > i32::MAX as u64 {
291            return None;
292        }
293
294        EDepotFileFlag::try_from(numeric as i32).ok()
295    }
296}
297
298/// The name of an `EDepotFileFlag` variant.
299fn flag_name(flag: EDepotFileFlag) -> &'static str {
300    match flag {
301        EDepotFileFlag::UserConfig => "UserConfig",
302        EDepotFileFlag::VersionedUserConfig => "VersionedUserConfig",
303        EDepotFileFlag::Encrypted => "Encrypted",
304        EDepotFileFlag::ReadOnly => "ReadOnly",
305        EDepotFileFlag::Hidden => "Hidden",
306        EDepotFileFlag::Executable => "Executable",
307        EDepotFileFlag::Directory => "Directory",
308        EDepotFileFlag::CustomExecutable => "CustomExecutable",
309        EDepotFileFlag::InstallScript => "InstallScript",
310        EDepotFileFlag::Symlink => "Symlink",
311    }
312}
313
314/// Parses an integer: optional surrounding whitespace and an optional sign.
315fn parse_integer(text: &str) -> Option<i64> {
316    let text = text.trim();
317    if text.is_empty() {
318        return None;
319    }
320    text.parse::<i64>().ok()
321}
322
323/// Parses an unsigned integer, with the same rules as [`parse_integer`].
324fn parse_unsigned_integer(text: &str) -> Option<u64> {
325    let text = text.trim();
326    if text.is_empty() {
327        return None;
328    }
329    text.parse::<u64>().ok()
330}
331
332/// Parses a float (invariant culture, thousands separators allowed).
333fn parse_float(text: &str) -> Option<f32> {
334    let text = text.trim();
335    if text.is_empty() {
336        return None;
337    }
338    text.replace(',', "").parse::<f32>().ok()
339}
340
341/// Ordinal, case-insensitive string equality.
342pub(crate) fn ordinal_ignore_case_eq(left: &str, right: &str) -> bool {
343    let mut left_chars = left.chars();
344    let mut right_chars = right.chars();
345
346    loop {
347        match (left_chars.next(), right_chars.next()) {
348            (None, None) => return true,
349            (Some(left_char), Some(right_char)) => {
350                if left_char == right_char {
351                    continue;
352                }
353
354                let left_upper = left_char.to_uppercase().next().unwrap_or(left_char);
355                let right_upper = right_char.to_uppercase().next().unwrap_or(right_char);
356                if left_upper != right_upper {
357                    return false;
358                }
359            }
360            _ => return false,
361        }
362    }
363}
364
365impl KeyValue {
366    /// Loads a text `KeyValue` from a file. `None` on any error.
367    pub async fn load_as_text(path: impl AsRef<Path>) -> Option<KeyValue> {
368        let bytes = tokio::fs::read(path).await.ok()?;
369        let mut key_value = KeyValue::new();
370        let mut cursor = Cursor::new(bytes);
371
372        match key_value.read_as_text(&mut cursor) {
373            Ok(()) => Some(key_value),
374            Err(_) => None,
375        }
376    }
377
378    /// Loads a binary `KeyValue` from a file. `None` on any error.
379    pub async fn try_load_as_binary(path: impl AsRef<Path>) -> Option<KeyValue> {
380        let bytes = tokio::fs::read(path).await.ok()?;
381        let mut key_value = KeyValue::new();
382        let mut cursor = Cursor::new(bytes);
383
384        if key_value.try_read_as_binary(&mut cursor) {
385            Some(key_value)
386        } else {
387            None
388        }
389    }
390
391    /// Parses a text `KeyValue` from a string. `None` on any error.
392    pub fn load_from_string(input: &str) -> Option<KeyValue> {
393        let mut key_value = KeyValue::new();
394        let mut cursor = Cursor::new(input.as_bytes());
395
396        match key_value.read_as_text(&mut cursor) {
397            Ok(()) => Some(key_value),
398            Err(_) => None,
399        }
400    }
401
402    /// Reads this node from a text `KeyValue` stream.
403    ///
404    /// A byte order mark is detected and stripped, so UTF-8 (`EF BB BF`),
405    /// UTF-16 LE (`FF FE`) and UTF-16 BE (`FE FF`) documents all work. Without
406    /// one, the bytes are read as UTF-8.
407    pub fn read_as_text(&mut self, input: &mut dyn Read) -> Result<(), SteamError> {
408        let mut bytes = Vec::new();
409        input.read_to_end(&mut bytes)?;
410
411        self.children.clear();
412
413        let mut reader = KvTextReader::new(&bytes)?;
414        let mut is_first_root = true;
415
416        loop {
417            let Some((token, _, _)) = reader.read_token() else {
418                break;
419            };
420
421            if token.is_empty() {
422                break;
423            }
424
425            // Only the first root object is kept; any further roots are parsed
426            // and thrown away.
427            let mut throwaway = KeyValue::new();
428
429            let target: &mut KeyValue = if is_first_root {
430                is_first_root = false;
431                self
432            } else {
433                &mut throwaway
434            };
435
436            target.name = Some(token);
437
438            let (mut value, mut was_quoted, was_conditional) = reader
439                .read_token()
440                .ok_or_else(|| malformed("LoadFromBuffer: missing {"))?;
441
442            if was_conditional {
443                let token = reader
444                    .read_token()
445                    .ok_or_else(|| malformed("LoadFromBuffer: missing {"))?;
446                value = token.0;
447                was_quoted = token.1;
448            }
449
450            if !(value.starts_with('{') && !was_quoted) {
451                return Err(malformed("LoadFromBuffer: missing {"));
452            }
453
454            target.recursive_load_from_buffer(&mut reader)?;
455
456            if reader.end_of_stream() {
457                break;
458            }
459        }
460
461        Ok(())
462    }
463
464    /// Reads this node from a text `KeyValue` file. The encoding is detected
465    /// like in [`KeyValue::read_as_text`].
466    pub async fn read_file_as_text(&mut self, filename: impl AsRef<Path>) -> Result<(), SteamError> {
467        let bytes = tokio::fs::read(filename).await?;
468        let mut cursor = Cursor::new(bytes);
469        self.read_as_text(&mut cursor)
470    }
471
472    /// Saves this node to a file.
473    pub async fn save_to_file(
474        &self,
475        path: impl AsRef<Path>,
476        as_binary: bool,
477    ) -> Result<(), SteamError> {
478        let mut buffer = Vec::new();
479        self.save_to_stream(&mut buffer, as_binary)?;
480        tokio::fs::write(path, buffer).await?;
481        Ok(())
482    }
483
484    /// Saves this node to a stream.
485    pub fn save_to_stream(
486        &self,
487        stream: &mut dyn Write,
488        as_binary: bool,
489    ) -> Result<(), SteamError> {
490        if as_binary {
491            self.recursive_save_binary_to_stream(stream)?;
492        } else {
493            self.recursive_save_text_to_stream(stream, 0)?;
494        }
495
496        Ok(())
497    }
498
499    /// Reads this node from a binary `KeyValue` stream. Returns `false` on
500    /// failure.
501    pub fn try_read_as_binary(&mut self, input: &mut dyn Read) -> bool {
502        let mut reader = ByteReader::new(input);
503
504        // Only the children are replaced, so the node's own name and value
505        // survive a parse that yields no entry.
506        let carrier = KeyValue {
507            name: self.name.clone(),
508            value: self.value.clone(),
509            children: Vec::new(),
510        };
511
512        match try_read_as_binary_core(&mut reader, carrier, None) {
513            Ok(filled) => {
514                *self = filled;
515                true
516            }
517            Err(_) => false,
518        }
519    }
520
521    /// Reads the children of this node from the text reader.
522    fn recursive_load_from_buffer(&mut self, reader: &mut KvTextReader) -> Result<(), SteamError> {
523        loop {
524            let Some((name, was_quoted, _was_conditional)) = reader.read_token() else {
525                return Err(malformed("RecursiveLoadFromBuffer: got EOF or empty keyname"));
526            };
527
528            if name.is_empty() {
529                return Err(malformed("RecursiveLoadFromBuffer: got EOF or empty keyname"));
530            }
531
532            // Top level closed, stop reading.
533            if name.starts_with('}') && !was_quoted {
534                break;
535            }
536
537            let mut child = KeyValue::new();
538            child.name = Some(name);
539
540            let (mut value, mut was_quoted, mut was_conditional) = reader
541                .read_token()
542                .ok_or_else(|| malformed("RecursiveLoadFromBuffer:  got NULL key"))?;
543
544            if was_conditional {
545                let token = reader
546                    .read_token()
547                    .ok_or_else(|| malformed("RecursiveLoadFromBuffer:  got NULL key"))?;
548                value = token.0;
549                was_quoted = token.1;
550                was_conditional = token.2;
551            }
552
553            if value.starts_with('}') && !was_quoted {
554                return Err(malformed("RecursiveLoadFromBuffer:  got } in key"));
555            }
556
557            if value.starts_with('{') && !was_quoted {
558                child.recursive_load_from_buffer(reader)?;
559            } else {
560                if was_conditional {
561                    return Err(malformed(
562                        "RecursiveLoadFromBuffer:  got conditional between key and value",
563                    ));
564                }
565
566                child.value = Some(value);
567            }
568
569            self.children.push(child);
570        }
571
572        Ok(())
573    }
574
575    /// Writes this node in the binary format, with the end marker.
576    fn recursive_save_binary_to_stream(&self, stream: &mut dyn Write) -> Result<(), SteamError> {
577        self.recursive_save_binary_to_stream_core(stream)?;
578        stream.write_all(&[KeyValueType::End as u8])?;
579        Ok(())
580    }
581
582    /// Writes this node in the binary format.
583    fn recursive_save_binary_to_stream_core(
584        &self,
585        stream: &mut dyn Write,
586    ) -> Result<(), SteamError> {
587        match self.value.as_deref() {
588            // Only these are supported for now:
589            // 1. a node with children (and no value)
590            // 2. a string value
591            None => {
592                stream.write_all(&[KeyValueType::None as u8])?;
593                write_null_term_string(stream, &self.name_for_serialization()?)?;
594
595                for child in &self.children {
596                    child.recursive_save_binary_to_stream_core(stream)?;
597                }
598
599                stream.write_all(&[KeyValueType::End as u8])?;
600            }
601            Some(value) => {
602                stream.write_all(&[KeyValueType::String as u8])?;
603                write_null_term_string(stream, &self.name_for_serialization()?)?;
604                write_null_term_string(stream, value)?;
605            }
606        }
607
608        Ok(())
609    }
610
611    /// Writes this node in the text format.
612    fn recursive_save_text_to_stream(
613        &self,
614        stream: &mut dyn Write,
615        indent_level: usize,
616    ) -> Result<(), SteamError> {
617        // Header
618        write_indents(stream, indent_level)?;
619        write_string(stream, &self.name_for_serialization()?, true)?;
620        write_string(stream, "\n", false)?;
621        write_indents(stream, indent_level)?;
622        write_string(stream, "{\n", false)?;
623
624        // Write every child.
625        for child in &self.children {
626            match child.value.as_deref() {
627                None => child.recursive_save_text_to_stream(stream, indent_level + 1)?,
628                Some(value) => {
629                    write_indents(stream, indent_level + 1)?;
630                    write_string(stream, &child.name_for_serialization()?, true)?;
631                    write_string(stream, "\t\t", false)?;
632                    write_string(stream, &escape_text(value), true)?;
633                    write_string(stream, "\n", false)?;
634                }
635            }
636        }
637
638        write_indents(stream, indent_level)?;
639        write_string(stream, "}\n", false)?;
640
641        Ok(())
642    }
643
644    /// The name to serialize, or an error if the node has none.
645    fn name_for_serialization(&self) -> Result<&str, SteamError> {
646        self.name.as_deref().ok_or_else(|| {
647            SteamError::invalid_argument("Cannot serialise a KeyValue object with a null name!")
648        })
649    }
650}
651
652/// Escapes the special characters of the text format.
653fn escape_text(value: &str) -> String {
654    let mut escaped = value.to_string();
655
656    for (key, replacement) in ESCAPED_MAPPING {
657        escaped = escaped.replace(replacement, &format!("\\{}", key));
658    }
659
660    escaped
661}
662
663/// Writes `indent_level` tabs.
664fn write_indents(stream: &mut dyn Write, indent_level: usize) -> Result<(), SteamError> {
665    let indents = "\t".repeat(indent_level);
666    write_string(stream, &indents, false)
667}
668
669/// Writes a string, optionally quoted.
670fn write_string(stream: &mut dyn Write, value: &str, quote: bool) -> Result<(), SteamError> {
671    let mut buffer = String::with_capacity(value.len() + 2);
672
673    if quote {
674        buffer.push('"');
675    }
676
677    buffer.push_str(&value.replace('"', "\\\""));
678
679    if quote {
680        buffer.push('"');
681    }
682
683    stream.write_all(buffer.as_bytes())?;
684    Ok(())
685}
686
687/// Writes a null-terminated UTF-8 string.
688fn write_null_term_string(stream: &mut dyn Write, value: &str) -> Result<(), SteamError> {
689    stream.write_all(value.as_bytes())?;
690    stream.write_all(&[0])?;
691    Ok(())
692}
693
694/// Builds a malformed data error.
695fn malformed(message: &str) -> SteamError {
696    SteamError::Malformed(message.to_string())
697}
698
699/// Reads a null-terminated UTF-8 string.
700fn read_null_term_string(reader: &mut ByteReader<'_>) -> std::io::Result<String> {
701    reader.read_null_term_string()
702}
703
704/// Reads binary `KeyValue` entries. Each filled entry is moved into the parent's
705/// children; without a parent, the first entry is kept.
706fn try_read_as_binary_core(
707    reader: &mut ByteReader<'_>,
708    mut current: KeyValue,
709    mut parent: Option<&mut Vec<KeyValue>>,
710) -> Result<KeyValue, ()> {
711    current.children.clear();
712
713    let mut first_entry: Option<KeyValue> = None;
714
715    loop {
716        let Some(type_byte) = reader.read_byte().ok().flatten() else {
717            // End of stream doesn't match any type, so the parse fails.
718            return Err(());
719        };
720
721        let Some(value_type) = KeyValueType::from_byte(type_byte) else {
722            return Err(());
723        };
724
725        if value_type == KeyValueType::End || value_type == KeyValueType::AlternateEnd {
726            break;
727        }
728
729        current.name = Some(read_null_term_string(reader).map_err(|_| ())?);
730
731        match value_type {
732            KeyValueType::None => {
733                let _child =
734                    try_read_as_binary_core(reader, KeyValue::new(), Some(&mut current.children))?;
735            }
736            KeyValueType::String => {
737                current.value = Some(read_null_term_string(reader).map_err(|_| ())?);
738            }
739            KeyValueType::WideString => {
740                tracing::warn!(
741                    "Encountered WideString type when parsing binary KeyValue, which is unsupported. Returning false."
742                );
743                return Err(());
744            }
745            KeyValueType::Int32 | KeyValueType::Color | KeyValueType::Pointer => {
746                current.value = Some(reader.read_i32().map_err(|_| ())?.to_string());
747            }
748            KeyValueType::UInt64 => {
749                current.value = Some(reader.read_u64().map_err(|_| ())?.to_string());
750            }
751            KeyValueType::Float32 => {
752                current.value = Some(reader.read_f32().map_err(|_| ())?.to_string());
753            }
754            KeyValueType::Int64 => {
755                current.value = Some(reader.read_i64().map_err(|_| ())?.to_string());
756            }
757            KeyValueType::End | KeyValueType::AlternateEnd => break,
758        }
759
760        match parent.as_deref_mut() {
761            Some(parent_children) => {
762                parent_children.push(current);
763                current = KeyValue::new();
764            }
765            None => {
766                // Without a parent, keep the first entry and drop any later
767                // top-level ones.
768                if first_entry.is_none() {
769                    first_entry = Some(std::mem::replace(&mut current, KeyValue::new()));
770                }
771            }
772        }
773    }
774
775    Ok(first_entry.unwrap_or(current))
776}
777
778/// A small buffered reader for the primitives of the binary format.
779struct ByteReader<'a> {
780    inner: &'a mut dyn Read,
781    buffer: Vec<u8>,
782    position: usize,
783}
784
785impl<'a> ByteReader<'a> {
786    fn new(inner: &'a mut dyn Read) -> ByteReader<'a> {
787        ByteReader {
788            inner,
789            buffer: Vec::new(),
790            position: 0,
791        }
792    }
793
794    /// Makes sure at least one byte is buffered. Returns `false` at the end of
795    /// the stream.
796    fn fill(&mut self) -> std::io::Result<bool> {
797        if self.position < self.buffer.len() {
798            return Ok(true);
799        }
800
801        self.buffer.clear();
802        self.position = 0;
803
804        let mut chunk = [0u8; 4096];
805        let read = self.inner.read(&mut chunk)?;
806        if read == 0 {
807            return Ok(false);
808        }
809
810        self.buffer.extend_from_slice(&chunk[..read]);
811        Ok(true)
812    }
813
814    fn read_byte(&mut self) -> std::io::Result<Option<u8>> {
815        if !self.fill()? {
816            return Ok(None);
817        }
818
819        let byte = self.buffer[self.position];
820        self.position += 1;
821        Ok(Some(byte))
822    }
823
824    fn read_exact_bytes(&mut self, count: usize) -> std::io::Result<Vec<u8>> {
825        let mut bytes = Vec::with_capacity(count);
826
827        while bytes.len() < count {
828            if !self.fill()? {
829                break;
830            }
831
832            let available = self.buffer.len() - self.position;
833            let take = available.min(count - bytes.len());
834            bytes.extend_from_slice(&self.buffer[self.position..self.position + take]);
835            self.position += take;
836        }
837
838        Ok(bytes)
839    }
840
841    fn read_null_term_string(&mut self) -> std::io::Result<String> {
842        let mut bytes = Vec::new();
843
844        while let Some(byte) = self.read_byte()? {
845            if byte == 0 {
846                break;
847            }
848
849            bytes.push(byte);
850        }
851
852        Ok(String::from_utf8_lossy(&bytes).into_owned())
853    }
854
855    fn read_u64(&mut self) -> std::io::Result<u64> {
856        let bytes = self.read_exact_bytes(8)?;
857        let array: [u8; 8] = bytes.try_into().map_err(|_| unexpected_eof())?;
858        Ok(u64::from_le_bytes(array))
859    }
860
861    fn read_i32(&mut self) -> std::io::Result<i32> {
862        let bytes = self.read_exact_bytes(4)?;
863        let array: [u8; 4] = bytes.try_into().map_err(|_| unexpected_eof())?;
864        Ok(i32::from_le_bytes(array))
865    }
866
867    fn read_i64(&mut self) -> std::io::Result<i64> {
868        let bytes = self.read_exact_bytes(8)?;
869        let array: [u8; 8] = bytes.try_into().map_err(|_| unexpected_eof())?;
870        Ok(i64::from_le_bytes(array))
871    }
872
873    fn read_f32(&mut self) -> std::io::Result<f32> {
874        let bytes = self.read_exact_bytes(4)?;
875        let array: [u8; 4] = bytes.try_into().map_err(|_| unexpected_eof())?;
876        Ok(f32::from_le_bytes(array))
877    }
878}
879
880/// Builds an "unexpected end of stream" error.
881fn unexpected_eof() -> std::io::Error {
882    std::io::Error::new(
883        std::io::ErrorKind::UnexpectedEof,
884        "unexpected end of stream",
885    )
886}
887
888/// The UTF-8 byte order mark.
889const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
890
891/// The UTF-16 little endian byte order mark.
892const UTF16_LE_BOM: [u8; 2] = [0xFF, 0xFE];
893
894/// The UTF-16 big endian byte order mark.
895const UTF16_BE_BOM: [u8; 2] = [0xFE, 0xFF];
896
897/// What malformed input decodes to.
898const REPLACEMENT_CHARACTER: char = '\u{FFFD}';
899
900/// The byte order of a UTF-16 encoded document.
901#[derive(Clone, Copy)]
902enum Utf16Endian {
903    Little,
904    Big,
905}
906
907/// Detects a byte order mark, strips it and decodes the document: UTF-8
908/// (`EF BB BF`), UTF-16 LE (`FF FE`) or UTF-16 BE (`FE FF`). Without one, it's
909/// read as UTF-8.
910fn decode_text(bytes: &[u8]) -> Result<String, SteamError> {
911    if bytes.starts_with(&UTF8_BOM) {
912        return Ok(String::from_utf8_lossy(&bytes[UTF8_BOM.len()..]).into_owned());
913    }
914
915    if bytes.starts_with(&UTF16_LE_BOM) {
916        return decode_utf16(&bytes[UTF16_LE_BOM.len()..], Utf16Endian::Little);
917    }
918
919    if bytes.starts_with(&UTF16_BE_BOM) {
920        return decode_utf16(&bytes[UTF16_BE_BOM.len()..], Utf16Endian::Big);
921    }
922
923    Ok(String::from_utf8_lossy(bytes).into_owned())
924}
925
926/// Decodes UTF-16. Unpaired surrogates become U+FFFD; a trailing half code unit
927/// is an error.
928fn decode_utf16(bytes: &[u8], endian: Utf16Endian) -> Result<String, SteamError> {
929    if bytes.len() % 2 != 0 {
930        return Err(malformed(
931            "ReadAsText: the utf-16 document ends with an incomplete code unit",
932        ));
933    }
934
935    let units: Vec<u16> = bytes
936        .chunks_exact(2)
937        .map(|pair| match endian {
938            Utf16Endian::Little => u16::from_le_bytes([pair[0], pair[1]]),
939            Utf16Endian::Big => u16::from_be_bytes([pair[0], pair[1]]),
940        })
941        .collect();
942
943    let mut text = String::with_capacity(units.len());
944    let mut index = 0;
945
946    while index < units.len() {
947        let unit = units[index];
948
949        if !(0xD800..=0xDBFF).contains(&unit) {
950            if (0xDC00..=0xDFFF).contains(&unit) {
951                text.push(REPLACEMENT_CHARACTER);
952            } else {
953                text.push(char::from_u32(u32::from(unit)).unwrap_or(REPLACEMENT_CHARACTER));
954            }
955
956            index += 1;
957            continue;
958        }
959
960        match units.get(index + 1) {
961            Some(&low) if (0xDC00..=0xDFFF).contains(&low) => {
962                let code =
963                    0x1_0000 + (u32::from(unit) - 0xD800) * 0x400 + (u32::from(low) - 0xDC00);
964                text.push(char::from_u32(code).unwrap_or(REPLACEMENT_CHARACTER));
965                index += 2;
966            }
967            _ => {
968                text.push(REPLACEMENT_CHARACTER);
969                index += 1;
970            }
971        }
972    }
973
974    Ok(text)
975}
976
977/// Reader for the text format.
978struct KvTextReader {
979    chars: Vec<char>,
980    position: usize,
981}
982
983impl KvTextReader {
984    /// Creates a reader over the given bytes, detecting the encoding (see
985    /// [`decode_text`]).
986    fn new(bytes: &[u8]) -> Result<KvTextReader, SteamError> {
987        let text = decode_text(bytes)?;
988
989        Ok(KvTextReader {
990            chars: text.chars().collect(),
991            position: 0,
992        })
993    }
994
995    fn end_of_stream(&self) -> bool {
996        self.position >= self.chars.len()
997    }
998
999    fn peek(&self) -> Option<char> {
1000        self.chars.get(self.position).copied()
1001    }
1002
1003    fn read_char(&mut self) -> Option<char> {
1004        let ch = self.peek();
1005        if ch.is_some() {
1006            self.position += 1;
1007        }
1008        ch
1009    }
1010
1011    fn read_line(&mut self) {
1012        while let Some(ch) = self.read_char() {
1013            if ch == '\n' {
1014                break;
1015            }
1016        }
1017    }
1018
1019    fn eat_white_space(&mut self) {
1020        while !self.end_of_stream() {
1021            match self.peek() {
1022                Some(ch) if ch.is_whitespace() => {
1023                    self.position += 1;
1024                }
1025                _ => break,
1026            }
1027        }
1028    }
1029
1030    /// A single forward slash comments out the rest of the line.
1031    fn eat_cpp_comment(&mut self) -> bool {
1032        if !self.end_of_stream() && self.peek() == Some('/') {
1033            self.read_line();
1034            return true;
1035        }
1036
1037        false
1038    }
1039
1040    /// Reads the next token. Returns the token, whether it was quoted and
1041    /// whether it was a conditional.
1042    fn read_token(&mut self) -> Option<(String, bool, bool)> {
1043        let mut was_quoted = false;
1044        let mut was_conditional = false;
1045
1046        loop {
1047            self.eat_white_space();
1048
1049            if self.end_of_stream() {
1050                return None;
1051            }
1052
1053            if !self.eat_cpp_comment() {
1054                break;
1055            }
1056        }
1057
1058        if self.end_of_stream() {
1059            return None;
1060        }
1061
1062        let next = self.peek()?;
1063
1064        if next == '"' {
1065            was_quoted = true;
1066
1067            // "
1068            self.read_char();
1069
1070            let mut token = String::new();
1071            while !self.end_of_stream() {
1072                if self.peek() == Some('\\') {
1073                    self.read_char();
1074
1075                    let escaped = self.read_char();
1076                    match escaped {
1077                        Some('\\') => token.push('\\'),
1078                        Some('n') => token.push('\n'),
1079                        Some('r') => token.push('\r'),
1080                        Some('t') => token.push('\t'),
1081                        Some(other) => token.push(other),
1082                        None => break,
1083                    }
1084
1085                    continue;
1086                }
1087
1088                if self.peek() == Some('"') {
1089                    break;
1090                }
1091
1092                match self.read_char() {
1093                    Some(ch) => token.push(ch),
1094                    None => break,
1095                }
1096            }
1097
1098            // "
1099            self.read_char();
1100
1101            return Some((token, was_quoted, was_conditional));
1102        }
1103
1104        if next == '{' || next == '}' {
1105            self.read_char();
1106            return Some((next.to_string(), was_quoted, was_conditional));
1107        }
1108
1109        let mut conditional_start = false;
1110        let mut token = String::new();
1111
1112        while !self.end_of_stream() {
1113            let next = self.peek()?;
1114
1115            if next == '"' || next == '{' || next == '}' {
1116                break;
1117            }
1118
1119            if next == '[' {
1120                conditional_start = true;
1121            }
1122
1123            if next == ']' && conditional_start {
1124                was_conditional = true;
1125            }
1126
1127            if next.is_whitespace() {
1128                break;
1129            }
1130
1131            token.push(next);
1132            self.read_char();
1133        }
1134
1135        Some((token, was_quoted, was_conditional))
1136    }
1137}