This page hosts a formal specification of .pak file format of Games based on Haxe Game Framework "Heaps" (e.g. Dead Cells) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.
For C++, the easiest way is to clone the runtime library sources and build them along with your project.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
std::istream
). One can open local file for that, or use existing std::string
or char*
buffer.
#include <fstream>
std::ifstream is("path/to/local/file.pak", std::ifstream::binary);
#include <sstream>
std::istringstream is(str);
#include <sstream>
const char buf[] = { ... };
std::string str(buf, sizeof buf);
std::istringstream is(str);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
heaps_pak_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.header() // => get header
#pragma once
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#include <vector>
#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
/**
* \sa https://github.com/HeapsIO/heaps/blob/2bbc2b386952dfd8856c04a854bb706a52cb4b58/hxd/fmt/pak/Reader.hx Source
*/
class heaps_pak_t : public kaitai::kstruct {
public:
class header_t;
heaps_pak_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, heaps_pak_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~heaps_pak_t();
class header_t : public kaitai::kstruct {
public:
class entry_t;
class file_t;
class dir_t;
header_t(kaitai::kstream* p__io, heaps_pak_t* p__parent = nullptr, heaps_pak_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~header_t();
/**
* \sa https://github.com/HeapsIO/heaps/blob/2bbc2b386952dfd8856c04a854bb706a52cb4b58/hxd/fmt/pak/Data.hx Source
*/
class entry_t : public kaitai::kstruct {
public:
class flags_t;
entry_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, heaps_pak_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~entry_t();
class flags_t : public kaitai::kstruct {
public:
flags_t(kaitai::kstream* p__io, heaps_pak_t::header_t::entry_t* p__parent = nullptr, heaps_pak_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~flags_t();
private:
uint64_t m_unused;
bool m_is_dir;
heaps_pak_t* m__root;
heaps_pak_t::header_t::entry_t* m__parent;
public:
uint64_t unused() const { return m_unused; }
bool is_dir() const { return m_is_dir; }
heaps_pak_t* _root() const { return m__root; }
heaps_pak_t::header_t::entry_t* _parent() const { return m__parent; }
};
private:
uint8_t m_len_name;
std::string m_name;
std::unique_ptr<flags_t> m_flags;
std::unique_ptr<kaitai::kstruct> m_body;
bool n_body;
public:
bool _is_null_body() { body(); return n_body; };
private:
heaps_pak_t* m__root;
kaitai::kstruct* m__parent;
public:
uint8_t len_name() const { return m_len_name; }
std::string name() const { return m_name; }
flags_t* flags() const { return m_flags.get(); }
kaitai::kstruct* body() const { return m_body.get(); }
heaps_pak_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
class file_t : public kaitai::kstruct {
public:
file_t(kaitai::kstream* p__io, heaps_pak_t::header_t::entry_t* p__parent = nullptr, heaps_pak_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~file_t();
private:
bool f_data;
std::string m_data;
public:
std::string data();
private:
uint32_t m_ofs_data;
uint32_t m_len_data;
std::string m_checksum;
heaps_pak_t* m__root;
heaps_pak_t::header_t::entry_t* m__parent;
public:
uint32_t ofs_data() const { return m_ofs_data; }
uint32_t len_data() const { return m_len_data; }
std::string checksum() const { return m_checksum; }
heaps_pak_t* _root() const { return m__root; }
heaps_pak_t::header_t::entry_t* _parent() const { return m__parent; }
};
class dir_t : public kaitai::kstruct {
public:
dir_t(kaitai::kstream* p__io, heaps_pak_t::header_t::entry_t* p__parent = nullptr, heaps_pak_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~dir_t();
private:
uint32_t m_num_entries;
std::unique_ptr<std::vector<std::unique_ptr<entry_t>>> m_entries;
heaps_pak_t* m__root;
heaps_pak_t::header_t::entry_t* m__parent;
public:
uint32_t num_entries() const { return m_num_entries; }
std::vector<std::unique_ptr<entry_t>>* entries() const { return m_entries.get(); }
heaps_pak_t* _root() const { return m__root; }
heaps_pak_t::header_t::entry_t* _parent() const { return m__parent; }
};
private:
std::string m_magic1;
uint8_t m_version;
uint32_t m_len_header;
uint32_t m_len_data;
std::unique_ptr<entry_t> m_root_entry;
std::string m_magic2;
heaps_pak_t* m__root;
heaps_pak_t* m__parent;
std::string m__raw_root_entry;
std::unique_ptr<kaitai::kstream> m__io__raw_root_entry;
public:
std::string magic1() const { return m_magic1; }
uint8_t version() const { return m_version; }
uint32_t len_header() const { return m_len_header; }
uint32_t len_data() const { return m_len_data; }
entry_t* root_entry() const { return m_root_entry.get(); }
std::string magic2() const { return m_magic2; }
heaps_pak_t* _root() const { return m__root; }
heaps_pak_t* _parent() const { return m__parent; }
std::string _raw_root_entry() const { return m__raw_root_entry; }
kaitai::kstream* _io__raw_root_entry() const { return m__io__raw_root_entry.get(); }
};
private:
std::unique_ptr<header_t> m_header;
heaps_pak_t* m__root;
kaitai::kstruct* m__parent;
public:
header_t* header() const { return m_header.get(); }
heaps_pak_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "heaps_pak.h"
#include "kaitai/exceptions.h"
heaps_pak_t::heaps_pak_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, heaps_pak_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_header = nullptr;
_read();
}
void heaps_pak_t::_read() {
m_header = std::unique_ptr<header_t>(new header_t(m__io, this, m__root));
}
heaps_pak_t::~heaps_pak_t() {
_clean_up();
}
void heaps_pak_t::_clean_up() {
}
heaps_pak_t::header_t::header_t(kaitai::kstream* p__io, heaps_pak_t* p__parent, heaps_pak_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_root_entry = nullptr;
m__io__raw_root_entry = nullptr;
_read();
}
void heaps_pak_t::header_t::_read() {
m_magic1 = m__io->read_bytes(3);
if (!(magic1() == std::string("\x50\x41\x4B", 3))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x50\x41\x4B", 3), magic1(), _io(), std::string("/types/header/seq/0"));
}
m_version = m__io->read_u1();
m_len_header = m__io->read_u4le();
m_len_data = m__io->read_u4le();
m__raw_root_entry = m__io->read_bytes((len_header() - 16));
m__io__raw_root_entry = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_root_entry));
m_root_entry = std::unique_ptr<entry_t>(new entry_t(m__io__raw_root_entry.get(), this, m__root));
m_magic2 = m__io->read_bytes(4);
if (!(magic2() == std::string("\x44\x41\x54\x41", 4))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x44\x41\x54\x41", 4), magic2(), _io(), std::string("/types/header/seq/5"));
}
}
heaps_pak_t::header_t::~header_t() {
_clean_up();
}
void heaps_pak_t::header_t::_clean_up() {
}
heaps_pak_t::header_t::entry_t::entry_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, heaps_pak_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_flags = nullptr;
_read();
}
void heaps_pak_t::header_t::entry_t::_read() {
m_len_name = m__io->read_u1();
m_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(len_name()), std::string("UTF-8"));
m_flags = std::unique_ptr<flags_t>(new flags_t(m__io, this, m__root));
n_body = true;
{
bool on = flags()->is_dir();
if (on == true) {
n_body = false;
m_body = std::unique_ptr<dir_t>(new dir_t(m__io, this, m__root));
}
else if (on == false) {
n_body = false;
m_body = std::unique_ptr<file_t>(new file_t(m__io, this, m__root));
}
}
}
heaps_pak_t::header_t::entry_t::~entry_t() {
_clean_up();
}
void heaps_pak_t::header_t::entry_t::_clean_up() {
if (!n_body) {
}
}
heaps_pak_t::header_t::entry_t::flags_t::flags_t(kaitai::kstream* p__io, heaps_pak_t::header_t::entry_t* p__parent, heaps_pak_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void heaps_pak_t::header_t::entry_t::flags_t::_read() {
m_unused = m__io->read_bits_int_be(7);
m_is_dir = m__io->read_bits_int_be(1);
}
heaps_pak_t::header_t::entry_t::flags_t::~flags_t() {
_clean_up();
}
void heaps_pak_t::header_t::entry_t::flags_t::_clean_up() {
}
heaps_pak_t::header_t::file_t::file_t(kaitai::kstream* p__io, heaps_pak_t::header_t::entry_t* p__parent, heaps_pak_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
f_data = false;
_read();
}
void heaps_pak_t::header_t::file_t::_read() {
m_ofs_data = m__io->read_u4le();
m_len_data = m__io->read_u4le();
m_checksum = m__io->read_bytes(4);
}
heaps_pak_t::header_t::file_t::~file_t() {
_clean_up();
}
void heaps_pak_t::header_t::file_t::_clean_up() {
if (f_data) {
}
}
std::string heaps_pak_t::header_t::file_t::data() {
if (f_data)
return m_data;
kaitai::kstream *io = _root()->_io();
std::streampos _pos = io->pos();
io->seek((_root()->header()->len_header() + ofs_data()));
m_data = io->read_bytes(len_data());
io->seek(_pos);
f_data = true;
return m_data;
}
heaps_pak_t::header_t::dir_t::dir_t(kaitai::kstream* p__io, heaps_pak_t::header_t::entry_t* p__parent, heaps_pak_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_entries = nullptr;
_read();
}
void heaps_pak_t::header_t::dir_t::_read() {
m_num_entries = m__io->read_u4le();
m_entries = std::unique_ptr<std::vector<std::unique_ptr<entry_t>>>(new std::vector<std::unique_ptr<entry_t>>());
const int l_entries = num_entries();
for (int i = 0; i < l_entries; i++) {
m_entries->push_back(std::move(std::unique_ptr<entry_t>(new entry_t(m__io, this, m__root))));
}
}
heaps_pak_t::header_t::dir_t::~dir_t() {
_clean_up();
}
void heaps_pak_t::header_t::dir_t::_clean_up() {
}