+ 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)
+{
+ const char*start;
+ char*result;
+ while(**p && strchr(" \t\n\r", **p)) {
+ (*p)++;
+ }
+ start = *p;
+ while(**p && !strchr(" \t\n\r", **p)) {
+ (*p)++;
+ }
+ result = malloc((*p)-start+1);
+ memcpy(result,start,(*p)-start+1);
+ result[(*p)-start] = 0;
+ return result;
+}
+
+float parsePercent(char*str);
+RGBA parseColor(char*str);
+
+GRADIENT parseGradient(const char*str)
+{
+ GRADIENT gradient;
+ int lastpos = -1;
+ const char* p = str;
+ memset(&gradient, 0, sizeof(GRADIENT));
+ gradient.ratios = rfx_calloc(16*sizeof(U8));
+ gradient.rgba = rfx_calloc(16*sizeof(RGBA));
+
+ while(*p)
+ {
+ char*posstr,*colorstr;
+ int pos;
+ RGBA color;
+ posstr = gradient_getToken(&p);
+ if(!*posstr)
+ {
+ free(posstr);
+ break;
+ }
+ pos = (int)(parsePercent(posstr)*255.0);
+ if(pos == lastpos)
+ pos++;
+ if(!*p)
+ {
+ rfx_free(gradient.ratios);
+ rfx_free(gradient.rgba);
+ free(posstr);
+ syntaxerror("Error in shape data: Color expected after %s", posstr);
+ }
+ colorstr = gradient_getToken(&p);
+ color = parseColor(colorstr);
+ if(gradient.num == 16)
+ {
+ warning("gradient record too big- max size is 16, rest ignored");
+ break;
+ }
+ gradient.ratios[gradient.num] = pos;
+ gradient.rgba[gradient.num] = color;
+ gradient.num++;
+ free(posstr);
+ free(colorstr);
+ lastpos = pos;
+ }
+ return gradient;
+}
+
+FILTERLIST* parseFilters(char* list)
+{
+ if (!strcmp(list, "no_filters"))
+ return 0;
+ FILTER* f;
+ FILTERLIST* f_list = (FILTERLIST*)malloc(sizeof(FILTERLIST));
+ f_list->num = 0;
+ char* f_start = list;
+ char* f_end;
+ while (f_start)
+ {
+ f_end = strchr(f_start, ',');
+ if (f_end)
+ *f_end = '\0';
+ f = dictionary_lookup(&filters, f_start);
+ if (!f)
+ {
+ free(f_list);
+ syntaxerror("unknown filter %s", f_start);
+ }
+ if (f_list->num == 8)
+ {
+ warning("too many filters in filterlist, no more than 8 please, rest ignored");
+ break;
+ }
+ f_list->filter[f_list->num] = f;
+ f_list->num++;
+ if (f_end)
+ {
+ *f_end = ',';
+ f_start = f_end + 1;
+ }
+ else
+ f_start = 0;
+ }
+ return f_list;
+}
+
+void s_gradient(char*name, const char*text, int radial, int rotate)
+{
+ gradient_t* gradient;
+ gradient = malloc(sizeof(gradient_t));
+ memset(gradient, 0, sizeof(gradient_t));
+ gradient->gradient = parseGradient(text);
+ gradient->radial = radial;
+ gradient->rotate = rotate;
+
+ dictionary_put2(&gradients, name, gradient);
+}
+
+void s_gradientglow(char*name, char*gradient, float blurx, float blury,
+ float angle, float distance, float strength, char innershadow,
+ char knockout, char composite, char ontop, int passes)
+{
+ if(dictionary_lookup(&filters, name))
+ syntaxerror("filter %s defined twice", name);
+
+ gradient_t* g = dictionary_lookup(&gradients, gradient);
+ if(!g)
+ syntaxerror("unknown gradient %s", gradient);
+
+ composite = 1;
+
+ FILTER_GRADIENTGLOW* filter = rfx_calloc(sizeof(FILTER_GRADIENTGLOW));
+ filter->type = FILTERTYPE_GRADIENTGLOW;
+ filter->gradient = &g->gradient;
+ filter->blurx = blurx;
+ filter->blury = blury;
+ filter->strength = strength;
+ filter->angle = angle;
+ filter->distance = distance;
+ filter->innershadow = innershadow;
+ filter->knockout = knockout;
+ filter->composite = composite;
+ filter->ontop = ontop;
+ filter->passes = passes;
+
+ dictionary_put2(&filters, name, filter);
+}
+
+void s_dropshadow(char*name, RGBA color, double blurx, double blury, double angle, double distance, double strength, char innershadow, char knockout, char composite, int passes)
+{
+ if(dictionary_lookup(&filters, name))
+ syntaxerror("filter %s defined twice", name);
+
+ composite = 1;
+ FILTER_DROPSHADOW* filter = rfx_calloc(sizeof(FILTER_DROPSHADOW));
+ filter->type = FILTERTYPE_DROPSHADOW;
+ filter->color= color;
+ filter->blurx = blurx;
+ filter->blury = blury;
+ filter->strength = strength;
+ filter->angle = angle;
+ filter->distance = distance;
+ filter->innershadow = innershadow;
+ filter->knockout = knockout;
+ filter->composite = composite;
+ filter->passes = passes;
+
+ dictionary_put2(&filters, name, filter);
+}
+
+void s_bevel(char*name, RGBA shadow, RGBA highlight, double blurx, double blury, double angle, double distance, double strength, char innershadow, char knockout, char composite, char ontop, int passes)
+{
+ if(dictionary_lookup(&filters, name))
+ syntaxerror("filter %s defined twice", name);
+
+ composite = 1;
+ FILTER_BEVEL* filter = rfx_calloc(sizeof(FILTER_BEVEL));
+ filter->type = FILTERTYPE_BEVEL;
+ filter->shadow = shadow;
+ filter->highlight = highlight;
+ filter->blurx = blurx;
+ filter->blury = blury;
+ filter->strength = strength;
+ filter->angle = angle;
+ filter->distance = distance;
+ filter->innershadow = innershadow;
+ filter->knockout = knockout;
+ filter->composite = composite;
+ filter->ontop = ontop;
+ filter->passes = passes;
+
+ dictionary_put2(&filters, name, filter);
+}
+
+void s_blur(char*name, double blurx, double blury, int passes)
+{
+ if(dictionary_lookup(&filters, name))
+ syntaxerror("filter %s defined twice", name);