BAM file format
FileExtension : BAM
Content : Description of animations format BAM V.1/BAMC V1
History : File done by IEFFHP
Devarrah 25.11.2001
Description

These files primarily hold animations. However, they also hold some other static images which are not meant to be animated, but are, rather, simply need to be packaged together. For instance, fonts in Infinity Engine games are typically packaged in these files. As another example, most GUI controls have all of their supporting graphics packaged in BAM files, with, for instance, one frame representing the unpressed state of a button, one representing the pressed state, one for the selected state, and one for the disabled state. Note that a maximum of 255 cycles (animations) are supported. Since the fonts are stored with each character being a 'cycle', there are a maximum of 255 displayable chars in an infinity engine font. (i.e. the character 0xff does not have a representative in the font.
BAM file versions

    * V1: Baldur's Gate, Planescape: Torment, Icewind Dale, Baldur's Gate 2
    * BAMC V1: Baldur's Gate 2

BAM V1

Overall structure

    * Header
    * Frame entries
    * Cycle entries
    * Palette
    * Frame Lookup Table
    * Frame data

BAM V1 Header
Offset Size (data type) Description
0x0000 4 (char array) Signature ('BAM ')
0x0004 4 (char array) Version ('V1 ')
0x0008 2 (word) Number of frames in this file
0x000a 1 (unsigned byte) Number of cycles in this file
0x000b 1 (unsigned byte) It is the compressed color index for RLE encoded bams... IE this is the color that is compressed and it isn't the color that represents transparency.

The transparency index is calculated thus: it's the first occurence of  rgb(0,255,0) (it can occur twice) and if that color isn't present in the palette than the color at index 0 is the transparency color
0x000c 4 (dword) Offset from start of file to frame entries (which are immediately followed by cycle entries)
0x0010 4 (dword) Offset from start of file to palette
0x0014 4 (dword) Offset from start of file to frame lookup table
BAM V1 Frame Entries

Several cycles may share the same frame. This is done by adding a layer of indirection. Instead of specifying which frames belong to a given cycle, each cycle has a list of frame indices.
Offset Size (data type) Description
0x0000 2 (word) Width of frame
0x0002 2 (word) Height of frame
0x0004 2 (word) X coordinate of center of frame
0x0006 2 (word) Y coordinate of center of frame
0x0008 4 (dword)

    * bits 30-0: Offset to frame data
    * bit 31: if set, frame is not compressed, else frame is RLE compressed

BAM V1 Cycle Entries

These entries refer to a range of indices in the frame lookup table, which in turn points to the actual frames. Note that entries in the frame lookup table can also be shared between cycles.
Offset Size (data type) Description
0x0000 2 (word) Number of frame indices in this cycle
0x0002 2 (word) Index into frame lookup table of first frame index in this cycle
BAM V1 Palette

The palette is a standard array of 'RGBQUAD' structures, which means that the layout looks like:

	BB GG RR 00
	BB GG RR 00
	BB GG RR 00
	...
	BB GG RR 00

Two possibilities for the purpose of the extra byte ('00') are that it is either the alpha component, always set to 0, or that, as is frequently the case with Windows programming via GDI, the extra byte could hold flags. (i.e. There is a flag that can be set in such entries under Windows to force it to treat the palette entry as a palette index instead of an RGB value.) In practice, it doesn't matter too much, since BG always runs in 16-bit or higher color mode, and doesn't use alpha transparency in its animations.
BAM V1 Frame Lookup Table

This is an array of frame indices. A cycle specifies a sequence of entries in this table. For instance, an animation might start at the 0th element in this array, and travel over 6 frames. (The next animation, then, would typically start at the 6th element and run over some number of indices.) If the first 6 entries in this table were { 0, 1, 1, 2, 3, 4 }, the animation would display frame #0, followed by frame #1 for two time periods, followed by frames 2, 3, 4 in order. To find the number of entries in this lookup table, all you need to do is find the largest value of start+count in the cycle entries table.
BAM V1 Frame Data

If this is not a compressed frame, then this is simply width*height bytes, which are pixel values using the palette specified earlier.

If this is a compressed frame, then a simple run-length-encoding scheme applies. The scheme is as follows:

    * Any byte which is not the transparent index from the header represents itself.
    * The transparent index followed by a byte x represents (x+1) copies of the transparent index

If you are not familiar with run length encoding, it is a big win because most pixels in this type of image are transparent, and up to 256 transparent pixels in a row can by compressed down to 2 bytes. Most of the time transparent pixels appear in groups of 2 or more, so this compression scheme gains a lot.
BAMC V1

Overall structure

    * Header
    * zlib compressed data

BAMC V1 Header

The BAMC V1 data is an entire BAM V1 resource compressed with zlib and with a 12-byte header prepended, as seen below.
Offset Size (data type) Description
0x0000 4 (char array) Signature ('BAMC')
0x0004 4 (char array) Version ('V1 ')
0x0008 4 (dword) Uncompressed data length

 

