1use std::env;
2use std::path::Path;
3use steam_vdf_parser::{binary, parse_binary, parse_text};
4
5fn dump_value(value: &steam_vdf_parser::Value, indent: usize) -> String {
6 let indent_str = " ".repeat(indent);
7 match value {
8 steam_vdf_parser::Value::Str(s) => format!("{}\"{}\"", indent_str, s),
9 steam_vdf_parser::Value::I32(n) => format!("{}{}", indent_str, n),
10 steam_vdf_parser::Value::U64(n) => format!("{}{}", indent_str, n),
11 steam_vdf_parser::Value::Float(n) => format!("{}{}", indent_str, n),
12 steam_vdf_parser::Value::Pointer(n) => format!("{}(pointer: {})", indent_str, n),
13 steam_vdf_parser::Value::Color(c) => format!(
14 "{}(color: #{:02x}{:02x}{:02x}{:02x})",
15 indent_str, c[0], c[1], c[2], c[3]
16 ),
17 steam_vdf_parser::Value::Obj(obj) => {
18 let mut out = format!("{}{{\n", indent_str);
19 for (k, v) in obj.iter() {
20 out.push_str(&format!("{}\"\"{}\"\": ", indent_str, k));
21 match v {
22 steam_vdf_parser::Value::Obj(_) => {
23 out.push_str(&dump_value(v, indent + 1));
24 }
25 steam_vdf_parser::Value::Str(s) => out.push_str(&format!("\"{}\"\n", s)),
26 steam_vdf_parser::Value::I32(n) => out.push_str(&format!("{}\n", n)),
27 steam_vdf_parser::Value::U64(n) => out.push_str(&format!("{}\n", n)),
28 steam_vdf_parser::Value::Float(n) => out.push_str(&format!("{}\n", n)),
29 steam_vdf_parser::Value::Pointer(n) => {
30 out.push_str(&format!("(pointer: {})\n", n))
31 }
32 steam_vdf_parser::Value::Color(c) => out.push_str(&format!(
33 "(color: #{:02x}{:02x}{:02x}{:02x})\n",
34 c[0], c[1], c[2], c[3]
35 )),
36 }
37 }
38 out.push_str(&format!("{}}}\n", indent_str));
39 out
40 }
41 }
42}
43
44fn main() {
45 let args: Vec<String> = env::args().collect();
46 if args.len() < 2 {
47 eprintln!("Usage: {} <vdf_file> [--text]", args[0]);
48 eprintln!(
49 " --text: force text format parsing (default: auto-detect based on file extension)"
50 );
51 std::process::exit(1);
52 }
53
54 let path = Path::new(&args[1]);
55 let force_text = args.len() > 2 && args[2] == "--text";
56
57 let result = if force_text || path.extension().is_some_and(|e| e == "vdf") {
58 let content = std::fs::read_to_string(path);
60 if let Ok(content) = content {
61 parse_text(&content).map(|v| v.into_owned())
62 } else {
63 let data = std::fs::read(path).expect("Failed to read file");
65 if path
66 .file_name()
67 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("packageinfo")))
68 {
69 binary::parse_packageinfo(&data).map(|v| v.into_owned())
70 } else if path
71 .file_name()
72 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("appinfo")))
73 {
74 binary::parse_appinfo(&data).map(|v| v.into_owned())
75 } else {
76 parse_binary(&data).map(|v| v.into_owned())
77 }
78 }
79 } else {
80 let data = std::fs::read(path).expect("Failed to read file");
82 if path
83 .file_name()
84 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("packageinfo")))
85 {
86 binary::parse_packageinfo(&data).map(|v| v.into_owned())
87 } else if path
88 .file_name()
89 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("appinfo")))
90 {
91 binary::parse_appinfo(&data).map(|v| v.into_owned())
92 } else {
93 parse_binary(&data).map(|v| v.into_owned())
94 }
95 };
96
97 match result {
98 Ok(vdf) => {
99 println!("\"{}\" {}", vdf.key(), dump_value(vdf.value(), 0));
100 }
101 Err(e) => {
102 eprintln!("Error parsing VDF: {:?}", e);
103 std::process::exit(1);
104 }
105 }
106}