Compressed resource data in 'dcmp' (1)
format,
as stored in compressed resources with header type 8
and decompressor ID 1
.
The 'dcmp' (1)
decompressor resource is included in the System file of System 7.0 and later.
This compression format is used for a few compressed resources in System 7.0's files
(such as the Finder Help file).
This decompressor is also included with and used by some other Apple applications,
such as ResEdit.
(Note: ResEdit includes the 'dcmp' (1)
resource,
but none of its resources actually use this decompressor.)
This compression format supports some basic general-purpose compression schemes, including backreferences to previous data and run-length encoding. It also includes some types of compression tailored specifically to Mac OS resources, including a set of single-byte codes that correspond to entries in a hard-coded lookup table.
The 'dcmp' (0)
compression format (see dcmp_0.ksy) is very similar to this format,
with the main difference that it operates mostly on units of 2 or 4 bytes.
This makes the ``dcmp' (0)format more suitable for word-aligned data, such as executable code, bitmaps, sounds, etc. The
'dcmp' (0)` format also appears to be generally preferred over `'dcmp' (1)`,
with the latter only being used in resource files that contain mostly unaligned data,
such as text.
This page hosts a formal specification of Compressed Macintosh resource data, Apple `'dcmp' (1)` format 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"
/**
* Compressed resource data in `'dcmp' (1)` format,
* as stored in compressed resources with header type `8` and decompressor ID `1`.
*
* The `'dcmp' (1)` decompressor resource is included in the System file of System 7.0 and later.
* This compression format is used for a few compressed resources in System 7.0's files
* (such as the Finder Help file).
* This decompressor is also included with and used by some other Apple applications,
* such as ResEdit.
* (Note: ResEdit includes the `'dcmp' (1)` resource,
* but none of its resources actually use this decompressor.)
*
* This compression format supports some basic general-purpose compression schemes,
* including backreferences to previous data and run-length encoding.
* It also includes some types of compression tailored specifically to Mac OS resources,
* including a set of single-byte codes that correspond to entries in a hard-coded lookup table.
*
* The `'dcmp' (0)` compression format (see dcmp_0.ksy) is very similar to this format,
* with the main difference that it operates mostly on units of 2 or 4 bytes.
* This makes the ``dcmp' (0)` format more suitable for word-aligned data,
* such as executable code, bitmaps, sounds, etc.
* The `'dcmp' (0)` format also appears to be generally preferred over `'dcmp' (1)`,
* with the latter only being used in resource files that contain mostly unaligned data,
* such as text.
* @see <a href="https://github.com/dgelessus/python-rsrcfork/blob/f891a6e/src/rsrcfork/compress/dcmp1.py">Source</a>
*/
type Dcmp1 struct {
Chunks []*Dcmp1_Chunk
_io *kaitai.Stream
_root *Dcmp1
_parent interface{}
}
func NewDcmp1() *Dcmp1 {
return &Dcmp1{
}
}
func (this *Dcmp1) Read(io *kaitai.Stream, parent interface{}, root *Dcmp1) (err error) {
this._io = io
this._parent = parent
this._root = root
for i := 1;; i++ {
tmp1 := NewDcmp1_Chunk()
err = tmp1.Read(this._io, this, this._root)
if err != nil {
return err
}
_it := tmp1
this.Chunks = append(this.Chunks, _it)
if _it.Tag == 255 {
break
}
}
return err
}
/**
* The sequence of chunks that make up the compressed data.
*/
/**
* A single chunk of compressed data.
* Each chunk in the compressed data expands to a sequence of bytes in the uncompressed data,
* except when `tag == 0xff`,
* which marks the end of the data and does not correspond to any bytes in the uncompressed data.
*
* Most chunks are stateless and always expand to the same data,
* regardless of where the chunk appears in the sequence.
* However,
* some chunks affect the behavior of future chunks,
* or expand to different data depending on which chunks came before them.
*/
type Dcmp1_Chunk_TagKind int
const (
Dcmp1_Chunk_TagKind__Invalid Dcmp1_Chunk_TagKind = -1
Dcmp1_Chunk_TagKind__Literal Dcmp1_Chunk_TagKind = 0
Dcmp1_Chunk_TagKind__Backreference Dcmp1_Chunk_TagKind = 1
Dcmp1_Chunk_TagKind__TableLookup Dcmp1_Chunk_TagKind = 2
Dcmp1_Chunk_TagKind__Extended Dcmp1_Chunk_TagKind = 3
Dcmp1_Chunk_TagKind__End Dcmp1_Chunk_TagKind = 4
)
type Dcmp1_Chunk struct {
Tag uint8
Body interface{}
_io *kaitai.Stream
_root *Dcmp1
_parent *Dcmp1
}
func NewDcmp1_Chunk() *Dcmp1_Chunk {
return &Dcmp1_Chunk{
}
}
func (this *Dcmp1_Chunk) Read(io *kaitai.Stream, parent *Dcmp1, root *Dcmp1) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp2, err := this._io.ReadU1()
if err != nil {
return err
}
this.Tag = tmp2
var tmp3 Dcmp1_Chunk_TagKind;
if ( ((this.Tag >= 0) && (this.Tag <= 31)) ) {
tmp3 = Dcmp1_Chunk_TagKind__Literal
} else {
var tmp4 Dcmp1_Chunk_TagKind;
if ( ((this.Tag >= 32) && (this.Tag <= 207)) ) {
tmp4 = Dcmp1_Chunk_TagKind__Backreference
} else {
var tmp5 Dcmp1_Chunk_TagKind;
if ( ((this.Tag >= 208) && (this.Tag <= 209)) ) {
tmp5 = Dcmp1_Chunk_TagKind__Literal
} else {
var tmp6 Dcmp1_Chunk_TagKind;
if (this.Tag == 210) {
tmp6 = Dcmp1_Chunk_TagKind__Backreference
} else {
var tmp7 Dcmp1_Chunk_TagKind;
if ( ((this.Tag >= 213) && (this.Tag <= 253)) ) {
tmp7 = Dcmp1_Chunk_TagKind__TableLookup
} else {
var tmp8 Dcmp1_Chunk_TagKind;
if (this.Tag == 254) {
tmp8 = Dcmp1_Chunk_TagKind__Extended
} else {
var tmp9 Dcmp1_Chunk_TagKind;
if (this.Tag == 255) {
tmp9 = Dcmp1_Chunk_TagKind__End
} else {
tmp9 = Dcmp1_Chunk_TagKind__Invalid
}
tmp8 = tmp9
}
tmp7 = tmp8
}
tmp6 = tmp7
}
tmp5 = tmp6
}
tmp4 = tmp5
}
tmp3 = tmp4
}
switch (tmp3) {
case Dcmp1_Chunk_TagKind__Extended:
tmp10 := NewDcmp1_Chunk_ExtendedBody()
err = tmp10.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Body = tmp10
case Dcmp1_Chunk_TagKind__Literal:
tmp11 := NewDcmp1_Chunk_LiteralBody(this.Tag)
err = tmp11.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Body = tmp11
case Dcmp1_Chunk_TagKind__End:
tmp12 := NewDcmp1_Chunk_EndBody()
err = tmp12.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Body = tmp12
case Dcmp1_Chunk_TagKind__TableLookup:
tmp13 := NewDcmp1_Chunk_TableLookupBody(this.Tag)
err = tmp13.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Body = tmp13
case Dcmp1_Chunk_TagKind__Backreference:
tmp14 := NewDcmp1_Chunk_BackreferenceBody(this.Tag)
err = tmp14.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Body = tmp14
}
return err
}
/**
* The chunk's tag byte.
* This controls the structure of the body and the meaning of the chunk.
*/
/**
* The chunk's body.
*
* Certain chunks do not have any data following the tag byte.
* In this case,
* the body is a zero-length structure.
*/
/**
* The body of a literal data chunk.
*
* The data that this chunk expands to is stored literally in the body (`literal`).
* Optionally,
* the literal data may also be stored for use by future backreference chunks (`do_store`).
*/
type Dcmp1_Chunk_LiteralBody struct {
LenLiteralSeparate uint8
Literal []byte
Tag uint8
_io *kaitai.Stream
_root *Dcmp1
_parent *Dcmp1_Chunk
_f_doStore bool
doStore bool
_f_lenLiteralM1InTag bool
lenLiteralM1InTag int
_f_isLenLiteralSeparate bool
isLenLiteralSeparate bool
_f_lenLiteral bool
lenLiteral int
}
func NewDcmp1_Chunk_LiteralBody(tag uint8) *Dcmp1_Chunk_LiteralBody {
return &Dcmp1_Chunk_LiteralBody{
Tag: tag,
}
}
func (this *Dcmp1_Chunk_LiteralBody) Read(io *kaitai.Stream, parent *Dcmp1_Chunk, root *Dcmp1) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp15, err := this.IsLenLiteralSeparate()
if err != nil {
return err
}
if (tmp15) {
tmp16, err := this._io.ReadU1()
if err != nil {
return err
}
this.LenLiteralSeparate = tmp16
}
tmp17, err := this.LenLiteral()
if err != nil {
return err
}
tmp18, err := this._io.ReadBytes(int(tmp17))
if err != nil {
return err
}
tmp18 = tmp18
this.Literal = tmp18
return err
}
/**
* Whether this literal should be stored for use by future backreference chunks.
*
* See the documentation of the `backreference_body` type for details about backreference chunks.
*/
func (this *Dcmp1_Chunk_LiteralBody) DoStore() (v bool, err error) {
if (this._f_doStore) {
return this.doStore, nil
}
var tmp19 bool;
tmp20, err := this.IsLenLiteralSeparate()
if err != nil {
return false, err
}
if (tmp20) {
tmp19 = this.Tag == 209
} else {
tmp19 = (this.Tag & 16) != 0
}
this.doStore = bool(tmp19)
this._f_doStore = true
return this.doStore, nil
}
/**
* The part of the tag byte that indicates the length of the literal data,
* in bytes,
* minus one.
*
* If the tag byte is 0xd0 or 0xd1,
* the length is stored in a separate byte after the tag byte and before the literal data.
*/
func (this *Dcmp1_Chunk_LiteralBody) LenLiteralM1InTag() (v int, err error) {
if (this._f_lenLiteralM1InTag) {
return this.lenLiteralM1InTag, nil
}
tmp21, err := this.IsLenLiteralSeparate()
if err != nil {
return 0, err
}
if (!(tmp21)) {
this.lenLiteralM1InTag = int((this.Tag & 15))
}
this._f_lenLiteralM1InTag = true
return this.lenLiteralM1InTag, nil
}
/**
* Whether the length of the literal is stored separately from the tag.
*/
func (this *Dcmp1_Chunk_LiteralBody) IsLenLiteralSeparate() (v bool, err error) {
if (this._f_isLenLiteralSeparate) {
return this.isLenLiteralSeparate, nil
}
this.isLenLiteralSeparate = bool(this.Tag >= 208)
this._f_isLenLiteralSeparate = true
return this.isLenLiteralSeparate, nil
}
/**
* The length of the literal data,
* in bytes.
*
* In practice,
* this value is always greater than zero,
* as there is no use in storing a zero-length literal.
*/
func (this *Dcmp1_Chunk_LiteralBody) LenLiteral() (v int, err error) {
if (this._f_lenLiteral) {
return this.lenLiteral, nil
}
var tmp22 uint8;
tmp23, err := this.IsLenLiteralSeparate()
if err != nil {
return 0, err
}
if (tmp23) {
tmp22 = this.LenLiteralSeparate
} else {
tmp24, err := this.LenLiteralM1InTag()
if err != nil {
return 0, err
}
tmp22 = (tmp24 + 1)
}
this.lenLiteral = int(tmp22)
this._f_lenLiteral = true
return this.lenLiteral, nil
}
/**
* The length of the literal data,
* in bytes.
*
* This field is only present if the tag byte is 0xd0 or 0xd1.
* In practice,
* this only happens if the length is 0x11 or greater,
* because smaller lengths can be encoded into the tag byte.
*/
/**
* The literal data.
*/
/**
* The body of a backreference chunk.
*
* This chunk expands to the data stored in a preceding literal chunk,
* indicated by an index number (`index`).
*/
type Dcmp1_Chunk_BackreferenceBody struct {
IndexSeparateMinus uint8
Tag uint8
_io *kaitai.Stream
_root *Dcmp1
_parent *Dcmp1_Chunk
_f_isIndexSeparate bool
isIndexSeparate bool
_f_indexInTag bool
indexInTag int
_f_indexSeparate bool
indexSeparate int
_f_index bool
index int
}
func NewDcmp1_Chunk_BackreferenceBody(tag uint8) *Dcmp1_Chunk_BackreferenceBody {
return &Dcmp1_Chunk_BackreferenceBody{
Tag: tag,
}
}
func (this *Dcmp1_Chunk_BackreferenceBody) Read(io *kaitai.Stream, parent *Dcmp1_Chunk, root *Dcmp1) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp25, err := this.IsIndexSeparate()
if err != nil {
return err
}
if (tmp25) {
tmp26, err := this._io.ReadU1()
if err != nil {
return err
}
this.IndexSeparateMinus = tmp26
}
return err
}
/**
* Whether the index is stored separately from the tag.
*/
func (this *Dcmp1_Chunk_BackreferenceBody) IsIndexSeparate() (v bool, err error) {
if (this._f_isIndexSeparate) {
return this.isIndexSeparate, nil
}
this.isIndexSeparate = bool(this.Tag == 210)
this._f_isIndexSeparate = true
return this.isIndexSeparate, nil
}
/**
* The index of the referenced literal chunk,
* as stored in the tag byte.
*/
func (this *Dcmp1_Chunk_BackreferenceBody) IndexInTag() (v int, err error) {
if (this._f_indexInTag) {
return this.indexInTag, nil
}
this.indexInTag = int((this.Tag - 32))
this._f_indexInTag = true
return this.indexInTag, nil
}
/**
* The index of the referenced literal chunk,
* as stored separately from the tag byte,
* with the implicit offset corrected for.
*/
func (this *Dcmp1_Chunk_BackreferenceBody) IndexSeparate() (v int, err error) {
if (this._f_indexSeparate) {
return this.indexSeparate, nil
}
tmp27, err := this.IsIndexSeparate()
if err != nil {
return 0, err
}
if (tmp27) {
this.indexSeparate = int((this.IndexSeparateMinus + 176))
}
this._f_indexSeparate = true
return this.indexSeparate, nil
}
/**
* The index of the referenced literal chunk.
*
* Stored literals are assigned index numbers in the order in which they appear in the compressed data,
* starting at 0.
* Non-stored literals are not counted in the numbering and cannot be referenced using backreferences.
* Once an index is assigned to a stored literal,
* it is never changed or unassigned for the entire length of the compressed data.
*
* As the name indicates,
* a backreference can only reference stored literal chunks found *before* the backreference,
* not ones that come after it.
*/
func (this *Dcmp1_Chunk_BackreferenceBody) Index() (v int, err error) {
if (this._f_index) {
return this.index, nil
}
var tmp28 int;
tmp29, err := this.IsIndexSeparate()
if err != nil {
return 0, err
}
if (tmp29) {
tmp30, err := this.IndexSeparate()
if err != nil {
return 0, err
}
tmp28 = tmp30
} else {
tmp31, err := this.IndexInTag()
if err != nil {
return 0, err
}
tmp28 = tmp31
}
this.index = int(tmp28)
this._f_index = true
return this.index, nil
}
/**
* The index of the referenced literal chunk,
* stored separately from the tag.
* The value in this field is stored minus 0xb0.
*
* This field is only present if the tag byte is 0xd2.
* For other tag bytes,
* the index is encoded in the tag byte.
* Values smaller than 0xb0 cannot be stored in this field,
* they must always be encoded in the tag byte.
*/
/**
* The body of a table lookup chunk.
* This body is always empty.
*
* This chunk always expands to two bytes (`value`),
* determined from the tag byte using a fixed lookup table (`lookup_table`).
* This lookup table is hardcoded in the decompressor and always the same for all compressed data.
*/
type Dcmp1_Chunk_TableLookupBody struct {
Tag uint8
_io *kaitai.Stream
_root *Dcmp1
_parent *Dcmp1_Chunk
_f_lookupTable bool
lookupTable [][]byte
_f_value bool
value []byte
}
func NewDcmp1_Chunk_TableLookupBody(tag uint8) *Dcmp1_Chunk_TableLookupBody {
return &Dcmp1_Chunk_TableLookupBody{
Tag: tag,
}
}
func (this *Dcmp1_Chunk_TableLookupBody) Read(io *kaitai.Stream, parent *Dcmp1_Chunk, root *Dcmp1) (err error) {
this._io = io
this._parent = parent
this._root = root
return err
}
/**
* Fixed lookup table that maps tag byte numbers to two bytes each.
*
* The entries in the lookup table are offset -
* index 0 stands for tag 0xd5, 1 for 0xd6, etc.
*/
func (this *Dcmp1_Chunk_TableLookupBody) LookupTable() (v [][]byte, err error) {
if (this._f_lookupTable) {
return this.lookupTable, nil
}
this.lookupTable = [][]byte([][]byte{[]uint8{0, 0}, []uint8{0, 1}, []uint8{0, 2}, []uint8{0, 3}, []uint8{46, 1}, []uint8{62, 1}, []uint8{1, 1}, []uint8{30, 1}, []uint8{255, 255}, []uint8{14, 1}, []uint8{49, 0}, []uint8{17, 18}, []uint8{1, 7}, []uint8{51, 50}, []uint8{18, 57}, []uint8{237, 16}, []uint8{1, 39}, []uint8{35, 34}, []uint8{1, 55}, []uint8{7, 6}, []uint8{1, 23}, []uint8{1, 35}, []uint8{0, 255}, []uint8{0, 47}, []uint8{7, 14}, []uint8{253, 60}, []uint8{1, 53}, []uint8{1, 21}, []uint8{1, 2}, []uint8{0, 7}, []uint8{0, 62}, []uint8{5, 213}, []uint8{2, 1}, []uint8{6, 7}, []uint8{7, 8}, []uint8{48, 1}, []uint8{1, 51}, []uint8{0, 16}, []uint8{23, 22}, []uint8{55, 62}, []uint8{54, 55}})
this._f_lookupTable = true
return this.lookupTable, nil
}
/**
* The two bytes that the tag byte expands to,
* based on the fixed lookup table.
*/
func (this *Dcmp1_Chunk_TableLookupBody) Value() (v []byte, err error) {
if (this._f_value) {
return this.value, nil
}
tmp32, err := this.LookupTable()
if err != nil {
return nil, err
}
this.value = []byte(tmp32[(this.Tag - 213)])
this._f_value = true
return this.value, nil
}
/**
* The body of an end chunk.
* This body is always empty.
*
* The last chunk in the compressed data must always be an end chunk.
* An end chunk cannot appear elsewhere in the compressed data.
*/
type Dcmp1_Chunk_EndBody struct {
_io *kaitai.Stream
_root *Dcmp1
_parent *Dcmp1_Chunk
}
func NewDcmp1_Chunk_EndBody() *Dcmp1_Chunk_EndBody {
return &Dcmp1_Chunk_EndBody{
}
}
func (this *Dcmp1_Chunk_EndBody) Read(io *kaitai.Stream, parent *Dcmp1_Chunk, root *Dcmp1) (err error) {
this._io = io
this._parent = parent
this._root = root
return err
}
/**
* The body of an extended chunk.
* The meaning of this chunk depends on the extended tag byte stored in the chunk data.
*/
type Dcmp1_Chunk_ExtendedBody struct {
Tag uint8
Body *Dcmp1_Chunk_ExtendedBody_RepeatBody
_io *kaitai.Stream
_root *Dcmp1
_parent *Dcmp1_Chunk
}
func NewDcmp1_Chunk_ExtendedBody() *Dcmp1_Chunk_ExtendedBody {
return &Dcmp1_Chunk_ExtendedBody{
}
}
func (this *Dcmp1_Chunk_ExtendedBody) Read(io *kaitai.Stream, parent *Dcmp1_Chunk, root *Dcmp1) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp33, err := this._io.ReadU1()
if err != nil {
return err
}
this.Tag = tmp33
switch (this.Tag) {
case 2:
tmp34 := NewDcmp1_Chunk_ExtendedBody_RepeatBody()
err = tmp34.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Body = tmp34
}
return err
}
/**
* The chunk's extended tag byte.
* This controls the structure of the body and the meaning of the chunk.
*/
/**
* The chunk's body.
*/
/**
* The body of a repeat chunk.
*
* This chunk expands to the same byte repeated a number of times,
* i. e. it implements a form of run-length encoding.
*/
type Dcmp1_Chunk_ExtendedBody_RepeatBody struct {
ToRepeatRaw *DcmpVariableLengthInteger
RepeatCountM1Raw *DcmpVariableLengthInteger
_io *kaitai.Stream
_root *Dcmp1
_parent *Dcmp1_Chunk_ExtendedBody
_f_toRepeat bool
toRepeat int
_f_repeatCountM1 bool
repeatCountM1 int
_f_repeatCount bool
repeatCount int
}
func NewDcmp1_Chunk_ExtendedBody_RepeatBody() *Dcmp1_Chunk_ExtendedBody_RepeatBody {
return &Dcmp1_Chunk_ExtendedBody_RepeatBody{
}
}
func (this *Dcmp1_Chunk_ExtendedBody_RepeatBody) Read(io *kaitai.Stream, parent *Dcmp1_Chunk_ExtendedBody, root *Dcmp1) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp35 := NewDcmpVariableLengthInteger()
err = tmp35.Read(this._io, this, nil)
if err != nil {
return err
}
this.ToRepeatRaw = tmp35
tmp36 := NewDcmpVariableLengthInteger()
err = tmp36.Read(this._io, this, nil)
if err != nil {
return err
}
this.RepeatCountM1Raw = tmp36
return err
}
/**
* The value to repeat.
*
* Although it is stored as a variable-length integer,
* this value must fit into an unsigned 8-bit integer.
*/
func (this *Dcmp1_Chunk_ExtendedBody_RepeatBody) ToRepeat() (v int, err error) {
if (this._f_toRepeat) {
return this.toRepeat, nil
}
tmp37, err := this.ToRepeatRaw.Value()
if err != nil {
return 0, err
}
this.toRepeat = int(tmp37)
this._f_toRepeat = true
return this.toRepeat, nil
}
/**
* The number of times to repeat the value,
* minus one.
*
* This value must not be negative.
*/
func (this *Dcmp1_Chunk_ExtendedBody_RepeatBody) RepeatCountM1() (v int, err error) {
if (this._f_repeatCountM1) {
return this.repeatCountM1, nil
}
tmp38, err := this.RepeatCountM1Raw.Value()
if err != nil {
return 0, err
}
this.repeatCountM1 = int(tmp38)
this._f_repeatCountM1 = true
return this.repeatCountM1, nil
}
/**
* The number of times to repeat the value.
*
* This value must be positive.
*/
func (this *Dcmp1_Chunk_ExtendedBody_RepeatBody) RepeatCount() (v int, err error) {
if (this._f_repeatCount) {
return this.repeatCount, nil
}
tmp39, err := this.RepeatCountM1()
if err != nil {
return 0, err
}
this.repeatCount = int((tmp39 + 1))
this._f_repeatCount = true
return this.repeatCount, nil
}
/**
* Raw variable-length integer representation of `to_repeat`.
*/
/**
* Raw variable-length integer representation of `repeat_count_m1`.
*/