Zchunk: Go parsing library

File extension

["zck", "zhr"]

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.10

References

This page hosts a formal specification of Zchunk using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Go source code to parse Zchunk

zchunk.go

// 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"
)


/**
 * @see <a href="https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/zchunk_format.txt">Source</a>
 */

type Zchunk_ChecksumTypes int
const (
	Zchunk_ChecksumTypes__Sha1 Zchunk_ChecksumTypes = 0
	Zchunk_ChecksumTypes__Sha256 Zchunk_ChecksumTypes = 1
	Zchunk_ChecksumTypes__Sha512 Zchunk_ChecksumTypes = 2
	Zchunk_ChecksumTypes__Sha512128 Zchunk_ChecksumTypes = 3
)
var values_Zchunk_ChecksumTypes = map[Zchunk_ChecksumTypes]struct{}{0: {}, 1: {}, 2: {}, 3: {}}
func (v Zchunk_ChecksumTypes) isDefined() bool {
	_, ok := values_Zchunk_ChecksumTypes[v]
	return ok
}

type Zchunk_CompressionTypes int
const (
	Zchunk_CompressionTypes__None Zchunk_CompressionTypes = 0
	Zchunk_CompressionTypes__Zstd Zchunk_CompressionTypes = 2
)
var values_Zchunk_CompressionTypes = map[Zchunk_CompressionTypes]struct{}{0: {}, 2: {}}
func (v Zchunk_CompressionTypes) isDefined() bool {
	_, ok := values_Zchunk_CompressionTypes[v]
	return ok
}
type Zchunk struct {
	Lead *Zchunk_HeaderLead
	HeaderRest *Zchunk_HeaderWithoutLead
	Dict []byte
	Chunks [][]byte
	_io *kaitai.Stream
	_root *Zchunk
	_parent kaitai.Struct
	_raw_HeaderRest []byte
}
func NewZchunk() *Zchunk {
	return &Zchunk{
	}
}

func (this Zchunk) IO_() *kaitai.Stream {
	return this._io
}

func (this *Zchunk) Read(io *kaitai.Stream, parent kaitai.Struct, root *Zchunk) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp1 := NewZchunk_HeaderLead()
	err = tmp1.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Lead = tmp1
	tmp2, err := this.Lead.LenHeaderRest.Value()
	if err != nil {
		return err
	}
	tmp3, err := this._io.ReadBytes(int(tmp2))
	if err != nil {
		return err
	}
	tmp3 = tmp3
	this._raw_HeaderRest = tmp3
	_io__raw_HeaderRest := kaitai.NewStream(bytes.NewReader(this._raw_HeaderRest))
	tmp4 := NewZchunk_HeaderWithoutLead()
	err = tmp4.Read(_io__raw_HeaderRest, this, this._root)
	if err != nil {
		return err
	}
	this.HeaderRest = tmp4
	tmp5, err := this.HeaderRest.Index.LenDict.Value()
	if err != nil {
		return err
	}
	tmp6, err := this._io.ReadBytes(int(tmp5))
	if err != nil {
		return err
	}
	tmp6 = tmp6
	this.Dict = tmp6
	tmp7, err := this.Lead.IsDetachedHeader()
	if err != nil {
		return err
	}
	if (!(tmp7)) {
		for i := 0; i < int(len(this.HeaderRest.Index.ChunksMetadata)); i++ {
			_ = i
			tmp8, err := this.HeaderRest.Index.ChunksMetadata[i].LenChunk.Value()
			if err != nil {
				return err
			}
			tmp9, err := this._io.ReadBytes(int(tmp8))
			if err != nil {
				return err
			}
			tmp9 = tmp9
			this.Chunks = append(this.Chunks, tmp9)
		}
	}
	return err
}

/**
 * Custom dictionary used when compressing each chunk. It's compressed itself
 * without a dictionary.
 * 
 * The official zchunk specification calls this section "Compressed Dict".
 * It's also called a "dictionary chunk". `zck_read_header -c` presents it as
 * "chunk 0" (which is always shown in the chunk table, but can have size 0
 * if the dictionary is not in use).
 */

/**
 * Chunks of data, each compressed with the custom dictionary `dict` (if
 * applicable).
 * 
 * They are not included in a detached header (`.zhr`) file. Detached headers
 * contain the dictionary, but none of the data chunks.
 */
type Zchunk_ChecksumType struct {
	Raw *Zchunk_CompressedInteger
	_io *kaitai.Stream
	_root *Zchunk
	_parent kaitai.Struct
	_f_lenChecksum bool
	lenChecksum int8
	_f_value bool
	value Zchunk_ChecksumTypes
}
func NewZchunk_ChecksumType() *Zchunk_ChecksumType {
	return &Zchunk_ChecksumType{
	}
}

func (this Zchunk_ChecksumType) IO_() *kaitai.Stream {
	return this._io
}

func (this *Zchunk_ChecksumType) Read(io *kaitai.Stream, parent kaitai.Struct, root *Zchunk) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp10 := NewZchunk_CompressedInteger()
	err = tmp10.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Raw = tmp10
	{
		_it := this.Raw
		tmp11, err := this.LenChecksum()
		if err != nil {
			return err
		}
		if !(tmp11 != 0) {
			return kaitai.NewValidationExprError(this.Raw, this._io, "/types/checksum_type/seq/0")
		}
	}
	return err
}
func (this *Zchunk_ChecksumType) LenChecksum() (v int8, err error) {
	if (this._f_lenChecksum) {
		return this.lenChecksum, nil
	}
	this._f_lenChecksum = true
	var tmp12 int8;
	tmp13, err := this.Value()
	if err != nil {
		return 0, err
	}
	if (tmp13 == Zchunk_ChecksumTypes__Sha1) {
		tmp12 = 20
	} else {
		var tmp14 int8;
		tmp15, err := this.Value()
		if err != nil {
			return 0, err
		}
		if (tmp15 == Zchunk_ChecksumTypes__Sha256) {
			tmp14 = 32
		} else {
			var tmp16 int8;
			tmp17, err := this.Value()
			if err != nil {
				return 0, err
			}
			if (tmp17 == Zchunk_ChecksumTypes__Sha512) {
				tmp16 = 64
			} else {
				var tmp18 int8;
				tmp19, err := this.Value()
				if err != nil {
					return 0, err
				}
				if (tmp19 == Zchunk_ChecksumTypes__Sha512128) {
					tmp18 = 16
				} else {
					tmp18 = 0
				}
				tmp16 = tmp18
			}
			tmp14 = tmp16
		}
		tmp12 = tmp14
	}
	this.lenChecksum = int8(tmp12)
	return this.lenChecksum, nil
}
func (this *Zchunk_ChecksumType) Value() (v Zchunk_ChecksumTypes, err error) {
	if (this._f_value) {
		return this.value, nil
	}
	this._f_value = true
	tmp20, err := this.Raw.Value()
	if err != nil {
		return nil, err
	}
	this.value = Zchunk_ChecksumTypes(Zchunk_ChecksumTypes(tmp20))
	return this.value, nil
}

/**
 * Raw integer, don't read this field - access `value` instead.
 */
type Zchunk_Chunk struct {
	ChunkStream *Zchunk_CompressedInteger
	ChunkChecksum []byte
	UncompressedChunkChecksum []byte
	LenChunk *Zchunk_CompressedInteger
	LenUncompressedChunk *Zchunk_CompressedInteger
	LenChecksum uint32
	HasDataStreams bool
	HasUncompressedSource bool
	_io *kaitai.Stream
	_root *Zchunk
	_parent *Zchunk_Index
}
func NewZchunk_Chunk(lenChecksum uint32, hasDataStreams bool, hasUncompressedSource bool) *Zchunk_Chunk {
	return &Zchunk_Chunk{
		LenChecksum: lenChecksum,
		HasDataStreams: hasDataStreams,
		HasUncompressedSource: hasUncompressedSource,
	}
}

func (this Zchunk_Chunk) IO_() *kaitai.Stream {
	return this._io
}

func (this *Zchunk_Chunk) Read(io *kaitai.Stream, parent *Zchunk_Index, root *Zchunk) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	if (this.HasDataStreams) {
		tmp21 := NewZchunk_CompressedInteger()
		err = tmp21.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.ChunkStream = tmp21
	}
	tmp22, err := this._io.ReadBytes(int(this.LenChecksum))
	if err != nil {
		return err
	}
	tmp22 = tmp22
	this.ChunkChecksum = tmp22
	if (this.HasUncompressedSource) {
		tmp23, err := this._io.ReadBytes(int(this.LenChecksum))
		if err != nil {
			return err
		}
		tmp23 = tmp23
		this.UncompressedChunkChecksum = tmp23
	}
	tmp24 := NewZchunk_CompressedInteger()
	err = tmp24.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.LenChunk = tmp24
	tmp25 := NewZchunk_CompressedInteger()
	err = tmp25.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.LenUncompressedChunk = tmp25
	return err
}

/**
 * Checksum of the uncompressed chunk. Used to detect whether a chunk
 * from an uncompressed source is identical to the compressed chunk.
 */

/**
 * Like `/common/vlq_base128_le` (LEB128), but the logic of the
 * "continuation" flag in the most significant bit is inverted, so instead of
 * `has_next`, it is called `is_last` (if the highest bit is set to zero, it
 * means "continue", whereas in standard LEB128, the highest bit set to
 * **one** means "continue"). Therefore, we cannot simply import
 * `/common/vlq_base128_le` and use it, because it is incompatible.
 */
type Zchunk_CompressedInteger struct {
	Groups []*Zchunk_CompressedInteger_Group
	_io *kaitai.Stream
	_root *Zchunk
	_parent kaitai.Struct
	_f_len bool
	len int
	_f_value bool
	value uint64
}
func NewZchunk_CompressedInteger() *Zchunk_CompressedInteger {
	return &Zchunk_CompressedInteger{
	}
}

func (this Zchunk_CompressedInteger) IO_() *kaitai.Stream {
	return this._io
}

func (this *Zchunk_CompressedInteger) Read(io *kaitai.Stream, parent kaitai.Struct, root *Zchunk) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	for i := 1;; i++ {
		tmp26 := NewZchunk_CompressedInteger_Group(i)
		err = tmp26.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		_it := tmp26
		this.Groups = append(this.Groups, _it)
		if _it.IsLast {
			break
		}
	}
	return err
}
func (this *Zchunk_CompressedInteger) Len() (v int, err error) {
	if (this._f_len) {
		return this.len, nil
	}
	this._f_len = true
	this.len = int(len(this.Groups))
	return this.len, nil
}

/**
 * Resulting unsigned value as normal integer
 */
func (this *Zchunk_CompressedInteger) Value() (v uint64, err error) {
	if (this._f_value) {
		return this.value, nil
	}
	this._f_value = true
	var tmp27 int;
	tmp28, err := this.Len()
	if err != nil {
		return 0, err
	}
	if (tmp28 >= 2) {
		tmp27 = this.Groups[1].Value << 7
	} else {
		tmp27 = 0
	}
	var tmp29 int;
	tmp30, err := this.Len()
	if err != nil {
		return 0, err
	}
	if (tmp30 >= 3) {
		tmp29 = this.Groups[2].Value << 14
	} else {
		tmp29 = 0
	}
	var tmp31 int;
	tmp32, err := this.Len()
	if err != nil {
		return 0, err
	}
	if (tmp32 >= 4) {
		tmp31 = this.Groups[3].Value << 21
	} else {
		tmp31 = 0
	}
	var tmp33 int;
	tmp34, err := this.Len()
	if err != nil {
		return 0, err
	}
	if (tmp34 >= 5) {
		tmp33 = this.Groups[4].Value << 28
	} else {
		tmp33 = 0
	}
	var tmp35 int;
	tmp36, err := this.Len()
	if err != nil {
		return 0, err
	}
	if (tmp36 >= 6) {
		tmp35 = this.Groups[5].Value << 35
	} else {
		tmp35 = 0
	}
	var tmp37 int;
	tmp38, err := this.Len()
	if err != nil {
		return 0, err
	}
	if (tmp38 >= 7) {
		tmp37 = this.Groups[6].Value << 42
	} else {
		tmp37 = 0
	}
	var tmp39 int;
	tmp40, err := this.Len()
	if err != nil {
		return 0, err
	}
	if (tmp40 >= 8) {
		tmp39 = this.Groups[7].Value << 49
	} else {
		tmp39 = 0
	}
	var tmp41 int;
	tmp42, err := this.Len()
	if err != nil {
		return 0, err
	}
	if (tmp42 >= 9) {
		tmp41 = this.Groups[8].Value << 56
	} else {
		tmp41 = 0
	}
	var tmp43 int;
	tmp44, err := this.Len()
	if err != nil {
		return 0, err
	}
	if (tmp44 >= 10) {
		tmp43 = this.Groups[9].Value << 63
	} else {
		tmp43 = 0
	}
	this.value = uint64(uint64(((((((((this.Groups[0].Value | tmp27) | tmp29) | tmp31) | tmp33) | tmp35) | tmp37) | tmp39) | tmp41) | tmp43))
	return this.value, nil
}

/**
 * One byte group, clearly divided into 7-bit "value" chunk and 1-bit "continuation" flag.
 */
type Zchunk_CompressedInteger_Group struct {
	IsLast bool
	Value uint64
	Idx int32
	_io *kaitai.Stream
	_root *Zchunk
	_parent *Zchunk_CompressedInteger
}
func NewZchunk_CompressedInteger_Group(idx int32) *Zchunk_CompressedInteger_Group {
	return &Zchunk_CompressedInteger_Group{
		Idx: idx,
	}
}

func (this Zchunk_CompressedInteger_Group) IO_() *kaitai.Stream {
	return this._io
}

func (this *Zchunk_CompressedInteger_Group) Read(io *kaitai.Stream, parent *Zchunk_CompressedInteger, root *Zchunk) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp45, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.IsLast = tmp45 != 0
	var tmp46 bool;
	if (this.Idx == 9) {
		tmp46 = true
	} else {
		tmp46 = this.IsLast
	}
	var tmp47 bool;
	if (this.Idx == 9) {
		tmp47 = true
	} else {
		tmp47 = this.IsLast
	}
	if !(this.IsLast == tmp46) {
		return kaitai.NewValidationNotEqualError(tmp47, this.IsLast, this._io, "/types/compressed_integer/types/group/seq/0")
	}
	tmp48, err := this._io.ReadBitsIntBe(7)
	if err != nil {
		return err
	}
	this.Value = tmp48
	var tmp49 int8;
	if (this.Idx == 9) {
		tmp49 = 1
	} else {
		tmp49 = 127
	}
	var tmp50 int8;
	if (this.Idx == 9) {
		tmp50 = 1
	} else {
		tmp50 = 127
	}
	if !(this.Value <= uint64(tmp49)) {
		return kaitai.NewValidationGreaterThanError(uint64(tmp50), this.Value, this._io, "/types/compressed_integer/types/group/seq/1")
	}
	return err
}

/**
 * If `true`, then this is the last byte of the compressed integer.
 * 
 * Since this implementation only supports serialized values up to 10
 * bytes, this must be `true` in the 10th group (`groups[9]`).
 */

/**
 * The 7-bit (base128) numeric value chunk of this group
 * 
 * Since this implementation only supports integer values up to 64 bits,
 * the `value` in the 10th group (`groups[9]`) can only be `0` or `1`
 * (otherwise the width of the represented value would be 65 bits or
 * more, which is not supported).
 */
type Zchunk_HeaderLead struct {
	Magic []byte
	OverallChecksumType *Zchunk_ChecksumType
	LenHeaderRest *Zchunk_CompressedInteger
	HeaderChecksum []byte
	_io *kaitai.Stream
	_root *Zchunk
	_parent *Zchunk
	_f_isDetachedHeader bool
	isDetachedHeader bool
}
func NewZchunk_HeaderLead() *Zchunk_HeaderLead {
	return &Zchunk_HeaderLead{
	}
}

func (this Zchunk_HeaderLead) IO_() *kaitai.Stream {
	return this._io
}

func (this *Zchunk_HeaderLead) Read(io *kaitai.Stream, parent *Zchunk, root *Zchunk) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp51, err := this._io.ReadBytes(int(5))
	if err != nil {
		return err
	}
	tmp51 = tmp51
	this.Magic = tmp51
	if !( ((bytes.Equal(this.Magic, []uint8{0, 90, 67, 75, 49})) || (bytes.Equal(this.Magic, []uint8{0, 90, 72, 82, 49}))) ) {
		return kaitai.NewValidationNotAnyOfError(this.Magic, this._io, "/types/header_lead/seq/0")
	}
	tmp52 := NewZchunk_ChecksumType()
	err = tmp52.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.OverallChecksumType = tmp52
	tmp53 := NewZchunk_CompressedInteger()
	err = tmp53.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.LenHeaderRest = tmp53
	tmp54, err := this.OverallChecksumType.LenChecksum()
	if err != nil {
		return err
	}
	tmp55, err := this._io.ReadBytes(int(tmp54))
	if err != nil {
		return err
	}
	tmp55 = tmp55
	this.HeaderChecksum = tmp55
	return err
}

/**
 * Determines whether this file is a zchunk detached header (`.zhr`). If
 * not, it is a complete zchunk file (`.zck`).
 */
func (this *Zchunk_HeaderLead) IsDetachedHeader() (v bool, err error) {
	if (this._f_isDetachedHeader) {
		return this.isDetachedHeader, nil
	}
	this._f_isDetachedHeader = true
	this.isDetachedHeader = bool(this.Magic[2] == 72)
	return this.isDetachedHeader, nil
}

/**
 * There are two valid magic numbers for zchunk files:
 * 
 * * `'\0ZCK1'` identifies a zchunk version 1 file (`.zck`)
 * * `'\0ZHR1'` identifies a zchunk version 1 detached header file (`.zhr`)
 */

/**
 * Type of the checksum used for `header_checksum` and
 * `_root.header_rest.preface.data_checksum`.
 */

/**
 * Size of the header, not including the lead
 */

/**
 * Checksum of the entire header, which consists of `_root.lead` and
 * `_root.header_rest` (i.e. everything from the beginning of the file to
 * the end of `_root.header_rest`), not including the `header_checksum`
 * field itself (i.e. the input for the checksum algorithm is a
 * concatenation of the bytes preceding the `header_checksum` field with
 * the bytes following it).
 * 
 * For detached headers, the checksum is calculated as if the `magic`
 * field were set to `'\0ZCK1'`, so that it matches the checksum in the
 * full zchunk file.
 */
type Zchunk_HeaderWithoutLead struct {
	Preface *Zchunk_Preface
	LenIndex *Zchunk_CompressedInteger
	Index *Zchunk_Index
	NumSignatures *Zchunk_CompressedInteger
	_io *kaitai.Stream
	_root *Zchunk
	_parent *Zchunk
	_raw_Index []byte
}
func NewZchunk_HeaderWithoutLead() *Zchunk_HeaderWithoutLead {
	return &Zchunk_HeaderWithoutLead{
	}
}

func (this Zchunk_HeaderWithoutLead) IO_() *kaitai.Stream {
	return this._io
}

func (this *Zchunk_HeaderWithoutLead) Read(io *kaitai.Stream, parent *Zchunk, root *Zchunk) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp56 := NewZchunk_Preface()
	err = tmp56.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Preface = tmp56
	tmp57 := NewZchunk_CompressedInteger()
	err = tmp57.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.LenIndex = tmp57
	tmp58, err := this.LenIndex.Value()
	if err != nil {
		return err
	}
	tmp59, err := this._io.ReadBytes(int(tmp58))
	if err != nil {
		return err
	}
	tmp59 = tmp59
	this._raw_Index = tmp59
	_io__raw_Index := kaitai.NewStream(bytes.NewReader(this._raw_Index))
	tmp60 := NewZchunk_Index()
	err = tmp60.Read(_io__raw_Index, this, this._root)
	if err != nil {
		return err
	}
	this.Index = tmp60
	tmp61 := NewZchunk_CompressedInteger()
	err = tmp61.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.NumSignatures = tmp61
	{
		_it := this.NumSignatures
		tmp62, err := _it.Value()
		if err != nil {
			return err
		}
		if !(tmp62 == 0) {
			return kaitai.NewValidationExprError(this.NumSignatures, this._io, "/types/header_without_lead/seq/3")
		}
	}
	return err
}

/**
 * Must be 0. The reference implementation also rejects any file with a
 * non-zero "Signature count", throwing a fatal error stating "Signatures
 * aren't supported yet" - see
 * [`src/lib/header.c:259-264`](https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/src/lib/header.c#L259-L264).
 * 
 * Although the structure of signatures is defined [in the official
 * textual
 * specification](https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/zchunk_format.txt#L219-L252),
 * no signature types are defined, and as of this writing no publicly
 * known implementation generates or interprets these signatures.
 * Therefore, we've decided not to implement them here either.
 * 
 * For more details, see
 * <https://github.com/kaitai-io/kaitai_struct_formats/pull/539#discussion_r3713109887>.
 */
type Zchunk_Index struct {
	ChunkChecksumType *Zchunk_ChecksumType
	NumChunks *Zchunk_CompressedInteger
	DictStream *Zchunk_CompressedInteger
	DictChecksum []byte
	UncompressedDictChecksum []byte
	LenDict *Zchunk_CompressedInteger
	LenUncompressedDict *Zchunk_CompressedInteger
	ChunksMetadata []*Zchunk_Chunk
	_io *kaitai.Stream
	_root *Zchunk
	_parent *Zchunk_HeaderWithoutLead
	_f_numDataChunks bool
	numDataChunks int
}
func NewZchunk_Index() *Zchunk_Index {
	return &Zchunk_Index{
	}
}

func (this Zchunk_Index) IO_() *kaitai.Stream {
	return this._io
}

func (this *Zchunk_Index) Read(io *kaitai.Stream, parent *Zchunk_HeaderWithoutLead, root *Zchunk) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp63 := NewZchunk_ChecksumType()
	err = tmp63.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.ChunkChecksumType = tmp63
	tmp64 := NewZchunk_CompressedInteger()
	err = tmp64.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.NumChunks = tmp64
	{
		_it := this.NumChunks
		tmp65, err := _it.Value()
		if err != nil {
			return err
		}
		if !(tmp65 >= 1) {
			return kaitai.NewValidationExprError(this.NumChunks, this._io, "/types/index/seq/1")
		}
	}
	tmp66, err := this._parent.Preface.HasDataStreams()
	if err != nil {
		return err
	}
	if (tmp66) {
		tmp67 := NewZchunk_CompressedInteger()
		err = tmp67.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.DictStream = tmp67
		{
			_it := this.DictStream
			tmp68, err := _it.Value()
			if err != nil {
				return err
			}
			if !(tmp68 == 0) {
				return kaitai.NewValidationExprError(this.DictStream, this._io, "/types/index/seq/2")
			}
		}
	}
	tmp69, err := this.ChunkChecksumType.LenChecksum()
	if err != nil {
		return err
	}
	tmp70, err := this._io.ReadBytes(int(tmp69))
	if err != nil {
		return err
	}
	tmp70 = tmp70
	this.DictChecksum = tmp70
	tmp71, err := this._parent.Preface.HasUncompressedSource()
	if err != nil {
		return err
	}
	if (tmp71) {
		tmp72, err := this.ChunkChecksumType.LenChecksum()
		if err != nil {
			return err
		}
		tmp73, err := this._io.ReadBytes(int(tmp72))
		if err != nil {
			return err
		}
		tmp73 = tmp73
		this.UncompressedDictChecksum = tmp73
	}
	tmp74 := NewZchunk_CompressedInteger()
	err = tmp74.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.LenDict = tmp74
	tmp75 := NewZchunk_CompressedInteger()
	err = tmp75.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.LenUncompressedDict = tmp75
	tmp76, err := this.NumDataChunks()
	if err != nil {
		return err
	}
	for i := 0; i < int(tmp76); i++ {
		_ = i
		tmp77, err := this.ChunkChecksumType.LenChecksum()
		if err != nil {
			return err
		}
		tmp78, err := this._parent.Preface.HasDataStreams()
		if err != nil {
			return err
		}
		tmp79, err := this._parent.Preface.HasUncompressedSource()
		if err != nil {
			return err
		}
		tmp80 := NewZchunk_Chunk(tmp77, tmp78, tmp79)
		err = tmp80.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.ChunksMetadata = append(this.ChunksMetadata, tmp80)
	}
	return err
}

/**
 * Number of data chunks. `num_chunks` counts the dictionary as chunk 0,
 * so it is one greater than this number.
 */
func (this *Zchunk_Index) NumDataChunks() (v int, err error) {
	if (this._f_numDataChunks) {
		return this.numDataChunks, nil
	}
	this._f_numDataChunks = true
	tmp81, err := this.NumChunks.Value()
	if err != nil {
		return 0, err
	}
	this.numDataChunks = int(tmp81 - 1)
	return this.numDataChunks, nil
}

/**
 * Type of the checksum used for `dict_checksum` and for all
 * `chunks_metadata[...].chunk_checksum` and
 * `chunks_metadata[...].uncompressed_chunk_checksum`.
 */

/**
 * Number of chunks, **including** the dictionary chunk.
 * 
 * Must be at least 1, because the dictionary chunk is always present,
 * even if it is empty. The reference implementation also fails when the
 * number of chunks is 0, see
 * [`src/lib/index/index_read.c:181-184`](https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/src/lib/index/index_read.c#L181-L184).
 */

/**
 * If present, it must always be 0.
 * @see <a href="https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/zchunk_format.txt#L159-L162">Source</a>
 */

/**
 * Checksum of the uncompressed dictionary. It has no real use, as the
 * uncompressed source won't have a dictionary.
 */

/**
 * Metadata of the data chunks. The dictionary is chunk 0 and its
 * metadata is stored in the `*dict*` fields above, so there is one fewer
 * entry here than indicated by `num_chunks`.
 */
type Zchunk_OptionalElement struct {
	ElementId *Zchunk_CompressedInteger
	LenData *Zchunk_CompressedInteger
	Data []byte
	_io *kaitai.Stream
	_root *Zchunk
	_parent *Zchunk_Preface
}
func NewZchunk_OptionalElement() *Zchunk_OptionalElement {
	return &Zchunk_OptionalElement{
	}
}

func (this Zchunk_OptionalElement) IO_() *kaitai.Stream {
	return this._io
}

func (this *Zchunk_OptionalElement) Read(io *kaitai.Stream, parent *Zchunk_Preface, root *Zchunk) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp82 := NewZchunk_CompressedInteger()
	err = tmp82.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.ElementId = tmp82
	tmp83 := NewZchunk_CompressedInteger()
	err = tmp83.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.LenData = tmp83
	tmp84, err := this.LenData.Value()
	if err != nil {
		return err
	}
	tmp85, err := this._io.ReadBytes(int(tmp84))
	if err != nil {
		return err
	}
	tmp85 = tmp85
	this.Data = tmp85
	return err
}
type Zchunk_Preface struct {
	DataChecksum []byte
	Flags *Zchunk_CompressedInteger
	CompressionTypeInt *Zchunk_CompressedInteger
	NumOptionalElements *Zchunk_CompressedInteger
	OptionalElements []*Zchunk_OptionalElement
	_io *kaitai.Stream
	_root *Zchunk
	_parent *Zchunk_HeaderWithoutLead
	_f_compressionType bool
	compressionType Zchunk_CompressionTypes
	_f_hasDataStreams bool
	hasDataStreams bool
	_f_hasOptionalElements bool
	hasOptionalElements bool
	_f_hasUncompressedSource bool
	hasUncompressedSource bool
}
func NewZchunk_Preface() *Zchunk_Preface {
	return &Zchunk_Preface{
	}
}

func (this Zchunk_Preface) IO_() *kaitai.Stream {
	return this._io
}

func (this *Zchunk_Preface) Read(io *kaitai.Stream, parent *Zchunk_HeaderWithoutLead, root *Zchunk) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp86, err := this._root.Lead.OverallChecksumType.LenChecksum()
	if err != nil {
		return err
	}
	tmp87, err := this._io.ReadBytes(int(tmp86))
	if err != nil {
		return err
	}
	tmp87 = tmp87
	this.DataChecksum = tmp87
	tmp88 := NewZchunk_CompressedInteger()
	err = tmp88.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Flags = tmp88
	{
		_it := this.Flags
		tmp89, err := _it.Value()
		if err != nil {
			return err
		}
		if !(tmp89 <= 7) {
			return kaitai.NewValidationExprError(this.Flags, this._io, "/types/preface/seq/1")
		}
	}
	tmp90 := NewZchunk_CompressedInteger()
	err = tmp90.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.CompressionTypeInt = tmp90
	{
		_it := this.CompressionTypeInt
		tmp91, err := _it.Value()
		if err != nil {
			return err
		}
		tmp92, err := _it.Value()
		if err != nil {
			return err
		}
		if !( ((tmp91 == Zchunk_CompressionTypes__None) || (tmp92 == Zchunk_CompressionTypes__Zstd)) ) {
			return kaitai.NewValidationExprError(this.CompressionTypeInt, this._io, "/types/preface/seq/2")
		}
	}
	tmp93, err := this.HasOptionalElements()
	if err != nil {
		return err
	}
	if (tmp93) {
		tmp94 := NewZchunk_CompressedInteger()
		err = tmp94.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.NumOptionalElements = tmp94
		{
			_it := this.NumOptionalElements
			tmp95, err := _it.Value()
			if err != nil {
				return err
			}
			if !(tmp95 >= 1) {
				return kaitai.NewValidationExprError(this.NumOptionalElements, this._io, "/types/preface/seq/3")
			}
		}
	}
	tmp96, err := this.HasOptionalElements()
	if err != nil {
		return err
	}
	if (tmp96) {
		tmp97, err := this.NumOptionalElements.Value()
		if err != nil {
			return err
		}
		for i := 0; i < int(tmp97); i++ {
			_ = i
			tmp98 := NewZchunk_OptionalElement()
			err = tmp98.Read(this._io, this, this._root)
			if err != nil {
				return err
			}
			this.OptionalElements = append(this.OptionalElements, tmp98)
		}
	}
	return err
}
func (this *Zchunk_Preface) CompressionType() (v Zchunk_CompressionTypes, err error) {
	if (this._f_compressionType) {
		return this.compressionType, nil
	}
	this._f_compressionType = true
	tmp99, err := this.CompressionTypeInt.Value()
	if err != nil {
		return nil, err
	}
	this.compressionType = Zchunk_CompressionTypes(Zchunk_CompressionTypes(tmp99))
	return this.compressionType, nil
}
func (this *Zchunk_Preface) HasDataStreams() (v bool, err error) {
	if (this._f_hasDataStreams) {
		return this.hasDataStreams, nil
	}
	this._f_hasDataStreams = true
	tmp100, err := this.Flags.Value()
	if err != nil {
		return false, err
	}
	this.hasDataStreams = bool(tmp100 & 1 != 0)
	return this.hasDataStreams, nil
}
func (this *Zchunk_Preface) HasOptionalElements() (v bool, err error) {
	if (this._f_hasOptionalElements) {
		return this.hasOptionalElements, nil
	}
	this._f_hasOptionalElements = true
	tmp101, err := this.Flags.Value()
	if err != nil {
		return false, err
	}
	this.hasOptionalElements = bool(tmp101 & 2 != 0)
	return this.hasOptionalElements, nil
}

/**
 * The file may be applied against an uncompressed source. This adds an
 * uncompressed checksum to every index entry, including the dictionary.
 */
func (this *Zchunk_Preface) HasUncompressedSource() (v bool, err error) {
	if (this._f_hasUncompressedSource) {
		return this.hasUncompressedSource, nil
	}
	this._f_hasUncompressedSource = true
	tmp102, err := this.Flags.Value()
	if err != nil {
		return false, err
	}
	this.hasUncompressedSource = bool(tmp102 & 4 != 0)
	return this.hasUncompressedSource, nil
}

/**
 * Total data checksum. Checksum of everything after the header,
 * including the compressed dictionary (`_root.dict`) and all compressed
 * chunks (`_root.chunks`). The type of this checksum is
 * `_root.lead.overall_checksum_type.value`.
 * 
 * If `has_uncompressed_source` is true, this checksum must not be
 * checked and should not be generated. In that case, the reference
 * implementation writes it as all zeros - see the sample file
 * [`mini-uncomp-cksums.zck`](https://github.com/kaitai-io/kaitai_struct_samples/blob/1d2fe11c971fb7e86f343b77a1ed341a0217e86a/archive/zchunk/README.md#mini-uncomp-cksumszck).
 */

/**
 * Compressed integer containing a bitmask of the flags. All unused flags
 * MUST be set to 0. If a decoder sees a flag set that it doesn't
 * recognize, it MUST exit with an error.
 * @see <a href="https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/zchunk_format.txt#L78-L81">Source</a>
 */

/**
 * Raw integer, don't read this field - access `compression_type`
 * instead.
 */

/**
 * If present, it must be at least 1. This is because if there are no
 * optional elements, `has_optional_elements` must be false, and then
 * neither this field nor `optional_elements` is present.
 * @see <a href="https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/zchunk_format.txt#L99-L102">Source</a>
 */