appid_to_name/
appid_to_name.rs

1// Example: Extract AppId to game name mapping from Steam's appinfo.vdf
2//
3// Usage:
4//   cargo run --example appid_to_name -- path/to/appinfo.vdf
5//
6// The appinfo.vdf file is typically located at:
7//   - Windows: C:\Program Files (x86)\Steam\appcache\appinfo.vdf
8//   - Linux: ~/.steam/steam/appcache/appinfo.vdf
9//   - macOS: ~/Library/Application Support/Steam/appcache/appinfo.vdf
10
11use std::env;
12use std::fs;
13use std::process::ExitCode;
14
15use steam_vdf_parser::parse_appinfo;
16
17fn main() -> ExitCode {
18    let args: Vec<String> = env::args().collect();
19
20    if args.len() < 2 {
21        eprintln!("Usage: {} <path/to/appinfo.vdf>", args[0]);
22        eprintln!();
23        eprintln!("Extracts AppId to game name mappings from Steam's appinfo.vdf file.");
24        eprintln!();
25        eprintln!("The appinfo.vdf file is typically located at:");
26        eprintln!("  Windows: C:\\Program Files (x86)\\Steam\\appcache\\appinfo.vdf");
27        eprintln!("  Linux:   ~/.steam/steam/appcache/appinfo.vdf");
28        eprintln!("  macOS:   ~/Library/Application Support/Steam/appcache/appinfo.vdf");
29        return ExitCode::FAILURE;
30    }
31
32    let path = &args[1];
33
34    // Read the file
35    let data = match fs::read(path) {
36        Ok(data) => data,
37        Err(e) => {
38            eprintln!("Error reading file '{}': {}", path, e);
39            return ExitCode::FAILURE;
40        }
41    };
42
43    // Parse the appinfo.vdf file
44    let vdf = match parse_appinfo(&data) {
45        Ok(vdf) => vdf.into_owned(),
46        Err(e) => {
47            eprintln!("Error parsing appinfo.vdf: {}", e);
48            return ExitCode::FAILURE;
49        }
50    };
51
52    // Get the root object containing all apps
53    let root = match vdf.as_obj() {
54        Some(obj) => obj,
55        None => {
56            eprintln!("Error: root is not an object");
57            return ExitCode::FAILURE;
58        }
59    };
60
61    // Iterate through all apps (keyed by AppID as string)
62    let mut apps = Vec::new();
63
64    for (app_id_str, app_value) in root.iter() {
65        // Skip non-numeric keys (metadata entries)
66        if app_id_str.parse::<u32>().is_err() {
67            continue;
68        }
69
70        let app_obj = match app_value.as_obj() {
71            Some(obj) => obj,
72            None => continue,
73        };
74
75        // Navigate the nested structure: appinfo -> common -> name
76        let name = app_obj
77            .get("appinfo")
78            .and_then(|v| v.as_obj())
79            .and_then(|appinfo| appinfo.get("common"))
80            .and_then(|common| common.as_obj())
81            .and_then(|common| common.get("name"))
82            .and_then(|v| v.as_str());
83
84        if let (Some(name), Ok(app_id)) = (name, app_id_str.parse::<u32>()) {
85            apps.push((app_id, name.to_string()));
86        }
87    }
88
89    // Sort by AppID
90    apps.sort_by_key(|(id, _)| *id);
91
92    // Print the results
93    println!("AppId\tName");
94    println!("------\t{}", "-".repeat(80));
95    for (app_id, name) in &apps {
96        println!("{}\t{}", app_id, name);
97    }
98
99    println!();
100    println!("Total games: {}", apps.len());
101
102    ExitCode::SUCCESS
103}