Skip to content

Instantly share code, notes, and snippets.

@lolzballs
Created July 23, 2015 14:38
Show Gist options
  • Save lolzballs/d8ad24ed17a381d9de71 to your computer and use it in GitHub Desktop.
Save lolzballs/d8ad24ed17a381d9de71 to your computer and use it in GitHub Desktop.
Very rough parser for Steam's appinfo.vdf
#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