Compressed Macintosh resource data, Apple `'dcmp' (1)` format: GraphViz block diagram (.dot) source

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.

Application

Mac OS

KS implementation details

License: MIT
Minimal Kaitai Struct required: 0.8

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.

GraphViz block diagram source

dcmp_1.dot

digraph {
	rankdir=LR;
	node [shape=plaintext];
	subgraph cluster__dcmp_1 {
		label="Dcmp1";
		graph[style=dotted];

		dcmp_1__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
			<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
			<TR><TD PORT="chunks_pos">0</TD><TD PORT="chunks_size">...</TD><TD>Chunk</TD><TD PORT="chunks_type">chunks</TD></TR>
			<TR><TD COLSPAN="4" PORT="chunks__repeat">repeat until _.tag == 255</TD></TR>
		</TABLE>>];
		subgraph cluster__chunk {
			label="Dcmp1::Chunk";
			graph[style=dotted];

			chunk__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
				<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
				<TR><TD PORT="tag_pos">0</TD><TD PORT="tag_size">1</TD><TD>u1</TD><TD PORT="tag_type">tag</TD></TR>
				<TR><TD PORT="body_pos">1</TD><TD PORT="body_size">...</TD><TD>switch (( ((tag &gt;= 0) &amp;&amp; (tag &lt;= 31))  ? :tag_kind_literal : ( ((tag &gt;= 32) &amp;&amp; (tag &lt;= 207))  ? :tag_kind_backreference : ( ((tag &gt;= 208) &amp;&amp; (tag &lt;= 209))  ? :tag_kind_literal : (tag == 210 ? :tag_kind_backreference : ( ((tag &gt;= 213) &amp;&amp; (tag &lt;= 253))  ? :tag_kind_table_lookup : (tag == 254 ? :tag_kind_extended : (tag == 255 ? :tag_kind_end : :tag_kind_invalid))))))))</TD><TD PORT="body_type">body</TD></TR>
			</TABLE>>];
chunk__seq_body_switch [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
	<TR><TD BGCOLOR="#F0F2E4">case</TD><TD BGCOLOR="#F0F2E4">type</TD></TR>
	<TR><TD>:tag_kind_extended</TD><TD PORT="case0">ExtendedBody</TD></TR>
	<TR><TD>:tag_kind_literal</TD><TD PORT="case1">LiteralBody</TD></TR>
	<TR><TD>:tag_kind_end</TD><TD PORT="case2">EndBody</TD></TR>
	<TR><TD>:tag_kind_table_lookup</TD><TD PORT="case3">TableLookupBody</TD></TR>
	<TR><TD>:tag_kind_backreference</TD><TD PORT="case4">BackreferenceBody</TD></TR>
</TABLE>>];
			subgraph cluster__literal_body {
				label="Dcmp1::Chunk::LiteralBody";
				graph[style=dotted];

				literal_body__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
					<TR><TD PORT="len_literal_separate_pos">0</TD><TD PORT="len_literal_separate_size">1</TD><TD>u1</TD><TD PORT="len_literal_separate_type">len_literal_separate</TD></TR>
					<TR><TD PORT="literal_pos">1</TD><TD PORT="literal_size">len_literal</TD><TD></TD><TD PORT="literal_type">literal</TD></TR>
				</TABLE>>];
				literal_body__inst__do_store [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
					<TR><TD>do_store</TD><TD>(is_len_literal_separate ? tag == 209 : (tag &amp; 16) != 0)</TD></TR>
				</TABLE>>];
				literal_body__inst__len_literal_m1_in_tag [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
					<TR><TD>len_literal_m1_in_tag</TD><TD>(tag &amp; 15)</TD></TR>
				</TABLE>>];
				literal_body__inst__is_len_literal_separate [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
					<TR><TD>is_len_literal_separate</TD><TD>tag &gt;= 208</TD></TR>
				</TABLE>>];
				literal_body__inst__len_literal [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
					<TR><TD>len_literal</TD><TD>(is_len_literal_separate ? len_literal_separate : (len_literal_m1_in_tag + 1))</TD></TR>
				</TABLE>>];
			}
			subgraph cluster__backreference_body {
				label="Dcmp1::Chunk::BackreferenceBody";
				graph[style=dotted];

				backreference_body__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
					<TR><TD PORT="index_separate_minus_pos">0</TD><TD PORT="index_separate_minus_size">1</TD><TD>u1</TD><TD PORT="index_separate_minus_type">index_separate_minus</TD></TR>
				</TABLE>>];
				backreference_body__inst__is_index_separate [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
					<TR><TD>is_index_separate</TD><TD>tag == 210</TD></TR>
				</TABLE>>];
				backreference_body__inst__index_in_tag [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
					<TR><TD>index_in_tag</TD><TD>(tag - 32)</TD></TR>
				</TABLE>>];
				backreference_body__inst__index_separate [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
					<TR><TD>index_separate</TD><TD>(index_separate_minus + 176)</TD></TR>
				</TABLE>>];
				backreference_body__inst__index [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
					<TR><TD>index</TD><TD>(is_index_separate ? index_separate : index_in_tag)</TD></TR>
				</TABLE>>];
			}
			subgraph cluster__table_lookup_body {
				label="Dcmp1::Chunk::TableLookupBody";
				graph[style=dotted];

				table_lookup_body__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
				</TABLE>>];
				table_lookup_body__inst__lookup_table [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
					<TR><TD>lookup_table</TD><TD>[[0, 0].pack('C*'), [0, 1].pack('C*'), [0, 2].pack('C*'), [0, 3].pack('C*'), [46, 1].pack('C*'), [62, 1].pack('C*'), [1, 1].pack('C*'), [30, 1].pack('C*'), [255, 255].pack('C*'), [14, 1].pack('C*'), [49, 0].pack('C*'), [17, 18].pack('C*'), [1, 7].pack('C*'), [51, 50].pack('C*'), [18, 57].pack('C*'), [237, 16].pack('C*'), [1, 39].pack('C*'), [35, 34].pack('C*'), [1, 55].pack('C*'), [7, 6].pack('C*'), [1, 23].pack('C*'), [1, 35].pack('C*'), [0, 255].pack('C*'), [0, 47].pack('C*'), [7, 14].pack('C*'), [253, 60].pack('C*'), [1, 53].pack('C*'), [1, 21].pack('C*'), [1, 2].pack('C*'), [0, 7].pack('C*'), [0, 62].pack('C*'), [5, 213].pack('C*'), [2, 1].pack('C*'), [6, 7].pack('C*'), [7, 8].pack('C*'), [48, 1].pack('C*'), [1, 51].pack('C*'), [0, 16].pack('C*'), [23, 22].pack('C*'), [55, 62].pack('C*'), [54, 55].pack('C*')]</TD></TR>
				</TABLE>>];
				table_lookup_body__inst__value [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
					<TR><TD>value</TD><TD>lookup_table[(tag - 213)]</TD></TR>
				</TABLE>>];
			}
			subgraph cluster__end_body {
				label="Dcmp1::Chunk::EndBody";
				graph[style=dotted];

				end_body__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
				</TABLE>>];
			}
			subgraph cluster__extended_body {
				label="Dcmp1::Chunk::ExtendedBody";
				graph[style=dotted];

				extended_body__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
					<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
					<TR><TD PORT="tag_pos">0</TD><TD PORT="tag_size">1</TD><TD>u1</TD><TD PORT="tag_type">tag</TD></TR>
					<TR><TD PORT="body_pos">1</TD><TD PORT="body_size">...</TD><TD>switch (tag)</TD><TD PORT="body_type">body</TD></TR>
				</TABLE>>];
extended_body__seq_body_switch [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
	<TR><TD BGCOLOR="#F0F2E4">case</TD><TD BGCOLOR="#F0F2E4">type</TD></TR>
	<TR><TD>2</TD><TD PORT="case0">RepeatBody</TD></TR>
</TABLE>>];
				subgraph cluster__repeat_body {
					label="Dcmp1::Chunk::ExtendedBody::RepeatBody";
					graph[style=dotted];

					repeat_body__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
						<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
						<TR><TD PORT="to_repeat_raw_pos">0</TD><TD PORT="to_repeat_raw_size">...</TD><TD>DcmpVariableLengthInteger</TD><TD PORT="to_repeat_raw_type">to_repeat_raw</TD></TR>
						<TR><TD PORT="repeat_count_m1_raw_pos">...</TD><TD PORT="repeat_count_m1_raw_size">...</TD><TD>DcmpVariableLengthInteger</TD><TD PORT="repeat_count_m1_raw_type">repeat_count_m1_raw</TD></TR>
					</TABLE>>];
					repeat_body__inst__to_repeat [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
						<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
						<TR><TD>to_repeat</TD><TD>to_repeat_raw.value</TD></TR>
					</TABLE>>];
					repeat_body__inst__repeat_count_m1 [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
						<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
						<TR><TD>repeat_count_m1</TD><TD>repeat_count_m1_raw.value</TD></TR>
					</TABLE>>];
					repeat_body__inst__repeat_count [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
						<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
						<TR><TD>repeat_count</TD><TD>(repeat_count_m1 + 1)</TD></TR>
					</TABLE>>];
				}
			}
		}
	}
	dcmp_1__seq:chunks_type -> chunk__seq [style=bold];
	chunk__seq:tag_type -> dcmp_1__seq:chunks__repeat [color="#404040"];
	chunk__seq:body_type -> chunk__seq_body_switch [style=bold];
	chunk__seq_body_switch:case0 -> extended_body__seq [style=bold];
	chunk__seq_body_switch:case1 -> literal_body__seq [style=bold];
	chunk__seq_body_switch:case2 -> end_body__seq [style=bold];
	chunk__seq_body_switch:case3 -> table_lookup_body__seq [style=bold];
	chunk__seq_body_switch:case4 -> backreference_body__seq [style=bold];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	chunk__seq:tag_type -> chunk__seq:body_type [color="#404040"];
	literal_body__inst__len_literal:len_literal_type -> literal_body__seq:literal_size [color="#404040"];
	literal_body__inst__is_len_literal_separate:is_len_literal_separate_type -> literal_body__inst__do_store [color="#404040"];
	literal_body__params:tag_type -> literal_body__inst__do_store [color="#404040"];
	literal_body__params:tag_type -> literal_body__inst__do_store [color="#404040"];
	literal_body__params:tag_type -> literal_body__inst__len_literal_m1_in_tag [color="#404040"];
	literal_body__params:tag_type -> literal_body__inst__is_len_literal_separate [color="#404040"];
	literal_body__inst__is_len_literal_separate:is_len_literal_separate_type -> literal_body__inst__len_literal [color="#404040"];
	literal_body__seq:len_literal_separate_type -> literal_body__inst__len_literal [color="#404040"];
	literal_body__inst__len_literal_m1_in_tag:len_literal_m1_in_tag_type -> literal_body__inst__len_literal [color="#404040"];
	backreference_body__params:tag_type -> backreference_body__inst__is_index_separate [color="#404040"];
	backreference_body__params:tag_type -> backreference_body__inst__index_in_tag [color="#404040"];
	backreference_body__seq:index_separate_minus_type -> backreference_body__inst__index_separate [color="#404040"];
	backreference_body__inst__is_index_separate:is_index_separate_type -> backreference_body__inst__index [color="#404040"];
	backreference_body__inst__index_separate:index_separate_type -> backreference_body__inst__index [color="#404040"];
	backreference_body__inst__index_in_tag:index_in_tag_type -> backreference_body__inst__index [color="#404040"];
	table_lookup_body__inst__lookup_table:lookup_table_type -> table_lookup_body__inst__value [color="#404040"];
	table_lookup_body__params:tag_type -> table_lookup_body__inst__value [color="#404040"];
	extended_body__seq:body_type -> extended_body__seq_body_switch [style=bold];
	extended_body__seq_body_switch:case0 -> repeat_body__seq [style=bold];
	extended_body__seq:tag_type -> extended_body__seq:body_type [color="#404040"];
	repeat_body__seq:to_repeat_raw_type -> dcmp_variable_length_integer__seq [style=bold];
	repeat_body__seq:repeat_count_m1_raw_type -> dcmp_variable_length_integer__seq [style=bold];
	dcmp_variable_length_integer__inst__value:value_type -> repeat_body__inst__to_repeat [color="#404040"];
	dcmp_variable_length_integer__inst__value:value_type -> repeat_body__inst__repeat_count_m1 [color="#404040"];
	repeat_body__inst__repeat_count_m1:repeat_count_m1_type -> repeat_body__inst__repeat_count [color="#404040"];
}