SWF files are used by Adobe Flash (AKA Shockwave Flash, Macromedia Flash) to encode rich interactive multimedia content and are, essentially, a container for special bytecode instructions to play back that content. In early 2000s, it was dominant rich multimedia web format (.swf files were integrated into web pages and played back with a browser plugin), but its usage largely declined in 2010s, as HTML5 and performant browser-native solutions (i.e. JavaScript engines and graphical approaches, such as WebGL) emerged.
There are a lot of versions of SWF (~36), format is somewhat documented by Adobe.
This page hosts a formal specification of Adobe Flash (AKA Shockwave Flash, Macromedia Flash) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.
import (
"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
"bytes"
)
/**
* SWF files are used by Adobe Flash (AKA Shockwave Flash, Macromedia
* Flash) to encode rich interactive multimedia content and are,
* essentially, a container for special bytecode instructions to play
* back that content. In early 2000s, it was dominant rich multimedia
* web format (.swf files were integrated into web pages and played
* back with a browser plugin), but its usage largely declined in
* 2010s, as HTML5 and performant browser-native solutions
* (i.e. JavaScript engines and graphical approaches, such as WebGL)
* emerged.
*
* There are a lot of versions of SWF (~36), format is somewhat
* documented by Adobe.
* @see <a href="https://open-flash.github.io/mirrors/swf-spec-19.pdf">Source</a>
*/
type Swf_Compressions int
const (
Swf_Compressions__Zlib Swf_Compressions = 67
Swf_Compressions__None Swf_Compressions = 70
Swf_Compressions__Lzma Swf_Compressions = 90
)
var values_Swf_Compressions = map[Swf_Compressions]struct{}{67: {}, 70: {}, 90: {}}
func (v Swf_Compressions) isDefined() bool {
_, ok := values_Swf_Compressions[v]
return ok
}
type Swf_TagType int
const (
Swf_TagType__EndOfFile Swf_TagType = 0
Swf_TagType__PlaceObject Swf_TagType = 4
Swf_TagType__RemoveObject Swf_TagType = 5
Swf_TagType__SetBackgroundColor Swf_TagType = 9
Swf_TagType__DefineSound Swf_TagType = 14
Swf_TagType__PlaceObject2 Swf_TagType = 26
Swf_TagType__RemoveObject2 Swf_TagType = 28
Swf_TagType__FrameLabel Swf_TagType = 43
Swf_TagType__ExportAssets Swf_TagType = 56
Swf_TagType__ScriptLimits Swf_TagType = 65
Swf_TagType__FileAttributes Swf_TagType = 69
Swf_TagType__PlaceObject3 Swf_TagType = 70
Swf_TagType__SymbolClass Swf_TagType = 76
Swf_TagType__Metadata Swf_TagType = 77
Swf_TagType__DefineScalingGrid Swf_TagType = 78
Swf_TagType__DoAbc Swf_TagType = 82
Swf_TagType__DefineSceneAndFrameLabelData Swf_TagType = 86
)
var values_Swf_TagType = map[Swf_TagType]struct{}{0: {}, 4: {}, 5: {}, 9: {}, 14: {}, 26: {}, 28: {}, 43: {}, 56: {}, 65: {}, 69: {}, 70: {}, 76: {}, 77: {}, 78: {}, 82: {}, 86: {}}
func (v Swf_TagType) isDefined() bool {
_, ok := values_Swf_TagType[v]
return ok
}
type Swf struct {
Compression Swf_Compressions
Signature []byte
Version uint8
LenFile uint32
PlainBody *Swf_SwfBody
ZlibBody *Swf_SwfBody
_io *kaitai.Stream
_root *Swf
_parent kaitai.Struct
_raw_PlainBody []byte
_raw_ZlibBody []byte
_raw__raw_ZlibBody []byte
}
func NewSwf() *Swf {
return &Swf{
}
}
func (this Swf) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf) Read(io *kaitai.Stream, parent kaitai.Struct, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp1, err := this._io.ReadU1()
if err != nil {
return err
}
this.Compression = Swf_Compressions(tmp1)
tmp2, err := this._io.ReadBytes(int(2))
if err != nil {
return err
}
tmp2 = tmp2
this.Signature = tmp2
if !(bytes.Equal(this.Signature, []uint8{87, 83})) {
return kaitai.NewValidationNotEqualError([]uint8{87, 83}, this.Signature, this._io, "/seq/1")
}
tmp3, err := this._io.ReadU1()
if err != nil {
return err
}
this.Version = tmp3
tmp4, err := this._io.ReadU4le()
if err != nil {
return err
}
this.LenFile = uint32(tmp4)
if (this.Compression == Swf_Compressions__None) {
tmp5, err := this._io.ReadBytesFull()
if err != nil {
return err
}
tmp5 = tmp5
this._raw_PlainBody = tmp5
_io__raw_PlainBody := kaitai.NewStream(bytes.NewReader(this._raw_PlainBody))
tmp6 := NewSwf_SwfBody()
err = tmp6.Read(_io__raw_PlainBody, this, this._root)
if err != nil {
return err
}
this.PlainBody = tmp6
}
if (this.Compression == Swf_Compressions__Zlib) {
tmp7, err := this._io.ReadBytesFull()
if err != nil {
return err
}
tmp7 = tmp7
this._raw__raw_ZlibBody = tmp7
tmp8, err := kaitai.ProcessZlib(this._raw__raw_ZlibBody)
if err != nil {
return err
}
this._raw_ZlibBody = tmp8
_io__raw_ZlibBody := kaitai.NewStream(bytes.NewReader(this._raw_ZlibBody))
tmp9 := NewSwf_SwfBody()
err = tmp9.Read(_io__raw_ZlibBody, this, this._root)
if err != nil {
return err
}
this.ZlibBody = tmp9
}
return err
}
type Swf_DefineSoundBody_Bps int
const (
Swf_DefineSoundBody_Bps__Sound8Bit Swf_DefineSoundBody_Bps = 0
Swf_DefineSoundBody_Bps__Sound16Bit Swf_DefineSoundBody_Bps = 1
)
var values_Swf_DefineSoundBody_Bps = map[Swf_DefineSoundBody_Bps]struct{}{0: {}, 1: {}}
func (v Swf_DefineSoundBody_Bps) isDefined() bool {
_, ok := values_Swf_DefineSoundBody_Bps[v]
return ok
}
type Swf_DefineSoundBody_Channels int
const (
Swf_DefineSoundBody_Channels__Mono Swf_DefineSoundBody_Channels = 0
Swf_DefineSoundBody_Channels__Stereo Swf_DefineSoundBody_Channels = 1
)
var values_Swf_DefineSoundBody_Channels = map[Swf_DefineSoundBody_Channels]struct{}{0: {}, 1: {}}
func (v Swf_DefineSoundBody_Channels) isDefined() bool {
_, ok := values_Swf_DefineSoundBody_Channels[v]
return ok
}
type Swf_DefineSoundBody_SamplingRates int
const (
Swf_DefineSoundBody_SamplingRates__Rate55Khz Swf_DefineSoundBody_SamplingRates = 0
Swf_DefineSoundBody_SamplingRates__Rate11Khz Swf_DefineSoundBody_SamplingRates = 1
Swf_DefineSoundBody_SamplingRates__Rate22Khz Swf_DefineSoundBody_SamplingRates = 2
Swf_DefineSoundBody_SamplingRates__Rate44Khz Swf_DefineSoundBody_SamplingRates = 3
)
var values_Swf_DefineSoundBody_SamplingRates = map[Swf_DefineSoundBody_SamplingRates]struct{}{0: {}, 1: {}, 2: {}, 3: {}}
func (v Swf_DefineSoundBody_SamplingRates) isDefined() bool {
_, ok := values_Swf_DefineSoundBody_SamplingRates[v]
return ok
}
type Swf_DefineSoundBody struct {
Id uint16
Format uint64
SamplingRate Swf_DefineSoundBody_SamplingRates
BitsPerSample Swf_DefineSoundBody_Bps
NumChannels Swf_DefineSoundBody_Channels
NumSamples uint32
_io *kaitai.Stream
_root *Swf
_parent *Swf_Tag
}
func NewSwf_DefineSoundBody() *Swf_DefineSoundBody {
return &Swf_DefineSoundBody{
}
}
func (this Swf_DefineSoundBody) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf_DefineSoundBody) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp10, err := this._io.ReadU2le()
if err != nil {
return err
}
this.Id = uint16(tmp10)
tmp11, err := this._io.ReadBitsIntBe(4)
if err != nil {
return err
}
this.Format = tmp11
tmp12, err := this._io.ReadBitsIntBe(2)
if err != nil {
return err
}
this.SamplingRate = Swf_DefineSoundBody_SamplingRates(tmp12)
tmp13, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.BitsPerSample = Swf_DefineSoundBody_Bps(tmp13)
tmp14, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.NumChannels = Swf_DefineSoundBody_Channels(tmp14)
this._io.AlignToByte()
tmp15, err := this._io.ReadU4le()
if err != nil {
return err
}
this.NumSamples = uint32(tmp15)
return err
}
/**
* Sound sampling rate, as per enum. Ignored for Nellymoser and Speex codecs.
*/
type Swf_DoAbcBody struct {
Flags uint32
Name string
Abcdata []byte
_io *kaitai.Stream
_root *Swf
_parent *Swf_Tag
}
func NewSwf_DoAbcBody() *Swf_DoAbcBody {
return &Swf_DoAbcBody{
}
}
func (this Swf_DoAbcBody) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf_DoAbcBody) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp16, err := this._io.ReadU4le()
if err != nil {
return err
}
this.Flags = uint32(tmp16)
tmp17, err := this._io.ReadBytesTerm(0, false, true, true)
if err != nil {
return err
}
this.Name = string(tmp17)
tmp18, err := this._io.ReadBytesFull()
if err != nil {
return err
}
tmp18 = tmp18
this.Abcdata = tmp18
return err
}
type Swf_RecordHeader struct {
TagCodeAndLength uint16
BigLen int32
_io *kaitai.Stream
_root *Swf
_parent *Swf_Tag
_f_len bool
len int
_f_smallLen bool
smallLen int
_f_tagType bool
tagType Swf_TagType
}
func NewSwf_RecordHeader() *Swf_RecordHeader {
return &Swf_RecordHeader{
}
}
func (this Swf_RecordHeader) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf_RecordHeader) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp19, err := this._io.ReadU2le()
if err != nil {
return err
}
this.TagCodeAndLength = uint16(tmp19)
tmp20, err := this.SmallLen()
if err != nil {
return err
}
if (tmp20 == 63) {
tmp21, err := this._io.ReadS4le()
if err != nil {
return err
}
this.BigLen = int32(tmp21)
}
return err
}
func (this *Swf_RecordHeader) Len() (v int, err error) {
if (this._f_len) {
return this.len, nil
}
this._f_len = true
var tmp22 int32;
tmp23, err := this.SmallLen()
if err != nil {
return 0, err
}
if (tmp23 == 63) {
tmp22 = this.BigLen
} else {
tmp24, err := this.SmallLen()
if err != nil {
return 0, err
}
tmp22 = tmp24
}
this.len = int(tmp22)
return this.len, nil
}
func (this *Swf_RecordHeader) SmallLen() (v int, err error) {
if (this._f_smallLen) {
return this.smallLen, nil
}
this._f_smallLen = true
this.smallLen = int(this.TagCodeAndLength & 63)
return this.smallLen, nil
}
func (this *Swf_RecordHeader) TagType() (v Swf_TagType, err error) {
if (this._f_tagType) {
return this.tagType, nil
}
this._f_tagType = true
this.tagType = Swf_TagType(Swf_TagType(this.TagCodeAndLength >> 6))
return this.tagType, nil
}
type Swf_Rect struct {
B1 uint8
Skip []byte
_io *kaitai.Stream
_root *Swf
_parent *Swf_SwfBody
_f_numBits bool
numBits int
_f_numBytes bool
numBytes int
}
func NewSwf_Rect() *Swf_Rect {
return &Swf_Rect{
}
}
func (this Swf_Rect) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf_Rect) Read(io *kaitai.Stream, parent *Swf_SwfBody, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp25, err := this._io.ReadU1()
if err != nil {
return err
}
this.B1 = tmp25
tmp26, err := this.NumBytes()
if err != nil {
return err
}
tmp27, err := this._io.ReadBytes(int(tmp26))
if err != nil {
return err
}
tmp27 = tmp27
this.Skip = tmp27
return err
}
func (this *Swf_Rect) NumBits() (v int, err error) {
if (this._f_numBits) {
return this.numBits, nil
}
this._f_numBits = true
this.numBits = int(this.B1 >> 3)
return this.numBits, nil
}
func (this *Swf_Rect) NumBytes() (v int, err error) {
if (this._f_numBytes) {
return this.numBytes, nil
}
this._f_numBytes = true
tmp28, err := this.NumBits()
if err != nil {
return 0, err
}
this.numBytes = int(((tmp28 * 4 - 3) + 7) / 8)
return this.numBytes, nil
}
type Swf_Rgb struct {
R uint8
G uint8
B uint8
_io *kaitai.Stream
_root *Swf
_parent *Swf_Tag
}
func NewSwf_Rgb() *Swf_Rgb {
return &Swf_Rgb{
}
}
func (this Swf_Rgb) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf_Rgb) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp29, err := this._io.ReadU1()
if err != nil {
return err
}
this.R = tmp29
tmp30, err := this._io.ReadU1()
if err != nil {
return err
}
this.G = tmp30
tmp31, err := this._io.ReadU1()
if err != nil {
return err
}
this.B = tmp31
return err
}
type Swf_ScriptLimitsBody struct {
MaxRecursionDepth uint16
ScriptTimeoutSeconds uint16
_io *kaitai.Stream
_root *Swf
_parent *Swf_Tag
}
func NewSwf_ScriptLimitsBody() *Swf_ScriptLimitsBody {
return &Swf_ScriptLimitsBody{
}
}
func (this Swf_ScriptLimitsBody) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf_ScriptLimitsBody) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp32, err := this._io.ReadU2le()
if err != nil {
return err
}
this.MaxRecursionDepth = uint16(tmp32)
tmp33, err := this._io.ReadU2le()
if err != nil {
return err
}
this.ScriptTimeoutSeconds = uint16(tmp33)
return err
}
type Swf_SwfBody struct {
Rect *Swf_Rect
FrameRate uint16
FrameCount uint16
FileAttributesTag *Swf_Tag
Tags []*Swf_Tag
_io *kaitai.Stream
_root *Swf
_parent *Swf
}
func NewSwf_SwfBody() *Swf_SwfBody {
return &Swf_SwfBody{
}
}
func (this Swf_SwfBody) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf_SwfBody) Read(io *kaitai.Stream, parent *Swf, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp34 := NewSwf_Rect()
err = tmp34.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Rect = tmp34
tmp35, err := this._io.ReadU2le()
if err != nil {
return err
}
this.FrameRate = uint16(tmp35)
tmp36, err := this._io.ReadU2le()
if err != nil {
return err
}
this.FrameCount = uint16(tmp36)
if (this._root.Version >= 8) {
tmp37 := NewSwf_Tag()
err = tmp37.Read(this._io, this, this._root)
if err != nil {
return err
}
this.FileAttributesTag = tmp37
}
for i := 0;; i++ {
tmp38, err := this._io.EOF()
if err != nil {
return err
}
if tmp38 {
break
}
tmp39 := NewSwf_Tag()
err = tmp39.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Tags = append(this.Tags, tmp39)
}
return err
}
type Swf_SymbolClassBody struct {
NumSymbols uint16
Symbols []*Swf_SymbolClassBody_Symbol
_io *kaitai.Stream
_root *Swf
_parent *Swf_Tag
}
func NewSwf_SymbolClassBody() *Swf_SymbolClassBody {
return &Swf_SymbolClassBody{
}
}
func (this Swf_SymbolClassBody) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf_SymbolClassBody) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp40, err := this._io.ReadU2le()
if err != nil {
return err
}
this.NumSymbols = uint16(tmp40)
for i := 0; i < int(this.NumSymbols); i++ {
_ = i
tmp41 := NewSwf_SymbolClassBody_Symbol()
err = tmp41.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Symbols = append(this.Symbols, tmp41)
}
return err
}
type Swf_SymbolClassBody_Symbol struct {
Tag uint16
Name string
_io *kaitai.Stream
_root *Swf
_parent *Swf_SymbolClassBody
}
func NewSwf_SymbolClassBody_Symbol() *Swf_SymbolClassBody_Symbol {
return &Swf_SymbolClassBody_Symbol{
}
}
func (this Swf_SymbolClassBody_Symbol) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf_SymbolClassBody_Symbol) Read(io *kaitai.Stream, parent *Swf_SymbolClassBody, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp42, err := this._io.ReadU2le()
if err != nil {
return err
}
this.Tag = uint16(tmp42)
tmp43, err := this._io.ReadBytesTerm(0, false, true, true)
if err != nil {
return err
}
this.Name = string(tmp43)
return err
}
type Swf_Tag struct {
RecordHeader *Swf_RecordHeader
TagBody interface{}
_io *kaitai.Stream
_root *Swf
_parent *Swf_SwfBody
_raw_TagBody []byte
}
func NewSwf_Tag() *Swf_Tag {
return &Swf_Tag{
}
}
func (this Swf_Tag) IO_() *kaitai.Stream {
return this._io
}
func (this *Swf_Tag) Read(io *kaitai.Stream, parent *Swf_SwfBody, root *Swf) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp44 := NewSwf_RecordHeader()
err = tmp44.Read(this._io, this, this._root)
if err != nil {
return err
}
this.RecordHeader = tmp44
tmp45, err := this.RecordHeader.TagType()
if err != nil {
return err
}
switch (tmp45) {
case Swf_TagType__DefineSound:
tmp46, err := this.RecordHeader.Len()
if err != nil {
return err
}
tmp47, err := this._io.ReadBytes(int(tmp46))
if err != nil {
return err
}
tmp47 = tmp47
this._raw_TagBody = tmp47
_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
tmp48 := NewSwf_DefineSoundBody()
err = tmp48.Read(_io__raw_TagBody, this, this._root)
if err != nil {
return err
}
this.TagBody = tmp48
case Swf_TagType__DoAbc:
tmp49, err := this.RecordHeader.Len()
if err != nil {
return err
}
tmp50, err := this._io.ReadBytes(int(tmp49))
if err != nil {
return err
}
tmp50 = tmp50
this._raw_TagBody = tmp50
_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
tmp51 := NewSwf_DoAbcBody()
err = tmp51.Read(_io__raw_TagBody, this, this._root)
if err != nil {
return err
}
this.TagBody = tmp51
case Swf_TagType__ExportAssets:
tmp52, err := this.RecordHeader.Len()
if err != nil {
return err
}
tmp53, err := this._io.ReadBytes(int(tmp52))
if err != nil {
return err
}
tmp53 = tmp53
this._raw_TagBody = tmp53
_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
tmp54 := NewSwf_SymbolClassBody()
err = tmp54.Read(_io__raw_TagBody, this, this._root)
if err != nil {
return err
}
this.TagBody = tmp54
case Swf_TagType__ScriptLimits:
tmp55, err := this.RecordHeader.Len()
if err != nil {
return err
}
tmp56, err := this._io.ReadBytes(int(tmp55))
if err != nil {
return err
}
tmp56 = tmp56
this._raw_TagBody = tmp56
_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
tmp57 := NewSwf_ScriptLimitsBody()
err = tmp57.Read(_io__raw_TagBody, this, this._root)
if err != nil {
return err
}
this.TagBody = tmp57
case Swf_TagType__SetBackgroundColor:
tmp58, err := this.RecordHeader.Len()
if err != nil {
return err
}
tmp59, err := this._io.ReadBytes(int(tmp58))
if err != nil {
return err
}
tmp59 = tmp59
this._raw_TagBody = tmp59
_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
tmp60 := NewSwf_Rgb()
err = tmp60.Read(_io__raw_TagBody, this, this._root)
if err != nil {
return err
}
this.TagBody = tmp60
case Swf_TagType__SymbolClass:
tmp61, err := this.RecordHeader.Len()
if err != nil {
return err
}
tmp62, err := this._io.ReadBytes(int(tmp61))
if err != nil {
return err
}
tmp62 = tmp62
this._raw_TagBody = tmp62
_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
tmp63 := NewSwf_SymbolClassBody()
err = tmp63.Read(_io__raw_TagBody, this, this._root)
if err != nil {
return err
}
this.TagBody = tmp63
default:
tmp64, err := this.RecordHeader.Len()
if err != nil {
return err
}
tmp65, err := this._io.ReadBytes(int(tmp64))
if err != nil {
return err
}
tmp65 = tmp65
this._raw_TagBody = tmp65
}
return err
}