From Wikipedia:
"XAR (short for eXtensible ARchive format) is an open source file archiver and the archiver's file format. It was created within the OpenDarwin project and is used in macOS X 10.5 and up for software installation routines, as well as browser extensions in Safari 5.0 and up."
This page hosts a formal specification of XAR (eXtensible ARchive) 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.xar", 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);
xar_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.header_prefix() // => internal; access `_root.header` instead
#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>
#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
/**
* From [Wikipedia](https://en.wikipedia.org/wiki/Xar_(archiver)):
*
* "XAR (short for eXtensible ARchive format) is an open source file archiver
* and the archiver's file format. It was created within the OpenDarwin project
* and is used in macOS X 10.5 and up for software installation routines, as
* well as browser extensions in Safari 5.0 and up."
* \sa https://github.com/mackyle/xar/wiki/xarformat Source
*/
class xar_t : public kaitai::kstruct {
public:
class file_header_prefix_t;
class file_header_t;
class toc_type_t;
enum checksum_algorithms_apple_t {
CHECKSUM_ALGORITHMS_APPLE_NONE = 0,
CHECKSUM_ALGORITHMS_APPLE_SHA1 = 1,
CHECKSUM_ALGORITHMS_APPLE_MD5 = 2,
CHECKSUM_ALGORITHMS_APPLE_SHA256 = 3,
CHECKSUM_ALGORITHMS_APPLE_SHA512 = 4
};
xar_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, xar_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~xar_t();
class file_header_prefix_t : public kaitai::kstruct {
public:
file_header_prefix_t(kaitai::kstream* p__io, xar_t* p__parent = nullptr, xar_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~file_header_prefix_t();
private:
std::string m_magic;
uint16_t m_len_header;
xar_t* m__root;
xar_t* m__parent;
public:
std::string magic() const { return m_magic; }
/**
* internal; access `_root.header.len_header` instead
*/
uint16_t len_header() const { return m_len_header; }
xar_t* _root() const { return m__root; }
xar_t* _parent() const { return m__parent; }
};
class file_header_t : public kaitai::kstruct {
public:
file_header_t(kaitai::kstream* p__io, xar_t* p__parent = nullptr, xar_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~file_header_t();
private:
bool f_checksum_algorithm_name;
std::string m_checksum_algorithm_name;
public:
/**
* If it is not
*
* * `""` (empty string), indicating an unknown integer value (access
* `checksum_algorithm_int` for debugging purposes to find out
* what that value is), or
* * `"none"`, indicating that the TOC checksum is not provided (in that
* case, the `<checksum>` property or its `style` attribute should be
* missing, or the `style` attribute must be set to `"none"`),
*
* it must exactly match the `style` attribute value of the
* `<checksum>` property in the root node `<toc>`. See
* <https://github.com/mackyle/xar/blob/66d451d/xar/lib/archive.c#L345-L371>
* for reference.
*
* The `xar` (eXtensible ARchiver) program [uses OpenSSL's function
* `EVP_get_digestbyname`](
* https://github.com/mackyle/xar/blob/66d451d/xar/lib/archive.c#L328
* ) to verify this value (if it's not `""` or `"none"`, of course).
* So it's reasonable to assume that this can only have one of the values
* that OpenSSL recognizes.
*/
std::string checksum_algorithm_name();
private:
bool f_has_checksum_alg_name;
bool m_has_checksum_alg_name;
public:
bool has_checksum_alg_name();
private:
bool f_len_header;
uint16_t m_len_header;
public:
uint16_t len_header();
private:
uint16_t m_version;
uint64_t m_len_toc_compressed;
uint64_t m_toc_length_uncompressed;
uint32_t m_checksum_algorithm_int;
std::string m_checksum_alg_name;
bool n_checksum_alg_name;
public:
bool _is_null_checksum_alg_name() { checksum_alg_name(); return n_checksum_alg_name; };
private:
xar_t* m__root;
xar_t* m__parent;
public:
uint16_t version() const { return m_version; }
uint64_t len_toc_compressed() const { return m_len_toc_compressed; }
uint64_t toc_length_uncompressed() const { return m_toc_length_uncompressed; }
/**
* internal; access `checksum_algorithm_name` instead
*/
uint32_t checksum_algorithm_int() const { return m_checksum_algorithm_int; }
/**
* internal; access `checksum_algorithm_name` instead
*/
std::string checksum_alg_name() const { return m_checksum_alg_name; }
xar_t* _root() const { return m__root; }
xar_t* _parent() const { return m__parent; }
};
class toc_type_t : public kaitai::kstruct {
public:
toc_type_t(kaitai::kstream* p__io, xar_t* p__parent = nullptr, xar_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~toc_type_t();
private:
std::string m_xml_string;
xar_t* m__root;
xar_t* m__parent;
public:
std::string xml_string() const { return m_xml_string; }
xar_t* _root() const { return m__root; }
xar_t* _parent() const { return m__parent; }
};
private:
bool f_checksum_algorithm_other;
int8_t m_checksum_algorithm_other;
public:
/**
* \sa https://github.com/mackyle/xar/blob/66d451d/xar/include/xar.h.in#L85 Source
*/
int8_t checksum_algorithm_other();
private:
std::unique_ptr<file_header_prefix_t> m_header_prefix;
std::unique_ptr<file_header_t> m_header;
std::unique_ptr<toc_type_t> m_toc;
xar_t* m__root;
kaitai::kstruct* m__parent;
std::string m__raw_header;
std::unique_ptr<kaitai::kstream> m__io__raw_header;
std::string m__raw_toc;
std::unique_ptr<kaitai::kstream> m__io__raw_toc;
std::string m__raw__raw_toc;
public:
/**
* internal; access `_root.header` instead
*/
file_header_prefix_t* header_prefix() const { return m_header_prefix.get(); }
file_header_t* header() const { return m_header.get(); }
/**
* zlib compressed XML further describing the content of the archive
*/
toc_type_t* toc() const { return m_toc.get(); }
xar_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
std::string _raw_header() const { return m__raw_header; }
kaitai::kstream* _io__raw_header() const { return m__io__raw_header.get(); }
std::string _raw_toc() const { return m__raw_toc; }
kaitai::kstream* _io__raw_toc() const { return m__io__raw_toc.get(); }
std::string _raw__raw_toc() const { return m__raw__raw_toc; }
};
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "xar.h"
#include "kaitai/exceptions.h"
xar_t::xar_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, xar_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_header_prefix = nullptr;
m_header = nullptr;
m__io__raw_header = nullptr;
m_toc = nullptr;
m__io__raw_toc = nullptr;
f_checksum_algorithm_other = false;
_read();
}
void xar_t::_read() {
m_header_prefix = std::unique_ptr<file_header_prefix_t>(new file_header_prefix_t(m__io, this, m__root));
m__raw_header = m__io->read_bytes((header_prefix()->len_header() - 6));
m__io__raw_header = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_header));
m_header = std::unique_ptr<file_header_t>(new file_header_t(m__io__raw_header.get(), this, m__root));
m__raw__raw_toc = m__io->read_bytes(header()->len_toc_compressed());
m__raw_toc = kaitai::kstream::process_zlib(m__raw__raw_toc);
m__io__raw_toc = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_toc));
m_toc = std::unique_ptr<toc_type_t>(new toc_type_t(m__io__raw_toc.get(), this, m__root));
}
xar_t::~xar_t() {
_clean_up();
}
void xar_t::_clean_up() {
}
xar_t::file_header_prefix_t::file_header_prefix_t(kaitai::kstream* p__io, xar_t* p__parent, xar_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void xar_t::file_header_prefix_t::_read() {
m_magic = m__io->read_bytes(4);
if (!(magic() == std::string("\x78\x61\x72\x21", 4))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x78\x61\x72\x21", 4), magic(), _io(), std::string("/types/file_header_prefix/seq/0"));
}
m_len_header = m__io->read_u2be();
}
xar_t::file_header_prefix_t::~file_header_prefix_t() {
_clean_up();
}
void xar_t::file_header_prefix_t::_clean_up() {
}
xar_t::file_header_t::file_header_t(kaitai::kstream* p__io, xar_t* p__parent, xar_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
f_checksum_algorithm_name = false;
f_has_checksum_alg_name = false;
f_len_header = false;
_read();
}
void xar_t::file_header_t::_read() {
m_version = m__io->read_u2be();
if (!(version() == 1)) {
throw kaitai::validation_not_equal_error<uint16_t>(1, version(), _io(), std::string("/types/file_header/seq/0"));
}
m_len_toc_compressed = m__io->read_u8be();
m_toc_length_uncompressed = m__io->read_u8be();
m_checksum_algorithm_int = m__io->read_u4be();
n_checksum_alg_name = true;
if (has_checksum_alg_name()) {
n_checksum_alg_name = false;
m_checksum_alg_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes_full(), 0, false), std::string("UTF-8"));
{
std::string _ = checksum_alg_name();
if (!( ((_ != std::string("")) && (_ != std::string("none"))) )) {
throw kaitai::validation_expr_error<std::string>(checksum_alg_name(), _io(), std::string("/types/file_header/seq/4"));
}
}
}
}
xar_t::file_header_t::~file_header_t() {
_clean_up();
}
void xar_t::file_header_t::_clean_up() {
if (!n_checksum_alg_name) {
}
}
std::string xar_t::file_header_t::checksum_algorithm_name() {
if (f_checksum_algorithm_name)
return m_checksum_algorithm_name;
m_checksum_algorithm_name = ((has_checksum_alg_name()) ? (checksum_alg_name()) : (((checksum_algorithm_int() == xar_t::CHECKSUM_ALGORITHMS_APPLE_NONE) ? (std::string("none")) : (((checksum_algorithm_int() == xar_t::CHECKSUM_ALGORITHMS_APPLE_SHA1) ? (std::string("sha1")) : (((checksum_algorithm_int() == xar_t::CHECKSUM_ALGORITHMS_APPLE_MD5) ? (std::string("md5")) : (((checksum_algorithm_int() == xar_t::CHECKSUM_ALGORITHMS_APPLE_SHA256) ? (std::string("sha256")) : (((checksum_algorithm_int() == xar_t::CHECKSUM_ALGORITHMS_APPLE_SHA512) ? (std::string("sha512")) : (std::string("")))))))))))));
f_checksum_algorithm_name = true;
return m_checksum_algorithm_name;
}
bool xar_t::file_header_t::has_checksum_alg_name() {
if (f_has_checksum_alg_name)
return m_has_checksum_alg_name;
m_has_checksum_alg_name = ((checksum_algorithm_int() == _root()->checksum_algorithm_other()) && (len_header() >= 32) && (kaitai::kstream::mod(len_header(), 4) == 0)) ;
f_has_checksum_alg_name = true;
return m_has_checksum_alg_name;
}
uint16_t xar_t::file_header_t::len_header() {
if (f_len_header)
return m_len_header;
m_len_header = _root()->header_prefix()->len_header();
f_len_header = true;
return m_len_header;
}
xar_t::toc_type_t::toc_type_t(kaitai::kstream* p__io, xar_t* p__parent, xar_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void xar_t::toc_type_t::_read() {
m_xml_string = kaitai::kstream::bytes_to_str(m__io->read_bytes_full(), std::string("UTF-8"));
}
xar_t::toc_type_t::~toc_type_t() {
_clean_up();
}
void xar_t::toc_type_t::_clean_up() {
}
int8_t xar_t::checksum_algorithm_other() {
if (f_checksum_algorithm_other)
return m_checksum_algorithm_other;
m_checksum_algorithm_other = 3;
f_checksum_algorithm_other = true;
return m_checksum_algorithm_other;
}