After the header, a movie is a run of tags. Each tag is a short record header and a body; the body is built from a small set of data types, some byte-aligned and some packed into bits. This page describes the record header, the run and every basic type.
Record header
Every tag starts with a RECORDHEADER. Its first two bytes are one little-endian
UI16, TagCodeAndLength: the tag code in its top 10 bits and the length
of the body in its low 6 bits. It is a 16-bit word, not two bit fields, so read the
UI16 first and then split it.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 2 | UI16 | TagCodeAndLength | Code = value >> 6; length = value & 0x3F |
| 2 | 4 | SI32 | Length | Only when the 6-bit length is 0x3F (63): the length of the body |
A body of 62 bytes or fewer can use the short form. A length field of 63 means the long form: the real length follows as 4 bytes (typed SI32 in the specification). A body of 63 bytes or more must use the long form, but a writer may use it for any length, and Flash does for some tags whatever their size.
00000015 43 02 FF FF FF 3F 03 51 1D 00 00 88 E4 01 …
43 02 is 0x0243: code 9 (SetBackgroundColor),
length 3. 3F 03 is 0x033F: code 12 (DoAction), length 63,
so the length is the next four bytes, 0x1D51 = 7,505Numbers
Numbers are little-endian and byte-aligned, except inside a run of bit fields. The zlib streams inside lossless bitmaps and alpha planes end with a big-endian Adler-32 checksum, as every zlib stream does.
| Type | Size | What it is |
|---|---|---|
| SI8, SI16, SI32 | 1, 2, 4 | Signed integers, two’s complement |
| UI8, UI16, UI32, UI64 | 1, 2, 4, 8 | Unsigned integers |
| FIXED | 4 | Signed 16.16 fixed point: a 32-bit value whose low 16 bits are the fraction (value ÷ 65,536) |
| FIXED8 | 2 | Signed 8.8 fixed point (value ÷ 256). The frame rate is 8.8 too, unsigned |
| FLOAT16 | 2 | IEEE half precision: 1 sign, 5 exponent and 10 fraction bits (SWF 8, in DefineFontAlignZones) |
| FLOAT | 4 | IEEE single precision |
| DOUBLE | 8 | IEEE double precision |
| EncodedU32 | 1 to 5 | An unsigned 32-bit number in 7-bit groups, lowest first; the top bit of each byte is set while another follows (SWF 9, in DefineSceneAndFrameLabelData; the ABC file’s u30 is the same encoding) |
One more number is stored oddly: a double in an AVM1
ActionPush is two 32-bit
halves, the high half first, each little-endian. 00 00 F8 3F 00 00 00 00
is 1.5.
Bit values
Many structures pack their fields into bits. There are three bit types:
| Type | What it is |
|---|---|
| UB[n] | An unsigned number of n bits |
| SB[n] | A signed number of n bits, two’s complement: if bit n − 1 is set, subtract 2n |
| FB[n] | A signed 16.16 fixed-point number of n bits: an SB[n] divided by 65,536 |
Bits are read most significant first, starting from the current byte and running on
into the next. A bit structure starts on a byte boundary and ends padded with zero bits
to the next one: RECT, MATRIX, CXFORM, each shape record run and each text glyph run.
Within a run of shape records the records are not aligned, only the run as a whole.
The number of bits is usually given by an earlier field, often named
Nbits. Writers use the fewest bits that hold every value, signed values
with room for the sign.
To read n bits: keep a bit position; for each of the n bits take bit (7 − position mod 8) of the current byte, shift it into the result, and move on, crossing into the next byte after bit 0. For SB, sign-extend from bit n − 1. After the structure, skip to the next byte.
Twips
Coordinates, widths and distances are in twips: 1/20 of a pixel at 100% zoom. 20 twips is one pixel, so 11,000 twips is 550 pixels.
Strings
A STRING is a run of bytes ending with a zero byte. In this format every STRING is UTF-8: frame labels, instance names, text field contents, export names, URLs and every string in the AVM1 actions. The few names that are not zero-terminated are length-prefixed: the font name in DefineFont2, DefineFont3, DefineFontInfo and DefineFontInfo2 is a UI8 length followed by that many bytes, and Flash often adds a zero byte inside that length. The ABC file has its own strings, a length and UTF-8 bytes.
Colours
| Type | Size | Layout |
|---|---|---|
| RGB | 3 | Red, green, blue, one UI8 each |
| RGBA | 4 | Red, green, blue, alpha; alpha 255 is opaque |
| ARGB | 4 | Alpha, red, green, blue: the 32-bit pixels of DefineBitsLossless2 |
Which one a tag uses depends on the tag: DefineShape and DefineShape2 have RGB, DefineShape3 and later RGBA. PlaceObject3’s background colour is read as RGBA by the explorer, as JPEXS reads it; that was not checked against the specification text.
RECT
A rectangle, in twips, packed into bits and padded to a byte:
| Field | Type | Meaning |
|---|---|---|
Nbits | UB[5] | The bits in each of the four values |
Xmin | SB[Nbits] | Left edge |
Xmax | SB[Nbits] | Right edge |
Ymin | SB[Nbits] | Top edge |
Ymax | SB[Nbits] | Bottom edge |
Note the order: both x values, then both y values. Its size is 5 + 4 × Nbits bits,
rounded up to whole bytes. A shape’s bounds 35 8A 58 A0: Nbits is 6
(00110), then −20, 20, −20 and 20 twips, a box from −1 to 1 pixel each
way, in 29 bits padded to 4 bytes.
MATRIX
A 2 × 3 transformation matrix, packed into bits and padded to a byte. The scale and the rotate-and-skew parts are each optional:
| Field | Type | Meaning |
|---|---|---|
HasScale | UB[1] | 1 if the scale terms follow |
NScaleBits | UB[5] | If HasScale: bits in each scale term |
ScaleX | FB[NScaleBits] | If HasScale: x scale, 16.16 |
ScaleY | FB[NScaleBits] | If HasScale: y scale, 16.16 |
HasRotate | UB[1] | 1 if the rotate and skew terms follow |
NRotateBits | UB[5] | If HasRotate: bits in each term |
RotateSkew0 | FB[NRotateBits] | If HasRotate: first rotate and skew term |
RotateSkew1 | FB[NRotateBits] | If HasRotate: second rotate and skew term |
NTranslateBits | UB[5] | Bits in each translation (always present) |
TranslateX | SB[NTranslateBits] | x translation, in twips |
TranslateY | SB[NTranslateBits] | y translation, in twips |
A point (x, y) maps to:
x' = x × ScaleX + y × RotateSkew1 + TranslateX
y' = x × RotateSkew0 + y × ScaleY + TranslateY
The four bytes 18 FA 04 B0, from a PlaceObject in a SWF 6 movie:
0 0 01100 011111010000 001001011000 0
HasScale HasRotate NTranslateBits TranslateX TranslateY padding
12 2000 600
CXFORM and CXFORMWITHALPHA
A colour transform, packed into bits and padded to a byte:
| Field | Type | Meaning |
|---|---|---|
HasAddTerms | UB[1] | 1 if the add terms follow |
HasMultTerms | UB[1] | 1 if the multiply terms follow |
Nbits | UB[4] | Bits in each term |
RedMultTerm, GreenMultTerm, BlueMultTerm | SB[Nbits] each | If HasMultTerms |
AlphaMultTerm | SB[Nbits] | If HasMultTerms, CXFORMWITHALPHA only |
RedAddTerm, GreenAddTerm, BlueAddTerm | SB[Nbits] each | If HasAddTerms |
AlphaAddTerm | SB[Nbits] | If HasAddTerms, CXFORMWITHALPHA only |
The add terms come after the multiply terms, although their flag is read first. The multiply terms are 8.8 fixed point, so 256 leaves a channel as it is. Each channel becomes:
result = max(0, min(255, (value × MultTerm) ÷ 256 + AddTerm))
CXFORM, without alpha, is used by PlaceObject and DefineButtonCxform; CXFORMWITHALPHA by PlaceObject2, PlaceObject3 and DefineButton2.