1use 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#[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
50const ESCAPED_MAPPING: [(char, char); 4] = [('\\', '\\'), ('n', '\n'), ('r', '\r'), ('t', '\t')];
52
53#[derive(Clone, Debug, Default, PartialEq, Eq)]
55pub struct KeyValue {
56 name: Option<String>,
57 value: Option<String>,
58 children: Vec<KeyValue>,
59}
60
61static INVALID_KEY_VALUE: OnceLock<KeyValue> = OnceLock::new();
63
64impl KeyValue {
65 pub fn new() -> KeyValue {
67 KeyValue {
68 name: None,
69 value: None,
70 children: Vec::new(),
71 }
72 }
73
74 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 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 pub fn invalid() -> &'static KeyValue {
94 INVALID_KEY_VALUE.get_or_init(KeyValue::new)
95 }
96
97 pub fn name(&self) -> Option<&str> {
99 self.name.as_deref()
100 }
101
102 pub fn set_name(&mut self, name: Option<String>) {
104 self.name = name;
105 }
106
107 pub fn value(&self) -> Option<&str> {
109 self.value.as_deref()
110 }
111
112 pub fn set_value(&mut self, value: Option<String>) {
114 self.value = value;
115 }
116
117 pub fn children(&self) -> &[KeyValue] {
119 &self.children
120 }
121
122 pub fn children_mut(&mut self) -> &mut Vec<KeyValue> {
124 &mut self.children
125 }
126
127 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 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 pub fn as_string(&self) -> Option<&str> {
160 self.value.as_deref()
161 }
162
163 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 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 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 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 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 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 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 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 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
234pub trait FromKeyValue: Sized {
237 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
298fn 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
314fn 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
323fn 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
332fn 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
341pub(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 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 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 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 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 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 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 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 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 pub fn try_read_as_binary(&mut self, input: &mut dyn Read) -> bool {
502 let mut reader = ByteReader::new(input);
503
504 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 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 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 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 fn recursive_save_binary_to_stream_core(
584 &self,
585 stream: &mut dyn Write,
586 ) -> Result<(), SteamError> {
587 match self.value.as_deref() {
588 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 fn recursive_save_text_to_stream(
613 &self,
614 stream: &mut dyn Write,
615 indent_level: usize,
616 ) -> Result<(), SteamError> {
617 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 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 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
652fn 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
663fn 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
669fn 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
687fn 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
694fn malformed(message: &str) -> SteamError {
696 SteamError::Malformed(message.to_string())
697}
698
699fn read_null_term_string(reader: &mut ByteReader<'_>) -> std::io::Result<String> {
701 reader.read_null_term_string()
702}
703
704fn 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 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 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
778struct 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 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
880fn unexpected_eof() -> std::io::Error {
882 std::io::Error::new(
883 std::io::ErrorKind::UnexpectedEof,
884 "unexpected end of stream",
885 )
886}
887
888const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
890
891const UTF16_LE_BOM: [u8; 2] = [0xFF, 0xFE];
893
894const UTF16_BE_BOM: [u8; 2] = [0xFE, 0xFF];
896
897const REPLACEMENT_CHARACTER: char = '\u{FFFD}';
899
900#[derive(Clone, Copy)]
902enum Utf16Endian {
903 Little,
904 Big,
905}
906
907fn 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
926fn 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
977struct KvTextReader {
979 chars: Vec<char>,
980 position: usize,
981}
982
983impl KvTextReader {
984 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 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 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 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 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}