Once the LZMA data is decoded, a ZWS movie is a run of tags after the frame fields of the header. This page describes the tag header and the data types every tag is built from. The offsets in its examples are offsets in the decompressed data, counted with the 8 header bytes in front.
The tag header (RECORDHEADER)
Every tag starts with a UI16, TagCodeAndLength. The tag code is in its top 10 bits and the length of the body in its low 6 bits:
- code = value >> 6
- length = value & 0x3F
The specification points out that this is one 16-bit little-endian word, not two bit fields read from the bytes. If the length is 0x3F (63), a 32-bit length follows (the specification types it SI32): the long form, needed for bodies of 63 bytes or more. A writer may use the long form for any length, and Flash did for some short tags.
| Form | Bytes | Layout |
|---|---|---|
| Short | 2 | UI16 (code << 6) | length, with length 0 to 62 |
| Long | 6 | UI16 (code << 6) | 0x3F, then UI32 length |
Tag headers are the same in every SWF, compressed or not. In an SWF 9 test movie
examined for this site (a CWS file; no ZWS movie was among them), the DoABC tag at
offset 0x31 of the decompressed data starts BF 14 49 03 00 00. 0x14BF >> 6 is 82, DoABC; 0x14BF &
0x3F is 63, the long form; 49 03 00 00 is 841, the length of the body,
which starts at 0x37. A FileAttributes tag (code 69) with its 4-byte
body has the short header 69 × 64 + 4 = 0x1144, stored 44 11.
Numbers
| Type | Size | Meaning |
|---|---|---|
| SI8, SI16, SI32 | 1, 2, 4 | Signed integers, little-endian |
| UI8, UI16, UI32, UI64 | 1, 2, 4, 8 | Unsigned integers, little-endian |
| FIXED | 4 | 32-bit signed 16.16 fixed point: a value whose low 16 bits are the fraction (÷ 65,536) |
| FIXED8 | 2 | 16-bit signed 8.8 fixed point (÷ 256). The header’s 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, little-endian |
| DOUBLE | 8 | IEEE double precision, little-endian |
| EncodedU32 | 1 to 5 | Seven bits a byte, lowest group first; the top bit is set while more bytes follow (SWF 9, in DefineSceneAndFrameLabelData, and the u30 of ABC) |
All multi-byte values are byte-aligned, except inside a run of bit fields. One
exception to the byte order: an ActionPush double is stored as two 32-bit halves, the
high half first, each little-endian, so 00 00 F8 3F 00 00 00 00 is
1.5.
Bit values
Many structures are packed into bits:
- UB[n] – an unsigned value of n bits
- SB[n] – a signed value of n bits, two’s complement, sign-extended from bit n − 1
- FB[n] – a signed 16.16 fixed-point value of n bits (SB[n] ÷ 65,536)
Bits are read most significant first, from the current byte on, crossing byte boundaries. A structure of bit fields starts on a byte boundary and is padded with zero bits to the next one: RECT, MATRIX, CXFORM and each run of shape records end aligned. The number of bits is often given by an earlier field (Nbits). Writers use the fewest bits that hold every value, with a sign bit for signed values.
Twips
Coordinates, sizes and line widths are in twips, 1/20 of a pixel (a logical pixel at 100% zoom). 20 twips are one pixel; a 550-pixel stage is 11,000 twips wide.
Strings
A STRING is a run of bytes up to and including a zero byte. A ZWS movie is SWF 13 or later, so its strings are UTF-8, as in every SWF 6 and later movie: frame labels, instance names, font names, text fields, action strings, class names.
A few names are length-prefixed instead: the font name in DefineFont2, DefineFont3, DefineFontInfo and DefineFontInfo2 is a UI8 length and that many bytes, with no terminator required (Flash often adds a zero). Metadata, SymbolClass names and DoABC names are UTF-8 too. Inside ABC code, strings are a u30 length and UTF-8 bytes.
Colours
| Type | Bytes | Order |
|---|---|---|
| RGB | 3 | red, green, blue |
| RGBA | 4 | red, green, blue, 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 use RGB, DefineShape3 and DefineShape4 RGBA, for example. PlaceObject3’s background colour is read as RGBA by the explorer, as JPEXS reads it; not checked against the specification text.
RECT
| Field | Type | Meaning |
|---|---|---|
Nbits | UB[5] | The bits in each of the four values |
Xmin | SB[Nbits] | Left, in twips |
Xmax | SB[Nbits] | Right |
Ymin | SB[Nbits] | Top |
Ymax | SB[Nbits] | Bottom |
Note the order: both x values, then both y values. Padded to a byte. The frame size works through a 9-byte RECT bit by bit.
MATRIX
| Field | Type | Meaning |
|---|---|---|
HasScale | UB[1] | Whether scale values follow |
NScaleBits | UB[5] | If HasScale: bits in each scale value |
ScaleX, ScaleY | FB[NScaleBits] | If HasScale |
HasRotate | UB[1] | Whether rotate and skew values follow |
NRotateBits | UB[5] | If HasRotate |
RotateSkew0, RotateSkew1 | FB[NRotateBits] | If HasRotate |
NTranslateBits | UB[5] | Bits in each translate value |
TranslateX, TranslateY | SB[NTranslateBits] | The translation, in twips |
Padded to a byte. A point (x, y) maps to:
- x′ = x × ScaleX + y × RotateSkew1 + TranslateX
- y′ = x × RotateSkew0 + y × ScaleY + TranslateY
A missing scale is 1 and a missing rotate or skew is 0. So the single byte
00 (no scale, no rotation, 0 translate bits) is the identity matrix.
CXFORM and CXFORMWITHALPHA
A colour transform:
| Field | Type | Meaning |
|---|---|---|
HasAddTerms | UB[1] | Whether add terms follow |
HasMultTerms | UB[1] | Whether multiply terms follow |
Nbits | UB[4] | The bits in each term |
RedMultTerm, GreenMultTerm, BlueMultTerm (and AlphaMultTerm) | SB[Nbits] each | If HasMultTerms; 8.8 fixed point, 256 is 1 |
RedAddTerm, GreenAddTerm, BlueAddTerm (and AlphaAddTerm) | SB[Nbits] each | If HasAddTerms |
Padded to a byte. The add terms come after the multiply terms although HasAddTerms is read first. Each channel becomes max(0, min(255, (value × Mult) ÷ 256 + Add)). CXFORM, without the alpha terms, is used by PlaceObject and DefineButtonCxform; CXFORMWITHALPHA by PlaceObject2, PlaceObject3 and DefineButton2.