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.
// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.
import (
"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
"bytes"
)
/**
* 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."
* @see <a href="https://github.com/mackyle/xar/wiki/xarformat">Source</a>
*/
type Xar_ChecksumAlgorithmsApple int
const (
Xar_ChecksumAlgorithmsApple__None Xar_ChecksumAlgorithmsApple = 0
Xar_ChecksumAlgorithmsApple__Sha1 Xar_ChecksumAlgorithmsApple = 1
Xar_ChecksumAlgorithmsApple__Md5 Xar_ChecksumAlgorithmsApple = 2
Xar_ChecksumAlgorithmsApple__Sha256 Xar_ChecksumAlgorithmsApple = 3
Xar_ChecksumAlgorithmsApple__Sha512 Xar_ChecksumAlgorithmsApple = 4
)
type Xar struct {
HeaderPrefix *Xar_FileHeaderPrefix
Header *Xar_FileHeader
Toc *Xar_TocType
_io *kaitai.Stream
_root *Xar
_parent interface{}
_raw_Header []byte
_raw_Toc []byte
_raw__raw_Toc []byte
_f_checksumAlgorithmOther bool
checksumAlgorithmOther int8
}
func NewXar() *Xar {
return &Xar{
}
}
func (this *Xar) Read(io *kaitai.Stream, parent interface{}, root *Xar) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp1 := NewXar_FileHeaderPrefix()
err = tmp1.Read(this._io, this, this._root)
if err != nil {
return err
}
this.HeaderPrefix = tmp1
tmp2, err := this._io.ReadBytes(int((this.HeaderPrefix.LenHeader - 6)))
if err != nil {
return err
}
tmp2 = tmp2
this._raw_Header = tmp2
_io__raw_Header := kaitai.NewStream(bytes.NewReader(this._raw_Header))
tmp3 := NewXar_FileHeader()
err = tmp3.Read(_io__raw_Header, this, this._root)
if err != nil {
return err
}
this.Header = tmp3
tmp4, err := this._io.ReadBytes(int(this.Header.LenTocCompressed))
if err != nil {
return err
}
tmp4 = tmp4
this._raw__raw_Toc = tmp4
tmp5, err := kaitai.ProcessZlib(this._raw__raw_Toc)
if err != nil {
return err
}
this._raw_Toc = tmp5
_io__raw_Toc := kaitai.NewStream(bytes.NewReader(this._raw_Toc))
tmp6 := NewXar_TocType()
err = tmp6.Read(_io__raw_Toc, this, this._root)
if err != nil {
return err
}
this.Toc = tmp6
return err
}
/**
* @see <a href="https://github.com/mackyle/xar/blob/66d451d/xar/include/xar.h.in#L85">Source</a>
*/
func (this *Xar) ChecksumAlgorithmOther() (v int8, err error) {
if (this._f_checksumAlgorithmOther) {
return this.checksumAlgorithmOther, nil
}
this.checksumAlgorithmOther = int8(3)
this._f_checksumAlgorithmOther = true
return this.checksumAlgorithmOther, nil
}
/**
* internal; access `_root.header` instead
*/
/**
* zlib compressed XML further describing the content of the archive
*/
type Xar_FileHeaderPrefix struct {
Magic []byte
LenHeader uint16
_io *kaitai.Stream
_root *Xar
_parent *Xar
}
func NewXar_FileHeaderPrefix() *Xar_FileHeaderPrefix {
return &Xar_FileHeaderPrefix{
}
}
func (this *Xar_FileHeaderPrefix) Read(io *kaitai.Stream, parent *Xar, root *Xar) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp7, err := this._io.ReadBytes(int(4))
if err != nil {
return err
}
tmp7 = tmp7
this.Magic = tmp7
if !(bytes.Equal(this.Magic, []uint8{120, 97, 114, 33})) {
return kaitai.NewValidationNotEqualError([]uint8{120, 97, 114, 33}, this.Magic, this._io, "/types/file_header_prefix/seq/0")
}
tmp8, err := this._io.ReadU2be()
if err != nil {
return err
}
this.LenHeader = uint16(tmp8)
return err
}
/**
* internal; access `_root.header.len_header` instead
*/
type Xar_FileHeader struct {
Version uint16
LenTocCompressed uint64
TocLengthUncompressed uint64
ChecksumAlgorithmInt uint32
ChecksumAlgName string
_io *kaitai.Stream
_root *Xar
_parent *Xar
_f_checksumAlgorithmName bool
checksumAlgorithmName string
_f_hasChecksumAlgName bool
hasChecksumAlgName bool
_f_lenHeader bool
lenHeader uint16
}
func NewXar_FileHeader() *Xar_FileHeader {
return &Xar_FileHeader{
}
}
func (this *Xar_FileHeader) Read(io *kaitai.Stream, parent *Xar, root *Xar) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp9, err := this._io.ReadU2be()
if err != nil {
return err
}
this.Version = uint16(tmp9)
if !(this.Version == 1) {
return kaitai.NewValidationNotEqualError(1, this.Version, this._io, "/types/file_header/seq/0")
}
tmp10, err := this._io.ReadU8be()
if err != nil {
return err
}
this.LenTocCompressed = uint64(tmp10)
tmp11, err := this._io.ReadU8be()
if err != nil {
return err
}
this.TocLengthUncompressed = uint64(tmp11)
tmp12, err := this._io.ReadU4be()
if err != nil {
return err
}
this.ChecksumAlgorithmInt = uint32(tmp12)
tmp13, err := this.HasChecksumAlgName()
if err != nil {
return err
}
if (tmp13) {
tmp14, err := this._io.ReadBytesFull()
if err != nil {
return err
}
tmp14 = kaitai.BytesTerminate(tmp14, 0, false)
this.ChecksumAlgName = string(tmp14)
{
_it := this.ChecksumAlgName
if !( ((_it != "") && (_it != "none")) ) {
return kaitai.NewValidationExprError(this.ChecksumAlgName, this._io, "/types/file_header/seq/4")
}
}
}
return err
}
/**
* 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.
*/
func (this *Xar_FileHeader) ChecksumAlgorithmName() (v string, err error) {
if (this._f_checksumAlgorithmName) {
return this.checksumAlgorithmName, nil
}
var tmp15 string;
tmp16, err := this.HasChecksumAlgName()
if err != nil {
return "", err
}
if (tmp16) {
tmp15 = this.ChecksumAlgName
} else {
var tmp17 string;
if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__None) {
tmp17 = "none"
} else {
var tmp18 string;
if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Sha1) {
tmp18 = "sha1"
} else {
var tmp19 string;
if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Md5) {
tmp19 = "md5"
} else {
var tmp20 string;
if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Sha256) {
tmp20 = "sha256"
} else {
var tmp21 string;
if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Sha512) {
tmp21 = "sha512"
} else {
tmp21 = ""
}
tmp20 = tmp21
}
tmp19 = tmp20
}
tmp18 = tmp19
}
tmp17 = tmp18
}
tmp15 = tmp17
}
this.checksumAlgorithmName = string(tmp15)
this._f_checksumAlgorithmName = true
return this.checksumAlgorithmName, nil
}
func (this *Xar_FileHeader) HasChecksumAlgName() (v bool, err error) {
if (this._f_hasChecksumAlgName) {
return this.hasChecksumAlgName, nil
}
tmp22, err := this._root.ChecksumAlgorithmOther()
if err != nil {
return false, err
}
tmp23, err := this.LenHeader()
if err != nil {
return false, err
}
tmp25, err := this.LenHeader()
if err != nil {
return false, err
}
tmp24 := tmp25 % 4
if tmp24 < 0 {
tmp24 += 4
}
this.hasChecksumAlgName = bool( ((this.ChecksumAlgorithmInt == tmp22) && (tmp23 >= 32) && (tmp24 == 0)) )
this._f_hasChecksumAlgName = true
return this.hasChecksumAlgName, nil
}
func (this *Xar_FileHeader) LenHeader() (v uint16, err error) {
if (this._f_lenHeader) {
return this.lenHeader, nil
}
this.lenHeader = uint16(this._root.HeaderPrefix.LenHeader)
this._f_lenHeader = true
return this.lenHeader, nil
}
/**
* internal; access `checksum_algorithm_name` instead
*/
/**
* internal; access `checksum_algorithm_name` instead
*/
type Xar_TocType struct {
XmlString string
_io *kaitai.Stream
_root *Xar
_parent *Xar
}
func NewXar_TocType() *Xar_TocType {
return &Xar_TocType{
}
}
func (this *Xar_TocType) Read(io *kaitai.Stream, parent *Xar, root *Xar) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp26, err := this._io.ReadBytesFull()
if err != nil {
return err
}
tmp26 = tmp26
this.XmlString = string(tmp26)
return err
}