Skip to content

Commit

Permalink
fix: added support for new appinfo-vdf version
Browse files Browse the repository at this point in the history
  • Loading branch information
Tormak9970 committed Sep 23, 2024
1 parent 84957ed commit 8447fe1
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 24 deletions.
60 changes: 46 additions & 14 deletions src-tauri/src/appinfo_vdf_parser.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::i64;
use std::{path::PathBuf, fs};
use std::io::Read;

Expand Down Expand Up @@ -26,13 +27,29 @@ fn read(reader: &mut Reader) -> Map<String, Value> {
let _universe = reader.read_uint32(true); //always 1

let entries: Vec<Value>;
let mut strings: Vec<String> = vec![];

if magic == 0x07564428 {
entries = read_app_sections(reader, 64);
} else if magic == 0x07564427 {
entries = read_app_sections(reader, 44);
if magic == 0x07564429 {
let string_table_offset = reader.read_int64(true);
let data_offset = reader.get_offset();

reader.seek(string_table_offset.try_into().expect("String table offset couldn't be converted to usize"), 0);

let string_count = reader.read_uint32(true);

for _i in 0..string_count {
let string = reader.read_string(None);

strings.push(string);
}

reader.seek(data_offset, 0);

entries = read_app_sections(reader, string_table_offset, Some(magic), &Some(&mut strings));
} else if magic == 0x07564428 {
entries = read_app_sections(reader, i64::MAX, None, &None);
} else {
panic!("Magic header is unknown. Expected 0x07564428 or 0x07564427 but got {magic}");
panic!("Magic header is unknown. Expected 0x07564428 or 0x07564429 but got {magic}");
}

let mut res: Map<String, Value> = Map::new();
Expand All @@ -42,19 +59,34 @@ fn read(reader: &mut Reader) -> Map<String, Value> {
}

/// Reads the appinfo.vdf app sections to a JSON array.
fn read_app_sections(reader: &mut Reader, skip: u8) -> Vec<Value> {
fn read_app_sections(reader: &mut Reader, string_table_offset: i64, magic: Option<u32>, strings: &Option<&mut Vec<String>>) -> Vec<Value> {
let mut entries: Vec<Value> = vec![];
let mut id: u32 = reader.read_uint32(true);

while id != 0x00000000 {
reader.seek(skip.into(), 1); // Skip a bunch of fields we don't care about
let eof: usize = (string_table_offset - 4).try_into().unwrap();

while id != 0x00000000 && reader.get_offset() < eof {
let size = reader.read_uint32(true);
let offset = reader.get_offset();

let _null_prefix = reader.read_uint8(true);
let name: String = reader.read_string(None).to_owned();
let data_end: usize = size.try_into().unwrap();

// let _state = reader.read_uint32(true);
// let _last_updated = reader.read_uint32(true);
// let _access_token = reader.read_uint64(true);
// reader.seek(20, 1);
// let _version_number = reader.read_uint32(true);
// reader.seek(20, 1);
reader.seek(60, 1);

let mut entry: Map<String, Value> = read_entry_map(reader);
entry.insert(String::from("name"), Value::String(name));
entry.insert(String::from("id"), Value::Number(id.into()));
let mut entry: Map<String, Value> = read_entry_map(reader, magic, strings);

if entry.contains_key("appinfo") {
let appinfo_val: &Value = entry.get("appinfo").expect("Should have been able to get \"appinfo\".");
let appinfo = appinfo_val.as_object().expect("appinfo should have been an object.");

entry = appinfo.clone();
}

if entry.contains_key("common") {
let common_val: &Value = entry.get("common").expect("Should have been able to get \"common\".");
Expand All @@ -68,7 +100,7 @@ fn read_app_sections(reader: &mut Reader, skip: u8) -> Vec<Value> {
}
}

reader.seek(1, 1);
reader.seek(offset + data_end, 0);
id = reader.read_uint32(true);
}

Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/shortcuts_vdf_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn read(reader: &mut Reader) -> Value {
panic!("Invalid Shortcuts File! File started with {} instead of \"shortcuts\"", fake_header);
}

return Value::Object(read_entry_map(reader));
return Value::Object(read_entry_map(reader, None, &None));
}

/// Writes the shortcuts.vdf file from JSON.
Expand Down
24 changes: 18 additions & 6 deletions src-tauri/src/vdf_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@ use serde_json::{Value, Map};

use crate::reader::Reader;

/// Reads a vdf entry string to JSON.
pub fn read_vdf_string(reader: &mut Reader, magic: Option<u32>, strings: &Option<&mut Vec<String>>) -> String {
if magic.is_some() && magic.unwrap() == 0x07564429 {
let index: usize = reader.read_uint32(true).try_into().unwrap();
let string_pool = strings.as_ref().unwrap();
let string = &string_pool[index];

return string.to_owned();
} else {
return reader.read_string(None);
}
}

/// Reads a vdf entry map to JSON.
pub fn read_entry_map(reader: &mut Reader) -> Map<String, Value> {
pub fn read_entry_map(reader: &mut Reader, magic: Option<u32>, strings: &Option<&mut Vec<String>>) -> Map<String, Value> {
let mut props = Map::new();

let mut field_type = reader.read_uint8(true);

while field_type != 0x08 {
let key = reader.read_string(None);
let value = read_entry_field(reader, field_type);
let key = read_vdf_string(reader, magic, strings);
let value = read_entry_field(reader, field_type, magic, strings);

props.insert(key, value);

Expand All @@ -21,10 +33,10 @@ pub fn read_entry_map(reader: &mut Reader) -> Map<String, Value> {
}

/// Reads a vdf entry field to JSON.
pub fn read_entry_field(reader: &mut Reader, field_type: u8) -> Value {
pub fn read_entry_field(reader: &mut Reader, field_type: u8, magic: Option<u32>, strings: &Option<&mut Vec<String>>) -> Value {
match field_type {
0x00 => { //? map
return Value::Object(read_entry_map(reader));
return Value::Object(read_entry_map(reader, magic, strings));
},
0x01 => { //? string
let value = reader.read_string(None);
Expand Down
5 changes: 2 additions & 3 deletions src/lib/controllers/SteamController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,8 @@ export class SteamController {

return vdf.entries.map((game: any) => {
return {
"appid": game.id,
// eslint-disable-next-line no-control-regex
"name": typeof game.common.name === "string" ? game.common.name.replace(/[^\x00-\x7F]/g, "") : game.common.name.toString()
appid: game.appid,
name: typeof game.common.name === "string" ? game.common.name.replace(/[^\x00-\x7F]/g, "") : game.common.name.toString()
};
}).sort((gameA: GameStruct, gameB: GameStruct) => gameA.name.localeCompare(gameB.name));
}
Expand Down

0 comments on commit 8447fe1

Please sign in to comment.