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 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 it, and a writer may
use it for any length: Flash 4 wrote BF 00 36 00 00 00 for a DefineShape
of 54 bytes.
00000015 43 02 FF FF FF 3F 03 3E 03 00 00 96 08 00 …
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, 0x033E = 830Numbers
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 | 1, 2, 4 | 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 |
| FLOAT | 4 | IEEE single precision, as pushed by ActionPush type 1 (SWF 4) |
| DOUBLE | 8 | IEEE double precision, as pushed by ActionPush type 6 (SWF 5) |
A double in an AVM1 ActionPush
is stored oddly: 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 a STRING is in the code page of the computer playing the movie: ANSI on Western systems, Shift-JIS on Japanese ones. Nothing in the file says which, so the same bytes read differently on different computers. This applies to frame labels, instance names, text field contents, export names, URLs and every string in the AVM1 actions. Ruffle assumes Windows-1252, and the explorer reads strings the same way.
The few names that are not zero-terminated are length-prefixed: the font name in DefineFont2 and DefineFontInfo is a UI8 length followed by that many bytes, and Flash often adds a zero byte inside that length. Fonts carry ANSI and ShiftJIS flags for their character codes.
Colours
| Type | Size | Layout |
|---|---|---|
| RGB | 3 | Red, green, blue, one UI8 each |
| RGBA | 4 | Red, green, blue, alpha; alpha 255 is opaque (SWF 3) |
| ARGB | 4 | Alpha, red, green, blue: the 32-bit pixels of DefineBitsLossless2 (SWF 3) |
Which one a tag uses depends on the tag: DefineShape and DefineShape2 have RGB, DefineShape3 RGBA; DefineText has RGB, DefineText2 RGBA.
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 45 FA 0D FA 08: Nbits is 8
(01000), then −65, 65, −65 and 65 twips, from −3.25 to 3.25 pixels each
way, in 37 bits padded to 5 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 five bytes 1C AA 49 EC 80, from a PlaceObject2 in a Flash 4
movie:
0 0 01110 01010101001001 00111101100100 00000
HasScale HasRotate NTranslateBits TranslateX TranslateY padding
14 5449 3940
The single byte 00 is the identity matrix: no scale, no rotation and
a translation of 0 bits.
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 (SWF 3) by PlaceObject2 and DefineButton2. Flash 4 wrote the byte
04 for a button record’s empty transform: no add or multiply terms, Nbits
1.