www.xbdev.net xbdev - software development
Wednesday June 19, 2019
home | about | contact | Donations

     
 

Image Formats

A single picture is worth a thousand words...

 

Flash (.swf) File Format - (Dumping all the tag names and sizes)

by bkenwright@xbdev.net

 

As I've sure I've mentioned, the swf file format is great in some ways, as its made up of chunks...or its correct name is tags!  So once you've got passed the header, and know if its compressed or non-compressed, then you have the whole rest of the file...and its divided up into tags...where you'll read an integer id and a size, then the data will follow, followed by the next integer id and size...etc etc....each id describes what type of tag is is, and what data to expect in it...so for example you commonly find that one of the first tags is a DefineBackgroundColour tag, which has a set of byte defining the red, green and blue background colour :)

 

Now the swf format has a monster number of different tags...and if thats not worse!...some of the tags have nested tags inside them!...just crazy....beleive me, when I started playing with this format I didn't know when it was going to end....still if you break it down into pieces you can usually digest it...but it takes a lot of chewing :)

 

So you could explore the format, I wrote this program and it basically goes through the swf file and dumps all the tag names to the output debug txt file, so you can see what ones are in there...pretty neat I think :) 

 

For testing I used a simple flash demo I found laying around called dialog.swf, which contains a lot of different tags....the good thing you'll notice though is a lot of the tags repeat...so once understand a few of them, you can grab a whole load of info from the file :D

 

 

Source - Download Source Code (56kb)
////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
// File:   main.cpp                                                                   //
// Date:   13-11-07 (winter)                                                          //
// Author: bkenwright@xbdev.net                                                       //
// URL:    www.xbdev.net                                                              //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////
/*
   Introduction to the flash swf file format - dumping tag information
*/
////////////////////////////////////////////////////////////////////////////////////////

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

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
//  Debug printf that writes to a txt file :)                                         //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

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

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
//  Various Flash SWF Container Structure Defines                                     //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

#pragma pack(1)
struct stSWFHeader
{
    unsigned char sig[3];
    unsigned char version;
    unsigned int  uncompSize;

    enum rectSize
    {
        xmin = 0,        // X minimum position for rectangle in twips
        xmax,            // X maximum position for rectangle in twips
        ymin,            // Y minimum position for rectangle in twips
        ymax            // Y maximum position for rectangle in twips
    };

    int    rectBits;        // Bits used for each subsequent field
    int rectSizes[4];    // rectSize indexes

    unsigned short int frameRate;
    unsigned short int frameCount;
};
#pragma pack()

////////////////////////////////////////////////////////////////////////////////////////

struct stTagType
{
    int        m_id;
    char    m_name[32];
};

stTagType Tags [] =
{
    { 0,  "END"                    },                    
    { 1,  "SHOWFRAME"            },              
    { 2,  "DEFINESHAPE"            },            
    { 3,  "FREECHARACTER"        },          
    { 4,  "PLACEOBJECT"            },            
    { 5,  "REMOVEOBJECT"        },           
    { 6,  "DEFINEBITS"            },             
    { 7,  "DEFINEBUTTON"        },           
    { 8,  "JPEGTABLES"            },             
    { 9,  "SETBACKGROUNDCOLOR"    },     
    { 10, "DEFINEFONT"            },             
    { 11, "DEFINETEXT"            },             
    { 12,  "DOACTION"            },              
    { 13,  "DEFINEFONTINFO"        },        
    { 14,  "DEFINESOUND"        },           
    { 15,  "STARTSOUND"            },            
    { 17,  "DEFINEBUTTONSOUND"    },     
    { 18,  "SOUNDSTREAMHEAD"    },       
    { 19,  "SOUNDSTREAMBLOCK"    },      
    { 20,  "DEFINELOSSLESS"        },        
    { 21,  "DEFINEBITSJPEG2"    },       
    { 22,  "DEFINESHAPE2"        },          
    { 23,  "DEFINEBUTTONCXFORM" },     
    { 24,  "PROTECT"            },               
    { 25,  "PATHSAREPOSTSCRIPT" },     
    { 26,  "PLACEOBJECT2"        },          
    { 28,  "REMOVEOBJECT2"        },         
    { 29,  "SYNCFRAME"            },             
    { 31,  "FREEALL"            },               
    { 32,  "DEFINESHAPE3"        },          
    { 33,  "DEFINETEXT2"        },           
    { 34,  "DEFINEBUTTON2"        },         
    { 35,  "DEFINEBITSJPEG3"    },       
    { 36,  "DEFINELOSSLESS2"    },       
    { 37,  "DEFINEEDITTEXT"        },        
    { 38,  "DEFINEVIDEO"        },           
    { 39,  "DEFINESPRITE"        },          
    { 40,  "NAMECHARACTER"        },         
    { 41,  "SERIALNUMBER"        },          
    { 42,  "DEFINETEXTFORMAT"    },      
    { 43,  "FRAMELABEL"            },            
    { 45,  "SOUNDSTREAMHEAD2"    },      
    { 46,  "DEFINEMORPHSHAPE"    },      
    { 47,  "FRAMETAG"            },              
    { 48,  "DEFINEFONT2"        },           
    { 49,  "GENCOMMAND"            },            
    { 50,  "DEFINECOMMANDOBJ"    },      
    { 51,  "CHARACTERSET"        },          
    { 52,  "FONTREF"            },               
    { 56,  "EXPORTASSETS"        },          
    { 57,  "IMPORTASSETS"        },          
    { 58,  "ENABLEDEBUGGER"        },        
    { 59,  "INITACTION"            },            
    { 60,  "DEFINEVIDEOSTREAM"    },     
    { 61,  "VIDEOFRAME"            },            
    { 62,  "DEFINEFONTINFO2"    },        
    { 1023,"DEFINEBITSPTR"        },         
};


struct stSmallTag
{
    unsigned short Tag    : 10;    // UB[10] Tag id 
    unsigned short Length : 6;    // UB[6] Length of tag 
};

struct stLargeTag
{
    unsigned short int Tag        : 10;    // UB[10] Tag id 
    unsigned short int LengthID    : 6;    // Long Header Flag UB[6] Always 0x3F  
    int                   Length;            // UI32 Length of tag 
};

////////////////////////////////////////////////////////////////////////////////////////


void ReadDataTag(int tagID, int tagLength, char * data)
{
    char * tagName = "**Unknown** - Wierd";
    for (int i=0; i<sizeof(Tags)/sizeof(stTagType); i++)
    {
        if (tagID == Tags[i].m_id)
        {
            tagName = Tags[i].m_name;
            break;
        }
    }

    dprintf("\tTag: %.2d",        tagID);
    dprintf(", Name: %21s",        tagName);
    dprintf(", Length: %4d",    tagLength);

    // Lets dump the first 2 raw data bytes for curiosity as well
    dprintf(", Raw-Data:0x%.2X, 0x%.2X\n", data[0], data[1]);

}// End ReadDataTag(..)

//--------------------------------------------------------------------------------------

void ReadDataChunks(char * data, int lenData)
{
    char * tmpData = data;
    int    tmpLen  = lenData;

    while (tmpLen > 0)
    {
        unsigned short int tmpx = tmpData[1] | (tmpData[0]<<8);
        unsigned short int *tmp0 = (unsigned short int*)&tmpData[0];
        tmpData+=2;
        tmpLen-=2;

        int Tag = (*tmp0 >> 6);
        int Length = (*tmp0 & 0x3F);

        if (Length != 0x3F)    // Small Tag Data
        {
            tmpData+=Length;
            tmpLen-=Length;

            ReadDataTag(Tag, Length, tmpData);
        }
        else //------------ // Large Tag Data
        {
            Length =  *((unsigned int*)&tmpData[0]);
            tmpData+=4;
            tmpLen-=4;

            tmpData+=Length;
            tmpLen-=Length;

            ReadDataTag(Tag, Length, tmpData);
        }
    }

    dprintf("\nEnd Reading Tags\n");

}// End ReadDataChunks(..)



////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
// Program Entry Point                                                                //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

void main()
{
    // For testing, we'll just read in a test flash swf file called dialog.swf
    FILE * fp = fopen("dialog.swf", "rb");

    // Basic error checking
    if (fp==NULL)
    {
        dprintf("** Error - unable to open swf file, ejecting..exiting..goodbye\n");
    }

    stSWFHeader head;

    dprintf("---\n");

    fread (    &head.sig,                    // void * buffer,
            3 * sizeof(char),            // size
            1,                            // count,
            fp );                        // FILE * stream

    fread (    &head.version,                // void * buffer,
            sizeof(head.version),        // size
            1,                            // count,
            fp );                        // FILE * stream

    fread (    &head.uncompSize,            // void * buffer,
            sizeof(head.uncompSize),    // size
            1,                            // count,
            fp );                        // FILE * stream

    dprintf("sig: %c %c %c\n",    head.sig[0], head.sig[1], head.sig[2]);
    dprintf("version: %d\n",    head.version);
    dprintf("uncom size: %d\n", head.uncompSize);


    // Just for this simple example we'll just allocate a big chunk of memory
    // to read in all our data
    int outSize = 500000;
    char * tmpIn  = new char [20000];
    char * tmpOut = new char [outSize];


    memset(tmpIn,  0, 4000);
    memset(tmpOut, 0, outSize);

    // Okay read in all the file data into our temp buffer!
    int done = 1;
    int size = 0;
    while (done)
    {
        done = (int)fread(tmpIn + size, 1, 1, fp);
        if (done)
        {
            size += 1;
        }
    }
    // Finished with the file now :)
    fclose(fp);
    

    // Now at this point we have all our data from the file in memory, so we 
    // have to determine if to just use as is, or uncompress it first, when
    // we've made this decidion, we will set them to variables
    char * uncompData        = NULL;    // Pointer to our swf data
    int    uncompDataSize    = 0;    // How many bytes or data is


    // Do we have a compressed swf?  
    if (head.sig[0]=='C' &&
        head.sig[1]=='W' &&
        head.sig[2]=='S')
    {

        int (*uncompress) (char *dest, unsigned int *destLen, const char *source, unsigned int sourceLen);
        HMODULE dll = LoadLibrary("zlib1.dll");
        uncompress = (int (*) (char *dest, unsigned int *destLen, const char *source, unsigned int sourceLen))GetProcAddress(dll, "uncompress");

        unsigned int tmpOutLen = outSize;
        int error = uncompress(tmpOut, &tmpOutLen, tmpIn, size);

        uncompDataSize = tmpOutLen;

        delete[] tmpIn;
        tmpIn = NULL;


        // Lets point to our uncompressed data
        uncompData = tmpOut;
    }
    // Not compressed
    else if (head.sig[0]=='F' &&
             head.sig[1]=='W' &&
             head.sig[2]=='S')
    {
        delete[] tmpOut;
        tmpOut = NULL;

        
        uncompData        = tmpIn;
        uncompDataSize    = size;
    }
    // Its a mutant unknown file!...bale out
    else
    {
        dprintf("*** Error, unknown file format! Ejecting, exiting..goodbye\n");
        return;
    }


    // We made it!...we made it!...we actually have the data now, so we progress
    // by now reading in the rest of the swf file format header...I think this is
    // bad to mix the compression with part of the header..maybe divided the header
    // into two parts...oh well...just the way it is.


    // Seems messy, but the swf rect is bit packed, so we do all this messy bit
    // fiddly stuff to get the data

    // Header Rect
    dprintf("---\n");
    unsigned char rectFirstByte = *uncompData;
    uncompData++;
    
    int nbits = rectFirstByte >> 3;
    head.rectBits = nbits;

    dprintf("Rect No: nbits: %d\n", nbits);

    int buf = 0;
    int numBits = 3;
    int data = rectFirstByte & 0x7;

    int i=0;
    while (i<4)
    {
        buf = 0;
        for (int j=0; j<nbits; j++)
        {
            if (data & 0x80)
            {
                buf = buf | 1;        // We have a 1 at this bit
            }
            buf = buf << 1;            // Defaults to 0, so we have 0 at this bit
            data = data<<1;

            numBits--;
            if (numBits==0)
            {
                data = *uncompData;
                uncompData++;
                numBits=8;

                uncompDataSize--;    // Total bytes left in our data
            }
        }
        buf = buf>>1;                // Took me a while to track this down, but we need
                                    // to go back one as we have found our value
        dprintf("\tbuf: %d\n", buf);                                                                            

        head.rectSizes[i] = buf;

        i++;
    }

    // Header FrameRate
    unsigned short int *tmpInt = (unsigned short int*)&uncompData[0];
    head.frameRate = *tmpInt;
    uncompData+=2;
    uncompDataSize-=2;

    // Header FrameCount
    tmpInt = (unsigned short int*)&uncompData[0];
    head.frameCount = *tmpInt;
    uncompData+=2;
    uncompDataSize-=2;

    dprintf("Head FrameRate: %d\n", head.frameRate);
    dprintf("Head FrameCount: %d\n", head.frameCount);

    ////////////////////////////////////////////////////////////////////////////////////
    // ** Dump Tags ** Dump Tags **  Dump Tags ** Dump Tags ** Dump Tags ** Dump Tags //
    // ** Dump Tags ** Dump Tags **  Dump Tags ** Dump Tags ** Dump Tags ** Dump Tags //
    // ** Dump Tags ** Dump Tags **  Dump Tags ** Dump Tags ** Dump Tags ** Dump Tags //
    ////////////////////////////////////////////////////////////////////////////////////

    // We are now at the start of the list of tags, so we go into this function, which
    // basically iterates through all the tags and dumps its name and size, one by
    // one to the debug text file..

    ReadDataChunks(&uncompData[0], uncompDataSize);

    ////////////////////////////////////////////////////////////////////////////////////

    // Well your mum always told you to tidy up before you leave!...
    if (tmpOut)
    {
        delete[] tmpOut;
        tmpOut = NULL;
    }

    if (tmpIn)
    {
        delete[] tmpIn;
        tmpIn = NULL;
    }


    dprintf("Done Parsing SWF File...thank you, goodbye :)\n");

}// End main(..)



/* output.txt

---
sig: C W S
version: 6
uncom size: 62352
---
Rect No: nbits: 15
    buf: 0
    buf: 12800
    buf: 0
    buf: 9600
Head FrameRate: 15360
Head FrameCount: 722
    Tag: 09, Name:    SETBACKGROUNDCOLOR, Length:    3, Raw-Data:0xFFFFFFFF, 0x0A
    Tag: 43, Name:            FRAMELABEL, Length:   11, Raw-Data:0x7F, 0x05
    Tag: 21, Name:       DEFINEBITSJPEG2, Length:  722, Raw-Data:0xFFFFFFBF, 0x00
    Tag: 02, Name:           DEFINESHAPE, Length:   36, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   16, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   34, Raw-Data:0xFFFFFF93, 0x06
    Tag: 26, Name:          PLACEOBJECT2, Length:   19, Raw-Data:0xFFFFFFFF, 0x08
    Tag: 35, Name:       DEFINEBITSJPEG3, Length: 1550, Raw-Data:0xFFFFFFBF, 0x00
    Tag: 02, Name:           DEFINESHAPE, Length:   37, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   16, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   35, Raw-Data:0xFFFFFF93, 0x06
    Tag: 26, Name:          PLACEOBJECT2, Length:   19, Raw-Data:0xFFFFFFFF, 0x08
    Tag: 35, Name:       DEFINEBITSJPEG3, Length:  755, Raw-Data:0xFFFFFFBF, 0x00
    Tag: 02, Name:           DEFINESHAPE, Length:   33, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   16, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   35, Raw-Data:0xFFFFFF93, 0x06
    Tag: 26, Name:          PLACEOBJECT2, Length:   19, Raw-Data:0xFFFFFFFF, 0x08
    Tag: 35, Name:       DEFINEBITSJPEG3, Length: 1506, Raw-Data:0xFFFFFFBF, 0x00
    Tag: 02, Name:           DEFINESHAPE, Length:   34, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   16, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   34, Raw-Data:0xFFFFFF93, 0x06
    Tag: 26, Name:          PLACEOBJECT2, Length:   19, Raw-Data:0x7F, 0x05
    Tag: 21, Name:       DEFINEBITSJPEG2, Length:  698, Raw-Data:0xFFFFFFBF, 0x00
    Tag: 02, Name:           DEFINESHAPE, Length:   33, Raw-Data:0xFFFFFFFF, 0x09

.. other same things...

    Tag: 01, Name:             SHOWFRAME, Length:    0, Raw-Data:0x00, 0x00
    Tag: 00, Name:                   END, Length:    0, Raw-Data:0x00, 0x00
    Tag: 00, Name:                   END, Length:    0, Raw-Data:0x00, 0x00

End Reading Tags
Done Parsing SWF File...thank you, goodbye :)

*/

 

You'll find if you run the demo code, it will generate a large 290kb txt file, which you can look at, and gives you all the chunk id's....these id's are just numbers for now, but its good to see that you can actually just cycle through the whole contents of the file, and pick out bits of data that we want ...compressed and uncompressed.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 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.