#include "swf.h"

////////////////////////////////////////////////////////////
//  This file is derived from the 'buggy' SWF parser provided
//  by Macromedia.
//
//  Modifications : Olivier Debon  <odebon@club-internet.fr>
//  

#ifdef RCSID
static char *rcsid = "$Id: script.cc,v 1.1 2001/10/30 05:59:18 jryland Exp $";
#endif

#define printf(fmt,args...)

//////////////////////////////////////////////////////////////////////
// Inline input script object methods.
//////////////////////////////////////////////////////////////////////

//
// Inlines to parse a Flash file.
//
inline U8 CInputScript::GetByte(void) 
{
    return m_fileBuf[m_filePos++];
}

inline U16 CInputScript::GetWord(void)
{
    U8 * s = m_fileBuf + m_filePos;
    m_filePos += 2;
    return (U16) s[0] | ((U16) s[1] << 8);
}

inline U32 CInputScript::GetDWord(void)
{
    U8 * s = m_fileBuf + m_filePos;
    m_filePos += 4;
    return (U32) s[0] | ((U32) s[1] << 8) | ((U32) s[2] << 16) | ((U32) s [3] << 24);
}




//////////////////////////////////////////////////////////////////////
// Input script object methods.
//////////////////////////////////////////////////////////////////////

CInputScript::CInputScript(int level)
// Class constructor.
{
    this->level = level;

    // Initialize the input pointer.
    m_fileBuf = NULL;

    // Initialize the file information.
    m_filePos = 0;
    m_fileSize = 0;
    m_fileVersion = 0;

    // Initialize the bit position and buffer.
    m_bitPos = 0;
    m_bitBuf = 0;

    // Initialize the output file.
    m_outputFile = NULL;

    // Set to true if we wish to dump all contents long form
    m_dumpAll = false;

    // if set to true will dump image guts (i.e. jpeg, zlib, etc. data)
    m_dumpGuts = false;

    needHeader = 1;
    program = 0;

    outOfMemory = 0;

    next = NULL;

    return;
}


CInputScript::~CInputScript(void)
// Class destructor.
{
    // Free the buffer if it is there.
    if (m_fileBuf)
    {
	delete program;
        m_fileBuf = NULL;
        m_fileSize = 0;
    }
}


U16 CInputScript::GetTag(void)
{
    // Save the start of the tag.
    m_tagStart = m_filePos;
    
    if (m_actualSize-m_filePos < 2) return notEnoughData;

    // Get the combined code and length of the tag.
    U16 code = GetWord();

    // The length is encoded in the tag.
    U32 len = code & 0x3f;

    // Remove the length from the code.
    code = code >> 6;

    // Determine if another long word must be read to get the length.
    if (len == 0x3f) {
        if (m_actualSize-m_filePos < 4) return notEnoughData;
    	len = (U32) GetDWord();
    }

    // Determine the end position of the tag.
    m_tagEnd = m_filePos + (U32) len;
    m_tagLen = (U32) len;

    return code;
}


void CInputScript::GetRect (Rect * r)
{
    InitBits();
    int nBits = (int) GetBits(5);
    r->xmin = GetSBits(nBits);
    r->xmax = GetSBits(nBits);
    r->ymin = GetSBits(nBits);
    r->ymax = GetSBits(nBits);
}

void CInputScript::GetMatrix(Matrix* mat)
{
    InitBits();

    // Scale terms
    if (GetBits(1))
    {
        int nBits = (int) GetBits(5);
        mat->a = (float)(GetSBits(nBits))/(float)0x10000;
        mat->d = (float)(GetSBits(nBits))/(float)0x10000;
    }
    else
    {
     	mat->a = mat->d = 1.0;
    }

    // Rotate/skew terms
    if (GetBits(1))
    {
        int nBits = (int)GetBits(5);
        mat->c = (float)(GetSBits(nBits))/(float)0x10000;
        mat->b = (float)(GetSBits(nBits))/(float)0x10000;
    }
    else
    {
     	mat->b = mat->c = 0.0;
    }

    // Translate terms
    int nBits = (int) GetBits(5);
    mat->tx = GetSBits(nBits);
    mat->ty = GetSBits(nBits);
}


void CInputScript::GetCxform(Cxform* cx, BOOL hasAlpha)
{
    int flags;
    int nBits;
    float aa; long ab;
    float ra; long rb;
    float ga; long gb;
    float ba; long bb;

    InitBits();

    flags = (int) GetBits(2);
    nBits = (int) GetBits(4);
    aa = 1.0; ab = 0;
    if (flags & 1)
    {
        ra = (float) GetSBits(nBits)/256.0;
        ga = (float) GetSBits(nBits)/256.0;
        ba = (float) GetSBits(nBits)/256.0;
        if (hasAlpha) aa = (float) GetSBits(nBits)/256.0;
    }
    else
    {
        ra = ga = ba = 1.0;
    }
    if (flags & 2)
    {
        rb = (S32) GetSBits(nBits);
        gb = (S32) GetSBits(nBits);
        bb = (S32) GetSBits(nBits);
        if (hasAlpha) ab = (S32) GetSBits(nBits);
    }
    else
    {
        rb = gb = bb = 0;
    }
    if (cx) {
    	cx->aa = aa;
    	cx->ab = ab;
    	cx->ra = ra;
    	cx->rb = rb;
    	cx->ga = ga;
    	cx->gb = gb;
    	cx->ba = ba;
    	cx->bb = bb;
    }
}


/* XXX: should allocate string */
char *CInputScript::GetString(void)
{
    // Point to the string.
    char *str = (char *) &m_fileBuf[m_filePos];

    // Skip over the string.
    while (GetByte());

    return str;
}

void CInputScript::InitBits(void)
{
    // Reset the bit position and buffer.
    m_bitPos = 0;
    m_bitBuf = 0;
}


S32 CInputScript::GetSBits (S32 n)
// Get n bits from the string with sign extension.
{
    // Get the number as an unsigned value.
    S32 v = (S32) GetBits(n);

    // Is the number negative?
    if (v & (1L << (n - 1)))
    {
        // Yes. Extend the sign.
        v |= -1L << n;
    }

    return v;
}


U32 CInputScript::GetBits (S32 n)
// Get n bits from the stream.
{
    U32 v = 0;

    for (;;)
    {
        S32 s = n - m_bitPos;
        if (s > 0)
        {
            // Consume the entire buffer
            v |= m_bitBuf << s;
            n -= m_bitPos;

            // Get the next buffer
            m_bitBuf = GetByte();
            m_bitPos = 8;
        }
        else
        {
         	// Consume a portion of the buffer
            v |= m_bitBuf >> -s;
            m_bitPos -= n;
            m_bitBuf &= 0xff >> (8 - m_bitPos);	// mask off the consumed bits
            return v;
        }
    }
}

void CInputScript::ParseFreeCharacter()
{
    U32 tagid = (U32) GetWord();

    tagid = tagid;

    printf("tagFreeCharacter \ttagid %-5u\n", tagid);
}


void CInputScript::ParsePlaceObject()
{
    Control *ctrl;

    ctrl = new Control;
    if (ctrl == NULL) {
	outOfMemory = 1;
    	return;
    }
    ctrl->type = ctrlPlaceObject;
    ctrl->flags = (PlaceFlags)(placeHasMatrix | placeHasCharacter);

    ctrl->character = getCharacter(GetWord());
    ctrl->depth = GetWord();

    GetMatrix(&(ctrl->matrix));

    if ( m_filePos < m_tagEnd ) 
    {
	ctrl->flags = (PlaceFlags)(ctrl->flags | placeHasColorXform);

	GetCxform(&ctrl->cxform, false);
    }

    program->addControlInCurrentFrame(ctrl);
}


void CInputScript::ParsePlaceObject2()
{
    Control *ctrl;

    ctrl = new Control;
    if (ctrl == NULL) {
	outOfMemory = 1;
    	return;
    }
    ctrl->type = ctrlPlaceObject2;

    ctrl->flags = (PlaceFlags)GetByte();
    ctrl->depth = GetWord();

    // Get the tag if specified.
    if (ctrl->flags & placeHasCharacter)
    {
	ctrl->character = getCharacter(GetWord());
    }

    // Get the matrix if specified.
    if (ctrl->flags & placeHasMatrix)
    {
	GetMatrix(&(ctrl->matrix));
    }

    // Get the color transform if specified.
    if (ctrl->flags & placeHasColorXform) 
    {
	GetCxform(&ctrl->cxform, true);
    }        

    // Get the ratio if specified.
    if (ctrl->flags & placeHasRatio)
    {
	ctrl->ratio = GetWord();
    }        

    // Get the ratio if specified.
    if (ctrl->flags & placeHasName)
    {
	ctrl->name = strdup(GetString());
    }        

    // Get the clipdepth if specified.
    if (ctrl->flags & placeHasClip) 
    {
	ctrl->clipDepth = GetWord();
    }        

    program->addControlInCurrentFrame(ctrl);
}


void CInputScript::ParseRemoveObject()
{
    Control *ctrl;

    ctrl = new Control;
    if (ctrl == NULL) {
	outOfMemory = 1;
    	return;
    }
    ctrl->type = ctrlRemoveObject;
    ctrl->character = getCharacter(GetWord());
    ctrl->depth = GetWord();

    program->addControlInCurrentFrame(ctrl);
}


void CInputScript::ParseRemoveObject2()
{
    Control *ctrl;

    ctrl = new Control;
    if (ctrl == NULL) {
	outOfMemory = 1;
    	return;
    }
    ctrl->type = ctrlRemoveObject2;
    ctrl->depth = GetWord();

    program->addControlInCurrentFrame(ctrl);
}


void CInputScript::ParseSetBackgroundColor()
{
    Control *ctrl;

    ctrl = new Control;
    if (ctrl == NULL) {
	outOfMemory = 1;
    	return;
    }
    ctrl->type = ctrlBackgroundColor;
    ctrl->color.red = GetByte();
    ctrl->color.green = GetByte();
    ctrl->color.blue = GetByte();

    program->addControlInCurrentFrame(ctrl);
}


void CInputScript::ParseDoAction()
{
    Control *ctrl;
    ActionRecord *ar;

    ctrl = new Control;
    if (ctrl == NULL) {
	outOfMemory = 1;
    	return;
    }
    ctrl->type = ctrlDoAction;

    do {
	ar = ParseActionRecord();
	if (ar) {
	    ctrl->addActionRecord( ar );
	}
	if (outOfMemory) {
		return;
	}
    } while (ar);

    program->addControlInCurrentFrame(ctrl);

}


void CInputScript::ParseStartSound()
{
    Control *ctrl;

    ctrl = new Control;
    if (ctrl == NULL) {
	outOfMemory = 1;
    	return;
    }
    ctrl->character = getCharacter(GetWord());
    ctrl->type = ctrlStartSound;

    program->addControlInCurrentFrame(ctrl);

    if (!m_dumpAll)
	return;

    U32 code = GetByte();

    printf("code %-3u", code);

    if ( code & soundHasInPoint )
	printf(" inpoint %u ", GetDWord());
    if ( code & soundHasOutPoint )
	printf(" oupoint %u", GetDWord());
    if ( code & soundHasLoops )
	printf(" loops %u", GetWord());

    printf("\n");
    if ( code & soundHasEnvelope ) 
    {
	int points = GetByte();

	for ( int i = 0; i < points; i++ ) 
	{
	    printf("\n");
	    printf("mark44 %u", GetDWord());
	    printf(" left chanel %u", GetWord());
	    printf(" right chanel %u", GetWord());
	    printf("\n");
	}
    }
}


void CInputScript::ParseStopSound()
{
    Control *ctrl;

    ctrl = new Control;
    if (ctrl == NULL) {
	outOfMemory = 1;
    	return;
    }
    ctrl->type = ctrlStopSound;

    program->addControlInCurrentFrame(ctrl);
}


void CInputScript::ParseShapeData(int getAlpha, int getStyles)
{
    int shapeRecord = 0;

    if (getStyles) {
	// ShapeWithStyle
	ParseFillStyle(getAlpha);
	ParseLineStyle(getAlpha);
    }

    InitBits();
    m_nFillBits = (U16) GetBits(4);
    m_nLineBits = (U16) GetBits(4);

    do {
	shapeRecord = ParseShapeRecord(getAlpha);
    } while (shapeRecord);
}

int
CInputScript::ParseShapeRecord(long getAlpha)
{
    // Determine if this is an edge.
    BOOL isEdge = (BOOL) GetBits(1);

    if (!isEdge)
    {
	// Handle a state change
	U16 flags = (U16) GetBits(5);

	// Are we at the end?
	if (flags == 0)
	{
	    // End of shape
	    return 0;
	}

	// Process a move to.
	if (flags & flagsMoveTo)
	{
	    U16 nBits = (U16) GetBits(5);
	    GetSBits(nBits);
	    GetSBits(nBits);
	}

	// Get new fill info.
	if (flags & flagsFill0)
	{
	    GetBits(m_nFillBits);
	}
	if (flags & flagsFill1)
	{
	    GetBits(m_nFillBits);
	}

	// Get new line info
	if (flags & flagsLine)
	{
	    GetBits(m_nLineBits);
	}

	// Check to get a new set of styles for a new shape layer.
	if (flags & flagsNewStyles)
	{
	    // Parse the style.
	    ParseFillStyle(getAlpha);
	    ParseLineStyle(getAlpha);

	    InitBits();	// Bug !

	    // Reset.
	    m_nFillBits = (U16) GetBits(4);
	    m_nLineBits = (U16) GetBits(4);
	}

	return flags & flagsEndShape ? 0 : 1;
    }
    else
    {
	if (GetBits(1))
	{
	    // Handle a line
	    U16 nBits = (U16) GetBits(4) + 2;	// nBits is biased by 2

	    // Save the deltas
	    if (GetBits(1))
	    {
		// Handle a general line.
		GetSBits(nBits);
		GetSBits(nBits);
	    }
	    else
	    {
		// Handle a vert or horiz line.
		GetBits(1);
		GetSBits(nBits);
	    }
	}
	else
	{
	    // Handle a curve
	    U16 nBits = (U16) GetBits(4) + 2;	// nBits is biased by 2

	    // Get the control
	    GetSBits(nBits);
	    GetSBits(nBits);

	    // Get the anchor
	    GetSBits(nBits);
	    GetSBits(nBits);
	}

	return 1;
    }
}


void CInputScript::ParseFillStyle(long getAlpha)
	// 
{
    U16 i = 0;
    FillType type;
    Matrix matrix;

    // Get the number of fills.
    U16 nFills = GetByte();

    // Do we have a larger number?
    if (nFills == 255)
    {
	// Get the larger number.
	nFills = GetWord();
    }

    // Get each of the fill style.
    for (i = 0; i < nFills; i++)
    {
	U16 fillStyle = GetByte();

	type = (FillType) fillStyle;

	printf("fillstyle: type=%d\n",defs[i].type);
	if (fillStyle & 0x10)
	{
	    U16 nbGradients;

	    type = (FillType) (fillStyle & 0x12);

	    // Get the gradient matrix.
	    GetMatrix(&matrix);

	    // Get the number of colors.
	    nbGradients = GetByte();

	    // Get each of the colors.
	    for (U16 j = 0; j < nbGradients; j++)
	    {
		GetByte();
		GetByte();
		GetByte();
		GetByte();
		if (getAlpha) {
		    GetByte();
		}
	    }
	}
	else if (fillStyle & 0x40)
	{
	    type = (FillType) (fillStyle & 0x41);

	    // Get the bitmapId
	    GetWord();

	    // Get the bitmap matrix.
	    GetMatrix(&matrix);
	}
	else
	{
	    type = (FillType) 0;

	    // A solid color
	    GetByte();
	    GetByte();
	    GetByte();
	    if (getAlpha) {
		GetByte();
	    }

	    printf("fillstyle: %x %x %x %x\n",			
		   defs[i].color.red,
		   defs[i].color.green,
		   defs[i].color.blue,
		   defs[i].color.alpha);
	}
    }
}

void CInputScript::ParseLineStyle(long getAlpha)
{
    long i;

    // Get the number of lines.
    U16 nLines = GetByte();

    // Do we have a larger number?
    if (nLines == 255)
    {
	// Get the larger number.
	nLines = GetWord();
    }

    // Get each of the line styles.
    for (i = 0; i < nLines; i++)
    {
	GetWord();
	GetByte();
	GetByte();
	GetByte();
	if (getAlpha) {
	    GetByte();
	}
    }
}


void CInputScript::ParseDefineShape(int level)
{
    Shape *shape;
    Rect rect;
    U32 tagid;

    tagid = (U32) GetWord();
    shape = new Shape(tagid,level);
    if (shape == NULL) {
	outOfMemory = 1;
    	return;
    }
    shape->dict = this;

    // Get the frame information.
    GetRect(&rect);

    shape->setBoundingBox(rect);

    shape->file_ptr = (unsigned char*)malloc(m_tagEnd-m_filePos);
    if (shape->file_ptr == NULL) {
	outOfMemory = 1;
    	delete shape;
	return;
    }
    memcpy((void*)shape->file_ptr,(void*)&m_fileBuf[m_filePos], m_tagEnd-m_filePos);

    shape->getStyles = 1;
    shape->getAlpha = (level == 3);

    ParseShapeData(level == 3, 1);

    addCharacter(shape);
}

void CInputScript::S_DumpImageGuts()
{
#if 0
    U32 lfCount = 0;                
    printf("----- dumping image details -----");
    while (m_filePos < m_tagEnd)
    {
	if ((lfCount % 16) == 0)
	{
	    fprintf(stdout, "\n");
	}
	lfCount += 1;
	fprintf(stdout, "%02x ", GetByte());
    }
    fprintf(stdout, "\n");
#endif
}

void CInputScript::ParseDefineBits()
{
    Bitmap *bitmap;
    U32 tagid = (U32) GetWord();
    int status;

    bitmap = new Bitmap(tagid,1);
    if (bitmap == NULL) {
	outOfMemory = 1;
    	return;
    }

    status = bitmap->buildFromJpegAbbreviatedData(&m_fileBuf[m_filePos]);

    if (status < 0) {
	fprintf(stderr,"Unable to read JPEG data\n");
	delete bitmap;
	return;
    }

    addCharacter(bitmap);
}


void CInputScript::ParseDefineBitsJPEG2()
{
    Bitmap *bitmap;
    U32 tagid = (U32) GetWord();
    int status;

    bitmap = new Bitmap(tagid,2);
    if (bitmap == NULL) {
	outOfMemory = 1;
    	return;
    }

    status = bitmap->buildFromJpegInterchangeData(&m_fileBuf[m_filePos], 0, 0);

    if (status < 0) {
	fprintf(stderr,"Unable to read JPEG data\n");
	delete bitmap;
	return;
    }

    addCharacter(bitmap);
}

void CInputScript::ParseDefineBitsJPEG3()
{
    Bitmap *bitmap;
    U32 tagid = (U32) GetWord();
    int status;
    long offset;

    printf("tagDefineBitsJPEG3 \ttagid %-5u\n", tagid);

    bitmap = new Bitmap(tagid,3);
    if (bitmap == NULL) {
	outOfMemory = 1;
    	return;
    }

    offset = GetDWord();	// Not in the specs !!!!

    status = bitmap->buildFromJpegInterchangeData(&m_fileBuf[m_filePos], 1, offset);
    if (status < 0) {
	fprintf(stderr,"Unable to read JPEG data\n");
	delete bitmap;
	return;
    }

    addCharacter(bitmap);
}


void CInputScript::ParseDefineBitsLossless(int level)
{
    Bitmap *bitmap;
    U32 tagid = (U32) GetWord();
    int status;
    int tableSize;         

    bitmap = new Bitmap(tagid,0);
    if (bitmap == NULL) {
	outOfMemory = 1;
    	return;
    }

    int format = GetByte();
    int width  =  GetWord();
    int height = GetWord();

    tableSize = 0;

    if (format == 3) {
	tableSize = GetByte();
    }

    status = bitmap->buildFromZlibData(&m_fileBuf[m_filePos], width, height, format, tableSize, level == 2);

    if (status < 0) {
	fprintf(stderr,"Unable to read ZLIB data\n");
	delete bitmap;
	return;
    }

    addCharacter(bitmap);
}

void CInputScript::ParseJPEGTables()
{
    Bitmap::readJpegTables(&m_fileBuf[m_filePos]);
}


ButtonRecord * CInputScript::ParseButtonRecord(long getCxform)
{
    U16 state;
    ButtonRecord *br;
    long tagid;
    Matrix matrix;
    long layer;
    Cxform *cxform;

    state = (U16) GetByte();

    if (state == 0) return 0;

    br = new ButtonRecord;
    if (br == NULL) {
	outOfMemory = 1;
    	return 0;
    }

    tagid = GetWord();
    layer = GetWord();
    GetMatrix(&matrix);

    if (br) {
        br->state = (ButtonState) state;
        br->character = getCharacter(tagid);
        br->layer = layer;
        br->cxform = 0;
	br->buttonMatrix = matrix;
    }

    if (getCxform) {
	cxform = new Cxform;
	GetCxform(cxform, true);
	if (br) {
		br->cxform = cxform;
		if (cxform == NULL) {
			outOfMemory = 1;
		}
	}
    }

    return br;
}

ActionRecord * CInputScript::ParseActionRecord()
{
    U8 action;
    U16 length = 0;
    char *url, *target, *label;
    long frameIndex, skipCount;
    ActionRecord *ar;

    action = GetByte();
    if (action == 0) return 0;

    ar = new ActionRecord;
    if (ar == NULL) {
    	outOfMemory = 1;
	return 0;
    }

    ar->action = (Action)action;

    if (action & 0x80) {
	length = GetWord();
    }

    switch (action) {
    case ActionGotoFrame:
	frameIndex = GetWord();
	if (ar) {
		ar->frameIndex = frameIndex;
	}
	break;
    case ActionGetURL:
	url = GetString();
	target = GetString();
	if (ar) {
		ar->url = strdup(url);
		ar->target = strdup(target);
	}
	break;
    case ActionWaitForFrame:
	frameIndex = GetWord();
	skipCount = GetByte();
	if (ar) {
		ar->frameIndex = frameIndex;
		ar->skipCount = skipCount;
	}
	break;
    case ActionSetTarget:
	target = strdup(GetString());
	if (ar) {
		ar->target = target;
	}
	break;
    case ActionGoToLabel:
	label = GetString();
	if (ar) {
		ar->frameLabel = strdup(label);
	}
	break;
    default:
	while (length--) {
		GetByte();
	}
    	break;
    }

    return ar;
}

void CInputScript::ParseDefineButton()
{
    Button		*button;
    ButtonRecord	*buttonRecord;
    ActionRecord	*actionRecord;

    U32 tagid = (U32) GetWord();

    button = new Button(tagid);
    if (button == NULL) {
	outOfMemory = 1;
    	return;
    }

    do {
	buttonRecord = ParseButtonRecord();
	if (buttonRecord) {
	    button->addButtonRecord( buttonRecord );
	}
	if (outOfMemory) {
		return;
	}
    } while (buttonRecord);

    do {
	actionRecord = ParseActionRecord();
	if (actionRecord) {
	    button->addActionRecord( actionRecord );
	}
	if (outOfMemory) {
		return;
	}
    } while (actionRecord);

    addCharacter(button);
}


void CInputScript::ParseDefineButton2()
{
    Button		*button;
    ButtonRecord	*buttonRecord;
    ActionRecord	*actionRecord;
    U16		 transition;
    U16		 offset;
    U8		 menu;

    U32 tagid = (U32) GetWord();

    button = new Button(tagid);

    if (button == NULL) {
    	outOfMemory = 1;
	return;
    }

    menu = GetByte();

    offset = GetWord();

    do {
	buttonRecord = ParseButtonRecord(true);
	if (buttonRecord) {
	    button->addButtonRecord( buttonRecord );
	}
	if (outOfMemory) {
		return;
	}
    } while (buttonRecord);

    while (offset) {
	offset = GetWord();

	transition = GetWord();

	do {
	    actionRecord = ParseActionRecord();
	    if (actionRecord) {
		button->addActionRecord( actionRecord );
	    }
	    if (outOfMemory) {
		    return;
	    }
	} while (actionRecord);

	button->addCondition( transition );
    }

    addCharacter(button);
}


void CInputScript::ParseDefineFont()
{
    SwfFont	*font = 0;
    U32 tagid = (U32) GetWord();
    long	 start;
    long	 nb,n;
    long	 offset;
    long	*offsetTable = 0;
    Shape	*shapes = 0;

    font = new SwfFont(tagid);
    if (font == NULL) {
	outOfMemory = 1;
    	return;
    }
    start = m_filePos;

    offset = GetWord();
    nb = offset/2;
    offsetTable = new long[nb];
    if (offsetTable == NULL) {
	goto memory_error;
    }
    offsetTable[0] = offset;

    for(n=1; n<nb; n++)
    {
	offsetTable[n] = GetWord();
    }

    shapes = new Shape[nb];
    if (shapes == NULL) {
	goto memory_error;
    }

    for(n=0; n<nb; n++)
    {
	long here;

	m_filePos = offsetTable[n]+start;

	here = m_filePos;
	ParseShapeData(0, 0);

	// Keep data for later parsing
	shapes[n].file_ptr = (unsigned char*)malloc(m_filePos-here);
	if (shapes[n].file_ptr == NULL) {
		goto memory_error;
	}
	memcpy((void*)shapes[n].file_ptr,(void*)&m_fileBuf[here],m_filePos-here);
    }

    font->setFontShapeTable(shapes,nb);

    delete[] offsetTable;

    addCharacter(font);
    return;

memory_error:
    outOfMemory = 1;
    if (offsetTable) delete offsetTable;
    if (font) delete font;
    if (shapes) delete[] shapes;
}


void CInputScript::ParseDefineMorphShape()
{
    U32 tagid = (U32) GetWord();

    tagid = tagid;
    printf("tagDefineMorphShape \ttagid %-5u\n", tagid);
}

void CInputScript::ParseDefineFontInfo()
{
    SwfFont	*font;
    U32 tagid = (U32) GetWord();
    long	 nameLen;
    char	*name;
    long	 n,nb;
    FontFlags    flags;
    long	*lut;

    font = (SwfFont *)getCharacter(tagid);

    if (font == NULL) {
    	outOfMemory = 1;
	return;
    }

    nameLen = GetByte();
    name = new char[nameLen+1];
    if (name == NULL) {
    	outOfMemory = 1;
	return;
    }
    for(n=0; n < nameLen; n++)
    {
	name[n] = GetByte();
    }
    name[n]=0;

    font->setFontName(name);

    delete name;

    flags = (FontFlags)GetByte();

    font->setFontFlags(flags);

    nb = font->getNbGlyphs();

    lut = new long[nb];
    if (lut == NULL) {
    	outOfMemory = 1;
    	delete font;
	return;
    }

    for(n=0; n < nb; n++)
    {
	if (flags & fontWideCodes) {
	    lut[n] = GetWord();
	} else {
	    lut[n] = GetByte();
	}
    }

    font->setFontLookUpTable(lut);
}





void CInputScript::ParseDefineFont2()
{
    int n;
    U32 tagid = (U32) GetWord();
    FontFlags	 flags;
    char		*name;
    long		 nameLen;
    long		 fontGlyphCount;
    long		*offsetTable = NULL;
    Shape       	*shapes = NULL;
    long        	 start;
    SwfFont     	*font;
    long 		*lut = NULL;

    font = new SwfFont(tagid);
    if (font == NULL) {
    	goto memory_error;
    }

    flags = (FontFlags)GetWord();

    font->setFontFlags(flags);

    nameLen = GetByte();
    name = new char[nameLen+1];
    if (name == NULL) {
    	goto memory_error;
    }
    for(n=0; n < nameLen; n++)
    {
	name[n] = GetByte();
    }
    name[n]=0;

    font->setFontName(name);

    delete name;

    fontGlyphCount = GetWord();

    start = m_filePos;

    offsetTable = new long[fontGlyphCount];
    if (offsetTable == NULL) {
    	goto memory_error;
    }
    for (n=0; n<fontGlyphCount; n++) {
	if (flags & 8) {
	    offsetTable[n] = GetDWord();
	} else {
	    offsetTable[n] = GetWord();
	}
    }

    shapes = new Shape[fontGlyphCount];
    if (shapes == NULL) {
    	goto memory_error;
    }

    for (n=0; n<fontGlyphCount; n++) {
	long here;

	m_filePos = offsetTable[n]+start;

	here = m_filePos;
	ParseShapeData(0, 0);

	// Keep data for later parsing
	shapes[n].file_ptr = (unsigned char*)malloc(m_filePos-here);
	if (shapes[n].file_ptr == NULL) {
		goto memory_error;
	}
	memcpy((void*)shapes[n].file_ptr,(void*)&m_fileBuf[here],m_filePos-here);
    }

    font->setFontShapeTable(shapes,fontGlyphCount);

    lut = new long[fontGlyphCount];
    if (lut == NULL) {
    	goto memory_error;
    }

    for(n=0; n < fontGlyphCount; n++)
    {
	if (flags & 4) {
	    lut[n] = GetWord();
	} else {
	    lut[n] = GetByte();
	}
    }

    font->setFontLookUpTable(lut);

    delete offsetTable;

    addCharacter(font);

    // This is an incomplete parsing
    return;

memory_error:
    outOfMemory = 1;
    if (font) delete font;
    if (offsetTable) delete offsetTable;
    if (lut) delete lut;
    if (shapes) delete[] shapes;
}

TextRecord * CInputScript::ParseTextRecord(int hasAlpha)
{
    TextRecord *tr;
    TextFlags   flags;

    flags = (TextFlags) GetByte();
    if (flags == 0) return 0;

    tr = new TextRecord;
    if (tr == NULL) {
    	outOfMemory = 1;
	return 0;
    }

    tr->flags = flags;

    if (flags & isTextControl) {
	if (flags & textHasFont) {
	    long fontId;

	    fontId = GetWord();
	    tr->font = (SwfFont *)getCharacter(fontId);
	}
	if (flags & textHasColor) {
	    tr->color.red = GetByte();
	    tr->color.green = GetByte();
	    tr->color.blue = GetByte();
	    if (hasAlpha) {
		tr->color.alpha = GetByte();
	    } else {
		tr->color.alpha = ALPHA_OPAQUE;
	    }
	}
	if (flags & textHasXOffset) {
	    tr->xOffset = GetWord();
	}
	if (flags & textHasYOffset) {
	    tr->yOffset = GetWord();
	}
	if (flags & textHasFont) {
	    tr->fontHeight = GetWord();
	}
	tr->nbGlyphs = GetByte();
    } else {
	tr->flags = (TextFlags)0;
	tr->nbGlyphs = (long)flags;
    }

    tr->glyphs = new Glyph[ tr->nbGlyphs ];
    if (tr->glyphs == NULL) {
    	outOfMemory = 1;
	delete tr;
	return 0;
    }

    InitBits();
    for (int g = 0; g < tr->nbGlyphs; g++)
    {
	tr->glyphs[g].index = GetBits(m_nGlyphBits);
	tr->glyphs[g].xAdvance = GetBits(m_nAdvanceBits);
    }

    return tr;
}

void CInputScript::ParseDefineText(int hasAlpha)
{
    Text		*text;
    TextRecord	*textRecord;
    Matrix  	 m;
    Rect		 rect;
    U32 tagid = (U32) GetWord();

    text = new Text(tagid);
    if (text == NULL) {
    	outOfMemory = 1;
	return;
    }

    GetRect(&rect);
    text->setTextBoundary(rect);

    GetMatrix(&m);
    text->setTextMatrix(m);

    m_nGlyphBits = GetByte();
    m_nAdvanceBits = GetByte();

    do {
	textRecord = ParseTextRecord(hasAlpha);
	if (textRecord) {
	    text->addTextRecord( textRecord );
	}
	if (outOfMemory) {
		delete text;
		return;
	}
	if (m_filePos >= m_tagEnd) break;
    } while (textRecord);

    addCharacter(text);
}


void CInputScript::ParseDefineSound()
{
    Sound		*sound;
    U32 tagid = (U32) GetWord();
    long		 nbSamples;
    long		 flags;
    char		*buffer;

    sound = new Sound(tagid);

    flags = GetByte();
    sound->setSoundFlags(flags);

    nbSamples = GetDWord();
    buffer = sound->setNbSamples(nbSamples);
    if (buffer == NULL) {
    	outOfMemory = 1;
	delete sound;
	return;
    }

    if (flags & soundIsADPCMCompressed) {
	Adpcm		*adpcm;

	adpcm = new Adpcm( &m_fileBuf[m_filePos] , flags & soundIsStereo );

	adpcm->Decompress((short *)buffer, nbSamples);

	delete adpcm;
    } else {
	memcpy(buffer, &m_fileBuf[m_filePos], m_tagLen-5);
    }

    addCharacter(sound);
}


void CInputScript::ParseDefineButtonSound()
{
    U32 tagid = (U32) GetWord();
    Button	*button;

    tagid = tagid;

    printf("tagDefineButtonSound \ttagid %-5u\n", tagid);

    button = (Button *)getCharacter(tagid);

    if (button == 0) {
	printf("	Couldn't find Button id %d\n", tagid);
	return;
    }

    // step through for button states
    for (int i = 0; i < 4; i++)
    {
	Sound	*sound;
	U32 soundTag = GetWord();

	sound = (Sound *)getCharacter(soundTag);

	if (sound) {
	    button->setButtonSound(sound,i);
	} else if (soundTag) {
	    printf("	Couldn't find Sound id %d\n", soundTag);
	}

	switch (i)
	{
	case 0:         
	    printf("upState \ttagid %-5u\n", soundTag);
	    break;
	case 1:            
	    printf("overState \ttagid %-5u\n", soundTag);
	    break;
	case 2:            
	    printf("downState \ttagid %-5u\n", soundTag);
	    break;
	}

	if (soundTag)
	{
	    U32 code = GetByte();
	    printf("sound code %u", code);

	    if ( code & soundHasInPoint )
		printf(" inpoint %u", GetDWord());
	    if ( code & soundHasOutPoint )
		printf(" outpoint %u", GetDWord());
	    if ( code & soundHasLoops )
		printf(" loops %u", GetWord());

	    printf("\n");
	    if ( code & soundHasEnvelope ) 
	    {
		int points = GetByte();

		for ( int p = 0; p < points; p++ ) 
		{
		    printf("\n");
		    printf("mark44 %u", GetDWord());
		    printf(" left chanel %u", GetWord());
		    printf(" right chanel %u", GetWord());
		    printf("\n");
		}
	    }
	}
	if (m_filePos == m_tagEnd) break;
    }
}

void CInputScript::ParseSoundStreamHead()
{
    int mixFormat = GetByte();

    // The stream settings
    int format = GetByte();
    int nSamples = GetWord();

    mixFormat = mixFormat;
    format = format;
    nSamples = nSamples;

    printf("tagSoundStreamHead \tmixFrmt %-3u frmt  %-3u nSamples %-5u\n", mixFormat, format, nSamples);
}

void CInputScript::ParseSoundStreamHead2()
{
    int mixFormat = GetByte();

    // The stream settings
    int format = GetByte();
    int nSamples = GetWord();

    mixFormat = mixFormat;
    format = format;
    nSamples = nSamples;

    //printf("tagSoundStreamHead2 \tmixFormat %-3u format %-3u nSamples %-5u\n", mixFormat, format, nSamples);
}

void CInputScript::ParseSoundStreamBlock()
{
    printf("tagSoundStreamBlock\n");
}

void CInputScript::ParseDefineButtonCxform()
{
    ButtonRecord *br;
    Button	*button;
    U32 tagid = (U32) GetWord();

    button = (Button *)getCharacter(tagid);

    for (br = button->getButtonRecords(); br; br = br->next)
    {
	br->cxform = new Cxform;
	GetCxform(br->cxform, false);
    }
}

void CInputScript::ParseNameCharacter()
{
    U32 tagid = (U32) GetWord();
    char *label = strdup(GetString());

    nameCharacter(tagid, label);
}


void CInputScript::ParseFrameLabel()
{
    char *label = strdup(GetString());
    program->setCurrentFrameLabel(label);
}


void CInputScript::ParseDefineMouseTarget()
{
    printf("tagDefineMouseTarget\n");
}


void CInputScript::ParseDefineSprite()
{
    Sprite  *sprite;
    Program *prg;
    int status;

    U32 tagid = (U32) GetWord();
    U32 frameCount = (U32) GetWord();

    if (frameCount == 0) return;

    printf("tagDefineSprite \ttagid %-5u \tframe count %-5u\n", tagid, frameCount);

    sprite = new Sprite(program->movie, tagid, frameCount);
    if (sprite == NULL) {
    	outOfMemory = 1;
	return;
    }
    if (sprite->getProgram() == NULL) {
    	delete sprite;
    	outOfMemory = 1;
	return;
    }

    prg = sprite->getProgram();

    // Set current program
    program = prg;

    ParseTags(&status);

    if (outOfMemory) {
    	delete sprite;
	return;
    }

    addCharacter(sprite);
}


void CInputScript::ParseUnknown(long code, long len)
{
    printf("Unknown Tag : %d  - Length = %d\n", code, len);
}


void
CInputScript::ParseTags(int *status)
	// Parses the tags within the file.
{

    // Initialize the end of frame flag.
    BOOL atEnd = false;

    // Loop through each tag.
    while (!atEnd)
    {
	U32 here;

	// Get the current tag.
	U16 code = GetTag();

	if (code == notEnoughData) {
		m_filePos = m_tagStart;
		*status |= FLASH_PARSE_NEED_DATA;
		return;
	}

	//printf("Code %d, tagLen %8u \n", code, m_tagLen);

	here = m_filePos;

	// Get the tag ending position.
	U32 tagEnd = m_tagEnd;

	if (m_tagEnd > m_actualSize) {
		m_filePos = m_tagStart;
		*status |= FLASH_PARSE_NEED_DATA;
	    	return;
	}

	switch (code)
	{
	case stagProtect:
	    break;

	case stagEnd:

	    // We reached the end of the file.
	    atEnd = true;

	    printf("End of Movie\n");

	    break;

	case stagShowFrame:

	    // Validate frame
	    program->validateLoadingFrame();
	    *status |= FLASH_PARSE_WAKEUP;

	    break;

	case stagFreeCharacter:
	    ParseFreeCharacter();
	    break;

	case stagPlaceObject:
	    ParsePlaceObject();
	    break;

	case stagPlaceObject2:
	    ParsePlaceObject2();
	    break;

	case stagRemoveObject:
	    ParseRemoveObject();
	    break;

	case stagRemoveObject2:
	    ParseRemoveObject2();
	    break;

	case stagSetBackgroundColor:
	    ParseSetBackgroundColor();
	    break;

	case stagDoAction:
	    ParseDoAction();
	    break;

	case stagStartSound:
	    ParseStartSound();
	    break;

	case stagStopSound:
	    ParseStopSound();
	    break;

	case stagDefineShape: 
	    ParseDefineShape(1);
	    break;

	case stagDefineShape2:
	    ParseDefineShape(2);
	    break;

	case stagDefineShape3:
	    ParseDefineShape(3);
	    break;

	case stagDefineBits:
	    ParseDefineBits();
	    break;

	case stagDefineBitsJPEG2:
	    ParseDefineBitsJPEG2();
	    break;

	case stagDefineBitsJPEG3:
	    ParseDefineBitsJPEG3();
	    break;

	case stagDefineBitsLossless:
	    ParseDefineBitsLossless(1);
	    break;

	case stagDefineBitsLossless2:
	    ParseDefineBitsLossless(2);
	    break;

	case stagJPEGTables:
	    ParseJPEGTables();
	    break;

	case stagDefineButton:
	    ParseDefineButton();
	    break;

	case stagDefineButton2:
	    ParseDefineButton2();
	    break;

	case stagDefineFont:
	    ParseDefineFont();
	    break;

	case stagDefineMorphShape:
	    ParseDefineMorphShape();
	    break;

	case stagDefineFontInfo:
	    ParseDefineFontInfo();
	    break;

	case stagDefineText:
	    ParseDefineText(0);
	    break;

	case stagDefineText2:
	    ParseDefineText(1);
	    break;

	case stagDefineSound:
	    ParseDefineSound();
	    break;

	case stagDefineButtonSound:
	    ParseDefineButtonSound();
	    break;

	case stagSoundStreamHead:
	    ParseSoundStreamHead();
	    break;

	case stagSoundStreamHead2:
	    ParseSoundStreamHead2();
	    break;

	case stagSoundStreamBlock:
	    ParseSoundStreamBlock();
	    break;

	case stagDefineButtonCxform:
	    ParseDefineButtonCxform();
	    break;

	case stagDefineSprite:
	    Program *save;

	    save = program;
	    ParseDefineSprite();
	    program->rewindMovie();
	    program = save;
	    break;

	case stagNameCharacter:
	    ParseNameCharacter();
	    break;

	case stagFrameLabel:
	    ParseFrameLabel();
	    break;

	case stagDefineFont2:
	    ParseDefineFont2();
	    break;

	default:
	    ParseUnknown(code, m_tagLen);
	    break;
	}

	//printf("Bytes read = %d\n", m_filePos-here);

	// Increment the past the tag.
	m_filePos = tagEnd;

	if (outOfMemory) {
		fprintf(stderr,"Flash: Out of memory\n");
		*status |= FLASH_PARSE_OOM;
		return;
	}
    }

    program->validateLoadingFrame();
    *status |= FLASH_PARSE_EOM;
}

int
CInputScript::ParseData(FlashMovie *movie, char * data, long size)
{
    int status = FLASH_PARSE_ERROR;

    m_fileBuf = (unsigned char *)data;
    m_actualSize = size;

    if (needHeader) {

	    // Do we have sufficient data to read the header ?
	    if (size < 21) {
		return FLASH_PARSE_NEED_DATA;	// No, need more data
	    }

	    needHeader = 0;	// Yes

	    U8 fileHdr[8];

	    memcpy(fileHdr,data,8);

	    // Verify the header and get the file size.
	    if (fileHdr[0] != 'F' || fileHdr[1] != 'W' || fileHdr[2] != 'S' )
	    {
		//fprintf(stderr, "Not a Flash File.\n");
		return FLASH_PARSE_ERROR;	// Error
	    }
	    else
	    {
		// Get the file version.
		m_fileVersion = (U16) fileHdr[3];
	    }

	    // Get the file size.
	    m_fileSize = (U32) fileHdr[4]
	    	      | ((U32) fileHdr[5] << 8)
		      | ((U32) fileHdr[6] << 16)
		      | ((U32) fileHdr[7] << 24);

	    // Verify the minimum length of a Flash file.
	    if (m_fileSize < 21)
	    {
		//printf("ERROR: File size is too short\n");
		return FLASH_PARSE_ERROR;	// Error
	    }

	    // Set the file position past the header and size information.
	    m_filePos = 8;

	    // Get the frame information.
	    GetRect(&frameRect);

	    frameRate = GetWord() >> 8;

	    frameCount = GetWord();

	    program = new Program(movie, frameCount);

	    if (program == NULL || program->totalFrames == 0) {
	    	return FLASH_PARSE_ERROR;
	    }

	    status |= FLASH_PARSE_START;
    }

    ParseTags(&status);

    return status;
}


