pub fn parse_appinfo(input: &[u8]) -> Result<Vdf<'_>>Expand description
Parse appinfo.vdf format binary data.
This function returns zero-copy data where possible - strings are borrowed from the input buffer (including string table entries in v41 format).
Format:
- 4 bytes: Magic number (0x07564428 or 0x07564429)
- 4 bytes: Universe
- If magic == 0x07564429: 8 bytes: String table offset
- Apps continue until EOF (or string table for v41)
- For each app:
- 4 bytes: App ID
- 4 bytes: Size (remaining data size for this entry)
- 4 bytes: InfoState
- 4 bytes: LastUpdated (Unix timestamp)
- 8 bytes: AccessToken
- 20 bytes: SHA1 of text data
- 4 bytes: ChangeNumber
- 20 bytes: SHA1 of binary data
- Then the VDF data for the app (starts with 0x00)
- String table (if magic == 0x07564429, at string_table_offset)
App entry header is APPINFO_ENTRY_HEADER_SIZE (68) bytes.
Examples found in repository?
examples/verify_compactified.rs (line 26)
6fn main() {
7 let args: Vec<String> = env::args().collect();
8
9 if args.len() < 2 {
10 eprintln!("Usage: {} <vdf_file>", args[0]);
11 std::process::exit(1);
12 }
13
14 let path = &args[1];
15
16 // Read the file
17 let data = match std::fs::read(path) {
18 Ok(d) => d,
19 Err(e) => {
20 eprintln!("Error reading file: {}", e);
21 std::process::exit(1);
22 }
23 };
24
25 // Parse it
26 match parse_appinfo(&data) {
27 Ok(vdf) => {
28 println!("Successfully parsed {}", path);
29 println!("Root key: {}", vdf.key());
30 if let Some(obj) = vdf.as_obj() {
31 println!("Number of apps: {}", obj.len());
32 for (key, _) in obj.iter().take(5) {
33 println!(" - app_id: {}", key);
34 }
35 if obj.len() > 5 {
36 println!(" ... and {} more", obj.len() - 5);
37 }
38 }
39 }
40 Err(e) => {
41 eprintln!("Error parsing file: {:?}", e);
42 std::process::exit(1);
43 }
44 }
45}More examples
examples/dump_vdf.rs (line 74)
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 // Try text first for .vdf files
59 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 // Fall back to binary
64 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 // Binary parsing
81 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}