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

Compressed resource data in 'dcmp' (2) format, as stored in compressed resources with header type 9 and decompressor ID 2.

The 'dcmp' (2) 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 System file). This decompressor is also included with and used by some other Apple applications, such as ResEdit. (Note: ResEdit includes the 'dcmp' (2) resource, but none of its resources actually use this decompressor.)

This compression format is based on simple dictionary coding, where each byte in the compressed data expands to two bytes, based on a lookup table (either included in the compressed data or provided by the decompressor). An alternative "tagged" compression format is also supported, which allows using two-byte literals in addition to single-byte table references, at the cost of requiring an extra "tag" byte every 16 output bytes, to differentiate literals and table references.

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' (2)` 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_2.dot

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

		dcmp_2__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="custom_lookup_table_pos">0</TD><TD PORT="custom_lookup_table_size">2</TD><TD></TD><TD PORT="custom_lookup_table_type">custom_lookup_table</TD></TR>
			<TR><TD COLSPAN="4" PORT="custom_lookup_table__repeat">repeat header_parameters.num_custom_lookup_table_entries times</TD></TR>
			<TR><TD PORT="data_pos">...</TD><TD PORT="data_size">...</TD><TD>switch (header_parameters.flags.tagged)</TD><TD PORT="data_type">data</TD></TR>
			<TR><TD PORT="last_byte_pos">...</TD><TD PORT="last_byte_size">1</TD><TD></TD><TD PORT="last_byte_type">last_byte</TD></TR>
		</TABLE>>];
		dcmp_2__inst__header_parameters [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="header_parameters_pos">0</TD><TD PORT="header_parameters_size">4</TD><TD>HeaderParameters</TD><TD PORT="header_parameters_type">header_parameters</TD></TR>
		</TABLE>>];
		dcmp_2__inst__is_len_decompressed_odd [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
			<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
			<TR><TD>is_len_decompressed_odd</TD><TD>(len_decompressed % 2) != 0</TD></TR>
		</TABLE>>];
		dcmp_2__inst__default_lookup_table [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
			<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
			<TR><TD>default_lookup_table</TD><TD>[[0, 0].pack('C*'), [0, 8].pack('C*'), [78, 186].pack('C*'), [32, 110].pack('C*'), [78, 117].pack('C*'), [0, 12].pack('C*'), [0, 4].pack('C*'), [112, 0].pack('C*'), [0, 16].pack('C*'), [0, 2].pack('C*'), [72, 110].pack('C*'), [255, 252].pack('C*'), [96, 0].pack('C*'), [0, 1].pack('C*'), [72, 231].pack('C*'), [47, 46].pack('C*'), [78, 86].pack('C*'), [0, 6].pack('C*'), [78, 94].pack('C*'), [47, 0].pack('C*'), [97, 0].pack('C*'), [255, 248].pack('C*'), [47, 11].pack('C*'), [255, 255].pack('C*'), [0, 20].pack('C*'), [0, 10].pack('C*'), [0, 24].pack('C*'), [32, 95].pack('C*'), [0, 14].pack('C*'), [32, 80].pack('C*'), [63, 60].pack('C*'), [255, 244].pack('C*'), [76, 238].pack('C*'), [48, 46].pack('C*'), [103, 0].pack('C*'), [76, 223].pack('C*'), [38, 110].pack('C*'), [0, 18].pack('C*'), [0, 28].pack('C*'), [66, 103].pack('C*'), [255, 240].pack('C*'), [48, 60].pack('C*'), [47, 12].pack('C*'), [0, 3].pack('C*'), [78, 208].pack('C*'), [0, 32].pack('C*'), [112, 1].pack('C*'), [0, 22].pack('C*'), [45, 64].pack('C*'), [72, 192].pack('C*'), [32, 120].pack('C*'), [114, 0].pack('C*'), [88, 143].pack('C*'), [102, 0].pack('C*'), [79, 239].pack('C*'), [66, 167].pack('C*'), [103, 6].pack('C*'), [255, 250].pack('C*'), [85, 143].pack('C*'), [40, 110].pack('C*'), [63, 0].pack('C*'), [255, 254].pack('C*'), [47, 60].pack('C*'), [103, 4].pack('C*'), [89, 143].pack('C*'), [32, 107].pack('C*'), [0, 36].pack('C*'), [32, 31].pack('C*'), [65, 250].pack('C*'), [129, 225].pack('C*'), [102, 4].pack('C*'), [103, 8].pack('C*'), [0, 26].pack('C*'), [78, 185].pack('C*'), [80, 143].pack('C*'), [32, 46].pack('C*'), [0, 7].pack('C*'), [78, 176].pack('C*'), [255, 242].pack('C*'), [61, 64].pack('C*'), [0, 30].pack('C*'), [32, 104].pack('C*'), [102, 6].pack('C*'), [255, 246].pack('C*'), [78, 249].pack('C*'), [8, 0].pack('C*'), [12, 64].pack('C*'), [61, 124].pack('C*'), [255, 236].pack('C*'), [0, 5].pack('C*'), [32, 60].pack('C*'), [255, 232].pack('C*'), [222, 252].pack('C*'), [74, 46].pack('C*'), [0, 48].pack('C*'), [0, 40].pack('C*'), [47, 8].pack('C*'), [32, 11].pack('C*'), [96, 2].pack('C*'), [66, 110].pack('C*'), [45, 72].pack('C*'), [32, 83].pack('C*'), [32, 64].pack('C*'), [24, 0].pack('C*'), [96, 4].pack('C*'), [65, 238].pack('C*'), [47, 40].pack('C*'), [47, 1].pack('C*'), [103, 10].pack('C*'), [72, 64].pack('C*'), [32, 7].pack('C*'), [102, 8].pack('C*'), [1, 24].pack('C*'), [47, 7].pack('C*'), [48, 40].pack('C*'), [63, 46].pack('C*'), [48, 43].pack('C*'), [34, 110].pack('C*'), [47, 43].pack('C*'), [0, 44].pack('C*'), [103, 12].pack('C*'), [34, 95].pack('C*'), [96, 6].pack('C*'), [0, 255].pack('C*'), [48, 7].pack('C*'), [255, 238].pack('C*'), [83, 64].pack('C*'), [0, 64].pack('C*'), [255, 228].pack('C*'), [74, 64].pack('C*'), [102, 10].pack('C*'), [0, 15].pack('C*'), [78, 173].pack('C*'), [112, 255].pack('C*'), [34, 216].pack('C*'), [72, 107].pack('C*'), [0, 34].pack('C*'), [32, 75].pack('C*'), [103, 14].pack('C*'), [74, 174].pack('C*'), [78, 144].pack('C*'), [255, 224].pack('C*'), [255, 192].pack('C*'), [0, 42].pack('C*'), [39, 64].pack('C*'), [103, 2].pack('C*'), [81, 200].pack('C*'), [2, 182].pack('C*'), [72, 122].pack('C*'), [34, 120].pack('C*'), [176, 110].pack('C*'), [255, 230].pack('C*'), [0, 9].pack('C*'), [50, 46].pack('C*'), [62, 0].pack('C*'), [72, 65].pack('C*'), [255, 234].pack('C*'), [67, 238].pack('C*'), [78, 113].pack('C*'), [116, 0].pack('C*'), [47, 44].pack('C*'), [32, 108].pack('C*'), [0, 60].pack('C*'), [0, 38].pack('C*'), [0, 80].pack('C*'), [24, 128].pack('C*'), [48, 31].pack('C*'), [34, 0].pack('C*'), [102, 12].pack('C*'), [255, 218].pack('C*'), [0, 56].pack('C*'), [102, 2].pack('C*'), [48, 44].pack('C*'), [32, 12].pack('C*'), [45, 110].pack('C*'), [66, 64].pack('C*'), [255, 226].pack('C*'), [169, 240].pack('C*'), [255, 0].pack('C*'), [55, 124].pack('C*'), [229, 128].pack('C*'), [255, 220].pack('C*'), [72, 104].pack('C*'), [89, 79].pack('C*'), [0, 52].pack('C*'), [62, 31].pack('C*'), [96, 8].pack('C*'), [47, 6].pack('C*'), [255, 222].pack('C*'), [96, 10].pack('C*'), [112, 2].pack('C*'), [0, 50].pack('C*'), [255, 204].pack('C*'), [0, 128].pack('C*'), [34, 81].pack('C*'), [16, 31].pack('C*'), [49, 124].pack('C*'), [160, 41].pack('C*'), [255, 216].pack('C*'), [82, 64].pack('C*'), [1, 0].pack('C*'), [103, 16].pack('C*'), [160, 35].pack('C*'), [255, 206].pack('C*'), [255, 212].pack('C*'), [32, 6].pack('C*'), [72, 120].pack('C*'), [0, 46].pack('C*'), [80, 79].pack('C*'), [67, 250].pack('C*'), [103, 18].pack('C*'), [118, 0].pack('C*'), [65, 232].pack('C*'), [74, 110].pack('C*'), [32, 217].pack('C*'), [0, 90].pack('C*'), [127, 255].pack('C*'), [81, 202].pack('C*'), [0, 92].pack('C*'), [46, 0].pack('C*'), [2, 64].pack('C*'), [72, 199].pack('C*'), [103, 20].pack('C*'), [12, 128].pack('C*'), [46, 159].pack('C*'), [255, 214].pack('C*'), [128, 0].pack('C*'), [16, 0].pack('C*'), [72, 66].pack('C*'), [74, 107].pack('C*'), [255, 210].pack('C*'), [0, 72].pack('C*'), [74, 71].pack('C*'), [78, 209].pack('C*'), [32, 111].pack('C*'), [0, 65].pack('C*'), [96, 12].pack('C*'), [42, 120].pack('C*'), [66, 46].pack('C*'), [50, 0].pack('C*'), [101, 116].pack('C*'), [103, 22].pack('C*'), [0, 68].pack('C*'), [72, 109].pack('C*'), [32, 8].pack('C*'), [72, 108].pack('C*'), [11, 124].pack('C*'), [38, 64].pack('C*'), [4, 0].pack('C*'), [0, 104].pack('C*'), [32, 109].pack('C*'), [0, 13].pack('C*'), [42, 64].pack('C*'), [0, 11].pack('C*'), [0, 62].pack('C*'), [2, 32].pack('C*')]</TD></TR>
		</TABLE>>];
		dcmp_2__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>(header_parameters.flags.has_custom_lookup_table ? custom_lookup_table : default_lookup_table)</TD></TR>
		</TABLE>>];
dcmp_2__seq_data_switch [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
	<TR><TD BGCOLOR="#F0F2E4">case</TD><TD BGCOLOR="#F0F2E4">type</TD></TR>
	<TR><TD>true</TD><TD PORT="case0">TaggedData</TD></TR>
	<TR><TD>_</TD><TD PORT="case1">UntaggedData</TD></TR>
</TABLE>>];
		subgraph cluster__header_parameters {
			label="Dcmp2::HeaderParameters";
			graph[style=dotted];

			header_parameters__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="unknown_pos">0</TD><TD PORT="unknown_size">2</TD><TD>u2be</TD><TD PORT="unknown_type">unknown</TD></TR>
				<TR><TD PORT="num_custom_lookup_table_entries_m1_pos">2</TD><TD PORT="num_custom_lookup_table_entries_m1_size">1</TD><TD>u1</TD><TD PORT="num_custom_lookup_table_entries_m1_type">num_custom_lookup_table_entries_m1</TD></TR>
				<TR><TD PORT="flags_pos">3</TD><TD PORT="flags_size">1</TD><TD>Flags</TD><TD PORT="flags_type">flags</TD></TR>
			</TABLE>>];
			header_parameters__inst__num_custom_lookup_table_entries [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
				<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
				<TR><TD>num_custom_lookup_table_entries</TD><TD>(num_custom_lookup_table_entries_m1 + 1)</TD></TR>
			</TABLE>>];
			subgraph cluster__flags {
				label="Dcmp2::HeaderParameters::Flags";
				graph[style=dotted];

				flags__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="reserved_pos">0</TD><TD PORT="reserved_size">6b</TD><TD>b6</TD><TD PORT="reserved_type">reserved</TD></TR>
					<TR><TD PORT="tagged_pos">0:6</TD><TD PORT="tagged_size">1b</TD><TD>BitsType1(BigBitEndian)</TD><TD PORT="tagged_type">tagged</TD></TR>
					<TR><TD PORT="has_custom_lookup_table_pos">0:7</TD><TD PORT="has_custom_lookup_table_size">1b</TD><TD>BitsType1(BigBitEndian)</TD><TD PORT="has_custom_lookup_table_type">has_custom_lookup_table</TD></TR>
				</TABLE>>];
				flags__inst__as_int [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="as_int_pos">0</TD><TD PORT="as_int_size">1</TD><TD>u1</TD><TD PORT="as_int_type">as_int</TD></TR>
				</TABLE>>];
			}
		}
		subgraph cluster__untagged_data {
			label="Dcmp2::UntaggedData";
			graph[style=dotted];

			untagged_data__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="table_references_pos">0</TD><TD PORT="table_references_size">1</TD><TD>u1</TD><TD PORT="table_references_type">table_references</TD></TR>
				<TR><TD COLSPAN="4" PORT="table_references__repeat">repeat to end of stream</TD></TR>
			</TABLE>>];
		}
		subgraph cluster__tagged_data {
			label="Dcmp2::TaggedData";
			graph[style=dotted];

			tagged_data__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 to end of stream</TD></TR>
			</TABLE>>];
			subgraph cluster__chunk {
				label="Dcmp2::TaggedData::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">1b</TD><TD>BitsType1(BigBitEndian)</TD><TD PORT="tag_type">tag</TD></TR>
					<TR><TD COLSPAN="4" PORT="tag__repeat">repeat 8 times</TD></TR>
					<TR><TD PORT="units_pos">1</TD><TD PORT="units_size">...</TD><TD>switch (tag[i])</TD><TD PORT="units_type">units</TD></TR>
					<TR><TD COLSPAN="4" PORT="units__repeat">repeat until  ((i &gt;= 7) || (_io.eof?)) </TD></TR>
				</TABLE>>];
chunk__seq_units_switch [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
	<TR><TD BGCOLOR="#F0F2E4">case</TD><TD BGCOLOR="#F0F2E4">type</TD></TR>
</TABLE>>];
			}
		}
	}
	header_parameters__inst__num_custom_lookup_table_entries:num_custom_lookup_table_entries_type -> dcmp_2__seq:custom_lookup_table__repeat [color="#404040"];
	dcmp_2__seq:data_type -> dcmp_2__seq_data_switch [style=bold];
	dcmp_2__seq_data_switch:case0 -> tagged_data__seq [style=bold];
	dcmp_2__seq_data_switch:case1 -> untagged_data__seq [style=bold];
	flags__seq:tagged_type -> dcmp_2__seq:data_type [color="#404040"];
	dcmp_2__inst__header_parameters:header_parameters_type -> header_parameters__seq [style=bold];
	dcmp_2__params:len_decompressed_type -> dcmp_2__inst__is_len_decompressed_odd [color="#404040"];
	flags__seq:has_custom_lookup_table_type -> dcmp_2__inst__lookup_table [color="#404040"];
	dcmp_2__seq:custom_lookup_table_type -> dcmp_2__inst__lookup_table [color="#404040"];
	dcmp_2__inst__default_lookup_table:default_lookup_table_type -> dcmp_2__inst__lookup_table [color="#404040"];
	header_parameters__seq:flags_type -> flags__seq [style=bold];
	header_parameters__seq:num_custom_lookup_table_entries_m1_type -> header_parameters__inst__num_custom_lookup_table_entries [color="#404040"];
	tagged_data__seq:chunks_type -> chunk__seq [style=bold];
	chunk__seq:units_type -> chunk__seq_units_switch [style=bold];
	chunk__seq:tag_type -> chunk__seq:units_type [color="#404040"];
}