Mon, Jun 22, 2009

Manipulate SWF files with AS3SWF

During a project last year I was faced with an interesting challenge, altering a constant value inside an already compiled SWF. Due to the specifics of the project it turned out to be as simple as hardcoding the address of the constant and overwriting with the new value but my interest in a native AS3 SWF file library had been sparked.

I had experimented with creating a (C++) SWF file reader in the past but at that point SWF was still a closed format and so any information had been reverse engineered by other coders. Since then Adobe has opened the SWF file format and now provide a full detailed specification on their developer site (link at the end of this post). So with spec in hand and a weekend to spare I wrote as3swf.
SWF files are made up primarily of chunks of data called tags. Every tag has a code that represents what type of tag it is and a length property that tells the reader how many following bytes of data belong to the tag. This structure makes it easy for a reader to jump over tags it doesn't understand or care about.

To replicate this structure in AS3 I created a base Tag class that reads the tag code, length and data from a ByteArray. Adding support for specific tags is simply a case of subclassing Tag and overriding the _parseBytes method to process the tag specific data.
So far as3swf can read SWF file info and metadata, scene and frame info, library bitmaps and symbol names. It also has early support for the DoABC tag which is where AS3 bytecode is stored.

Once a SWF has been parsed, its tags can be retrieved by index or alternatively all tags of a certain code can retrieved. This makes it easy to for example access all bitmaps in a SWF. Included with as3swf is an example Air app that will display the structure and contents of any SWF file dragged into it.
The following is an example of using as3swf to trace out the contents of a SWF file. The SWF file could be loaded using a URLLoader object with its dataFormat property set to URLLoaderDataFormat.BINARY, or in Air using a FileSystem object.
import com.kode80.swf.SWF;
import com.kode80.swf.tags.Tag;
		
function traceSWF( bytes:ByteArray):void
{
	var swf:SWF = new SWF();
	swf.readFrom( bytes);
	
	trace( "Header:");
	trace( "is compressed: " + swf.isCompressed);
	trace( "version: " + swf.version);
	trace( "uncompressed size: " + swf.uncompressedFileSize);
	trace( "frame rectangle: " + swf.frameRectangle);  // SWF stage dimensions
	trace( "frame rate: " + swf.frameRate);
	trace( "number of frames: " + swf.numberOfFrames);
	
	var tag:Tag;
	trace( "Tags:");
	for ( var i:uint = 0; i < swf.numberOfTags; i++)
	{
		tag = swf.getTagAt(i);
		
		trace( "Tag #" + i);
		trace( "Tag: " + TagCodes.tagCodeToString( tag.tagCode));
		trace( "as3swf supported: " + swf.isSupportedTag( tag.tagCode));  // supported tags will be a subclass of Tag
		trace( "info: " + tag.toString());  // tag classes provide textual info through toString()
	}
}