Skip to content

Habitat Object image format

StuBlad edited this page Jun 16, 2023 · 11 revisions

Habitat Object image format

These notes were written by Aric Wilmunder, original creator of Habitats animation engine. They were written on 06/16/2023 based on looking at the following image from Habitat:

https://github.com/Museum-of-Art-and-Digital-Entertainment/habitat/blob/master/aric/mic/Gr/Heads/afro0.m

https://github.com/Museum-of-Art-and-Digital-Entertainment/habitat/blob/master/aric/mic/Gr/Heads/afro0.bin

https://github.com/Museum-of-Art-and-Digital-Entertainment/habitat/blob/master/aric/mic/Gr/Heads/afro0.cel

Let’s start with the cel data itself. This cels can contain one of four colors, so they are represented in base 4, so each byte contains two bit pairs with values of 0, 1, 2 or 3. Below shows decimal and binary values, and the pixel color they represent

0 or 0b00 - Transparent
1 or 0b01 - Blue
2 or 0b10 - Black
3 or 0b11 - Pink

Same info in Hex where each Hex value represents two colors and Binary:

Hex Hex Color
0x0 0b 00 00 Transparent, Transparent
0x1 0b 00 01 Transparent, Blue
0x2 0b 00 10 Transparent, Black
0x3 0b 00 11 Transparent, Pink
0x4 0b 01 00 Blue, Transparent
0x5 0b 01 01 Blue, Blue
0x6 0b 01 10 Blue Black
0x7 0b 01 11 Blue Pink
0x8 0b 10 00 Black Transparent
0x9 0b 10 01 Black Blue
0xA 0b 10 10 Black Black
0xB 0b 10 11 Black Pink
0xC 0b 11 00 Pink Transparent
0xD 0b 11 01 Pink Blue
0xE 0b 11 10 Pink Black
0xF 0b 11 11 Pink Pink

So a Byte containing Hex 0x75 would be Blue Pink for the upper 0x70 hex value, and Blue Blue for the lower 0x05 hex value.

The Cel compression format

The cel compression format is very simplistic.

A non-zero value indicates raw data, so just convert the hex values into the pattern using the table above. A 0xA7 would represent a pattern of Black Black Blue Pink.

A zero value (0x00) indicates a run will follow. A run is a repeated pattern.

If the byte following the run indicator has the high bit set (0x80) this is a transparent run, and the low 7 bits are the number of repeats of transparency.

A 0x00 (run) followed by 0x80 + 4 indicates four rows of transparency four pixels wide.

If the high bit is NOT set, the next byte represents the length of the run, and the third byte indicates the pattern of the run.

A 0x00 (run) followed by 14 (the run length) followed by the pattern 149 (0x95) means the pattern of Black Blue Blue Blue repeated 14 times.

A few more points. The cels are drawn from the bottom up. The first byte in the cel data header is the width (a 0x04 indicates 4 bytes wide) and the next value indicates the height (a 0x1e indicates the cel is 30 pixels high) When you hit the height of the cel, move over four pixels and start back at the bottom of the cel.

Object Header

The object image data is not well documented within the file. Inside the ‘Heads’ folder I found a file named Object_data. It appears to contain just the header info from most of the head files along with some info on what the header bytes contain. Here’s a bit of that file…

Object x_fix y_fix how held disk width walkto location container? gr_states /expres/nobend/rght/frnt/down/back
afro0 0 63 swing 11000000 3 244+right,28+left,255 no_cont 0,0,1,1,0,0,2,2 10000000 01000000 01100000

X_fix and y_fix may be the position of the start of the cel in relation to the base of the object. Honestly, I’m not really sure.

How Held indicates how the object is held when the Avatar is walking. Do your arms swing when carrying it, or do you need to hold your arms straight out, like when carrying a box.

Not sure about disk or width.

Walkto Location I believe indicates where the Avatar will walk to in relation to the object when it is on the ground. This depends on the direction the Avatar is facing.

The container flag is 0 if this is object is no_cont or not a container, and presumably non-zero if it is.

Gr_states I think should be right/left/down/back. This must control what animation is done based on the orientation of the object.

I have tried to map that info over the afro0 file. Not perfect, but it is a close match.

;---------------------------------------
;
;	animation cel data
;

afro0_data::
	byte	swing	+ 2 // How object is held (arms swinging or straight out?)
	byte	0b11000000 // Unknown
	byte	afro0_start_end - afro0_data // offset to end of header
	byte	no_cont // Not a container (0)   I’m presuming objects that are containers are set true

	byte	244+right,28+left,255 // Walk to location and face (where to stand when picking up right=1, left=0)

	byte	0b10000000 // Unknown at this time
	byte	0b01000000
	byte	0b01100000

	word	afro0_data_a - afro0_data // Offsets to the start of each cel
	word	afro0_data_b - afro0_data
	word	afro0_data_c - afro0_data

afro0_start_end:
	byte	0,0,1,1,0,0,2,2 // Graphical states?  Animation data?  right, left, down, back?

Here is the data for the first cel

afro0_data_a:

byte

Cel Width Height Horizontal Run Index Vert Run Index Cel X Offset Cel Y Offset
0x04 0x1e 0x00 0x08 0x00 0x00

The first two bytes of the header indicate that this cel is 4 strips wide, and 0x1e or 30 pixels tall.

byte run,0x80+4 // a run (0x00) followed by High bit=indicates transparent + length of four bytes

This is the start of literal or raw data:

	byte	2 // 0x02 transparent transparent transparent black
	byte	10 // 0x0a transparent transparent black black
	byte	9
	byte	9
	byte	41
	byte	165


	byte	run,14,149 // a run (0x00) followed by a run length of 14 of the pattern 149 (0x95) or Black Blue Blue Blue

	byte	165
	byte	41
	byte	10
	byte	2
	byte	2

	byte	run,0x80+2

	byte	47
	byte	42
	byte	47
	byte	175
	byte	175
	byte	106
	byte	106
	byte	85
	byte	86
	byte	91
	byte	91
	byte	90
	byte	86
	byte	run,15,85
	byte	170
	byte	40
	byte	184
	byte	248
	byte	250
	byte	239
	byte	239
	byte	run,3,191
	byte	run,3,255
	byte	239
	byte	175
	byte	111
	byte	107
	byte	run,3,91
	byte	90
	byte	run,8,85
	byte	86
	byte	170
	byte	run,0x80+3
	byte	128
	byte	run,3,224
	byte	run,4,248
	byte	run,3,254
	byte	248
	byte	run,4,224
	byte	168
	byte	90
	byte	run,4,86
	byte	90
	byte	104
	byte	160
	byte	128
	byte	run,0x80+1

So, what does this data look like decompressed?

I had to flip the image because I forgot that Cels start at the bottom. Also C64 pixels are not square, so adding the empty columns helps correct the aspect ratio. Eyes and mouths are added at a second pass to give the character expressions.

A piece of graph paper with a head from Lucasfilm's Habitat mapped out onto it