#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <stdio.h>
#include <memory.h>
#include <setjmp.h>
#include "FlashScriptC.h"
#include "PajaTypes.h"

#include "SWFChar.h"
#include "SWFDispList.h"
#include "SWFFont.h"
#include "SWFShape.h"
#include "SWFText.h"
#include "SWFSpriteC.h"
#include "SWFBitmapC.h"
#include "SWFDictionaryC.h"

#include "zlib.h"
//#include "jpeglib.h"
#include "ijl.h"


static
void
DbgPrintf( const char* szFormat, ...  )
{
	return;

/*	char	szMsg[256];
	va_list	rList;
	va_start( rList, szFormat );
	_vsnprintf( szMsg, 255, szFormat, rList );
	va_end( rList );
	OutputDebugString( szMsg );*/
}


using namespace PajaTypes;



static	bool					g_bHaveTables = false;
static	JPEG_CORE_PROPERTIES	g_rJpegProp;



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

FlashScriptC::FlashScriptC() :
	m_bPrepared( false )
// Class constructor.
{
	// Initialize the input pointer.
	m_fileBuf = NULL;

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

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

	m_pCurSprite = &m_rSprite;
	m_pCurDispList = &m_rDispList;
}


// Class destructor.
FlashScriptC::~FlashScriptC()
{
	// empty

	if( g_bHaveTables ) {
		if( ijlFree( &g_rJpegProp ) != IJL_OK ) {
			DbgPrintf( "Cannot free Intel(R) JPEG library" );
		}
	}
}


uint16 FlashScriptC::get_tag()
{
	// Save the start of the tag.
	m_tagStart = m_filePos;
	m_tagZero  = m_tagStart;

	// Get the combined code and length of the tag.
	uint16 wRawCode = get_word();
	uint16 code = wRawCode;

	// The length is encoded in the tag.
	uint32 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)
	{
		len = (uint32) get_dword();
		DbgPrintf("\nGetTag: long tag: raw-code: %04x len: 0x%08x\n", wRawCode, len);
		m_tagZero += 4;
	}

	DbgPrintf("->> GetTag: code:%04x len:%08x\n", code, len);

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

	return code;
}


PajaTypes::BBox2C
FlashScriptC::get_rect()
{
	BBox2C	rBox;

	init_bits();
	int nBits = (int) get_bits( 5 );

	rBox[0][0] = (float32)get_sbits( nBits ) / 20.0f;
	rBox[1][0] = (float32)get_sbits( nBits ) / 20.0f;
	rBox[0][1] = (float32)get_sbits( nBits ) / 20.0f;
	rBox[1][1] = (float32)get_sbits( nBits ) / 20.0f;

	return rBox;
}

Matrix2C
FlashScriptC::get_matrix()
{
	Matrix2C	rMat;

	rMat = rMat.set_identity();

	init_bits();

	// Scale terms
	if (get_bits(1))
	{
		int nBits = (int) get_bits( 5 );
		int32	i32A = get_sbits( nBits );
		int32	i32D = get_sbits( nBits );

		rMat[0][0] = (float32)i32A / 65536.0f;
		rMat[1][1] = (float32)i32D / 65536.0f;
	}
//	else
//	{
//		mat->a = mat->d = 0x00010000L;
//	}

	// Rotate/skew terms
	if (get_bits(1))
	{
		int nBits = (int)get_bits( 5 );
		int32	i32B = get_sbits( nBits );
		int32	i32C = get_sbits( nBits );

		rMat[0][1] = (float32)i32B / 65536.0f;
		rMat[1][0] = (float32)i32C / 65536.0f;
	}
//	else
//	{
//		mat->b = mat->c = 0;
//	}

	// Translate terms
	int nBits = (int) get_bits( 5 );
	int32	i32TX = get_sbits( nBits );
	int32	i32TY = get_sbits( nBits );

	rMat[2][0] = (float32)i32TX / 20.0f;
	rMat[2][1] = (float32)i32TY / 20.0f;

	return rMat;
}


SWFCXFormC
FlashScriptC::get_cxform( bool hasAlpha )
{
	SWFCXFormC	rXForm;
	int16		i16R, i16G, i16B, i16A;

	init_bits();

	// !!! The spec has these bits reversed !!!
	bool fNeedAdd = (get_bits( 1 ) != 0);
	bool fNeedMul = (get_bits( 1 ) != 0);
	// !!! The spec has these bits reversed !!!

	DbgPrintf( "fNeedMul:%d fNeedAdd:%d\n", fNeedMul, fNeedAdd );

	int nBits = (int)get_bits( 4 );

	if( fNeedMul ) {
		i16R = (int16)get_sbits( nBits );
		i16G = (int16)get_sbits( nBits );
		i16B = (int16)get_sbits( nBits );
		if( hasAlpha )
			i16A = (int16)get_sbits( nBits );
		else
			i16A = 255;

		rXForm.set_color_mult( ColorC( (float32)i16R / 255.0f,
									   (float32)i16G / 255.0f,
									   (float32)i16B / 255.0f,
									   (float32)i16A / 255.0f) );
	}

	if( fNeedAdd ) {
		i16R = (int16)get_sbits( nBits );
		i16G = (int16)get_sbits( nBits );
		i16B = (int16)get_sbits( nBits );
		if( hasAlpha )
			i16A = (int16)get_sbits( nBits );
		else
			i16A = 255;

		rXForm.set_color_add( ColorC( (float32)i16R / 255.0f,
									  (float32)i16G / 255.0f,
									  (float32)i16B / 255.0f,
									  (float32)i16A / 255.0f) );
	}

	return rXForm;
}

char *FlashScriptC::get_string()
{
	// Point to the string.
	char *str = (char *) &m_fileBuf[m_filePos];

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

	return str;
}

ColorC
FlashScriptC::get_color( bool fWithAlpha )
{
	ColorC	rRes;

	uint8 r = get_byte();
	uint8 g = get_byte();
	uint8 b = get_byte();
	uint8 a = 0xff;

	if (fWithAlpha)
		a = get_byte();

	rRes.convert_from_uint8( r, g, b, a );

	return rRes;
}

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


int32 FlashScriptC::get_sbits(int32 n)
// Get n bits from the string with sign extension.
{
	// Get the number as an unsigned value.
	int32 v = (int32) get_bits(n);

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

	return v;
}


uint32 FlashScriptC::get_bits (int32 n)
// Get n bits from the stream.
{
	uint32 v = 0;

	while (true)
	{
//		if (m_bitPos == 0)
//			DbgPrintf("bitPos is ZERO: m_bitBuf: %02x\n", m_bitBuf);

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

			// Get the next buffer
			m_bitBuf = get_byte();
			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

//			DbgPrintf("get_bits: nBitsToRead:%d m_bitPos:%d m_bitBuf:%02x v:%d\n", nBitsToRead, m_bitPos, m_bitBuf, v);
			return v;
		}
	}
}


void FlashScriptC::parse_end( char *str )
{
	  DbgPrintf("%stagEnd\n", str);
}


void FlashScriptC::parse_show_frame(char *str, uint32 frame, uint32 offset)
{
	  DbgPrintf("%stagShowFrame\n", str);
	  DbgPrintf("\n%s<----- dumping frame %d file offset 0x%04x ----->\n", str, frame + 1, offset);

	  // Goto next frame
	  m_pCurSprite->add_displaylist( new SWFDisplayListC( *m_pCurDispList ) );
}


void FlashScriptC::parse_free_character( char *str )
{
	uint32 tagid = (uint32) get_word();
	DbgPrintf("%stagFreeCharacter \ttagid %-5u\n", str, tagid);
}


void FlashScriptC::parse_place_object( char *str )
{
	uint32 tagid = (uint32) get_word();
	uint32 depth = (uint32) get_word();
	
	DbgPrintf("%stagPlaceObject \ttagid %-5u depth %-5u\n", str, tagid, depth);

	Matrix2C rTM;
	SWFCXFormC rCXForm;

	rTM = get_matrix();
	DbgPrintf( "pos: (%f, %f)\n", rTM[2][0], rTM[2][1] );
//	PrintMatrix(matrix, str);

	if( m_filePos < m_tagEnd ) {
		rCXForm = get_cxform( false );
//		PrintCxform(str, &cxform);
	}

	m_pCurDispList->place_object( tagid, depth, rTM, rCXForm );
}


void FlashScriptC::parse_place_object2( char *str )
{
	uint8 flags = get_byte();
	uint32 depth = get_word();
	uint32 tag;

	Matrix2C	rTM;
	SWFCXFormC	rCXForm;

	rTM = rTM.set_identity();

	DbgPrintf(" %stagPlaceObject2 \tflags %-5u depth %-5u ", str, (int)flags, (int)depth );

	if( flags & PLACE2_MOVE )
		DbgPrintf("move ");

	// Get the tag if specified.
	if( flags & PLACE2_CHARACTER ) {
		tag = get_word();
		DbgPrintf( "tag %-5u\n", tag );
	}
	else {
		DbgPrintf( "\n" );
	}

	// Get the matrix if specified.
	if( flags & PLACE2_MATRIX ) {
		rTM = get_matrix();
		DbgPrintf( "pos: (%f, %f)\n", rTM[2][0], rTM[2][1] );
//		PrintMatrix(matrix, str);
	}

	// Get the color transform if specified.
	if( flags & PLACE2_COLORTRANSFORM ) {
		rCXForm = get_cxform( true );
//		PrintCxform(str, &cxform);
	}		 

	// Get the ratio if specified.
	if( flags & PLACE2_RATIO ) {
		uint32 ratio = get_word();
//		  INDENT;
		DbgPrintf( "%ratio %u\n", ratio );
	}		 

	// Get the clipdepth if specified.
	if( flags & PLACE2_DEFINECLIP ) {
		uint32 clipDepth = get_word();
//		  INDENT;
		DbgPrintf( "clipDepth %i\n", clipDepth );
	}

	// Get the instance name
	if( flags & PLACE2_NAME ) {
		char* pszName = get_string();
//		  INDENT;
		DbgPrintf( "instance name %s\n", pszName );
	}

	if( (flags & PLACE2_MOVE) && !(flags & PLACE2_CHARACTER) ) {
		// Move existing
		m_pCurDispList->move_object( depth, rTM, rCXForm, (flags & PLACE2_MATRIX) ? true : false, (flags & PLACE2_COLORTRANSFORM) ? true : false );
	}
	else if( (flags & PLACE2_MOVE) && (flags & PLACE2_CHARACTER) ) {
		// Replace
		m_pCurDispList->replace_object( tag, depth, rTM, rCXForm, (flags & PLACE2_MATRIX) ? true : false, (flags & PLACE2_COLORTRANSFORM) ? true : false );
	}
	else if( !(flags & PLACE2_MOVE) && (flags & PLACE2_CHARACTER) ) {
		// Add
		m_pCurDispList->place_object( tag, depth, rTM, rCXForm );
	}
}


void FlashScriptC::parse_remove_object(char *str)
{
	uint32 tagid = (uint32) get_word();
	uint32 depth = (uint32) get_word();
	
	DbgPrintf("%stagRemoveObject \ttagid %-5u depth %-5u\n", str, tagid, depth);

	m_pCurDispList->remove_object( depth );
}


void FlashScriptC::parse_remove_object2(char *str)
{
	uint32 depth = (uint32) get_word();
	
	DbgPrintf("%stagRemoveObject2 depth %-5u\n", str, depth);

	m_pCurDispList->remove_object( depth );
}


void FlashScriptC::parse_set_background_color( char *str )
{
	uint8 r = get_byte();
	uint8 g = get_byte();
	uint8 b = get_byte();
	
	m_rBgColor.convert_from_uint8( r, g, b );

	DbgPrintf( "%stagSetBackgroundColor \tRGB %.2f %.2f %.2f\n", str, m_rBgColor[0], m_rBgColor[1], m_rBgColor[2] );
}


bool FlashScriptC::parse_shape_record( char *str, SWFShapeRecordC* pShape, PajaTypes::int32& xLast, PajaTypes::int32& yLast, bool fWithAlpha )
{
//	if( !pShape )
//		return false;

	// Determine if this is an edge.
	bool isEdge = get_bits( 1 ) ? true : false;

	if( !isEdge ) {
		// Handle a state change
		uint16 flags = (uint16)get_bits( 5 );

		// Are we at the end?
		if( flags == 0 ) {
			DbgPrintf("\tEnd of shape.\n\n");
			return true;
		}

		// Process a move to.
		if( flags & SHAPE_MOVETO ) {
			uint16 nBits = (uint16) get_bits( 5 );
			int32 x = get_sbits( nBits );
			int32 y = get_sbits( nBits );
			//printf("\tmoveto: nBits:%d x:%d y:%d\n", nBits, x, y);
			xLast = x;
			yLast = y;
			DbgPrintf( "\n\tmoveto: (%g,%g)\n", double(xLast)/20.0, double(yLast)/20.0 );
		}

		// Get new fill info.
		if( flags & SHAPE_FILL0 ) {
			m_ui16FillStyle0 = get_bits( m_nFillBits );
			if( m_ui16FillStyle0 )
				m_ui16FillStyle0 += m_i32FillStyleBase;
			DbgPrintf( "\tFillStyle0: %d (%d bits) (%d)\n", m_ui16FillStyle0, m_nFillBits, m_i32FillStyleBase );
		}
		if( flags & SHAPE_FILL1 ) {
			m_ui16FillStyle1 = get_bits( m_nFillBits );
			if( m_ui16FillStyle1 )
				m_ui16FillStyle1 += m_i32FillStyleBase;
			DbgPrintf( "\tFillStyle1: %d (%d bits) (%d)\n", m_ui16FillStyle1, m_nFillBits, m_i32FillStyleBase );
		}

		// Get new line info
		if( flags & SHAPE_LINE ) {
			m_ui16LineStyle = get_bits( m_nLineBits );
			if( m_ui16LineStyle )
				m_ui16LineStyle += m_i32LineStyleBase;
			DbgPrintf( "\tLineStyle: %d\n", m_ui16LineStyle );
		}

		// Check to get a new set of styles for a new shape layer.
		if( flags & SHAPE_NEWSTYLES ) {
			DbgPrintf("\tFound more Style info\n");

			m_i32FillStyleBase = pShape->get_fill_count();
			m_i32LineStyleBase = pShape->get_line_style_count();

			// Parse the style.
			parse_shape_style( str, pShape, fWithAlpha );

			// Reset.
			m_nFillBits = (uint16)get_bits( 4 );
			m_nLineBits = (uint16)get_bits( 4 );

			DbgPrintf( "\tm_nFillBits:%d  m_nLineBits:%d\n", m_nFillBits, m_nLineBits );
		}

		if( flags & SHAPE_END )
		{
			DbgPrintf("\tEnd of shape.\n\n");
		}
  
		return flags & SHAPE_END ? true : false;
	}
	else {
		if( get_bits( 1 ) )
		{
			// Handle a line
			uint16 nBits = (uint16)get_bits( 4 ) + 2;	// nBits is biased by 2

			// Save the deltas
			if( get_bits( 1 ) ) {
				// Handle a general line.
				int32 x = get_sbits( nBits );
				int32 y = get_sbits( nBits );

				SWFEdgeC	rEdge;

				rEdge.set_fill_style0( m_ui16FillStyle0 );
				rEdge.set_fill_style1( m_ui16FillStyle1 );
				rEdge.set_line_style( m_ui16LineStyle );

				rEdge.set_start_point( Vector2C( float32( xLast ) / 20.0f, float32( yLast ) / 20.0f ) );
				rEdge.set_end_point( Vector2C( float32( xLast + x ) / 20.0f, float32( yLast + y ) / 20.0f ) );

				if( pShape )
					pShape->add_edge( &rEdge );

				xLast += x;
				yLast += y;

				DbgPrintf( "\tlineto: (%g,%g).\n", double(xLast)/20.0, double(yLast)/20.0 );
			}
			else {
				// Handle a vert or horiz line.
				if( get_bits( 1 ) ) {
					// Vertical line
					int32 y = get_sbits( nBits );

					SWFEdgeC	rEdge;

					rEdge.set_fill_style0( m_ui16FillStyle0 );
					rEdge.set_fill_style1( m_ui16FillStyle1 );
					rEdge.set_line_style( m_ui16LineStyle );

					rEdge.set_start_point( Vector2C( float32( xLast ) / 20.0f, float32( yLast ) / 20.0f ) );
					rEdge.set_end_point( Vector2C( float32( xLast ) / 20.0f, float32( yLast + y ) / 20.0f ) );

					if( pShape )
						pShape->add_edge( &rEdge );


					yLast += y;

					DbgPrintf( "\tvlineto: (%g,%g).\n", double(xLast)/20.0, double(yLast)/20.0 );
				}
				else {
					// Horizontal line
					int32 x = get_sbits( nBits );


					SWFEdgeC	rEdge;

					rEdge.set_fill_style0( m_ui16FillStyle0 );
					rEdge.set_fill_style1( m_ui16FillStyle1 );
					rEdge.set_line_style( m_ui16LineStyle );

					rEdge.set_start_point( Vector2C( float32( xLast ) / 20.0f, float32( yLast ) / 20.0f ) );
					rEdge.set_end_point( Vector2C( float32( xLast + x ) / 20.0f, float32( yLast ) / 20.0f ) );

					if( pShape )
						pShape->add_edge( &rEdge );


					xLast += x;

					DbgPrintf( "\thlineto: (%g,%g).\n", double(xLast)/20.0, double(yLast)/20.0 );
				}
			}
		}
		else {
			// Handle a curve
			uint16 nBits = (uint16)get_bits( 4 ) + 2;	// nBits is biased by 2

			SWFEdgeC	rEdge;

			rEdge.set_is_curve();
			rEdge.set_fill_style0( m_ui16FillStyle0 );
			rEdge.set_fill_style1( m_ui16FillStyle1 );
			rEdge.set_line_style( m_ui16LineStyle );

			rEdge.set_start_point( Vector2C( float32( xLast ) / 20.0f, float32( yLast ) / 20.0f ) );

			// Get the control
			int32 cx = get_sbits( nBits );
			int32 cy = get_sbits( nBits );

			rEdge.set_control_point( Vector2C( float32( xLast + cx ) / 20.0f, float32( yLast + cy ) / 20.0f ) );

			xLast += cx;
			yLast += cy;

			DbgPrintf( "\tcurveto: (%g,%g)", double(xLast)/20.0, double(yLast)/20.0 );

			// Get the anchor
			int32 ax = get_sbits( nBits );
			int32 ay = get_sbits( nBits );

			rEdge.set_end_point( Vector2C( float32( xLast + ax ) / 20.0f, float32( yLast + ay ) / 20.0f ) );

			xLast += ax;
			yLast += ay;

			DbgPrintf( "(%g,%g)\n", double(xLast)/20.0, double(yLast)/20.0 );

			if( pShape )
				pShape->add_edge( &rEdge );
		}

		return false;
	}
}

void FlashScriptC::parse_shape_style( char *str, SWFShapeRecordC* pShape, bool fWithAlpha )
{
	uint16 i = 0;

	// Get the number of fills.
	uint16 nFills = get_byte();

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

	DbgPrintf( "\tNumber of fill styles \t%u\n", nFills );

	// Get each of the fill style.
	for( i = 1; i <= nFills; i++ ) {
		uint16 fillStyle = get_byte();

		if( fillStyle & FILL_GRADIENT ) {
			// Get the gradient matrix.
			Matrix2C rMat;
			rMat = get_matrix();

			// Get the number of colors.
			uint16 nColors = (uint16)get_byte();
			DbgPrintf( "%s\tUNSUP! Gradient Fill with %u colors\n", str, nColors );

			ColorC rColor;
			SWFGradientC	rGradient( (fillStyle == FILL_LINEARGRADIENT) ? GRADIENT_LINEAR : GRADIENT_RADIAL );

			// Get each of the colors.
			for( uint16 j = 0; j < nColors; j++ ) {
				uint8 ui8Pos = get_byte();
				rColor = get_color( fWithAlpha );
				DbgPrintf( "%s\tcolor:%d: at:%d  RGBA: %.2f %.2f %.2f %.2f\n", str, j, ui8Pos, rColor[0], rColor[1], rColor[2], rColor[3] );
				rGradient.add_color( rColor, ui8Pos );
			}
//			  printf("%s\tGradient Matrix:\n", str);
//			PrintMatrix(mat, str);

			SWFFillStyleC	rStyle( FILLTYPE_GRADIENT );
			rStyle.set_color( rColor );
			rStyle.set_matrix( rMat );
			rStyle.set_gradient( &rGradient );

			if( pShape )
				pShape->add_fill( &rStyle );
		}
		else if( fillStyle & FILL_BITS )
		{
			// Get the bitmap ID.
			uint16 uBitmapID = get_word();
			DbgPrintf( "%s\tBitmap Fill: %04x\n", str, uBitmapID );
			// Get the bitmap matrix.
			Matrix2C rMat;
			rMat = get_matrix();
//			PrintMatrix(mat, str);

			SWFFillStyleC	rStyle( FILLTYPE_BITMAP );
			rStyle.set_matrix( rMat );
			rStyle.set_bitmap_id( uBitmapID );

			if( pShape )
				pShape->add_fill( &rStyle );
		}
		else
		{
			// A solid color
			ColorC	rColor = get_color( fWithAlpha );
			DbgPrintf( "%s\tSolid Color Fill RGB: %.2f %.2f %.2f %.2f\n", str, rColor[0], rColor[1], rColor[2], rColor[3] );

			SWFFillStyleC	rStyle( FILLTYPE_SOLID );
			rStyle.set_color( rColor );

			if( pShape )
				pShape->add_fill( &rStyle );
		}
	}

	// Get the number of lines.
	uint16 nLines = get_byte();

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

	DbgPrintf("\tNumber of line styles \t%u\n", nLines);

	// Get each of the line styles.
	for (i = 1; i <= nLines; i++)
	{
		uint16 ui16Width = get_word();
		ColorC rColor = get_color( fWithAlpha );
	
		DbgPrintf("\tLine style %-5u width %g color RGB_HEX\n", i, (double)ui16Width/20.0 );

		SWFLineStyleC	rStyle( (float32)ui16Width / 20.0f, rColor );

		if( pShape )
			pShape->add_line_style( &rStyle );
	}
}

void FlashScriptC::parse_define_shape( char *str, bool fWithAlpha )
{
	uint32 tagid = (uint32) get_word();
	DbgPrintf( "%stagDefineShape \ttagid %-5u\n", str, tagid );

	// Create new shape
	SWFShapeC*	pShape = new SWFShapeC;
	SWFShapeRecordC*	pShapeRec = pShape->get_shape();

	pShape->set_id( tagid );

	// Get the bounding rectangle
	BBox2C rect;
	rect = get_rect();

	parse_shape_style( str, pShapeRec, fWithAlpha );

	init_bits(); 	// Bug!  this was not in the original swfparse.cpp
					// Required to reset bit counters and read byte aligned.

	m_nFillBits = (uint16)get_bits( 4 );
	m_nLineBits = (uint16)get_bits( 4 );

	DbgPrintf( "%sm_nFillBits:%d  m_nLineBits:%d\n", str, m_nFillBits, m_nLineBits );

	int xLast = 0;
	int yLast = 0;

	m_ui16LineStyle = 0;
	m_ui16FillStyle0 = 0;
	m_ui16FillStyle1 = 0;
	m_i32FillStyleBase = 0;
	m_i32LineStyleBase = 0;

	bool atEnd = false;
	while( !atEnd )
		atEnd = parse_shape_record( str, pShapeRec, xLast, yLast, fWithAlpha );

	m_rDict.add_character( pShape );
}


void FlashScriptC::parse_define_shape2( char *str )
{
	DbgPrintf( "%stagDefineShape2 -> ", str );
	parse_define_shape( str );
}


void FlashScriptC::parse_define_shape3(char *str)
{
	DbgPrintf("%stagDefineShape3: -> ", str);
	parse_define_shape(str, true);
}


void FlashScriptC::parse_do_action( char *str, bool fPrintTag )
{
/*	if (fPrintTag)
	{
		DbgPrintf("%stagDoAction\n",  str);
	}

	for (;;) 
	{
		// Handle the action
		int actionCode = get_byte();
		DbgPrintf("%saction code 0x%02x ", str, actionCode);
		if (actionCode == 0)
		{
			// Action code of zero indicates end of actions
			DbgPrintf("\n");
			return;
		}

		int len = 0;
		if (actionCode & sactionHasLength) 
		{
			len = get_word();
			DbgPrintf("has length %-5u ", len);
		}		 

		int32 pos = m_filePos + len;

		switch ( actionCode ) 
		{
			case sactionNextFrame:
			{
				DbgPrintf( "gotoNextFrame\n" );
				break;
			}

			case sactionPrevFrame:
			{
				DbgPrintf( "gotoPrevFrame\n" );
				break;
			}

			case sactionPlay:
			{
				DbgPrintf( "play\n" );
				break;
			}

			case sactionStop:
			{
				DbgPrintf( "stop\n" );
				break;
			}

			case sactionGotoFrame:
			{
				DbgPrintf("gotoFrame %5u\n", get_word());
				break;
			}

			case sactionGotoLabel: 
			{
				// swfparse used to crash here!
				DbgPrintf("gotoLabel %s\n", &m_fileBuf[m_filePos]);
				break;
			}

			default:
			{
				DbgPrintf("UNKNOWN?\n");
				break;
			}
		}

		m_filePos = pos;
	}*/
}



void FlashScriptC::parse_define_font( char *str )
{
	uint32 iFontID = (uint32)get_word();
	DbgPrintf("%stagDefineFont \t\tFont ID %-5u\n", str, iFontID);

	int iStart = m_filePos;

	int iOffset = get_word();
//	DbgPrintf("%s\tiOffset: 0x%04x\n", str, iOffset);

	int iGlyphCount = iOffset / 2;

//	m_iGlyphCounts[iFontID] = iGlyphCount;

	DbgPrintf("%s\tnumber of glyphs: %d\n", str, iGlyphCount);

	int* piOffsetTable = new int[iGlyphCount];
	piOffsetTable[0] = iOffset;

	SWFFontC*	pFont = new SWFFontC;

	pFont->set_id( iFontID );

	int n;

	for( n = 1; n < iGlyphCount; n++ )
		piOffsetTable[n] = get_word();

	for( n = 0; n < iGlyphCount; n++ ) {
		m_filePos = piOffsetTable[n] + iStart;

		init_bits(); // reset bit counter

		m_nFillBits = (uint16) get_bits( 4 );
		m_nLineBits = (uint16) get_bits( 4 );

		DbgPrintf("%s\tm_nFillBits:%d m_nLineBits:%d\n", str, m_nFillBits, m_nLineBits);

		bool fAtEnd = false;
		int xLast = 0;
		int yLast = 0;

		SWFGlyphC*	pGlyph = new SWFGlyphC;

		while( !fAtEnd ) {
			fAtEnd = parse_shape_record( str, pGlyph->get_shape(), xLast, yLast );
		}

		pFont->add_glyph( pGlyph );
	}

	delete [] piOffsetTable;

	m_rDict.add_character( pFont );
}


void FlashScriptC::parse_define_font_info(char *str)
{
	uint32 iFontID = (uint32) get_word();
	DbgPrintf("%stagDefineFontInfo \tFont ID %-5u\n", str, iFontID);

	int iNameLen = get_byte();
	char* pszName = new char[iNameLen+1];
	for(int n=0; n < iNameLen; n++)
		pszName[n] = (char)get_byte();
	pszName[n] = '\0';

	DbgPrintf("%s\tFontName: '%s'\n",str, pszName);

	delete [] pszName;

	uint8 flags = (FontInfoE)get_byte();

	SWFFontC*	pFont = (SWFFontC*)m_rDict.get_char_by_id( iFontID );

	int iGlyphCount = pFont->get_glyph_count(); //m_iGlyphCounts[iFontID];

//	int *piCodeTable = new int[iGlyphCount];

	DbgPrintf("%s\t", str);
	for( n = 0; n < iGlyphCount; n++ ) {
		uint16	ui16Code;
		if( flags & FONTINFO_WIDECODES )
			ui16Code = (int)get_word();
		else
			ui16Code = (int)get_byte();


		pFont->get_glyph( n )->set_code( ui16Code );

		DbgPrintf("[%d,'%c'] ", ui16Code, (char)ui16Code);
	}

	DbgPrintf("\n\n");

//	delete [] piCodeTable;
}

bool FlashScriptC::parse_text_record( char* str, SWFTextRecordC* pText, int nGlyphBits, int nAdvanceBits )
{
	uint8 flags = (TextE)get_byte();
	if( flags == 0 )
		return false;

	DbgPrintf("\n%s\tflags: 0x%02x\n", str, flags);

	if( flags & TEXT_ISCONTROL ) {

		uint32		ui32FontId = 0;
		ColorC		rCol;
		Vector2C	rOffset( 0, 0 );
		float32		f32FontHeight = 0;

		if( flags & TEXT_HASFONT ) {
			ui32FontId = get_word();
			DbgPrintf( "%s\tfontId: %d\n", str, ui32FontId );
		}
		if( flags & TEXT_HASCOLOR ) {
			int r = get_byte();
			int g = get_byte();
			int b = get_byte();
			DbgPrintf( "%s\tfontColour: (%d,%d,%d)\n", str, r, g, b );

			rCol.convert_from_uint8( r, g, b );
		}
		if( flags & TEXT_HASXOFFSET ) {
			rOffset[0] = (float32)get_word() / 20.0f;
			DbgPrintf( "%s\tX-offset: %f\n", str, rOffset[0] );
		}
		if( flags & TEXT_HASYOFFSET ) {
			rOffset[1] = (float32)get_word() / 20.0f;
			DbgPrintf( "%s\tY-offset: %f\n", str, rOffset[1] );
		}
		if( flags & TEXT_HASFONT ) {
			f32FontHeight = (float32)get_word() / 20.0f;
			DbgPrintf( "%s\tFont Height: %f\n", str, f32FontHeight );
		}

		pText->set_font_id( ui32FontId );
		pText->set_color( rCol );
		pText->set_offset( rOffset );
		pText->set_font_height( f32FontHeight );
	}
	else {
		int iGlyphCount = flags;
		DbgPrintf("%s\tnumber of glyphs: %d\n", str, iGlyphCount);

		init_bits(); 	// reset bit counter

		DbgPrintf("%s\t", str);
		for( int g = 0; g < iGlyphCount; g++ ) {
			uint16 ui16Index = get_bits( nGlyphBits );
			float32 f32Advance = (float32)get_bits( nAdvanceBits ) / 20.0f;
			DbgPrintf("[%d,%f] ", ui16Index, f32Advance);

			pText->add_glyph( ui16Index, f32Advance );
		}
		DbgPrintf( "\n" );
	}

	return true;
}


void FlashScriptC::parse_define_text( char *str )
{
	uint32 tagid = (uint32) get_word();
	DbgPrintf("%stagDefineText \t\ttagid %-5u\n", str, tagid);

	SWFTextC*	pText = new SWFTextC;

	BBox2C	rect;
	rect = get_rect();
	pText->set_bbox( rect );
//	PrintRect(rect, str);

	Matrix2C	rMat;
	rMat = get_matrix();
	pText->set_tm( rMat );
//	PrintMatrix(m, str);

	pText->set_id( tagid );

	int nGlyphBits = (int)get_byte();
	int nAdvanceBits = (int)get_byte();

	DbgPrintf("%s\tnGlyphBits:%d nAdvanceBits:%d\n", str, nGlyphBits, nAdvanceBits);

	bool fContinue = true;

	do {
		SWFTextRecordC*	pTextRec = new SWFTextRecordC;
		fContinue = parse_text_record( str, pTextRec, nGlyphBits, nAdvanceBits );
		pText->add_text( pTextRec );
	} while( fContinue );

	DbgPrintf("\n");

	m_rDict.add_character( pText );
}


void FlashScriptC::parse_define_font2( char *str )
{
	uint32 tagid = (uint32) get_word();

	uint16 flags = get_word();


	SWFFontC*	pFont = new SWFFontC;

	pFont->set_id( tagid );


	// Skip the font name
	int iNameLen = get_byte();
	char szFontName[256];
	for (int i=0; i<iNameLen; i++)
		szFontName[i] = get_byte();
	szFontName[i] = NULL;
	
	// Get the number of glyphs.
	uint16 nGlyphs = get_word();

	int iDataPos = m_filePos;

	DbgPrintf("%stagDefineFont2 \ttagid %-5u flags:%04x nGlyphs:%d\n", str, tagid, flags, nGlyphs);

	if( nGlyphs > 0 ) {
		//
		// Get the FontOffsetTable
		//

		uint32* puOffsetTable = new uint32[nGlyphs];
		for( int n = 0; n < nGlyphs; n++ )
			if( flags & FONT2_WIDEOFFSETS )
				puOffsetTable[n] = get_dword();
			else
				puOffsetTable[n] = get_word();

		//
		// Get the CodeOffset
		//

		uint32 iCodeOffset = 0;
		if( flags & FONT2_WIDEOFFSETS )
			iCodeOffset = get_dword();
		else
			iCodeOffset = get_word();

		//
		// Get the Glyphs
		//

		for( n = 0; n < nGlyphs; n++ ) {
			DbgPrintf( "\n\t%s>>> Glyph:%d", str, n );
			m_filePos = iDataPos + puOffsetTable[n];

			init_bits(); // reset bit counter

			m_nFillBits = (uint16)get_bits( 4 );
			m_nLineBits = (uint16)get_bits( 4 );

			int xLast = 0;
			int yLast = 0;

			bool fAtEnd = false;

			SWFGlyphC*	pGlyph = new SWFGlyphC;

			while( !fAtEnd )
				fAtEnd = parse_shape_record( str, pGlyph->get_shape(), xLast, yLast );

			pFont->add_glyph( pGlyph );
		}

		delete [] puOffsetTable;


		if (m_filePos != iDataPos + iCodeOffset)
		{
			DbgPrintf("Bad CodeOffset\n");
			return;
		}

		//
		// Get the CodeTable
		//
			
		m_filePos = iDataPos + iCodeOffset;
		DbgPrintf("\n%sCodeTable:\n%s", str, str);

		for( int i = 0; i < nGlyphs; i++ ) {
			uint16	ui16Code;
			if( flags & FONT2_WIDEOFFSETS ) {
				ui16Code = get_word();
				DbgPrintf( "%02x:[%04x] ", i, ui16Code );
			}
			else {
				ui16Code = get_byte();
				DbgPrintf("%02x:[%c] ", i, ui16Code );
			}

			pFont->get_glyph( i )->set_code( ui16Code );
		}
		DbgPrintf("\n");
	}

	if( flags & FONT2_HASLAYOUT )
	{
		//
		// Get "layout" fields
		//

		int16 iAscent = get_word();
		int16 iDescent = get_word();
		int16 iLeading = get_word();

		DbgPrintf("\n%sHasLayout: iAscent:%d iDescent:%d iLeading:%d\n", str, iAscent, iDescent, iLeading);

		// Skip Advance table
		skip_bytes( nGlyphs * 2 );


		// Get BoundsTable
		int i;
		for( i = 0; i < nGlyphs; i++ ) {
			BBox2C rBounds;
			rBounds = get_rect();
			//printf("rBounds: (%d,%d)(%d,%d)\n", rBounds.xmin, rBounds.ymin, rBounds.xmax, rBounds.ymax);
		}

		//
		// Get Kerning Pairs
		//

		int16 iKerningCount = get_word();
		DbgPrintf("\n%sKerning Pair Count:%d\n", str, iKerningCount);
		for( i = 0; i < iKerningCount; i++ ) {
			uint16 iCode1, iCode2;
			if( flags & FONT2_WIDEOFFSETS ) {
				iCode1 = get_word();
				iCode2 = get_word();
			}
			else {
				iCode1 = get_byte();
				iCode2 = get_byte();
			}
			int16 iAdjust = get_word();

			DbgPrintf("%sKerningPair:%-4d %c <--> %c : %d\n", str, i, iCode1, iCode2, iAdjust);
		}

		DbgPrintf("m_tagEnd:%08x m_filePos:%08x\n", m_tagEnd, m_filePos);
	}

	m_rDict.add_character( pFont );
}



void FlashScriptC::parse_define_text2( char *str )
{
	uint32 tagid = (uint32) get_word();

	DbgPrintf("%stagDefineText2 \ttagid %-5u\n", str, tagid);
}


void FlashScriptC::parse_name_character(char *str)
{
	uint32 tagid = (uint32) get_word();
	char *label = get_string();

	DbgPrintf( "%stagNameCharacter \ttagid %-5u label '%s'\n", str, tagid, label );
}


void FlashScriptC::parse_frame_label(char *str)
{
	char *label = get_string();

	DbgPrintf( "%stagFrameLabel lable \"%s\"\n", str, label );
}


void WriteTGA( const char* szName, int iWidth, int iHeight, int iDepth, unsigned char* pData )
{
	FILE*			pStream;
	unsigned char	ucHeader[18];
	int				i, j;

	memset( ucHeader, 0, 18 );

	ucHeader[2] = 2;
	ucHeader[16] = 0x18;
	ucHeader[17] = 0x20;

	ucHeader[12] = iWidth;
	ucHeader[13] = iWidth >> 8;
	ucHeader[14] = iHeight;
	ucHeader[15] = iHeight >> 8;

	if( (pStream = fopen( szName, "wb" )) == 0 ) {
		return;
	}

	fwrite( ucHeader, 1, 18, pStream );

	for( i = 0; i < iHeight; i++ ) {
		for( j = 0; j < iWidth; j++ ) {
			fputc( pData[(j + i * iWidth) * iDepth + 2], pStream );
			fputc( pData[(j + i * iWidth) * iDepth + 1], pStream );
			fputc( pData[(j + i * iWidth) * iDepth], pStream );
		}
	}
	
	fclose( pStream );
}


void FlashScriptC::parse_define_bits()
{
	uint32	tagid = get_word();

	DbgPrintf( "parse_define_bits() tagid %-5u\n", tagid );

	uint8*	pData;
	int32	i32Width, i32Height, i32Bpp;

	g_rJpegProp.JPGBytes = &m_fileBuf[m_filePos];
	g_rJpegProp.JPGSizeBytes = m_tagEnd - m_filePos;
    
    if( ijlRead( &g_rJpegProp, IJL_JBUFF_READPARAMS ) != IJL_OK )
    {
        DbgPrintf( "Cannot read JPEG file header file (params)\n" );
		return;
    }

    switch( g_rJpegProp.JPGChannels ) {
    case 1:
		g_rJpegProp.JPGColor = IJL_G;
		g_rJpegProp.DIBColor = IJL_RGB;
		g_rJpegProp.DIBChannels = 3;
		break;
		
    case 3:
		g_rJpegProp.JPGColor = IJL_YCBCR;
		g_rJpegProp.DIBColor = IJL_RGB;
		g_rJpegProp.DIBChannels = 3;
		break;
		
    default:
		// This catches everything else, but no
		// color twist will be performed by the IJL.
		g_rJpegProp.DIBColor = (IJL_COLOR)IJL_OTHER;
		g_rJpegProp.JPGColor = (IJL_COLOR)IJL_OTHER;
		break;
    }

    g_rJpegProp.DIBWidth    = g_rJpegProp.JPGWidth;
    g_rJpegProp.DIBHeight   = g_rJpegProp.JPGHeight;
    g_rJpegProp.DIBPadBytes = 0;

    int32	i32ImageSize = g_rJpegProp.DIBWidth * g_rJpegProp.DIBHeight * g_rJpegProp.DIBChannels;

	i32Width = g_rJpegProp.DIBWidth;
	i32Height = g_rJpegProp.DIBHeight;
	i32Bpp = g_rJpegProp.DIBChannels * 8;

    pData = new uint8[i32ImageSize];
    if( !pData ) {
        DbgPrintf( "Cannot allocate memory for image\n" );
        return;
    }

    g_rJpegProp.DIBBytes = pData;

    if( ijlRead( &g_rJpegProp, IJL_JBUFF_READENTROPY ) != IJL_OK ) {
        DbgPrintf( "Cannot read image data from file (entropy)\n" );
        delete[] pData;
		pData = 0;
        return;
    }

/*	if( i32Bpp == 32 || i32Bpp == 24 ) {
		char	szName[256];
		sprintf( szName, "tagjpeg%08x.tga", tagid );
		WriteTGA( szName, i32Width, i32Height, i32Bpp / 8, pData );
	}*/

	SWFBitmapC*	pBitmap = new SWFBitmapC;

	pBitmap->set_id( tagid );
	pBitmap->set_width( i32Width );
	pBitmap->set_height( i32Height );
	pBitmap->set_bpp( i32Bpp );
	pBitmap->set_data( pData );
	
	m_rDict.add_character( pBitmap );

}

//@@
void FlashScriptC::parse_define_bits_jpeg2()
{
	uint32	tagid = get_word();

	DbgPrintf( "parse_define_bits_jpeg2() tagid %-5u\n", tagid );

	uint8*	pData;
	int32	i32Width, i32Height, i32Bpp;

    JPEG_CORE_PROPERTIES	image;
    memset( &image, 0, sizeof( JPEG_CORE_PROPERTIES ) );

    if( ijlInit( &image ) != IJL_OK ) {
        DbgPrintf( "Cannot initialize Intel JPEG library\n" );
        return;
    }

	image.JPGBytes = &m_fileBuf[m_filePos];
	image.JPGSizeBytes = m_tagEnd - m_filePos;

    if( ijlRead( &image, IJL_JBUFF_READHEADER ) != IJL_OK )
    {
        DbgPrintf( "Cannot read JPEG file header file (params)\n" );
		return;
    }

	//
	// Find next stream
	//
	uint32	ui32SearchPos = m_filePos + 2;	// skip SOI

	while( ui32SearchPos <= m_tagEnd ) {
		if( m_fileBuf[ui32SearchPos] == 0xff ) {
			ui32SearchPos++;
			if( m_fileBuf[ui32SearchPos] == 0xd9 ) {
				// found end
				ui32SearchPos++;
				break;
			}
			else {
				// Skip segment
				ui32SearchPos++;
				int32	i32Length = (m_fileBuf[ui32SearchPos] << 8) + m_fileBuf[ui32SearchPos + 1];
				ui32SearchPos += i32Length;
			}
		}
		else
			ui32SearchPos++;
	}

	image.JPGBytes = &m_fileBuf[ui32SearchPos];
	image.JPGSizeBytes = m_tagEnd - ui32SearchPos;

    if( ijlRead( &image, IJL_JBUFF_READPARAMS ) != IJL_OK ) {
        DbgPrintf( "Cannot read JPEG file header file (params)\n" );
		return;
    }

    switch( image.JPGChannels ) {
    case 1:
		image.JPGColor = IJL_G;
		image.DIBColor = IJL_RGB;
		image.DIBChannels = 3;
		break;
		
    case 3:
		image.JPGColor = IJL_YCBCR;
		image.DIBColor = IJL_RGB;
		image.DIBChannels = 3;
		break;
		
    default:
		// This catches everything else, but no
		// color twist will be performed by the IJL.
		image.DIBColor = (IJL_COLOR)IJL_OTHER;
		image.JPGColor = (IJL_COLOR)IJL_OTHER;
		break;
    }

    image.DIBWidth    = image.JPGWidth;
    image.DIBHeight   = image.JPGHeight;
    image.DIBPadBytes = 0;

    int32	i32ImageSize = image.DIBWidth * image.DIBChannels * image.DIBHeight;

	i32Width = image.DIBWidth;
	i32Height = image.DIBHeight;
	i32Bpp = image.DIBChannels * 8;

    pData = new uint8[i32ImageSize];
    if( !pData ) {
        DbgPrintf( "Cannot allocate memory for image\n" );
        return;
    }

    image.DIBBytes = pData;

	uint32	ui32Error;

    if( (ui32Error = ijlRead( &image, IJL_JBUFF_READWHOLEIMAGE )) != IJL_OK )
    {
        DbgPrintf( "Cannot read image data from file (entropy)\n" );
        delete[] pData;
		pData = 0;
        return;
    }

    if( ijlFree( &image ) != IJL_OK ) {
        DbgPrintf( "Cannot free Intel(R) JPEG library" );
    }

/*
	if( i32Bpp == 32 || i32Bpp == 24 ) {
		char	szName[256];
		sprintf( szName, "tagjpeg%08x.tga", tagid );
		WriteTGA( szName, i32Width, i32Height, i32Bpp / 8, pData );
	}
*/

//	delete [] pData;

	SWFBitmapC*	pBitmap = new SWFBitmapC;

	pBitmap->set_id( tagid );
	pBitmap->set_width( i32Width );
	pBitmap->set_height( i32Height );
	pBitmap->set_bpp( i32Bpp );
	pBitmap->set_data( pData );
	
	m_rDict.add_character( pBitmap );
}


void FlashScriptC::parse_define_bits_jpeg3()
{
	uint32	tagid = get_word();

	uint32	ui32Offset = get_dword();

	DbgPrintf( "parse_define_bits_jpeg3() tagid %-5u\n", tagid );

	uint8*	pData;
	int32	i32Width, i32Height, i32Bpp;

    JPEG_CORE_PROPERTIES	image;
    memset( &image, 0, sizeof( JPEG_CORE_PROPERTIES ) );

    if( ijlInit( &image ) != IJL_OK ) {
        DbgPrintf( "Cannot initialize Intel JPEG library\n" );
        return;
    }

	image.JPGBytes = &m_fileBuf[m_filePos];
	image.JPGSizeBytes = m_tagEnd - m_filePos;

    if( ijlRead( &image, IJL_JBUFF_READHEADER ) != IJL_OK ) {
        DbgPrintf( "Cannot read JPEG file header file (params)\n" );
		return;
    }

	//
	// Find next stream
	//
	uint32	ui32SearchPos = m_filePos + 2;	// skip SOI

	while( ui32SearchPos <= m_tagEnd ) {
		if( m_fileBuf[ui32SearchPos] == 0xff ) {
			ui32SearchPos++;
			if( m_fileBuf[ui32SearchPos] == 0xd9 ) {
				// found end
				ui32SearchPos++;
				break;
			}
			else {
				// Skip segment
				ui32SearchPos++;
				int32	i32Length = (m_fileBuf[ui32SearchPos] << 8) + m_fileBuf[ui32SearchPos + 1];
				ui32SearchPos += i32Length;
			}
		}
		else
			ui32SearchPos++;
	}

	image.JPGBytes = &m_fileBuf[ui32SearchPos];
	image.JPGSizeBytes = m_tagEnd - ui32SearchPos;

    if( ijlRead( &image, IJL_JBUFF_READPARAMS ) != IJL_OK )
    {
        DbgPrintf( "Cannot read JPEG file header file (params)\n" );
		return;
    }

    switch( image.JPGChannels ) {
    case 1:
		image.JPGColor = IJL_G;
		image.DIBColor = IJL_RGB;
		image.DIBChannels = 3;
		break;
		
    case 3:
		image.JPGColor = IJL_YCBCR;
		image.DIBColor = IJL_RGB;
		image.DIBChannels = 3;
		break;
		
    default:
		// This catches everything else, but no
		// color twist will be performed by the IJL.
		image.DIBColor = (IJL_COLOR)IJL_OTHER;
		image.JPGColor = (IJL_COLOR)IJL_OTHER;
		break;
    }

    image.DIBWidth    = image.JPGWidth;
    image.DIBHeight   = image.JPGHeight;
    image.DIBPadBytes = 0;

    int32	i32ImageSize = image.DIBWidth * image.DIBChannels * image.DIBHeight;

	i32Width = image.DIBWidth;
	i32Height = image.DIBHeight;
	i32Bpp = image.DIBChannels * 8;

    pData = new uint8[i32ImageSize];
    if( !pData ) {
        DbgPrintf( "Cannot allocate memory for image\n" );
        return;
    }

    image.DIBBytes = pData;

    if( ijlRead( &image, IJL_JBUFF_READWHOLEIMAGE ) != IJL_OK )
    {
        DbgPrintf( "Cannot read image data from file (entropy)\n" );
        delete[] pData;
		pData = 0;
        return;
    }

    if( ijlFree( &image ) != IJL_OK ) {
        DbgPrintf( "Cannot free Intel(R) JPEG library" );
    }


	//
	// Alpha channel
	//

    z_stream	stream;
    int			status;
    uint8*		pAlphaData;

    pAlphaData = new unsigned char[i32Width * i32Height];

    stream.next_in = &m_fileBuf[m_filePos + ui32Offset];
    stream.avail_in = 1;
    stream.next_out = pAlphaData;
    stream.avail_out = i32Width * i32Height;
    stream.zalloc = Z_NULL;
    stream.zfree = Z_NULL;
            
    status = inflateInit( &stream );

    while( 1 ) {
        status = inflate( &stream, Z_SYNC_FLUSH );
        if( status == Z_STREAM_END )
            break;
        if( status != Z_OK ) {
            DbgPrintf( "Zlib data error : %s\n", stream.msg );
            return;
        }
        stream.avail_in = 1;
    }

    inflateEnd( &stream );

	// Combine buffers
	uint8*	pCombined = new uint8[i32Width * i32Height * 4];
	uint8*	pDest = pCombined;
	uint8*	pSrc = pData;
	uint8*	pSrcA = pAlphaData;

	for( uint32 i = 0; i < i32Width * i32Height; i++ ) {
		*pDest++ = *pSrc++;
		*pDest++ = *pSrc++;
		*pDest++ = *pSrc++;
		*pDest++ = *pSrcA++;
	}

	delete [] pData;
	delete [] pAlphaData;
	pData = pCombined;
	i32Bpp = 32;

/*	if( i32Bpp == 32 || i32Bpp == 24 ) {
		char	szName[256];
		sprintf( szName, "tagjpeg%08x.tga", tagid );
		WriteTGA( szName, i32Width, i32Height, i32Bpp / 8, pData );
	}*/

//	delete [] pData;

	SWFBitmapC*	pBitmap = new SWFBitmapC;

	pBitmap->set_id( tagid );
	pBitmap->set_width( i32Width );
	pBitmap->set_height( i32Height );
	pBitmap->set_bpp( i32Bpp );
	pBitmap->set_data( pData );
	
	m_rDict.add_character( pBitmap );
}



void FlashScriptC::parse_define_bits_lossless( bool bWithAlpha )
{
	uint32	tagid = get_word();

	DbgPrintf("tagDefineBitsLossless tagid %-5u\n", tagid);
 
	int32	i32Format = get_byte();

	int32	i32Width  = get_word();
	int32	i32Height = get_word();
	int32	i32Bpp = 8;
	int32	i32TableSize = 0;

	if( i32Format == 3 ) {
		i32Bpp = 8;
		i32Width = (i32Width + 3) & 0xfffffffc;
	}
	else if( i32Format == 4 ) {
		i32Bpp = 16;
	}
	else if( i32Format == 5 ) {
		i32Bpp = 32;
	}
	
	if( i32Format == 3 )
		i32TableSize = get_byte() + 1;

	// pbBuffer points to the current pos in the file buffer
	uint8*	pBuffer = &m_fileBuf[m_filePos];
	uint8*	pColorTable;

	z_stream stream;
	int status;

	if( i32Format == 3 ) {
		// Allocate space for the color table
		pColorTable = new uint8[i32TableSize * 4];

		//
		// Decompress the color table
		//

		// Set up ZLIB structure
		stream.next_in = pBuffer;
		stream.avail_in = 1;
		stream.zalloc = Z_NULL;
		stream.zfree = Z_NULL;
		stream.next_out = pColorTable;
		if( bWithAlpha )
			stream.avail_out = i32TableSize * 4;
		else
			stream.avail_out = i32TableSize * 3;

		// Initialize ZLIB decompressor
		inflateInit( &stream );

		// Read the colormap byte-by-byte
		while( true ) {
			status = inflate( &stream, Z_SYNC_FLUSH );
			if( status == Z_STREAM_END )
				break;
			if( status != Z_OK) {
				DbgPrintf( "Zlib cmap error : %s\n", stream.msg );
				return;
			}
			stream.avail_in = 1;

			if( stream.avail_out == 0 )
				break;      // Colormap full
		}
	}

	//
	// Decompress the image data
	//

	// Allocate space for the image data
	int32	i32DataSize = i32Width * i32Height * i32Bpp / 8;
	uint8*	pData = new uint8[i32DataSize];

	stream.next_out = pData;
	stream.avail_out = i32DataSize;

	// Read the image data byte-by-byte
	while( true ) {
		status = inflate( &stream, Z_SYNC_FLUSH );
		if( status == Z_STREAM_END )
			break;

		if( status != Z_OK ) {
			printf( "Zlib data error : %s\n", stream.msg );
			return;
		}
		stream.avail_in = 1;
	}

	// Tell ZLIB decompressor we're finished
	inflateEnd( &stream );

	if( i32Format == 3 ) {
		//
		// Convert 8-bit paletted to 32-bit
		//
		uint8*	pConvData = new uint8[i32Width * i32Height * 4];

		uint8*	pDest = pConvData;
		uint8*	pSrc = pData;

		if( bWithAlpha ) {
			for( uint32 i = 0; i < i32Width * i32Height; i++ ) {
				*pDest++ = pColorTable[(uint32)(*pSrc) * 4 + 0];
				*pDest++ = pColorTable[(uint32)(*pSrc) * 4 + 1];
				*pDest++ = pColorTable[(uint32)(*pSrc) * 4 + 2];
				*pDest++ = pColorTable[(uint32)(*pSrc) * 4 + 3];
				pSrc++;
			}
			i32Bpp = 32;
		}
		else {
			for( uint32 i = 0; i < i32Width * i32Height; i++ ) {
				*pDest++ = pColorTable[(uint32)(*pSrc) * 3 + 0];
				*pDest++ = pColorTable[(uint32)(*pSrc) * 3 + 1];
				*pDest++ = pColorTable[(uint32)(*pSrc) * 3 + 2];
				*pDest++ = 255;
				pSrc++;
			}
			i32Bpp = 32;
		}

		delete [] pData;
		pData = pConvData;
	}
	else if( i32Format == 4 ) {
		//
		// Convert 16-bit to 32-bit
		//
		uint8*	pConvData = new uint8[i32Width * i32Height * 3];

		uint8*	pDest = pConvData;
		uint16*	pSrc = (uint16*)pData;

		for( uint32 i = 0; i < i32Width * i32Height; i++ ) {
			*pDest++ = ((*pSrc & 0x001f) << 3);
			*pDest++ = ((*pSrc & 0x03e0) >> 2);
			*pDest++ = ((*pSrc & 0x7c00) >> 7);
			pSrc++;
		}
		i32Bpp = 24;

		delete [] pData;
		pData = pConvData;
	}


/*	if( i32Bpp == 32 ) {
		char	szName[256];
		sprintf( szName, "tagloss%08x.tga", tagid );
		WriteTGA( szName, i32Width, i32Height, i32Bpp / 8, pData );
	}*/

//	delete pData;
	delete pColorTable;

	SWFBitmapC*	pBitmap = new SWFBitmapC;

	pBitmap->set_id( tagid );
	pBitmap->set_width( i32Width );
	pBitmap->set_height( i32Height );
	pBitmap->set_bpp( i32Bpp );
	pBitmap->set_data( pData );
	
	m_rDict.add_character( pBitmap );

}

void FlashScriptC::parse_define_bits_lossless2()
{
	DbgPrintf( "FlashScriptC::parse_define_bits_lossless2()\n" );
	parse_define_bits_lossless( true );
}

void FlashScriptC::parse_jpeg_tables()
{
	DbgPrintf( "FlashScriptC::parse_jpeg_tables()\n" );

	if( g_bHaveTables )
		return;

    memset( &g_rJpegProp, 0, sizeof( JPEG_CORE_PROPERTIES ) );

    if( ijlInit( &g_rJpegProp ) != IJL_OK ) {
        DbgPrintf( "Cannot initialize Intel JPEG library\n" );
        return;
    }

	g_rJpegProp.JPGBytes = &m_fileBuf[m_filePos];
	g_rJpegProp.JPGSizeBytes = m_tagEnd - m_filePos;
    
    if( ijlRead( &g_rJpegProp, IJL_JBUFF_READHEADER ) != IJL_OK ) {
        DbgPrintf( "Cannot read JPEG file header file (params)\n" );
		return;
    }

	g_bHaveTables = true;
}

void FlashScriptC::parse_unknown( char *str, uint16 code )
{
	DbgPrintf("%sUnknown Tag:0x%02x len:0x%02x\n", str, code, m_tagLen);
}


// Parses the tags within the file.
void FlashScriptC::parse_tags( bool sprite, uint32 tabs )
{

	char str[33];	// indent level
	
	{
		uint32 i = 0;
	
		for( i = 0; i < tabs && i < 32; i++ ) {
			str[i] = '\t';
		}
		str[i] = 0;
	}		 

	if( sprite ) {
		uint32 tagid = (uint32) get_word();
		uint32 frameCount = (uint32) get_word();

		m_pCurSprite->set_id( tagid );

		DbgPrintf("%stagDefineSprite \ttagid %-5u \tframe count %-5u\n", str, tagid, frameCount);
	}
	else {		 
		DbgPrintf("\n%s<----- dumping frame %d file offset 0x%04x ----->\n", str, 0, m_filePos);
		
		// Set the position to the start position.
		m_filePos = m_fileStart;
	}		 
	
	// Initialize the end of frame flag.
	bool atEnd = false;

	// Reset the frame position.
	uint32 frame = 0;

	// Loop through each tag.
	while( !atEnd ) {
		// Get the current tag.
		uint16 code = get_tag();

//		if (false)
//		{
//			  printf("Tag dump: %04x: ", m_tagStart);
//			  for (uint32 i=m_tagStart; i<m_tagStart+8; i++)
//				  printf("%02x ", m_fileBuf[i]);
//		}


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

		switch (code)
		{
			case TAG_END:
				// Parse the end tag.
				parse_end( str );
				// We reached the end of the file.
				atEnd = true;
				break;
		
			case TAG_SHOWFRAME:
				parse_show_frame( str, frame, tagEnd );

				// Increment to the next frame.
				++frame;
				break;

			case TAG_FREECHARACTER:
				parse_free_character( str );
				break;

			case TAG_PLACEOBJECT:
				parse_place_object( str );
				break;

			case TAG_PLACEOBJECT2:
				parse_place_object2( str );
				break;

			case TAG_REMOVEOBJECT:
				parse_remove_object( str );
				break;

			case TAG_REMOVEOBJECT2:
				parse_remove_object2( str );
				break;

			case TAG_SETBACKGROUNDCOLOR:
				parse_set_background_color( str );
				break;

//			case TAG_DOACTION:
//				parse_do_action( str );
//				break;

			case TAG_DEFINESHAPE: 
				parse_define_shape( str );
				break;

			case TAG_DEFINESHAPE2:
				parse_define_shape2( str );
				break;

			case TAG_DEFINESHAPE3:
				parse_define_shape3( str );
				break;

			case TAG_DEFINEFONT:
				parse_define_font( str );
				break;

			case TAG_DEFINEFONTINFO:
				parse_define_font_info( str );
				break;

			case TAG_DEFINETEXT:
				parse_define_text( str );
				break;

			case TAG_DEFINETEXT2:
				parse_define_text2( str );
				break;

			case TAG_DEFINESPRITE:
				{
					SWFSpriteC*			rCurSprite = m_pCurSprite;
					SWFDisplayListC*	pCurDispList = m_pCurDispList;

					SWFSpriteC*			pSprite = new SWFSpriteC;
					SWFDisplayListC		rTempDispList;

					m_pCurSprite = pSprite;
					m_pCurDispList = &rTempDispList;

					parse_tags( true, tabs + 1 );

					m_rDict.add_character( pSprite );

					m_pCurSprite = rCurSprite;
					m_pCurDispList = pCurDispList;
				}
				break;

			case TAG_NAMECHARACTER:
				parse_name_character( str );
				break;

			case TAG_FRAMELABEL:
				parse_frame_label( str );
				break;

			case TAG_DEFINEFONT2:
				parse_define_font2( str );
				break;

			case TAG_DEFINEBITS:
				parse_define_bits();
				break;

			case TAG_JPEGTABLES:
				parse_jpeg_tables();
				break;

			case TAG_DEFINEBITSLOSSLESS:
				parse_define_bits_lossless();
				break;

			case TAG_DEFINEBITSJPEG2:
				parse_define_bits_jpeg2();
				break;

			case TAG_DEFINEBITSJPEG3:
				parse_define_bits_jpeg3();
				break;

			case TAG_DEFINEBITSLOSSLESS2:
				parse_define_bits_lossless2();
				break;

			default:
				parse_unknown( str, code );
				break;
		}

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


bool FlashScriptC::parse_file( uint8* pInput, uint32 ui32InputSize )
{
	bool sts = true;

	m_fileBuf = pInput;
	m_fileSize = ui32InputSize;

	// Are we OK?
	if( sts ) {
//		  printf("----- Reading the file header -----\n");

		// Verify the header and get the file size.
		if (m_fileBuf[0] != 'F' || m_fileBuf[1] != 'W' || m_fileBuf[2] != 'S' ) {
			DbgPrintf("ERROR: Illegal Header - not a Shockwave Flash File (%c%c%c != %c%c%c)\n", m_fileBuf[0], m_fileBuf[1], m_fileBuf[2], 'F', 'W', 'S' );

			// Bad header.
			sts = false;
		}
		else {
			// Get the file version.
			m_fileVersion = (uint16) m_fileBuf[3];

			DbgPrintf("FWS\n");
			DbgPrintf("File version \t%u\n", m_fileVersion);
		}
	}
		
	// Are we OK?
	if( sts ) {
		// Get the file size.
		m_fileSize = (uint32) m_fileBuf[4] | ((uint32) m_fileBuf[5] << 8) | ((uint32) m_fileBuf[6] << 16) | ((uint32) m_fileBuf[7] << 24);

		DbgPrintf("File size \t%u\n", m_fileSize);

		// Verify the minimum length of a Flash file.
		if (m_fileSize < 21)
		{
			OutputDebugString("ERROR: file size is too short\n");
			// The file is too short.
			sts = false;
		}
	}

	// Are we OK?
	if( sts ) {
		BBox2C rect;
		
		// Set the file position past the header and size information.
		m_filePos = 8;

		// Get the frame information.
		rect = get_rect();
		DbgPrintf("Movie width \t%u\n", rect.width() );
		DbgPrintf("Movie height \t%u\n", rect.height() );

		m_i32Width = rect.width();
		m_i32Height = rect.height();

		m_i32FrameRate = (int32)get_word() >> 8;
		DbgPrintf("Frame rate \t%u\n", m_i32FrameRate);

		m_i32FrameCount = (int32)get_word();
		DbgPrintf("Frame count \t%u\n", m_i32FrameCount);

		// Set the start position.
		m_fileStart = m_filePos;	

		DbgPrintf("\n----- Reading movie details -----\n");
//		fflush(stdout);

		// Parse the tags within the file.
		parse_tags( false, 0 );
		DbgPrintf("\n***** Finished Dumping SWF File Information *****\n");
	}

	uint32 i;

	// update sprite frames
	m_rSprite.update_frames();
	for( i = 0; i < m_rDict.get_char_count(); i++ ) {
		SWFCharC*	pChar = m_rDict.get_char( i );
		if( pChar->get_type() == SWF_CHAR_SPRITE ) {
			SWFSpriteC*	pSprite = (SWFSpriteC*)pChar;
			pSprite->update_frames();
		}
	}


	// Find minimum and maximum scale for each shape.
	// The min and max scale is used later when the curves are tesselated.
	m_rSprite.update_shape_scalerange( &m_rDict );
	for( i = 0; i < m_rDict.get_char_count(); i++ ) {
		SWFCharC*	pChar = m_rDict.get_char( i );
		if( pChar->get_type() == SWF_CHAR_SPRITE ) {
			SWFSpriteC*	pSprite = (SWFSpriteC*)pChar;
			pSprite->update_shape_scalerange( &m_rDict );
		}
	}

	// Reset the file information.
	m_filePos = 0;
	m_fileSize = 0;
	m_fileStart = 0;
	m_fileVersion = 0;

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

	return sts;
}

int32
FlashScriptC::get_width()
{
	return m_i32Width;
}

int32
FlashScriptC::get_height()
{
	return m_i32Height;
}

int32
FlashScriptC::get_frame_rate()
{
	return m_i32FrameRate;
}

int32
FlashScriptC::get_frame_count()
{
	return m_rSprite.get_displaylist_count();
}

const ColorC&
FlashScriptC::get_bg_color()
{
	return m_rBgColor;
}

uint32
FlashScriptC::get_dict_size()
{
	return m_rDict.get_char_count();
}

SWFCharC*
FlashScriptC::get_dict_char( uint32 ui32Idx )
{
	return m_rDict.get_char( ui32Idx );
}

SWFCharC*
FlashScriptC::get_dict_char_by_id( uint32 ui32Id )
{
	return m_rDict.get_char_by_id( ui32Id );
}

SWFDisplayListC*
FlashScriptC::get_frame( uint32 ui32Frame )
{
	return m_rSprite.get_displaylist( ui32Frame );
}

SWFDictionaryC*
FlashScriptC::get_dictionary()
{
	return &m_rDict;
}

void
FlashScriptC::prepare()
{
	for( uint32 i = 0; i < m_rDict.get_char_count(); i++ ) {
		SWFCharC*	pChar = m_rDict.get_char( i );

		if( pChar->get_type() == SWF_CHAR_SHAPE ) {
			SWFShapeC*	pShape = (SWFShapeC*)pChar;
			SWFShapeRecordC*	pRec = pShape->get_shape();
			pRec->prepare( false, &m_rDict );
		}
		else if( pChar->get_type() == SWF_CHAR_FONT ) {
			SWFFontC*	pFont = (SWFFontC*)pChar;
			for( uint32 j = 0; j < pFont->get_glyph_count(); j++ ) {
				SWFGlyphC*	pGlyph = pFont->get_glyph( j );
				SWFShapeRecordC*	pRec = pGlyph->get_shape();
				pRec->prepare( true, &m_rDict );
			}
		}
		else if( pChar->get_type() == SWF_CHAR_BITMAP ) {
			SWFBitmapC*	pBitmap = (SWFBitmapC*)pChar;
			pBitmap->prepare();
		}
	}

	m_bPrepared = true;
}

void
FlashScriptC::unprepare()
{
	for( uint32 i = 0; i < m_rDict.get_char_count(); i++ ) {
		SWFCharC*	pChar = m_rDict.get_char( i );

		if( pChar->get_type() == SWF_CHAR_SHAPE ) {
			SWFShapeC*	pShape = (SWFShapeC*)pChar;
			SWFShapeRecordC*	pRec = pShape->get_shape();
			pRec->unprepare();
		}
		else if( pChar->get_type() == SWF_CHAR_FONT ) {
			SWFFontC*	pFont = (SWFFontC*)pChar;
			for( uint32 j = 0; j < pFont->get_glyph_count(); j++ ) {
				SWFGlyphC*	pGlyph = pFont->get_glyph( j );
				SWFShapeRecordC*	pRec = pGlyph->get_shape();
				pRec->unprepare();
			}
		}
		else if( pChar->get_type() == SWF_CHAR_BITMAP ) {
			SWFBitmapC*	pBitmap = (SWFBitmapC*)pChar;
			pBitmap->unprepare();
		}
	}

	m_bPrepared = false;
}

bool
FlashScriptC::get_prepared() const
{
	return m_bPrepared;
}
