+int s_getframe()
+{
+ return currentframe+1;
+}
+
+void s_frame(int nr, int cut, char*name, char anchor)
+{
+ int t;
+ TAG*now = tag;
+
+ if(nr<1)
+ syntaxerror("Illegal frame number");
+ nr--; // internally, frame 1 is frame 0
+
+ for(t=currentframe;t<nr;t++) {
+ tag = swf_InsertTag(tag, ST_SHOWFRAME);
+ if(t==nr-1 && name && *name) {
+ tag = swf_InsertTag(tag, ST_FRAMELABEL);
+ swf_SetString(tag, name);
+ if(anchor)
+ swf_SetU8(tag, 1); //make this an anchor
+ }
+ }
+ if(nr == 0 && currentframe == 0 && name && *name) {
+ tag = swf_InsertTag(tag, ST_FRAMELABEL);
+ swf_SetString(tag, name);
+ if(anchor)
+ swf_SetU8(tag, 1); //make this an anchor
+ }
+
+ if(cut) {
+ if(now == tag) {
+ syntaxerror("Can't cut, frame empty");
+ }
+ stack[stackpos].cut = tag;
+ }
+
+ currentframe = nr;
+}
+
+int parseColor2(char*str, RGBA*color);
+
+int addFillStyle(SHAPE*s, SRECT*r, char*name)
+{
+ RGBA color;
+ character_t*image;
+ gradient_t*gradient;
+ texture_t*texture;
+ if(name[0] == '#') {
+ parseColor2(name, &color);
+ return swf_ShapeAddSolidFillStyle(s, &color);
+ } else if ((texture = dictionary_lookup(&textures, name))) {
+ return swf_ShapeAddFillStyle2(s, &texture->fs);
+ } else if((image = dictionary_lookup(&images, name))) {
+ MATRIX m;
+ swf_GetMatrix(0, &m);
+ m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
+ m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
+ m.tx = r->xmin;
+ m.ty = r->ymin;
+ return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
+ } else if ((gradient = dictionary_lookup(&gradients, name))) {
+ SRECT r2;
+ MATRIX rot,m;
+ double ccos,csin;
+ swf_GetMatrix(0, &rot);
+ ccos = cos(-gradient->rotate*2*PI/360);
+ csin = sin(-gradient->rotate*2*PI/360);
+ rot.sx = ccos*65536;
+ rot.r1 = -csin*65536;
+ rot.r0 = csin*65536;
+ rot.sy = ccos*65536;
+ r2 = swf_TurnRect(*r, &rot);
+ swf_GetMatrix(0, &m);
+ m.sx = (r2.xmax - r2.xmin)*2*ccos;
+ m.r1 = -(r2.xmax - r2.xmin)*2*csin;
+ m.r0 = (r2.ymax - r2.ymin)*2*csin;
+ m.sy = (r2.ymax - r2.ymin)*2*ccos;
+ m.tx = r->xmin + (r->xmax - r->xmin)/2;
+ m.ty = r->ymin + (r->ymax - r->ymin)/2;
+ return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
+ } else if (parseColor2(name, &color)) {
+ return swf_ShapeAddSolidFillStyle(s, &color);
+ } else {
+ syntaxerror("not a color/fillstyle: %s", name);
+ return 0;
+ }
+}
+
+RGBA black={r:0,g:0,b:0,a:0};
+void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
+{
+ SRECT r,r2;
+ SHAPE* s;
+ int ls1=0,fs1=0;
+ r2.xmin = 0;
+ r2.ymin = 0;
+ r2.xmax = width;
+ r2.ymax = height;
+ tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
+ swf_ShapeNew(&s);
+ if(linewidth) {
+ linewidth = linewidth>=20?linewidth-20:0;
+ ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+ }
+ if(texture)
+ fs1 = addFillStyle(s, &r2, texture);
+
+ swf_SetU16(tag,id);
+ r.xmin = r2.xmin-linewidth/2;
+ r.ymin = r2.ymin-linewidth/2;
+ r.xmax = r2.xmax+linewidth/2;
+ r.ymax = r2.ymax+linewidth/2;
+ swf_SetRect(tag,&r);
+ swf_SetShapeHeader(tag,s);
+ swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
+ swf_ShapeSetLine(tag,s,width,0);
+ swf_ShapeSetLine(tag,s,0,height);
+ swf_ShapeSetLine(tag,s,-width,0);
+ swf_ShapeSetLine(tag,s,0,-height);
+ swf_ShapeSetEnd(tag);
+ swf_ShapeFree(s);
+
+ s_addcharacter(name, id, tag, r);
+ incrementid();
+}
+
+void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
+{
+ SRECT rect,r2;
+ SHAPE* s;
+ int ls1,fs1=0;
+ outline_t* outline;
+ outline = dictionary_lookup(&outlines, outlinename);
+ if(!outline) {
+ syntaxerror("outline %s not defined", outlinename);
+ }
+ r2 = outline->bbox;
+
+ tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
+ swf_ShapeNew(&s);
+ if(linewidth) {
+ linewidth = linewidth>=20?linewidth-20:0;
+ ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+ }
+ if(texture)
+ fs1 = addFillStyle(s, &r2, texture);
+
+ swf_SetU16(tag,id);
+ rect.xmin = r2.xmin-linewidth/2;
+ rect.ymin = r2.ymin-linewidth/2;
+ rect.xmax = r2.xmax+linewidth/2;
+ rect.ymax = r2.ymax+linewidth/2;
+
+ swf_SetRect(tag,&rect);
+ swf_SetShapeStyles(tag, s);
+ swf_ShapeCountBits(s,0,0);
+ swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line,
+ &s->data, &s->bitlen, s->bits.fill, s->bits.line);
+ swf_SetShapeBits(tag, s);
+ swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
+ swf_ShapeFree(s);
+
+ s_addcharacter(name, id, tag, rect);
+ incrementid();
+}
+
+void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
+{
+ SRECT rect,r2;
+ SHAPE* s;
+ int ls1=0,fs1=0;
+ r2.xmin = r2.ymin = 0;
+ r2.xmax = 2*r;
+ r2.ymax = 2*r;
+
+ tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
+ swf_ShapeNew(&s);
+ if(linewidth) {
+ linewidth = linewidth>=20?linewidth-20:0;
+ ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+ }
+ if(texture)
+ fs1 = addFillStyle(s, &r2, texture);
+ swf_SetU16(tag,id);
+ rect.xmin = r2.xmin-linewidth/2;
+ rect.ymin = r2.ymin-linewidth/2;
+ rect.xmax = r2.xmax+linewidth/2;
+ rect.ymax = r2.ymax+linewidth/2;
+
+ swf_SetRect(tag,&rect);
+ swf_SetShapeHeader(tag,s);
+ swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
+ swf_ShapeSetCircle(tag, s, r,r,r,r);
+ swf_ShapeSetEnd(tag);
+ swf_ShapeFree(s);
+
+ s_addcharacter(name, id, tag, rect);
+ incrementid();
+}
+
+void s_textshape(char*name, char*fontname, float size, char*_text)
+{
+ int g;
+ U8*text = (U8*)_text;
+ outline_t* outline;
+
+ SWFFONT*font;
+ font = dictionary_lookup(&fonts, fontname);
+ if(!font)
+ syntaxerror("font \"%s\" not known!", fontname);
+
+ if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
+ warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
+ s_box(name, 0, 0, black, 20, 0);
+ return;
+ }
+ g = font->ascii2glyph[text[0]];
+
+ outline = malloc(sizeof(outline_t));
+ memset(outline, 0, sizeof(outline_t));
+ outline->shape = font->glyph[g].shape;
+ outline->bbox = font->layout->bounds[g];
+
+ {
+ drawer_t draw;
+ swf_Shape11DrawerInit(&draw, 0);
+ swf_DrawText(&draw, font, (int)(size*100), _text);
+ draw.finish(&draw);
+ outline->shape = swf_ShapeDrawerToShape(&draw);
+ outline->bbox = swf_ShapeDrawerGetBBox(&draw);
+ draw.dealloc(&draw);
+ }
+
+ if(dictionary_lookup(&outlines, name))
+ syntaxerror("outline %s defined twice", name);
+ dictionary_put2(&outlines, name, outline);
+}
+
+void s_text(char*name, char*fontname, char*text, int size, RGBA color)
+{
+ SRECT r;
+ MATRIX _m,*m=0;
+ SWFFONT*font;
+ font = dictionary_lookup(&fonts, fontname);
+ if(!font)
+ syntaxerror("font \"%s\" not known!", fontname);
+
+ tag = swf_InsertTag(tag, ST_DEFINETEXT2);
+ swf_SetU16(tag, id);
+ if(!font->numchars) {
+ s_box(name, 0, 0, black, 20, 0);
+ return;
+ }
+ r = swf_SetDefineText(tag, font, &color, text, size);
+
+ if(stack[0].swf->fileVersion >= 8) {
+ tag = swf_InsertTag(tag, ST_CSMTEXTSETTINGS);
+ swf_SetU16(tag, id);
+ swf_SetU8(tag, /*grid*/(1<<3)|/*flashtype*/0x40);
+ swf_SetU32(tag, 0);//thickness
+ swf_SetU32(tag, 0);//sharpness
+ swf_SetU8(tag, 0);//reserved
+ }
+
+ s_addcharacter(name, id, tag, r);
+ incrementid();
+}
+
+void s_quicktime(char*name, char*url)
+{
+ SRECT r;
+ MATRIX _m,*m=0;
+
+ memset(&r, 0, sizeof(r));
+
+ tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
+ swf_SetU16(tag, id);
+ swf_SetString(tag, url);
+
+ s_addcharacter(name, id, tag, r);
+ incrementid();
+}
+
+void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags, int align)
+{
+ SWFFONT*font = 0;
+ EditTextLayout layout;
+ SRECT r;
+
+ if(fontname && *fontname) {
+ flags |= ET_USEOUTLINES;
+ font = dictionary_lookup(&fonts, fontname);
+ if(!font)
+ syntaxerror("font \"%s\" not known!", fontname);
+ }
+ tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
+ swf_SetU16(tag, id);
+ layout.align = align;
+ layout.leftmargin = 0;
+ layout.rightmargin = 0;
+ layout.indent = 0;
+ layout.leading = 0;
+ r.xmin = 0;
+ r.ymin = 0;
+ r.xmax = width;
+ r.ymax = height;
+
+ swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
+
+ s_addcharacter(name, id, tag, r);
+ incrementid();
+}
+
+/* type: either "jpeg" or "png"
+ */
+void s_image(char*name, char*type, char*filename, int quality)
+{
+ /* an image is actually two folded: 1st bitmap, 2nd character.
+ Both of them can be used separately */
+
+ /* step 1: the bitmap */
+ SRECT r;
+ int imageID = id;
+ int width, height;
+ if(!strcmp(type,"jpeg")) {
+#ifndef HAVE_JPEGLIB
+ warning("no jpeg support compiled in");
+ s_box(name, 0, 0, black, 20, 0);
+ return;
+#else
+ tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
+ swf_SetU16(tag, imageID);
+
+ if(swf_SetJPEGBits(tag, filename, quality) < 0) {
+ syntaxerror("Image \"%s\" not found, or contains errors", filename);
+ }
+
+ swf_GetJPEGSize(filename, &width, &height);
+
+ r.xmin = 0;
+ r.ymin = 0;
+ r.xmax = width*20;
+ r.ymax = height*20;
+
+ s_addimage(name, id, tag, r);
+ incrementid();
+#endif
+ } else if(!strcmp(type,"png")) {
+ RGBA*data = 0;
+ swf_SetU16(tag, imageID);
+
+ getPNG(filename, &width, &height, (unsigned char**)&data);
+
+ if(!data) {
+ syntaxerror("Image \"%s\" not found, or contains errors", filename);
+ }
+
+ /*tag = swf_AddImage(tag, imageID, data, width, height, quality)*/
+ tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS);
+ swf_SetU16(tag, imageID);
+ swf_SetLosslessImage(tag, data, width, height);
+ free(data);
+
+ r.xmin = 0;
+ r.ymin = 0;
+ r.xmax = width*20;
+ r.ymax = height*20;
+ s_addimage(name, id, tag, r);
+ incrementid();
+ } else {
+ warning("image type \"%s\" not supported yet!", type);
+ s_box(name, 0, 0, black, 20, 0);
+ return;
+ }
+
+ /* step 2: the character */
+ tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
+ swf_SetU16(tag, id);
+ swf_ShapeSetBitmapRect(tag, imageID, width, height);
+
+ s_addcharacter(name, id, tag, r);
+ incrementid();
+}
+
+void s_getBitmapSize(char*name, int*width, int*height)
+{
+ character_t* image = dictionary_lookup(&images, name);
+ gradient_t* gradient = dictionary_lookup(&gradients,name);
+ if(image) {
+ *width = image->size.xmax;
+ *height = image->size.ymax;
+ return;
+ }
+ if(gradient) {
+ /* internal SWF gradient size */
+ if(gradient->radial) {
+ *width = 16384;
+ *height = 16384;
+ } else {
+ *width = 32768;
+ *height = 32768;
+ }
+ return;
+ }
+ syntaxerror("No such bitmap/gradient: %s", name);
+}
+
+void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
+{
+ if(dictionary_lookup(&textures, name))
+ syntaxerror("texture %s defined twice", name);
+ gradient_t* gradient = dictionary_lookup(&gradients, object);
+ character_t* bitmap = dictionary_lookup(&images, object);
+ texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
+ parameters_t p;
+ FILLSTYLE*fs = &texture->fs;
+
+ memset(&p, 0, sizeof(parameters_t));
+
+ if(bitmap) {
+ fs->type = FILL_TILED;
+ fs->id_bitmap = bitmap->id;
+ } else if(gradient) {
+ fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
+ fs->gradient = gradient->gradient;
+ }
+ p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
+ makeMatrix(&fs->m, &p);
+ if(gradient && !gradient->radial) {
+ MATRIX m = fs->m;
+ SPOINT p1,p2;
+ m.tx = 0;
+ m.ty = 0;
+ p1.x = 16384;
+ p1.y = 16384;
+ p2 = swf_TurnPoint(p1, &m);
+ fs->m.tx += p2.x;
+ fs->m.ty += p2.y;
+ }
+ if(bitmap) {
+ fs->m.sx *= 20;
+ fs->m.sy *= 20;
+ }
+
+ dictionary_put2(&textures, name, texture);
+}
+
+void s_font(char*name, char*filename)
+{
+ SWFFONT* font;
+ font = dictionary_lookup(&fonts, name);
+ if(0)
+ {
+ /* fix the layout. Only needed for old fonts */
+ int t;
+ for(t=0;t<font->numchars;t++) {
+ font->glyph[t].advance = 0;
+ }
+ font->layout = 0;
+ swf_FontCreateLayout(font);
+ }
+ font->id = id;
+ swf_FontReduce_swfc(font);
+ tag = swf_InsertTag(tag, ST_DEFINEFONT2);
+ swf_FontSetDefine2(tag, font);
+ tag = swf_InsertTag(tag, ST_EXPORTASSETS);
+ swf_SetU16(tag, 1);
+ swf_SetU16(tag, id);
+ swf_SetString(tag, name);
+
+ incrementid();
+}
+
+
+
+typedef struct _sound_t
+{
+ U16 id;
+ TAG*tag;
+} sound_t;
+
+void s_sound(char*name, char*filename)
+{
+ struct WAV wav, wav2;
+ struct MP3 mp3;
+ sound_t* sound;
+ U16*samples = NULL;
+ unsigned numsamples = 1;
+ unsigned blocksize = 1152;
+ int is_mp3 = 0;
+
+ if(dictionary_lookup(&sounds, name))
+ syntaxerror("sound %s defined twice", name);
+
+ if(wav_read(&wav, filename))
+ {
+ int t;
+ wav_convert2mono(&wav, &wav2, 44100);
+ samples = (U16*)wav2.data;
+ numsamples = wav2.size/2;
+ free(wav.data);
+#ifdef WORDS_BIGENDIAN
+ /* swap bytes */
+ for(t=0;t<numsamples;t++)
+ samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
+#endif
+ }
+ else
+ if(mp3_read(&mp3, filename))
+ {
+ fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
+ blocksize = 1;
+ is_mp3 = 1;
+ }
+ else
+ {
+ warning("Couldn't read WAV/MP3 file \"%s\"", filename);
+ samples = 0;
+ numsamples = 0;
+ }
+
+ if(numsamples%blocksize != 0)
+ {
+ // apply padding, so that block is a multiple of blocksize
+ int numblocks = (numsamples+blocksize-1)/blocksize;
+ int numsamples2;
+ U16* samples2;
+ numsamples2 = numblocks * blocksize;
+ samples2 = malloc(sizeof(U16)*numsamples2);
+ memcpy(samples2, samples, numsamples*sizeof(U16));
+ memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
+ numsamples = numsamples2;
+ free(samples);
+ samples = samples2;
+ }
+
+ tag = swf_InsertTag(tag, ST_DEFINESOUND);
+ swf_SetU16(tag, id); //id
+ if(is_mp3)
+ {
+ swf_SetSoundDefineMP3(
+ tag, mp3.data, mp3.size,
+ mp3.SampRate,
+ mp3.Channels,
+ mp3.NumFrames);
+ mp3_clear(&mp3);
+ }
+ else
+ swf_SetSoundDefine(tag, samples, numsamples);
+
+ tag = swf_InsertTag(tag, ST_NAMECHARACTER);
+ swf_SetU16(tag, id);
+ swf_SetString(tag, name);
+ tag = swf_InsertTag(tag, ST_EXPORTASSETS);
+ swf_SetU16(tag, 1);
+ swf_SetU16(tag, id);
+ swf_SetString(tag, name);
+
+ sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
+ sound->tag = tag;
+ sound->id = id;
+
+ dictionary_put2(&sounds, name, sound);
+
+ incrementid();
+
+ if (samples)
+ free(samples);
+}
+
+static char* gradient_getToken(const char**p)