Created
July 23, 2015 14:38
-
-
Save lolzballs/d8ad24ed17a381d9de71 to your computer and use it in GitHub Desktop.
Very rough parser for Steam's appinfo.vdf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdint> | |
#include <boost/any.hpp> | |
#include <string> | |
#include <unordered_map> | |
#include <fstream> | |
#include <iostream> | |
#define MAGIC_VALUE 0x07564426 | |
uint32_t read32_le(std::istream& stream) | |
{ | |
char b[4]; | |
stream.read(b, 4); | |
return static_cast<uint32_t>((b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)); | |
} | |
uint64_t read64_le(std::istream& stream) | |
{ | |
char b[8]; | |
stream.read(b, 8); | |
return static_cast<uint64_t>( | |
(b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24) | | |
((uint64_t)b[4] << 32) | ((uint64_t)b[5] << 40) | | |
((uint64_t)b[6] << 48) | ((uint64_t)b[7] << 56)); | |
} | |
std::string readString(std::istream& stream) | |
{ | |
std::string str; | |
std::getline(stream, str, '\0'); | |
return str; | |
} | |
struct Section | |
{ | |
std::string name; | |
std::unordered_map<std::string, boost::any> kv; | |
template <typename Type> | |
Type get(std::string key) | |
{ | |
try | |
{ | |
return boost::any_cast<Type>(kv[key]); | |
} | |
catch (const boost::bad_any_cast&) | |
{ | |
std::cerr << "Cannot cast value at " << key << " to the wanted type." << std::endl; | |
return {0}; | |
} | |
} | |
}; | |
struct Game | |
{ | |
uint32_t appID; | |
uint32_t size; | |
uint32_t infoState; // 1-unavailable, 2-available | |
uint32_t lastUpdated; | |
uint64_t accessToken; | |
uint8_t sha[20]; | |
uint32_t changeNumber; | |
std::unordered_map<std::string, Section> sections; | |
}; | |
Section parseSection(std::istream& input, bool root, std::string name) | |
{ | |
Section section; | |
section.name = name; | |
// std::cout << "Parsing section: " << section.name << std::endl; | |
while (true) | |
{ | |
uint8_t valueType = input.get(); | |
if (valueType == 0x08) | |
{ | |
// std::cout << "Done" << std::endl; | |
if (root) | |
{ | |
input.get(); | |
// std::cout << "Done root section" << std::endl; | |
} | |
break; | |
} | |
std::string key = readString(input); | |
boost::any value; | |
switch(valueType) | |
{ | |
case 0x00: // None | |
{ | |
value = parseSection(input, false, key); | |
break; | |
} | |
case 0x01: // string | |
{ | |
value = readString(input); | |
break; | |
} | |
case 0x02: // int | |
{ | |
value = read32_le(input); | |
break; | |
} | |
case 0x03: // float | |
{ | |
std::cerr << "I CANT PARSE FLOAT YOU CUNTBAG" << std::endl; | |
break; | |
} | |
case 0x04: // ptr | |
{ | |
std::cerr << "CAN YOU JUST... NOT DO POINTERS" << std::endl; | |
break; | |
} | |
case 0x05: // Wide string | |
{ | |
std::cerr << "What the fuck is a wide string m8" << std::endl; | |
break; | |
} | |
case 0x06: // Colour | |
{ | |
std::cerr << "WHY THE FUCK CAN'T YOU JUST USE A FUCKING FUCK" << std::endl; | |
break; | |
} | |
case 0x07: // 64 bit int | |
{ | |
value = read64_le(input); | |
break; | |
} | |
default: | |
std::cerr << "I don't know what this is" << (int) valueType << std::endl; | |
} | |
section.kv.insert(std::make_pair(key, value)); | |
} | |
return section; | |
} | |
Game parseGame(std::istream& input) | |
{ | |
Game game; | |
game.appID = read32_le(input); | |
if (game.appID == 0) | |
{ | |
return game; | |
} | |
game.size = read32_le(input); | |
game.infoState = read32_le(input); | |
game.lastUpdated = read32_le(input); | |
game.accessToken = read64_le(input); | |
for (int i = 0; i < 20; i++) | |
{ | |
game.sha[i] = input.get(); | |
} | |
game.changeNumber = read32_le(input); | |
// std::cout << "Parsing sections of: " << game.appID << std::endl; | |
while (true) | |
{ | |
uint8_t sectionID = input.get(); | |
if (sectionID == 0x00) | |
{ | |
// std::cout << "Done parsing game" << std::endl; | |
break; | |
} | |
input.get(); // read the 00 before name | |
std::string name = readString(input); | |
Section section = parseSection(input, true, name); | |
game.sections.insert(std::make_pair(name, section)); | |
} | |
return game; | |
} | |
int main() | |
{ | |
std::ifstream input("./appinfo.vdf", std::ifstream::binary); | |
uint32_t magic = read32_le(input); | |
uint32_t universe = read32_le(input); // Parse this later | |
if (magic != MAGIC_VALUE) | |
{ | |
std::cerr << "Invalid magic value!" << std::endl; | |
return 1; | |
} | |
while (!input.eof()) | |
{ | |
Game game = parseGame(input); | |
if (game.appID == 0) | |
{ | |
std::cout << "Finished" << std::endl; | |
break; | |
} | |
std::cout << game.sections["common"].get<std::string>("name") << std::endl; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment