/********************************************\
 * this is FREE software                    *
 * covered by the GPL                       *
 * for the full terms and conditions, visit *
 * http://www.fsf.org/copyleft              *
 *                                          *
 * copyright 2002 Musicman                  *
 * contact: musicman@haveashittyday.com     *
\********************************************/

#include <stdio.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <zlib.h>
#include "swf.h"

/* forward references */
void error(char *fmt, ...);

static verbose = {0};

struct bitstream
{	char lastch;
	char bitoff;
	int (*readc)(void *);
};
typedef struct bitstream *BITS;

#define alignbits(x) ((BITS)x)->bitoff = 0

getbits(BITS bp, int nbits)
{	int res = 0, nb, db;
	for(nb = 0 ; nb < nbits ; nb += db)
	{	if(bp->bitoff == 0)
		{	bp->lastch = bp->readc(bp);
			bp->bitoff = 8;
		}
		db = nbits - nb;
		if(db > bp->bitoff)
			db = bp->bitoff;
		/* db bits von vorn wegnehmen */
		res <<= db;
		res |= (bp->lastch >> (bp->bitoff -= db)) & ((1 << db) - 1);
	}
	return res;
}
getsbits(BITS bp, int nbits)
{	int res = getbits(bp, nbits);
	if(res & (1 << (nbits-1)))
		res |= (-1) << nbits;
	return(res);
}

/* rectangle */
rect(BITS bp)
{	int nbits, xmin, xmax, ymin, ymax;
	nbits = getbits(bp, 5);
	xmin = getbits(bp, nbits);
	xmax = getbits(bp, nbits);
	ymin = getbits(bp, nbits);
	ymax = getbits(bp, nbits);
	if(verbose)
		printf("rect %.2f,%.2f %.2f,%.2f\n", xmin/20., ymin/20., xmax/20., ymax/20.);
}
/* matrix */
matrix(BITS bp)
{	int hasscale, nscalebits, scalex, scaley;
	int hasrotate, nrotatebits, rotateskew0, rotateskew1;
	int ntranslatebits, translatex, translatey;
	if(hasscale = getbits(bp, 1))
	{	nscalebits = getbits(bp, 5);
		scalex = getbits(bp, nscalebits);
		scaley = getbits(bp, nscalebits);
		if(verbose)
			printf("scale %d %d\n", scalex, scaley);
	}
	if(hasrotate = getbits(bp, 1))
	{	nrotatebits = getbits(bp, 5);
		rotateskew0 = getsbits(bp, nrotatebits);
		rotateskew1 = getsbits(bp, nrotatebits);
		if(verbose)
			printf("skew %d %d\n", rotateskew0, rotateskew1);
	}
	ntranslatebits = getbits(bp, 5);
	translatex = getsbits(bp, ntranslatebits);
	translatey = getsbits(bp, ntranslatebits);
	if(verbose)
		printf("translate %d %d\n", translatex, translatey);
}
/* rgb */
rgb(BITS bp)
{	int r, g, b;
	alignbits(bp);
	r = bp->readc(bp); g = bp->readc(bp); b = bp->readc(bp);
	if(verbose)
		printf("rgb %x %x %x\n", r, g, b);
}
rgba(BITS bp)
{	int r, g, b, a;
	alignbits(bp);
	r = bp->readc(bp); g = bp->readc(bp); b = bp->readc(bp);
	a = bp->readc(bp);
	if(verbose)
		printf("rgba %x %x %x %x\n", r, g, b, a);
}
readint2(BITS bp)
{	int res;
	res = bp->readc(bp);
	res |= bp->readc(bp) << 8;
	return res;
}
readint4(BITS bp)
{	int res;
	res = bp->readc(bp);
	res |= bp->readc(bp) << 8;
	res |= bp->readc(bp) << 16;
	res |= bp->readc(bp) << 24;
	return res;
}
putint2(unsigned char *p, int val)
{	*p++ = val;
	*p++ = val >> 8;
}
putint4(unsigned char *p, int val)
{	*p++ = val;
	*p++ = val >> 8;
	*p++ = val >> 16;
	*p++ = val >> 24;
}

/* open a swf file as a stream */
struct swfile
{	char lastch;
	char bitoff;
	int (*readc)();

	char *name;	
	char vers[4];
	int fsize;
	
	char rect[9], rectlen;
	FILE *fp;
	short frames, rate;

	short compressed;
	unsigned char *zbuf, *zptr, *zend;
};
r_readc(struct swfile *sp)
{	return sp->rect[sp->rectlen++] = freadc(sp);
}
freadc(struct swfile *sp)
{	if(sp->compressed)
		return(sp->zptr < sp->zend ? *sp->zptr++ : 0);
	else
		return fgetc(sp->fp);
}
swftell(struct swfile *sp)
{	if(sp->compressed)
		return(sp->zptr - sp->zbuf + 8);
	else
		return ftell(sp->fp);
}
swfseek(struct swfile *sp, int delta)
{	if(sp->compressed)
		sp->zptr += delta;
	else
		fseek(sp->fp, delta, 1);
}
struct swfile *openswf(char *name)
{	struct swfile *res = (struct swfile *)malloc(sizeof(struct swfile));
	if(!(res->fp = fopen(name, "r")))
		error("cannot open %s for input\n", name);
	fread(res->vers, 4, 1, res->fp);
	if(memcmp(res->vers, "FWS", 3) && memcmp(res->vers, "CWS", 3))
		error("not a SWF stream: %s\n", name);
	fread(&res->fsize, 4, 1, res->fp);
	res->compressed = res->vers[0] == 'C';
	if(res->compressed)
	{	static z_stream z = {0};
		struct stat sbuf;
		fstat(fileno(res->fp), &sbuf);
		z.next_in = (unsigned char *)malloc(z.avail_in = sbuf.st_size - 8);
		fread(z.next_in, z.avail_in, 1, res->fp);
		res->zbuf = res->zptr = z.next_out = (unsigned char *)malloc(z.avail_out = res->fsize - 8);
		inflateInit(&z);
		inflate(&z, Z_FINISH);
		inflateEnd(&z);
		res->zend = z.next_out;
	}
	// setup to read that rect...
	alignbits(res);
	res->rectlen = 0;
	res->readc = r_readc;
	rect((BITS) res);
	res->readc = freadc;
	res->rate = readint2((BITS) res);
	res->frames = readint2((BITS) res);
	res->name = name;
	return res;
}

/* tag */
struct swftag
{	char lastch;
	char bitoff;
	int (*readc)();

	short type; int size;
	unsigned char hdr[6]; short hdrlen;
	unsigned char *datbuf, *datptr, *datend;
};
typedef struct swftag *TAG;

treadc(TAG tp)
{	return *tp->datptr++;
}

TAG readtag_common(BITS bp)
{	TAG res = (TAG) malloc(sizeof(struct swftag));
	res->type = readint2(bp);
	res->size = res->type & 63;
	putint2(res->hdr, res->type);
	res->type >>= 6;
	res->hdrlen = 2;
	if(res->size == 63)
	{	res->size = readint4(bp);
		putint4(res->hdr+2, res->size);
		res->hdrlen = 6;
	}
	res->bitoff = 0;
	res->readc = treadc;
	return res;
}
TAG readtag_file(struct swfile *sp)
{	TAG res = (TAG) readtag_common((BITS) sp);
	if(res->size)
	{	if(sp->compressed)
		{	res->datbuf = res->datptr = sp->zptr;
			sp->zptr += res->size;
			res->datend = sp->zptr;
		}
		else
		{	res->datbuf = res->datptr = (unsigned char *)malloc(res->size);
			res->datend = res->datbuf + res->size;
			fread(res->datbuf, res->size, 1, sp->fp);
		}
	}
	return res;
}
TAG readtag_sprite(TAG tp)
{	TAG res = readtag_common((BITS) tp);
	if(res->size)
	{	res->datbuf = res->datptr = tp->datptr;
		res->datend = res->datbuf + res->size;
		tp->datptr += res->size;
	}
	return res;
}

void error(char *fmt, ...)
{	va_list ap;
	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	exit(1);
}

static maxid = {0}, idoffset = {0};

main(int ac, char **av)
{	int totalsize, totalframes;
	int n, nfiles;
	int type;
	struct swfile *swf, **swfiles;
	TAG tp;
	char *ofname = 0;
	FILE *ofp;

	while(--ac > 0 && **++av == '-')
		if(*++*av == 'v')
			verbose = 1;
		else if(**av == 'o')
			if(*++*av)
				ofname = *av;
			else
				--ac, ofname = *++av;
			else
				error("unexpected option %s\n", (*av) - 1);
	swfiles = (struct swfile **)malloc(ac * sizeof(struct swfile *));
	swfiles[0] = swf = openswf(*av++);
	totalsize = swf->fsize;
	totalframes = swf->frames;
	for(nfiles = 1 ; --ac > 0 ; )
	{	swfiles[nfiles++] = swf = openswf(*av++);
		tp = readtag_file(swf);
		if(tp->type != stagSetBackgroundColor)
			swfseek(swf, -(tp->hdrlen + tp->size));
		free(tp);
		totalsize += swf->fsize - swftell(swf);
		totalframes += swf->frames;
	}
	totalsize -= (nfiles-1) * 2;	// do not copy End tags
	if(ofname)
		ofp = fopen(ofname, "w");
	else
		ofp = stdout;
	swf = swfiles[0];
	swf->vers[0] = 'F';	// uncompressed for the moment
	fwrite(swf->vers, 4, 1, ofp);
	fwrite(&totalsize, 4, 1, ofp);
	fwrite(swf->rect, swf->rectlen, 1, ofp);
	fwrite(&swf->rate, 2, 1, ofp);
	fwrite(&totalframes, 2, 1, ofp);
	for(n = 0 ; n < nfiles ; n++)
	{	swf = swfiles[n];
		if(verbose)
			printf("start file %s, offset %d\n", swf->name, idoffset);
		do
		{	tp = readtag_file(swf);
			handle_tag(tp);
			if((type = tp->type) || (n == nfiles-1))
			{	fwrite(tp->hdr, tp->hdrlen, 1, ofp);
				if(tp->size)
					fwrite(tp->datbuf, tp->size, 1, ofp);
			}
			free(tp);
		} while(type);
		if(swftell(swf) != swf->fsize)
			error("consistency check: file size wrong in %s\n", swf->name);
		idoffset = maxid;
	}
	fclose(ofp);
	exit(0);
}

int change_id(TAG tp)
{	int val = readint2((BITS) tp);
	if(val)
	{	val += idoffset;
		if(val > maxid)
			maxid = val;
		putint2(tp->datptr-2, val);
	}
	return val;
}

handle_tag(TAG tp)
{	int id;
	if(verbose)
	{	char *tagnam;
		switch(tp->type)
		{
			case stagEnd:
				tagnam = "stagEnd"; break;
			case stagShowFrame:
				tagnam = "stagShowFrame"; break;
			case stagDefineShape:
				tagnam = "stagDefineShape"; break;
			case stagFreeCharacter:
				tagnam = "stagFreeCharacter"; break;
			case stagPlaceObject:
				tagnam = "stagPlaceObject"; break;
			case stagRemoveObject:
				tagnam = "stagRemoveObject"; break;
			case stagDefineBits:
				tagnam = "stagDefineBits"; break;
			case stagDefineButton:
				tagnam = "stagDefineButton"; break;
			case stagJPEGTables:
				tagnam = "stagJPEGTables"; break;
			case stagSetBackgroundColor:
				tagnam = "stagSetBackgroundColor"; break;
			case stagDefineFont:
				tagnam = "stagDefineFont"; break;
			case stagDefineText:
				tagnam = "stagDefineText"; break;
			case stagDoAction:
				tagnam = "stagDoAction"; break;
			case stagDefineFontInfo:
				tagnam = "stagDefineFontInfo"; break;
			case stagDefineSound:
				tagnam = "stagDefineSound"; break;
			case stagStartSound:
				tagnam = "stagStartSound"; break;
			case stagDefineButtonSound:
				tagnam = "stagDefineButtonSound"; break;
			case stagSoundStreamHead:
				tagnam = "stagSoundStreamHead"; break;
			case stagSoundStreamBlock:
				tagnam = "stagSoundStreamBlock"; break;
			case stagDefineBitsLossless:
				tagnam = "stagDefineBitsLossless"; break;
			case stagDefineBitsJPEG2:
				tagnam = "stagDefineBitsJPEG2"; break;
			case stagDefineShape2:
				tagnam = "stagDefineShape2"; break;
			case stagDefineButtonCxform:
				tagnam = "stagDefineButtonCxform"; break;
			case stagProtect:
				tagnam = "stagProtect"; break;
			case stagPlaceObject2:
				tagnam = "stagPlaceObject2"; break;
			case stagRemoveObject2:
				tagnam = "stagRemoveObject2"; break;
			case stagDefineShape3:
				tagnam = "stagDefineShape3"; break;
			case stagDefineText2:
				tagnam = "stagDefineText2"; break;
			case stagDefineButton2:
				tagnam = "stagDefineButton2"; break;
			case stagDefineBitsJPEG3:
				tagnam = "stagDefineBitsJPEG3"; break;
			case stagDefineBitsLossless2:
				tagnam = "stagDefineBitsLossless2"; break;
			case stagDefineTextField:
				tagnam = "stagDefineTextField"; break;
			case stagDefineSprite:
				tagnam = "stagDefineSprite"; break;
			case stagNameCharacter:
				tagnam = "stagNameCharacter"; break;
			case stagFrameLabel:
				tagnam = "stagFrameLabel"; break;
			case stagSoundStreamHead2:
				tagnam = "stagSoundStreamHead2"; break;
			case stagDefineMorphShape:
				tagnam = "stagDefineMorphShape"; break;
			case stagDefineFont2:
				tagnam = "stagDefineFont2"; break;
			case stagExportAssets:
				tagnam = "stagExportAssets"; break;
			case stagClipInit:	
				tagnam = "stagClipInit"; break;
			case stagNewFontInfo:
				tagnam = "stagNewFontInfo"; break;
		}
		printf("tag %d %s\n", tp->type, tagnam);
	}
	switch(tp->type)
	{	/* tags without an id */
		case stagEnd:
		case stagShowFrame:
		case stagSetBackgroundColor:
		case stagDoAction:
		case stagProtect:
		case stagRemoveObject2:
		case stagFrameLabel:
		case stagSoundStreamHead:
		case stagSoundStreamHead2:
		case stagSoundStreamBlock:
		case stagJPEGTables:
		case stagClipInit:
			break;
		/* simple tags - one id at beginning */
		case stagFreeCharacter:
		case stagPlaceObject:
		case stagRemoveObject:
		case stagDefineBits:
		case stagDefineBitsJPEG2:
		case stagDefineBitsJPEG3:
		case stagDefineBitsLossless:
		case stagDefineBitsLossless2:
		case stagDefineFont:
		case stagDefineFontInfo:
		case stagDefineSound:
		case stagStartSound:
		case stagNameCharacter:
		case stagDefineFont2:
		case stagDefineButtonCxform:
		case stagNewFontInfo:
			id = change_id(tp);
			if(verbose)
				printf("id %d\n", id);
			break;
		/* to be processed specially */
		case stagDefineShape:
			defineshape(tp, 1);
			break;
		case stagDefineText:
			definetext(tp, 1);
			break;
		case stagDefineShape2:
			defineshape(tp, 2);
			break;
		case stagPlaceObject2:
			placeobject2(tp);
			break;
		case stagDefineShape3:
			defineshape(tp, 3);
			break;
		case stagDefineText2:
			definetext(tp, 2);
			break;
		case stagDefineButton:
			definebutton(tp);
			break;
		case stagDefineButton2:
			definebutton2(tp);
			break;
		case stagDefineTextField:
			definetextfield(tp);
			break;
		case stagDefineSprite:
			definesprite(tp);
			break;
		case stagDefineMorphShape:
			definemorphshape(tp);
			break;
		case stagExportAssets:
			exportassets(tp);
			break;
		case stagDefineButtonSound:
			definebuttonsound(tp);
			break;
		default:
			error("unknown tag %d\n", tp->type);
	}
}

definesprite(TAG sp)
{	TAG tp;
	int type;
	int id, frames;
	id = change_id(sp);
	frames = readint2((BITS) sp);
	if(verbose)
		printf("sprite %d, %d frames\n", id, frames);
	do
	{	tp = readtag_sprite(sp);
		handle_tag(tp);
		type = tp->type;
		free(tp);
	} while(type);
	if(sp->datptr != sp->datend)
		error("consistency check: file size wrong in sprite\n");
}

/* shape */
shaperecord(TAG tp, int nfillbits, int nlinebits, int lev)
{	int type, statenewstyles, statelinestyle, statefillstyle1, statefillstyle0;
	int statemoveto, movebits, movex, movey, fill0style, fill1style, linestyle;
	int edgeflag, nbits, genlineflag, dx, dy, vertlineflag;
	int cdx, cdy, adx, ady;
	
	while(1)
	{	type = getbits((BITS) tp, 1);
		
		if(!type)
		{	statenewstyles = getbits((BITS) tp, 1);
			statelinestyle = getbits((BITS) tp, 1);
			statefillstyle1 = getbits((BITS) tp, 1);
			statefillstyle0 = getbits((BITS) tp, 1);
			if(statemoveto = getbits((BITS) tp, 1))
			{	movebits = getbits((BITS) tp, 5);
				movex = getsbits((BITS) tp, movebits);
				movey = getsbits((BITS) tp, movebits);
				if(verbose)
					printf("move %d %d\n", movex, movey);
			}
			if(statenewstyles == 0 && statelinestyle == 0 && statefillstyle1 == 0&& statefillstyle0 == 0 && statemoveto == 0)
				break;
			if(statefillstyle0)
			{	fill0style = getbits((BITS) tp, nfillbits);
				if(verbose)
					printf("fillstyle0 %d\n", fill0style);
			}
			if(statefillstyle1)
			{	fill1style = getbits((BITS) tp, nfillbits);
				if(verbose)
					printf("fillstyle1 %d\n", fill1style);
			}
			if(statelinestyle)
			{	linestyle = getbits((BITS) tp, nlinebits);
				if(verbose)
					printf("linestyle %d\n", linestyle);
			}
/* contrary to document, but
 ... seems to work AND make sense
 */
			if(statefillstyle0 || statefillstyle1 || statelinestyle)
				;
			else
			if(statenewstyles)
				fillandlinestyles(tp, lev);
		}
		else
		{	edgeflag = getbits((BITS) tp, 1);
			if(!edgeflag)	/* curve */
			{	nbits = getbits((BITS) tp, 4)+2;
				cdx = getsbits((BITS) tp, nbits);
				cdy = getsbits((BITS) tp, nbits);
				adx = getsbits((BITS) tp, nbits);
				ady = getsbits((BITS) tp, nbits);
				if(verbose)
					printf("curve %d,%d %d,%d\n", cdx, cdy, adx, ady);
			}
			else
			{	nbits = getbits((BITS) tp, 4)+2;
				if(genlineflag = getbits((BITS) tp, 1))
				{	dx = getsbits((BITS) tp, nbits);
					dy = getsbits((BITS) tp, nbits);
				}
				else if(vertlineflag = getbits((BITS) tp, 1))
				{	dx = 0;
					dy = getsbits((BITS) tp, nbits);
				}
				else
				{	dx = getsbits((BITS) tp, nbits);
					dy = 0;
				}
				if(verbose)
					printf("line %d,%d\n", dx, dy);
			}
		}
	}
}

gradient(TAG tp, int alpha)
{	int n, ngrad, r1, r2;
	
	alignbits(tp);
	ngrad = tp->readc(tp);
	for(n = 0 ; n < ngrad ; n++)
	{	r1 = tp->readc(tp);
		if(verbose)
			printf("ratio %d\n", r1);
		if(alpha) rgba((BITS) tp); else rgb((BITS) tp);
	}
}

fillstyle(TAG tp, int lev)
{	unsigned short id;
	char cod;

	alignbits(tp);
	cod = tp->readc(tp);
	switch(cod)
	{	case 0:
			if(verbose) printf("solid fill:\n"); break;
		case 0x10:
			if(verbose) printf("linear gradient fill\n"); break;
		case 0x12:
			if(verbose) printf("radial gradient fill:\n"); break;
		case 0x40:
			if(verbose) printf("tiled bitmap fill:\n"); break;
		case 0x41:
			if(verbose) printf("clipped bitmap fill\n"); break;
	}
	if(cod == 0)
		if(lev == 3) rgba((BITS) tp); else rgb((BITS) tp);
	else if(cod == 0x10 || cod == 0x12)
	{	matrix((BITS) tp);
		gradient(tp, lev == 3);
	}
	else if(cod == 0x40 || cod == 0x41)
	{	id = change_id(tp);
		if(verbose)
			printf("fill with id %d\n", id);
		matrix((BITS) tp);
	}
	else
		printf("UNEXPEDCED %x\n", cod);
}

morphgradient(TAG tp)
{	int n, ngrad, r1, r2;
	
	alignbits(tp);
	ngrad = tp->readc(tp);
	for(n = 0 ; n < ngrad ; n++)
	{	r1 = tp->readc(tp);
		if(verbose)
			printf("ratio %d\n", r1);
		rgba((BITS) tp);
		r2 = tp->readc(tp);
		if(verbose)
			printf("ratio %d\n", r2);
		rgba((BITS) tp);
	}
}

morphfillstyle(TAG tp)
{	unsigned short id;
	char cod;

	alignbits(tp);
	cod = tp->readc(tp);
	switch(cod)
	{	case 0:
			if(verbose) printf("solid fill:\n"); break;
		case 0x10:
			if(verbose) printf("linear gradient fill\n"); break;
		case 0x12:
			if(verbose) printf("radial gradient fill:\n"); break;
		case 0x40:
			if(verbose) printf("tiled bitmap fill:\n"); break;
		case 0x41:
			if(verbose) printf("clipped bitmap fill\n"); break;
	}
	if(cod == 0)
	{	rgba((BITS) tp);
		rgba((BITS) tp);
	}
	else if(cod == 0x10 || cod == 0x12)
	{	matrix((BITS) tp);
		matrix((BITS) tp);
		morphgradient(tp);
	}
	else if(cod == 0x40 || cod == 0x41)
	{	id = change_id(tp);
		if(verbose) printf("fill with id %d\n", id);
		matrix((BITS) tp);
		matrix((BITS) tp);
	}
	else
		printf("UNEXPEDCED %x\n", cod);
}

fillandlinestyles(TAG tp, int lev)
{	int n, nfill, nline;
	unsigned short w1;
	alignbits(tp);
	nfill = tp->readc(tp);
	if(nfill == 0xff)
		nfill = readint2((BITS) tp);
	if(verbose)	
		printf ("%d fill styles\n", nfill);
	for(n = 0 ; n < nfill ; n++)
		fillstyle(tp, lev);
	alignbits(tp);
	nline = tp->readc(tp);
	if(nline == 0xff)
		nline = readint2((BITS) tp);
	if(verbose)
		printf ("%d line styles\n", nline);
	for(n = 0 ; n < nline ; n++)
		linestyle(tp, lev);
}

linestyle(TAG tp, int lev)
{	unsigned short w;
	w = readint2((BITS) tp);
	if(verbose)
		printf ("w %d\n", w);
	if(lev == 3) rgba((BITS) tp); else rgb((BITS) tp);
}

morphlinestyle(TAG tp)
{	unsigned short w1, w2;
	w1 = readint2((BITS) tp);
	w2 = readint2((BITS) tp);
	if(verbose)
		printf("w %d => %d\n", w1, w2);
	rgba((BITS) tp); rgba((BITS) tp);
}

defineshape(TAG tp, int lev)
{	int n, cod, nfill, nline;
	unsigned short id, w1, w2;
	id = change_id(tp);
	if(verbose)
		printf("shape %d\n", id);
	rect((BITS) tp);
	fillandlinestyles(tp, lev);
	shape(tp, lev);
}

shape(TAG tp, int lev)
{	int nfillbits, nlinebits;
	alignbits(tp);
	nfillbits = getbits((BITS) tp, 4);
	nlinebits = getbits((BITS) tp, 4);
	shaperecord(tp, nfillbits, nlinebits,lev);
}

bits_req(n)
{	int j, k;
	for(j = 1, k = 0 ; j <= n ; k++)
		j <<= 1;
	return k;
}

definemorphshape(TAG tp)
{	unsigned short id, fcnt, lcnt;
	unsigned long loff;
	unsigned char *endp;
	int n;

	id = change_id(tp);
	if(verbose)
		printf("morph %d\n", id);
	rect((BITS) tp);
	alignbits(tp);
	rect((BITS) tp);
	loff = readint4((BITS) tp);
	endp = tp->datptr + loff;
	fcnt = tp->readc(tp);
	if(fcnt == 0xff)
		fcnt = readint2((BITS) tp);
	if(verbose)
		printf ("%d fill styles\n", fcnt);
	for(n = 0 ; n < fcnt ; n++)
	{	alignbits(tp);
		morphfillstyle(tp);
	}
	lcnt = tp->readc(tp);
	if(lcnt == 0xff)
		lcnt = readint2((BITS) tp);
	if(verbose)
		printf ("%d line styles\n", lcnt);
	for(n = 0 ; n < lcnt ; n++)
	{	alignbits(tp);
		morphlinestyle(tp);
	}
	for(n = 0 ; tp->datptr < endp ; n++)
	{	alignbits(tp);
		shaperecord(tp, bits_req(fcnt), bits_req(lcnt), 3);
	}
	for( ; n > 0 ; --n)
	{	alignbits(tp);
		shaperecord(tp, bits_req(fcnt), bits_req(lcnt), 3);
	}
}

definetext(TAG tp, int lev)
{	unsigned short textid;
	int nglyphbits, nadvancebits;
	int n, nglyphs, chcode, dx;
	int hasfont, hascolor, hasyoffset, hasxoffset;
	unsigned short font, height;
	signed short xoffs, yoffs;
			
	textid = change_id(tp);
	if(verbose) printf("text %d\n", textid);
	rect((BITS) tp);
	alignbits(tp);
	matrix((BITS) tp);
	nglyphbits = tp->readc(tp);
	nadvancebits = tp->readc(tp);
	alignbits(tp);
	while(1)
	{	if(!getbits((BITS) tp, 1))
		{	nglyphs = getbits((BITS) tp, 7);
			if(!nglyphs)
				break;
			if(verbose) printf("%d chars:\n", nglyphs);
			for(n = 0 ; n < nglyphs ; n++)
			{	chcode = getbits((BITS) tp, nglyphbits);
				dx = getsbits((BITS) tp, nadvancebits);
				if(verbose) printf("code %d dx %d\n", chcode, dx);
			}
		}
		else
		{	getbits((BITS) tp, 3);
			hasfont = getbits((BITS) tp, 1);
			hascolor = getbits((BITS) tp, 1);
			hasyoffset = getbits((BITS) tp, 1);
			hasxoffset = getbits((BITS) tp, 1);
			if(hasfont)
				font = change_id(tp);
			if(hascolor)
				if(lev == 2) rgba((BITS) tp); else rgb((BITS) tp);
			if(hasxoffset)
			{	xoffs = readint2((BITS) tp);
				if(verbose) printf("dx %d\n", xoffs);
			}
			if(hasyoffset)
			{	yoffs = readint2((BITS) tp);
				if(verbose) printf("dy %d\n", yoffs);
			}
			if(hasfont)
			{	height = readint2((BITS) tp);
				if(verbose) printf("font %d size %d\n", font, height);
			}
		}
		alignbits(tp);
	}
}

placeobject2(TAG tp)
{	int hasname, hasratio, hascxform, hasmatrix, haschar, hasmove, hasactions, hasmask;
	short depth, charid, flags, act;
	int base, len;
	char ch;
	hasactions = getbits((BITS)tp, 1);
	hasmask = getbits((BITS)tp, 1);
	hasname = getbits((BITS)tp, 1);
	hasratio = getbits((BITS)tp, 1);
	hascxform = getbits((BITS)tp, 1);
	hasmatrix = getbits((BITS)tp, 1);
	haschar = getbits((BITS)tp, 1);
	hasmove = getbits((BITS)tp, 1);
	depth = readint2((BITS) tp);
	if(haschar)
	{	charid = change_id(tp);
		if(verbose) printf("char %d depth %d\n", charid, depth);
	}
}

definebutton(TAG tp)
{	short butid, charid, layer;
	unsigned char bstate, act;
	long base;
	butid = change_id(tp);
	if(verbose)
		printf("char %d:\n", butid);
	while(bstate = tp->readc(tp))
	{	if(bstate & 8)
			if(verbose) printf("hit test ");
		if(bstate & 4)
			if(verbose) printf("down ");
		if(bstate & 2)
			if(verbose) printf("over ");
		if(bstate & 1)
			if(verbose) printf("up");
		if(verbose) printf("\n");
		charid = change_id(tp);
		layer = readint2((BITS) tp);
		if(verbose) printf("char %d layer %d\n", charid, layer);
		alignbits(tp);
		matrix((BITS) tp);
	}
}

cxform(TAG tp, int alpha)
{	int hasadd, hasmult, nbits;
	int ra, ga, ba, aa, rm, gm, bm, am;
	hasadd = getbits((BITS) tp, 1);
	hasmult = getbits((BITS) tp, 1);
	nbits = getbits((BITS) tp, 4);
	if(verbose) printf("cxform ");
	if(hasmult)
	{	rm = getsbits((BITS) tp, nbits);
		gm = getsbits((BITS) tp, nbits);
		bm = getsbits((BITS) tp, nbits);
		if(alpha) am  = getsbits((BITS) tp, nbits);
		if(verbose) printf("mul %d %d %d ", rm, gm, bm);
		if(verbose) if(alpha) printf("%d ", am);
	}
	if(hasadd)
	{	ra = getsbits((BITS) tp, nbits);
		ga = getsbits((BITS) tp, nbits);
		ba = getsbits((BITS) tp, nbits);
		if(alpha) aa = getsbits((BITS) tp, nbits);
		if(verbose) printf("add %d %d %d", ra, ga, ba);
		if(verbose) if(alpha) printf(" %d", am);
	}
	if(verbose) printf("\n");
}

definebutton2(TAG tp)
{	short id, charid, offs, layer;
	unsigned char ch, bstate;
	long base;
	id = change_id(tp);
	ch = tp->readc(tp);
	offs = readint2((BITS) tp);
	if(verbose)
		printf("id %d %s action offset %d\n", id, ch ? "menu" : "button", offs);
	while(bstate = tp->readc(tp))
	{	if(bstate & 8)
			if(verbose) printf("hit test ");
		if(bstate & 4)
			if(verbose) printf("down ");
		if(bstate & 2)
			if(verbose) printf("over ");
		if(bstate & 1)
			if(verbose) printf("up");
		if(verbose) printf("\n");
		charid = change_id(tp);
		layer = readint2((BITS) tp);
		if(verbose) printf("char %d layer %d\n", charid, layer);
		alignbits(tp);
		matrix((BITS) tp);
		alignbits(tp);
		cxform(tp, 1);
	}
}

definetextfield(TAG tp)
{	short textid, fontid, maxc, ht, lmarg, rmarg, indent, lspace, align;
	int haslength, noedit, password, multiline, wordwrap, drawbox, noselect, html, usefont;
	int hascolor, haslayout, hastext, hasfont;
	char ch;
	
	textid = change_id(tp);
	if(verbose) printf("textfield %d\n", textid);
	rect((BITS) tp);
	alignbits(tp);
	hastext = getbits((BITS) tp, 1);
	wordwrap = getbits((BITS) tp, 1);
	multiline = getbits((BITS) tp, 1);
	password = getbits((BITS) tp, 1);
	noedit = getbits((BITS) tp, 1);
	hascolor = getbits((BITS) tp, 1);
	haslength = getbits((BITS) tp, 1);
	hasfont = getbits((BITS) tp, 1);
	getbits((BITS) tp, 2);
	haslayout = getbits((BITS) tp, 1);
	noselect = getbits((BITS) tp, 1);
	drawbox = getbits((BITS) tp, 1);
	getbits((BITS) tp, 1);
	html = getbits((BITS) tp, 1);
	usefont = getbits((BITS) tp, 1);
	if(hasfont)
		fontid = change_id(tp);
	if(verbose)
	{	printf("font %d ", fontid);
		if(noedit) printf("noedit ");
		if(password) printf("password ");
		if(multiline) printf("multiline ");
		if(wordwrap) printf("wordwrap ");
		if(drawbox) printf("drawbox ");
		if(noselect) printf("noselect ");
		if(html) printf("html ");
		if(usefont) printf("usefont ");
		if(hascolor) rgba((BITS) tp);
		putchar('\n');
	}
}

exportassets(TAG tp)
{	short n, nobj, id;
	char ch;
	nobj = readint2((BITS) tp);
	for(n = 0 ; n < nobj ; n++)
	{	id = change_id(tp);
		if(verbose) printf("%d ", id);
		while(ch = tp->readc(tp))
			if(verbose) putchar(ch);
		if(verbose)
			if(n < nobj-1) putchar(' '); else putchar('\n');
	}
}

soundinfo(TAG tp)
{	char flags, hasenvelope, hasloops, hasoutpoint, hasinpoint, npoints;
	signed short loops, lev0, lev1;
	int soundpoint, n;
	flags = getbits((BITS) tp, 4);
	hasenvelope = getbits((BITS) tp, 1);
	hasloops = getbits((BITS) tp, 1);
	hasoutpoint = getbits((BITS) tp, 1);
	hasinpoint = getbits((BITS) tp, 1);
	if(flags & 1)
		if(verbose) printf("no multiple ");
	if(flags & 2)
		if(verbose) printf("stop");
	if(flags & 3)
		if(verbose) putchar('\n');
	if(hasinpoint)
	{	soundpoint = readint4((BITS) tp);
		if(verbose) printf("inpoint %d\n", soundpoint);
	}
	if(hasoutpoint)
	{	soundpoint = readint4((BITS) tp);
		if(verbose) printf("outpoint %d\n", soundpoint);
	}
	if(hasloops)
	{	loops = readint2((BITS) tp);
		if(verbose) printf("%d loops\n", loops);
	}
	if(hasenvelope)
	{	npoints = tp->readc(tp);
		for(n = 0 ; n < npoints ; n++)
		{	soundpoint = readint4((BITS) tp);
			lev0 = readint2((BITS) tp);
			lev1 = readint2((BITS) tp);
			if(verbose) printf("%d: %d %d\n", soundpoint, lev0, lev1);
		}
	}
}

definebuttonsound(TAG tp)
{	int id, n, snds[4];
	id = change_id(tp);
	if(verbose)
		printf("id %d\n", id);
	for(n = 0 ; n < 4 ; n++)
	{	snds[n] = change_id(tp);
		if(verbose)
			printf("sound %d:\n", snds[n]);
		alignbits(tp);
		if(snds[n]) soundinfo(tp);
	}
}

