Adobe Flash (AKA Shockwave Flash, Macromedia Flash): Go parsing library

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.

File extension

swf

KS implementation details

License: CC0-1.0

References

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.

Go source code to parse Adobe Flash (AKA Shockwave Flash, Macromedia Flash)

swf.go

// 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
)

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
)
type Swf struct {
	Compression Swf_Compressions
	Signature []byte
	Version uint8
	LenFile uint32
	PlainBody *Swf_SwfBody
	ZlibBody *Swf_SwfBody
	_io *kaitai.Stream
	_root *Swf
	_parent interface{}
	_raw_PlainBody []byte
	_raw_ZlibBody []byte
	_raw__raw_ZlibBody []byte
}
func NewSwf() *Swf {
	return &Swf{
	}
}

func (this *Swf) Read(io *kaitai.Stream, parent interface{}, 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_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) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp10, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.R = tmp10
	tmp11, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.G = tmp11
	tmp12, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.B = tmp12
	return err
}
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) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp13, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.Flags = uint32(tmp13)
	tmp14, err := this._io.ReadBytesTerm(0, false, true, true)
	if err != nil {
		return err
	}
	this.Name = string(tmp14)
	tmp15, err := this._io.ReadBytesFull()
	if err != nil {
		return err
	}
	tmp15 = tmp15
	this.Abcdata = tmp15
	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) Read(io *kaitai.Stream, parent *Swf, root *Swf) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp16 := NewSwf_Rect()
	err = tmp16.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Rect = tmp16
	tmp17, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.FrameRate = uint16(tmp17)
	tmp18, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.FrameCount = uint16(tmp18)
	if (this._root.Version >= 8) {
		tmp19 := NewSwf_Tag()
		err = tmp19.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.FileAttributesTag = tmp19
	}
	for i := 1;; i++ {
		tmp20, err := this._io.EOF()
		if err != nil {
			return err
		}
		if tmp20 {
			break
		}
		tmp21 := NewSwf_Tag()
		err = tmp21.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Tags = append(this.Tags, tmp21)
	}
	return err
}
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) Read(io *kaitai.Stream, parent *Swf_SwfBody, root *Swf) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp22, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.B1 = tmp22
	tmp23, err := this.NumBytes()
	if err != nil {
		return err
	}
	tmp24, err := this._io.ReadBytes(int(tmp23))
	if err != nil {
		return err
	}
	tmp24 = tmp24
	this.Skip = tmp24
	return err
}
func (this *Swf_Rect) NumBits() (v int, err error) {
	if (this._f_numBits) {
		return this.numBits, nil
	}
	this.numBits = int((this.B1 >> 3))
	this._f_numBits = true
	return this.numBits, nil
}
func (this *Swf_Rect) NumBytes() (v int, err error) {
	if (this._f_numBytes) {
		return this.numBytes, nil
	}
	tmp25, err := this.NumBits()
	if err != nil {
		return 0, err
	}
	this.numBytes = int(((((tmp25 * 4) - 3) + 7) / 8))
	this._f_numBytes = true
	return this.numBytes, nil
}
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) Read(io *kaitai.Stream, parent *Swf_SwfBody, root *Swf) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp26 := NewSwf_RecordHeader()
	err = tmp26.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.RecordHeader = tmp26
	tmp27, err := this.RecordHeader.TagType()
	if err != nil {
		return err
	}
	switch (tmp27) {
	case Swf_TagType__DefineSound:
		tmp28, err := this.RecordHeader.Len()
		if err != nil {
			return err
		}
		tmp29, err := this._io.ReadBytes(int(tmp28))
		if err != nil {
			return err
		}
		tmp29 = tmp29
		this._raw_TagBody = tmp29
		_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
		tmp30 := NewSwf_DefineSoundBody()
		err = tmp30.Read(_io__raw_TagBody, this, this._root)
		if err != nil {
			return err
		}
		this.TagBody = tmp30
	case Swf_TagType__SetBackgroundColor:
		tmp31, err := this.RecordHeader.Len()
		if err != nil {
			return err
		}
		tmp32, err := this._io.ReadBytes(int(tmp31))
		if err != nil {
			return err
		}
		tmp32 = tmp32
		this._raw_TagBody = tmp32
		_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
		tmp33 := NewSwf_Rgb()
		err = tmp33.Read(_io__raw_TagBody, this, this._root)
		if err != nil {
			return err
		}
		this.TagBody = tmp33
	case Swf_TagType__ScriptLimits:
		tmp34, err := this.RecordHeader.Len()
		if err != nil {
			return err
		}
		tmp35, err := this._io.ReadBytes(int(tmp34))
		if err != nil {
			return err
		}
		tmp35 = tmp35
		this._raw_TagBody = tmp35
		_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
		tmp36 := NewSwf_ScriptLimitsBody()
		err = tmp36.Read(_io__raw_TagBody, this, this._root)
		if err != nil {
			return err
		}
		this.TagBody = tmp36
	case Swf_TagType__DoAbc:
		tmp37, err := this.RecordHeader.Len()
		if err != nil {
			return err
		}
		tmp38, err := this._io.ReadBytes(int(tmp37))
		if err != nil {
			return err
		}
		tmp38 = tmp38
		this._raw_TagBody = tmp38
		_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
		tmp39 := NewSwf_DoAbcBody()
		err = tmp39.Read(_io__raw_TagBody, this, this._root)
		if err != nil {
			return err
		}
		this.TagBody = tmp39
	case Swf_TagType__ExportAssets:
		tmp40, err := this.RecordHeader.Len()
		if err != nil {
			return err
		}
		tmp41, err := this._io.ReadBytes(int(tmp40))
		if err != nil {
			return err
		}
		tmp41 = tmp41
		this._raw_TagBody = tmp41
		_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
		tmp42 := NewSwf_SymbolClassBody()
		err = tmp42.Read(_io__raw_TagBody, this, this._root)
		if err != nil {
			return err
		}
		this.TagBody = tmp42
	case Swf_TagType__SymbolClass:
		tmp43, err := this.RecordHeader.Len()
		if err != nil {
			return err
		}
		tmp44, err := this._io.ReadBytes(int(tmp43))
		if err != nil {
			return err
		}
		tmp44 = tmp44
		this._raw_TagBody = tmp44
		_io__raw_TagBody := kaitai.NewStream(bytes.NewReader(this._raw_TagBody))
		tmp45 := NewSwf_SymbolClassBody()
		err = tmp45.Read(_io__raw_TagBody, this, this._root)
		if err != nil {
			return err
		}
		this.TagBody = tmp45
	default:
		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
	}
	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) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp48, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.NumSymbols = uint16(tmp48)
	for i := 0; i < int(this.NumSymbols); i++ {
		_ = i
		tmp49 := NewSwf_SymbolClassBody_Symbol()
		err = tmp49.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Symbols = append(this.Symbols, tmp49)
	}
	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) Read(io *kaitai.Stream, parent *Swf_SymbolClassBody, root *Swf) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp50, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Tag = uint16(tmp50)
	tmp51, err := this._io.ReadBytesTerm(0, false, true, true)
	if err != nil {
		return err
	}
	this.Name = string(tmp51)
	return err
}

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
)

type Swf_DefineSoundBody_Bps int
const (
	Swf_DefineSoundBody_Bps__Sound8Bit Swf_DefineSoundBody_Bps = 0
	Swf_DefineSoundBody_Bps__Sound16Bit Swf_DefineSoundBody_Bps = 1
)

type Swf_DefineSoundBody_Channels int
const (
	Swf_DefineSoundBody_Channels__Mono Swf_DefineSoundBody_Channels = 0
	Swf_DefineSoundBody_Channels__Stereo Swf_DefineSoundBody_Channels = 1
)
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) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp52, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Id = uint16(tmp52)
	tmp53, err := this._io.ReadBitsIntBe(4)
	if err != nil {
		return err
	}
	this.Format = tmp53
	tmp54, err := this._io.ReadBitsIntBe(2)
	if err != nil {
		return err
	}
	this.SamplingRate = Swf_DefineSoundBody_SamplingRates(tmp54)
	tmp55, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.BitsPerSample = Swf_DefineSoundBody_Bps(tmp55)
	tmp56, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.NumChannels = Swf_DefineSoundBody_Channels(tmp56)
	this._io.AlignToByte()
	tmp57, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.NumSamples = uint32(tmp57)
	return err
}

/**
 * Sound sampling rate, as per enum. Ignored for Nellymoser and Speex codecs.
 */
type Swf_RecordHeader struct {
	TagCodeAndLength uint16
	BigLen int32
	_io *kaitai.Stream
	_root *Swf
	_parent *Swf_Tag
	_f_tagType bool
	tagType Swf_TagType
	_f_smallLen bool
	smallLen int
	_f_len bool
	len int
}
func NewSwf_RecordHeader() *Swf_RecordHeader {
	return &Swf_RecordHeader{
	}
}

func (this *Swf_RecordHeader) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp58, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.TagCodeAndLength = uint16(tmp58)
	tmp59, err := this.SmallLen()
	if err != nil {
		return err
	}
	if (tmp59 == 63) {
		tmp60, err := this._io.ReadS4le()
		if err != nil {
			return err
		}
		this.BigLen = int32(tmp60)
	}
	return err
}
func (this *Swf_RecordHeader) TagType() (v Swf_TagType, err error) {
	if (this._f_tagType) {
		return this.tagType, nil
	}
	this.tagType = Swf_TagType(Swf_TagType((this.TagCodeAndLength >> 6)))
	this._f_tagType = true
	return this.tagType, nil
}
func (this *Swf_RecordHeader) SmallLen() (v int, err error) {
	if (this._f_smallLen) {
		return this.smallLen, nil
	}
	this.smallLen = int((this.TagCodeAndLength & 63))
	this._f_smallLen = true
	return this.smallLen, nil
}
func (this *Swf_RecordHeader) Len() (v int, err error) {
	if (this._f_len) {
		return this.len, nil
	}
	var tmp61 int32;
	tmp62, err := this.SmallLen()
	if err != nil {
		return 0, err
	}
	if (tmp62 == 63) {
		tmp61 = this.BigLen
	} else {
		tmp63, err := this.SmallLen()
		if err != nil {
			return 0, err
		}
		tmp61 = tmp63
	}
	this.len = int(tmp61)
	this._f_len = true
	return this.len, nil
}
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) Read(io *kaitai.Stream, parent *Swf_Tag, root *Swf) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp64, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.MaxRecursionDepth = uint16(tmp64)
	tmp65, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.ScriptTimeoutSeconds = uint16(tmp65)
	return err
}