www.xbdev.net xbdev - software development
Monday August 19, 2019
home | about | contact | Donations

     
 

Image Formats

A single picture is worth a thousand words...

 

Flash (.swf) File Format - (Basics)

by bkenwright@xbdev.net

 

There's a lot of cool things you'll come to love and hate about the flash swf format....I mean its data is stored using chunks or tokens...so as you read in each token descriptor, if you don't know what it is, or havn't wrote that bit of code yet, you can just skip over it :)  So as you continue to add additional functionality to your parser, well you can continue to add in the parts that you need :)

 

But it does have a few downsides as well, it has a lot of different token types...and I mean a lot...each new version they seem to add a dozen more types...and some of the tokens are recursive!...so some tokens store more tokens within themselves!....criky!...it can soon get whacky complicated!

 

But hey!..don't worry...I'll hold your hand through it....once you get past most of the quirks, you'll be okay...I mean I found it okay, but its can soon get very time consuming....might need to put the kettle on and make a strong black coffee :D

 

To start the exploration of the swf file format, where going to use a very simple swf file called simple.swf...and you can see a screenshot of it on the right.  Its not compressed, and it only has a single frame, so it doesn't animate...I suppose we could make it even more simple...but I suppose this will do for now.

 

The demo code is compiled using visual studio 2k5, and I tend to use sprintf(..) and other standard c/c++ librarys, because I'm writing a lot of the information to a debug output file, and adding lots of debug information, so you have to add in '_CRT_SECURE_NO_DEPRECATE' to the project define....as Microsoft doesn't like us using them anymore!

 

What we'll do first!....is read in the first 4 bytes!  Yup, nothing to extreme, we'll read them in, and write them out to a text file...and what you'll find is that the first 3 bytes will tell us what where dealing with.  I just read the 4th one in to show that its not a null terminated string ;)

 

Source - Download Source Code
////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
// File:   main.cpp                                                                   //
// Date:   20-12-06 (xmas)                                                            //
// Author: bkenwright@xbdev.net                                                       //
// URL:    www.xbdev.net                                                              //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////
/*
   Introduction to the flash swf file format
*/
////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>	// sprintf(..), fopen(..)
#include <stdarg.h>     // So we can use ... (in dprintf)

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
//  dprintf(..)                                                                       //
//  Debug function, so we can write various output information to a log file as we    //
//  read in new information - I usually prefair to write it to a txt file, so I can   //
//  later open it in notepad or something and take a careful look at it, instead      //
//  of dumping it to the command window.                                              //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

//Saving debug information to a log file
void dprintf(const char *fmt, ...) 
{
	va_list parms;
	char buf[256];

	// Try to print in the allocated space.
	va_start(parms, fmt);
	vsprintf (buf, fmt, parms);
	va_end(parms);

	// Write the information out to a txt file
	FILE *fp = fopen("output.txt", "a+");
	fprintf(fp, "%s", buf);
	fclose(fp);

}// End dprintf(..)

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
//  main(..)                                                                          //
//  Program entry point - its where we start and end                                  //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

void main()
{
	dprintf("Opening File: simple.swf\n");

	FILE * fp = fopen("simple.swf", "rb");


	char buf[4];
	fread (	buf,			// void * buffer,
		3 * sizeof(char),	// size
		1,			// count,
		fp );			// FILE * stream

	dprintf("Sig: '%c' '%c' '%c' '%c'\n",	buf[0], buf[1], buf[2], buf[3]);

	fclose(fp);

	dprintf("Closing File.\n Done\n");
}// End main(..)

/*
Opening File: simple.swf
Sig: 'F' 'W' 'S' ''
Closing File.
Done
*/

 

Not to bad eh?  Most of the code is just setup code, and things...so as long as your familiar with standard c/c++ its not to bad.  So what you'll see from the output file is we have 'FWS' which means where dealing with a bog standard uncompressed .swf file.  If we'd have 'CWS' then it would be a compressed flash .swf file, and we'd have to uncompress it using zlib, which we'll go over later :)

 

So lets take a look at what the header file format looks like:

 

Field Type Comment
Signature UI8[3] Signature byte 'F' 'W' 'S' or 'C' 'W' 'S' for compressed with zlib
Version UI8 Single byte file version
File Length UI32 Length of entire file in bytes
Frame Size RECT

RECT format is:

UI5   - nBits,

nBits - xMin

nBits - xMax

nBits - yMin

nBits - yMax

Frame size in TWIPS
Frame Rate UI16 Frame delay in 8.8 fixed number of frames per second
Frame Count UI16 Total number of frames in movie

 

Now the first thing that just shouted out at me when I first seen that header format was ' RECT'!  You just want to go, "Whats that all about"...well as I mentioned earlier, lots of the information in this file format is bitwise packed...so a lot of times you have to read in a few bits, to determine how many bits to read in next!...be clear I'm saying bits, not bytes!.

 

Seems clear though, we have a Signature, Version, FileLength, all seems nice and clear to me, so I think we can create a nice tidy header structure and read in all our header data :)

 

What your about to see might look like a lot!  And if your shaky on bit shifting, and all that boolean logic of ANDing and ORing data, well your in for fun now :)  As all where doing below is reading in the header file and storing it in a structure.  The biggest chunk is because of the RECT bounds part....which is messy I think...I did it all in the main so you could see how you do it....later on I create a ReadRect(..) function where you can just pass the start pointer to the data, and it returns how many bytes to increment along by to the next data you want.

 

Source - Download Source Code
////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
// File:   main.cpp                                                                   //
// Date:   20-12-06 (xmas)                                                            //
// Author: bkenwright@xbdev.net                                                       //
// URL:    www.xbdev.net                                                              //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////
/*
   Introduction to the flash swf file format
*/
////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>	// sprintf(..), fopen(..)
#include <stdarg.h>     // So we can use ... (in dprintf)

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
//  SWF Header Container Structures                                                   //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

struct stRect
{
	int m_Nbits;		// nBits = UB[5] Bits in each rect value field 
	int m_Xmin;		// SB[nBits] X minimum position for rect 
	int m_Xmax;		// SB[nBits] X maximum position for rect 
	int m_Ymin;		// SB[nBits] Y minimum position for rect 
	int m_Ymax;		// SB[nBits] Y maximum position for rect 

};

struct stSWFHeader
{
	char		m_Sig[3];	// UI8[3] Signature byte 1 - 'FWS' or 'CWS'
	unsigned int	m_Version;	// UI8 Single byte file version 
	unsigned int	m_FileLength;	// UI32 Length of entire file in bytes 
	stRect		m_FrameSize;	// RECT Frame size in TWIPS 
	unsigned short  m_FrameRate;	// UI16 Frame delay in 8.8 fixed number of fps
	unsigned short  m_FrameCount;	// UI16 Total number of frames in movie 
};


//Saving debug information to a log file
void dprintf(const char *fmt, ...) 
{
	.... (as above)

}// End dprintf(..)

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
//  main(..)                                                                          //
//  Program entry point - its where we start and end                                  //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

void main()
{
	dprintf("Reading in simple.swf\n");

	// Open the file, and read in all its contents int a large buffer
	FILE * fp = fopen("simple.swf", "rb");

	// Large temporary buffer which we'll use to read in the file to
	char fileData[20000];

	int size = 0;
	while (true)
	{
		int done = (int)fread(fileData + size, 1, 1, fp);
		if (done)
		{
			size += 1;
		}
		else
		{
			break;
		}
	}// End while(..)

	// Close file
	fclose(fp);

	// Pointer to the start of the file data, which we can increment as 
	// we go along and determine what each byte is for
	char* data = fileData;

	// At this point, we have read in the whole file, and its stored
	// in our temporary buffer.

	dprintf("Sig: '%c' '%c' '%c' \n",	data[0], data[1], data[2]);

	// Create a temp instance of our header structure, and read data 
	// into it
	stSWFHeader head;

	head.m_Sig[0] = data[0];
	head.m_Sig[1] = data[1];
	head.m_Sig[2] = data[2];
	// Increment data offset by 3 bytes
	data += 3;

	head.m_Version = data[0];
	dprintf("Version: %d\n", head.m_Version);
	// Increment data offset by 1 byte
	data += 1;
	

	head.m_FileLength = ((unsigned int*)data)[0];
	dprintf("FileLength: %d\n", head.m_FileLength);
	// Increment data offset by 4 bytes (unsigned int is 4 bytes)
	data += 4;

	// Get a pointer to the rect from the header, easier to reference then
	stRect* rect = &head.m_FrameSize;

	// Need to get the first 5 bits!
	int nBits = data[0] >> 3;
	rect->m_Nbits = nBits;
	dprintf("Rect: nBits: %d\n", rect->m_Nbits);

	// Now this is where it gets tricky, as we have some of our data
	// in this byte, and some in the next bytes..so we have
	// to do lots of bit manipulation

	// Temp variable which we will use to store the value in as we
	// get each bit, as we go from byte to byte
	int buf = 0;

	// Current number of bits left in the byte where working with, as
	// remember the first 5 bytes where use to hold the number of bits
	// for the other size
	int numBits = 3;

	// Byte data, which we change as we go from byte to byte
	unsigned char byte = data[0] & 0x7;

	// Read 4 rect values into here, i.e. xMin, xMax etc
	int val[4];

	// Loop around the 4 values
	for (int i=0; i<4; i++)
	{
		buf = 0;
		for (int j=0; j<nBits; j++) // Go bit by bit along the byte
		{
			if (byte & 0x80)		
			{
				buf = buf | 1;	// We have a 1 at this bit
			}
			buf = buf << 1;		// Defaults to 0, so we have 0 at this bit
			byte = byte<<1;

			numBits--;
			if (numBits==0)		// Reached the last bit, we get the next byte
			{			// along
				data++;
				byte = data[0];
				numBits=8;	// Just got a new byte, so we now have 8
						// bits to work with
			}
		}
		buf = buf>>1;			// Took me a while to track this down, but we need
						// to go back one as we have found our value
		val[i] = buf;
	}

	// Just to note, we always start on a fresh aligned byte after readin
	// in a rect!  So there could be padding/unused bits on the end :)
	if (numBits>0)
	{
		data++;
	}


	// Store the values and write them out to our debug log
	rect->m_Xmin = val[0];
	rect->m_Xmax = val[1];
	rect->m_Ymin = val[2];
	rect->m_Ymax = val[3];

	dprintf("Rect Xmin: %d\n", rect->m_Xmin);
	dprintf("Rect Xmax: %d\n", rect->m_Xmax);
	dprintf("Rect Ymin: %d\n", rect->m_Ymin);
	dprintf("Rect Ymax: %d\n", rect->m_Ymax);

	head.m_FrameRate = ((unsigned short*)data)[0];
	dprintf("FrameRate: %d\n", head.m_FrameRate);
	data+=2;

	head.m_FrameCount = ((unsigned short*)data)[0];
	dprintf("FrameCount: %d\n", head.m_FrameCount);
	data+=2;


}// End main(..)


/*
Reading in simple.swf
Sig: 'F' 'W' 'S' 
Version: 3
FileLength: 151
Rect: nBits: 15
Rect Xmin: 0
Rect Xmax: 12000
Rect Ymin: 0
Rect Ymax: 8000
FrameRate: 3072
FrameCount: 1
*/

 

 

Doesn't look like much?  Well its pretty good I think...as after the header, its just tags!  Well some people call them chunks...basically, you have 6 bytes which say what type and how long...and you just keep going like that...and any chunks we arn't interested in, we can just get its type, and skip over its contents :)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.