SWF AVM Specification Errata - ruffle-rs/ruffle GitHub Wiki
This page lists errata and undocumented information from the official Flash specifications provided by Adobe:
- SWF File Format Specification v19 (SWF19)
- ActionScript Virtual Machine 2 (AVM2) Overview (AVM2)
- Video File Format Specification v10
- ActionScript 2.0 Language Reference
(TODO: Jot down anything I vaguely remember being incorrect)
SWF19
DefineFunction2
If a function is running on the root timeline, _parent
is undefined and does not get preloaded into a register. Instead, _global
mistakenly uses the same register.
This means that this code will be incorrect when placed on the root timeline:
function f()
{
trace(_parent); // traces _global
trace(_global); // trace undefined
}
f();
See the define_function2_preload_order
test for an example.
This doesn't seem to apply to other preloaded registers like super
. If super
is undefined, it still gets set to a register.
Double-precision floats
(SWF19 p.16)
Double-precision floats in AVM1 ActionPush actions are not stored as 64-bit little endian, but instead stored as two 32-bit little endian chunks. This requires some byte swapping to reconstruct the double. This is the order of the bytes in the SWF file:
45670123
Or this code which swaps it into proper 64-bit little endian:
let num: [u8; 4];
num.swap(0, 4);
num.swap(1, 5);
num.swap(2, 6);
num.swap(3, 7);
(&num[..]).read_f64::<LittleEndian>()
Note that this only applies to AVM1; doubles in AVM2 are standard little-endian.
DebugId tag
Undocumented tag generated in debug builds. See http://wahlers.com.br/claus/blog/undocumented-swf-tags-written-by-mxmlc/ for more info.
Data | Size |
---|---|
Unknown | UI8[16] |
DefineButtonCxform tag
(SWF19 p.199)
This tag was only used briefly in Flash 2(?) and is not well documented (TODO: Is there any content in the wild that uses this?)
The tag can actually contain multiple color transforms. It applies the color transforms respectively to each character record in the define button tag.
PlaceObject3
(SWF19 pp.48-52)
The spec says that ClassName
will be present if (HasClassName || (HasImage && HasCharacterId))
, but it should actually be (HasClassName || (HasImage && !HasCharacterId))
. That is, if we are placing an image without a character ID, we need a class name to know which image to create.
ProductInfo tag
Undocumented ID of the software that published the SWF. See http://wahlers.com.br/claus/blog/undocumented-swf-tags-written-by-mxmlc/ for more info.
Data | Size |
---|---|
ProductId | U32 |
Edition | U32 |
MajorVersion | U8 |
MinorVersion | U8 |
BuildNumber | U64 |
CompileDate | U64 |
Protect tag
(SWF19 p.53)
If the Protect tag has an MD5 hash, there will be two bytes of data before the MD5 (they seem to always be 0?) Tag format:
Data | Size |
---|---|
Unknown | U16 |
MD5 | STRING |
ActionDelete2
(SWF19 p.94)
ActionDelete2 push true
or false
based on if the property actually existed prior to deletion.
ActionGetURL2
(SWF19 pp.82-83)
The SendVarsFlag
, LoadTargetFlag
, and LoadVariablesFlags
are listed in reverse order. This is the proper order:
LoadVariablesFlag UB[1]
LoadTargetFlag UB[1]
Reserved UB[4]
SendVarsFlag UB[2]
BevelFilter
(SWF19 pp.46-47)
The ShadowColor
and HighlightColor
variables are listed in reverse. This is the correct order:
HighlightColor RGBA
ShadowColor RGBA
AVM2
s32
Variable-length encoding and (AVM2 p.18)
When reading an variable-length encoded s32
, no sign-extension is done. The value is read as a variable-length u32
and reinterpreted as signed. This means that any negative integer must takes up the full 5 bytes.
0b0100_0000
as an s32
is 64
, not as -64
.
0b1000_0000 0b0100_0000
as an s32
is 8192
, not as -8192
.
0b1000_0000 0b1000_0000 0b1000_0000 0b1000_0000 0b0000_1000
is -2147483648
.
Video
FLV type flags are specified backwards
(VFFS p.4, p.6, p.9)
The type flags in the FLV header indicate if an FLV has Audio or Video tags. The specification reserves 5 bits for future purposes, one bit for if there's audio tags, another reserved bit, and one bit for if there's video tags. This implies that a video file with an audio track should have a type flags byte of 0xA0
, but actual FLV files (at least those encoded by FFmpeg) have a 0x05
, which is reversed.
The same mistake is made for bit fields in the audio and video data headers. An H.263 keyframe is type byte 0x12
, not 0x21
; 44.1khz 16-bit mono MP3 is 0x2E
not 0x72
.
Script data blocks start with an extra trash byte
(VFFS p.10)
FLV files can include arbitrary ActionScript data. This is structured as calling arbitrary methods on the NetStream
that is loading the FLV, including the onMetaData
event handler that gets information about the loaded movie.
In every movie that I've tried to load this script block data is prepended with a 0x02
trash byte. There is no documentation about this byte and it does not appear to have any significance.
Script data blocks have missing terminators
(VFFS p.10)
Script data allows the use of a structure called a SCRIPTDATAOBJECT
, which is a list of key and value pairs. These are terminated by three bytes: 00 00 09
.
Since SCRIPTDATAOBJECT
is intended to provide arbitrary ActionScript data, this is a recursively nested type. The FLV spec designers, in the name of development time economy, have the actual list of methods to call be a SCRIPTDATAOBJECT
. This implies that a method being called with an object should have two 00 00 09
terminators at the end.
Every script data I have only has one terminator; which implies that the whole data block is terminated by it's length and not by an explicit terminator. Further testing will be necessary to determine if Flash Player is tolerant to missing terminators in other cases.
ActionScript 2.0
TextField.restrict
An empty string in The documentation claims that an empty string means that no characters may be entered.
In reality, it is equivalent to null
and means that any character may be entered.
This has been fixed in AVM2.