applied patches from Huub Schaeks
[swftools.git] / src / swfc.c
index cb7aaff..f4d55da 100644 (file)
 #include "../lib/log.h"
 #include "../lib/args.h"
 #include "../lib/q.h"
+#include "../lib/mp3.h"
+#include "../lib/wav.h"
 #include "parser.h"
-#include "wav.h"
+#include "../lib/png.h"
+#include "../lib/interpolation.h"
+#include "../lib/history.h"
 
 //#define DEBUG
-
-static char * filename = 0;
 static char * outputname = "output.swf";
 static int verbose = 2;
+static int optimize = 0;
 static int override_outputname = 0;
+static int do_cgi = 0;
 
 static struct options_t options[] = {
 {"h", "help"},
 {"V", "version"},
+{"C", "cgi"},
 {"v", "verbose"},
 {"o", "output"},
 {0,0}
@@ -61,6 +66,14 @@ int args_callback_option(char*name,char*val)
        override_outputname = 1;
        return 1;
     }
+    else if(!strcmp(name, "O")) {
+       optimize = 1;
+       return 0;
+    }
+    else if(!strcmp(name, "C")) {
+       do_cgi = 1;
+       return 0;
+    }
     else if(!strcmp(name, "v")) {
        verbose ++;
        return 0;
@@ -82,6 +95,7 @@ void args_callback_usage(char *name)
     printf("\n");
     printf("-h , --help                    Print short help message and exit\n");
     printf("-V , --version                 Print version info and exit\n");
+    printf("-C , --cgi                     Output to stdout (for use in CGI environments)\n");
     printf("-v , --verbose                 Increase verbosity. \n");
     printf("-o , --output <filename>       Set output file to <filename>.\n");
     printf("\n");
@@ -102,29 +116,6 @@ static int pos;
 static char*text;
 static int textlen;
 static int type;
-static int line;
-static int column;
-
-static void syntaxerror(char*format, ...)
-{
-    char buf[1024];
-    va_list arglist;
-    va_start(arglist, format);
-    vsprintf(buf, format, arglist);
-    va_end(arglist);
-    printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
-    exit(1);
-}
-
-static void warning(char*format, ...)
-{
-    char buf[1024];
-    va_list arglist;
-    va_start(arglist, format);
-    vsprintf(buf, format, arglist);
-    va_end(arglist);
-    printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
-}
 
 static void readToken()
 {
@@ -186,8 +177,11 @@ static int stackpos = 0;
 
 static dictionary_t characters;
 static dictionary_t images;
+static dictionary_t textures;
 static dictionary_t outlines;
 static dictionary_t gradients;
+static dictionary_t filters;
+static dictionary_t interpolations;
 static char idmap[65536];
 static TAG*tag = 0; //current tag
 
@@ -198,6 +192,7 @@ static U16 currentdepth;
 static dictionary_t instances;
 static dictionary_t fonts;
 static dictionary_t sounds;
+static dictionary_t fontUsage;
 
 typedef struct _parameters {
     int x,y; 
@@ -207,6 +202,9 @@ typedef struct _parameters {
     float shear;
     SPOINT pivot;
     SPOINT pin;
+    U8 blendmode; //not interpolated
+    FILTER*filter;
+    U16 set; // bits indicating wether a parameter was set in the c_placement function
 } parameters_t;
 
 typedef struct _character {
@@ -221,6 +219,7 @@ typedef struct _instance {
     parameters_t parameters;
     TAG* lastTag; //last tag which set the object
     U16 lastFrame; //frame lastTag is in
+    history_t* history;
 } instance_t;
 
 typedef struct _outline {
@@ -231,12 +230,60 @@ typedef struct _outline {
 typedef struct _gradient {
     GRADIENT gradient;
     char radial;
+    int rotate;
 } gradient_t;
 
+typedef struct _filter {
+    FILTER filter;
+} filter_t;
+
+typedef struct _texture {
+    FILLSTYLE fs;
+} texture_t;
+
+char* interpolationFunctions[] = {"linear", \
+        "quadIn", "quadOut", "quadInOut", \
+        "cubicIn", "cubicOut", "cubicInOut", \
+        "quartIn", "quartOut", "quartInOut", \
+        "quintIn", "quintOut", "quintInOut", \
+        "circleIn", "circleOut", "circleInOut", \
+        "exponentialIn", "exponentialOut", "exponentialInOut", \
+        "sineIn", "sineOut", "sineInOut", \
+        "elasticIn", "elasticOut", "elasticInOut", \
+        "backIn", "backOut", "backInOut", \
+        "bounceIn", "bounceOut", "bounceInOut", \
+        "fastBounceIn", "fastBounceOut", "fastBounceInOut"};
+
+typedef struct _fontData {
+    char *glyphs;
+    int notUsed, needsAll;
+} fontData;
+
+void addFontData(char *name)
+{
+    fontData* newFont;
+    newFont = (fontData *)malloc(sizeof(fontData));
+    memset(newFont, 0, sizeof(fontData));
+    newFont->notUsed = 1;
+    dictionary_put2(&fontUsage, name, newFont);
+}
+
+void freeFontData(fontData* font)
+{
+    free(font->glyphs);
+    free(font);
+}
+
+fontData *getFontData(char *name)
+{
+    return (fontData *)dictionary_lookup(&fontUsage, name);
+}
+
 static void character_init(character_t*c)
 {
     memset(c, 0, sizeof(character_t));
 }
+
 static character_t* character_new()
 {
     character_t*c;
@@ -244,10 +291,19 @@ static character_t* character_new()
     character_init(c);
     return c;
 }
+
 static void instance_init(instance_t*i)
 {
     memset(i, 0, sizeof(instance_t));
+    i->history = history_new();
 }
+
+static void instance_free(instance_t* i)
+{
+    history_free(i->history);
+    free(i);
+}
+
 static instance_t* instance_new()
 {
     instance_t*c;
@@ -256,6 +312,60 @@ static instance_t* instance_new()
     return c;
 }
 
+static void free_instance(void* i)
+{
+    instance_free((instance_t*)i);
+}
+
+static void free_font(void* f)
+{
+    swf_FontFree((SWFFONT*)f);
+}
+
+static void free_fontData(void* fd)
+{
+    freeFontData((fontData*)fd);
+}
+
+static void gradient_free(GRADIENT* grad)
+{
+    free(grad->ratios);
+    free(grad->rgba);
+    free(grad);
+}
+
+static void free_gradient(void* grad)
+{
+    gradient_free((GRADIENT*) grad);
+}
+
+static void outline_free(outline_t* o)
+{
+    free(o->shape->data);
+    free(o->shape);
+    free(o);
+}
+
+static void free_outline(void* o)
+{
+    outline_free((outline_t*)o);
+}
+
+static void freeDictionaries()
+{
+   dictionary_free_all(&instances, free_instance);
+   dictionary_free_all(&characters, free);
+    dictionary_free_all(&images, free);
+    dictionary_free_all(&textures, free);
+    dictionary_free_all(&outlines, free_outline);
+    dictionary_free_all(&gradients, free_gradient);
+    dictionary_free_all(&filters, free);
+    dictionary_free_all(&fonts, free_font);
+    dictionary_free_all(&sounds, free);
+    dictionary_free_all(&fontUsage, free_fontData);
+    dictionary_free_all(&interpolations, free);
+}
+
 static void incrementid()
 {
     while(idmap[++id]) {
@@ -267,13 +377,13 @@ static void incrementid()
 
 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
 {
+    if(dictionary_lookup(&characters, name))
+        syntaxerror("character %s defined twice", name);
     character_t* c = character_new();
     
     c->definingTag = ctag;
     c->id = id;
     c->size = r;
-    if(dictionary_lookup(&characters, name))
-       syntaxerror("character %s defined twice", name);
     dictionary_put2(&characters, name, c);
 
     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
@@ -286,36 +396,27 @@ static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
 }
 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
 {
+    if(dictionary_lookup(&images, name))
+        syntaxerror("image %s defined twice", name);
+        
     character_t* c = character_new();
     c->definingTag = ctag;
     c->id = id;
     c->size = r;
-
-    if(dictionary_lookup(&images, name))
-       syntaxerror("image %s defined twice", name);
     dictionary_put2(&images, name, c);
 }
 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
 {
+    if(dictionary_lookup(&instances, name))
+        syntaxerror("object %s defined twice", name);
     instance_t* i = instance_new();
     i->character = c;
     i->depth = depth;
     //swf_GetMatrix(0, &i->matrix);
-    if(dictionary_lookup(&instances, name))
-       syntaxerror("object %s defined twice", name);
     dictionary_put2(&instances, name, i);
     return i;
 }
 
-static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, float shear, SPOINT pivot, SPOINT pin, CXFORM cxform)
-{
-    p->x = x; p->y = y; 
-    p->scalex = scalex; p->scaley = scaley;
-    p->pin    = pin; p->pivot = pivot;
-    p->rotate = rotate; p->cxform = cxform;
-    p->shear = shear;
-}
-
 static void parameters_clear(parameters_t*p)
 {
     p->x = 0; p->y = 0; 
@@ -325,6 +426,8 @@ static void parameters_clear(parameters_t*p)
     p->pivot.x = 0; p->pivot.y = 0;
     p->rotate = 0; 
     p->shear = 0; 
+    p->blendmode = 0;
+    p->filter = 0;
     swf_GetCXForm(0, &p->cxform, 1);
 }
 
@@ -337,10 +440,10 @@ static void makeMatrix(MATRIX*m, parameters_t*p)
      *       \r0 sy/ \y/
      */
 
-    sx =  p->scalex*cos(p->rotate/360*2*3.14159265358979);
-    r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
-    r0 =  p->scaley*sin(p->rotate/360*2*3.14159265358979);
-    sy =  p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
+    sx =  p->scalex*cos(p->rotate/360*2*PI);
+    r1 = -p->scalex*sin(p->rotate/360*2*PI)+sx*p->shear;
+    r0 =  p->scaley*sin(p->rotate/360*2*PI);
+    sy =  p->scaley*cos(p->rotate/360*2*PI)+r0*p->shear;
 
     m->sx = (int)(sx*65536+0.5);
     m->r1 = (int)(r1*65536+0.5);
@@ -368,12 +471,92 @@ static MATRIX s_instancepos(SRECT rect, parameters_t*p)
     return m;
 }
 
+void builtInInterpolations()
+{
+    interpolation_t* new;
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_LINEAR;
+    dictionary_put2(&interpolations, "linear", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_IN;
+    dictionary_put2(&interpolations, "quadIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_OUT;
+    dictionary_put2(&interpolations, "quadOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_IN_OUT;
+    dictionary_put2(&interpolations, "quadInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_IN;
+    dictionary_put2(&interpolations, "cubicIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_OUT;
+    dictionary_put2(&interpolations, "cubicOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_IN_OUT;
+    dictionary_put2(&interpolations, "cubicInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_IN;
+    dictionary_put2(&interpolations, "quartIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_OUT;
+    dictionary_put2(&interpolations, "quartOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_IN_OUT;
+    dictionary_put2(&interpolations, "quartInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_IN;
+    dictionary_put2(&interpolations, "quintIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_OUT;
+    dictionary_put2(&interpolations, "quintOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_IN_OUT;
+    dictionary_put2(&interpolations, "quintInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_IN;
+    dictionary_put2(&interpolations, "circleIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_OUT;
+    dictionary_put2(&interpolations, "circleOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_IN_OUT;
+    dictionary_put2(&interpolations, "circleInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_IN;
+    dictionary_put2(&interpolations, "exponentialIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_OUT;
+    dictionary_put2(&interpolations, "exponentialOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_IN_OUT;
+    dictionary_put2(&interpolations, "exponentialInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_IN;
+    dictionary_put2(&interpolations, "sineIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_OUT;
+    dictionary_put2(&interpolations, "sineOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_IN_OUT;
+    dictionary_put2(&interpolations, "sineInOut", new);
+}
+
 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
 {
-    SWF*swf = (SWF*)malloc(sizeof(SWF));
-
     if(stackpos)
-       syntaxerror(".swf blocks can't be nested");
+        syntaxerror(".swf blocks can't be nested");
+    if(stackpos==sizeof(stack)/sizeof(stack[0]))
+        syntaxerror("too many levels of recursion");
+        
+    SWF*swf = (SWF*)malloc(sizeof(SWF));
 
     memset(swf, 0, sizeof(swf));
     swf->fileVersion = version;
@@ -383,16 +566,18 @@ void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA backgrou
     swf->compressed = compress;
     swf_SetRGB(tag,&background);
     
-    if(stackpos==sizeof(stack)/sizeof(stack[0]))
-       syntaxerror("too many levels of recursion");
-    
     dictionary_init(&characters);
     dictionary_init(&images);
+    dictionary_init(&textures);
     dictionary_init(&outlines);
     dictionary_init(&gradients);
+    dictionary_init(&filters);
     dictionary_init(&instances);
     dictionary_init(&fonts);
     dictionary_init(&sounds);
+    dictionary_init(&interpolations);
+    builtInInterpolations();
+    cleanUp = &freeDictionaries;
 
     memset(&stack[stackpos], 0, sizeof(stack[0]));
     stack[stackpos].type = 0;
@@ -593,6 +778,89 @@ TAG* removeFromTo(TAG*from, TAG*to)
     return save;
 }
 
+static void readParameters(history_t* history, parameters_t* p, int frame)
+{
+    p->x = history_value(history, frame, "x");
+    p->y = history_value(history, frame, "y");
+    p->scalex = history_value(history, frame, "scalex");
+    p->scaley = history_value(history, frame, "scaley");
+    p->cxform.r0 = history_value(history, frame, "cxform.r0");
+    p->cxform.g0 = history_value(history, frame, "cxform.g0");
+    p->cxform.b0 = history_value(history, frame, "cxform.b0");
+    p->cxform.a0 = history_value(history, frame, "cxform.a0");
+    p->cxform.r1 = history_value(history, frame, "cxform.r1");
+    p->cxform.g1 = history_value(history, frame, "cxform.g1");
+    p->cxform.b1 = history_value(history, frame, "cxform.b1");
+    p->cxform.a1 = history_value(history, frame, "cxform.a1");
+    p->rotate = history_value(history, frame, "rotate");
+    p->shear = history_value(history, frame, "shear");
+    p->pivot.x = history_value(history, frame, "pivot.x");
+    p->pivot.y = history_value(history, frame, "pivot.y");
+    p->pin.x = history_value(history, frame, "pin.x");
+    p->pin.y = history_value(history, frame, "pin.y");
+    p->blendmode = history_value(history, frame, "blendmode");
+    p->filter = history_valueFilter(history, frame);
+}
+
+void setPlacement(TAG*tag, U16 id, U16 depth, MATRIX m, char*name, parameters_t*p, char move)
+{
+    SWFPLACEOBJECT po;
+    FILTERLIST flist;
+    swf_GetPlaceObject(NULL, &po);
+    po.id = id;
+    po.depth = depth;
+    po.matrix = m;
+    po.cxform = p->cxform;
+    po.name = name;
+    po.move = move;
+    if(move)
+    po.id = 0;
+    if(p->blendmode) {
+    po.blendmode = p->blendmode;
+    }
+    if(p->filter) {
+    flist.num = 1;
+    flist.filter[0] = p->filter;
+    po.filters = &flist;
+    }
+    swf_SetPlaceObject(tag, &po);
+}
+
+static void writeInstance(instance_t* i)
+{
+    parameters_t p;
+    MATRIX m;
+    int frame = i->history->firstFrame;
+    TAG* tag = i->history->firstTag;
+    while (frame < currentframe)
+    {
+        frame++;
+        readParameters(i->history, &p, frame);
+        while (tag->id != ST_SHOWFRAME)
+            tag = tag->next;
+        m = s_instancepos(i->character->size, &p);
+
+        if(p.blendmode || p.filter)
+            tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
+        else
+            tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+        setPlacement(tag, 0, i->depth, m, 0, &p, 1);
+        if (p.filter)
+            free(p.filter);        
+    }
+}
+
+void dumpSWF(SWF*swf)
+{
+    TAG* tag = swf->firstTag;
+    printf("vvvvvvvvvvvvvvvvvvvvv\n");
+    while(tag) {
+    printf("%8d %s\n", tag->len, swf_TagGetName(tag));
+    tag = tag->next;
+    }
+    printf("^^^^^^^^^^^^^^^^^^^^^\n");
+}
+
 static void s_endSprite()
 {
     SRECT r = currentrect;
@@ -601,23 +869,34 @@ static void s_endSprite()
        tag = removeFromTo(stack[stackpos].cut, tag);
 
     stackpos--;
-   
+    instance_t *i;
+    stringarray_t* index =dictionary_index(&instances);
+    int num = 0;
+    char* name = stringarray_at(index, num);
+    while (name)
+    {
+        i = dictionary_lookup(&instances, name);
+        writeInstance(i);
+        num++;
+        name = stringarray_at(index, num);
+    }
+    
+    tag = swf_InsertTag(tag, ST_SHOWFRAME);
+    tag = swf_InsertTag(tag, ST_END);
+
+    tag = stack[stackpos].tag;
+    swf_FoldSprite(tag);
+    if(tag->next != 0)
+        syntaxerror("internal error(7)");
     /* TODO: before clearing, prepend "<spritename>." to names and
              copy into old instances dict */
-    dictionary_clear(&instances);
+   dictionary_free_all(&instances, free_instance);
 
     currentframe = stack[stackpos].oldframe;
     currentrect = stack[stackpos].oldrect;
     currentdepth = stack[stackpos].olddepth;
     instances = stack[stackpos].oldinstances;
 
-    tag = swf_InsertTag(tag, ST_END);
-
-    tag = stack[stackpos].tag;
-    swf_FoldSprite(tag);
-    if(tag->next != 0)
-       syntaxerror("internal error(7)");
-
     s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
     free(stack[stackpos].name);
 }
@@ -628,20 +907,38 @@ static void s_endSWF()
     SWF* swf;
     char*filename;
     
+    instance_t *i;
+    stringarray_t* index =dictionary_index(&instances);
+    int num = 0;
+    char* name = stringarray_at(index, num);
+    while (name)
+    {
+        i = dictionary_lookup(&instances, name);
+        writeInstance(i);
+        num++;
+        name = stringarray_at(index, num);
+    }
+
     if(stack[stackpos].cut)
        tag = removeFromTo(stack[stackpos].cut, tag);
 
     stackpos--;
-
+   
     swf = stack[stackpos].swf;
     filename = stack[stackpos].filename;
-   
-    //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
+  
+    //if(tag->prev && tag->prev->id != ST_SHOWFRAME)
+    //    tag = swf_InsertTag(tag, ST_SHOWFRAME);
+    tag = swf_InsertTag(tag, ST_SHOWFRAME);
 
     tag = swf_InsertTag(tag, ST_END);
 
     swf_OptimizeTagOrder(swf);
-
+   
+    if(optimize) {
+       swf_Optimize(swf);
+    }
+    
     if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
        swf->movieSize = currentrect; /* "autocrop" */
     }
@@ -649,26 +946,26 @@ static void s_endSWF()
     if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
        swf->movieSize.xmax += 20; /* 1 by 1 pixels */
        swf->movieSize.ymax += 20;
+       warning("Empty bounding box for movie");
     }
-
-    fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
+    
+    if(do_cgi || !strcmp(filename, "-"))
+       fi = fileno(stdout);
+    else
+       fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
     if(fi<0) {
        syntaxerror("couldn't create output file %s", filename);
     }
-    if(swf->compressed) 
+    if(do_cgi)
+       {if(swf_WriteCGI(swf)<0) syntaxerror("WriteCGI() failed.\n");}
+    else if(swf->compressed) 
        {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
     else
        {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
 
     close(fi);
-    
-    dictionary_clear(&instances);
-    dictionary_clear(&characters);
-    dictionary_clear(&images);
-    dictionary_clear(&outlines);
-    dictionary_clear(&gradients);
-    dictionary_clear(&fonts);
-    dictionary_clear(&sounds);
+
+    freeDictionaries();
 
     swf_FreeTags(swf);
     free(swf);
@@ -689,21 +986,33 @@ void s_close()
 
 int s_getframe()
 {
-    return currentframe;
+    return currentframe+1;
 }
 
-void s_frame(int nr, int cut, char*name)
+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) {
@@ -717,15 +1026,18 @@ void s_frame(int nr, int cut, char*name)
 
 int parseColor2(char*str, RGBA*color);
 
-int addFillStyle(SHAPE*s, SRECT*r, char*texture)
+int addFillStyle(SHAPE*s, SRECT*r, char*name)
 {
     RGBA color;
     character_t*image;
     gradient_t*gradient;
-    if(texture[0] == '#') {
-       parseColor2(texture, &color);
+    texture_t*texture;
+    if(name[0] == '#') {
+       parseColor2(name, &color);
        return swf_ShapeAddSolidFillStyle(s, &color);
-    } else if((image = dictionary_lookup(&images, texture))) {
+    } 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;
@@ -733,19 +1045,30 @@ int addFillStyle(SHAPE*s, SRECT*r, char*texture)
        m.tx = r->xmin;
        m.ty = r->ymin;
        return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
-    } /*else if ((texture = dictionary_lookup(&textures, texture))) {
-    } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
-       MATRIX m;
+    }  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 = (r->xmax - r->xmin)*2;
-       m.sy = (r->ymax - r->ymin)*2;
+       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(texture, &color)) {
+    }  else if (parseColor2(name, &color)) {
        return swf_ShapeAddSolidFillStyle(s, &color);
     } else {
-       syntaxerror("not a color/fillstyle: %s", texture);
+       syntaxerror("not a color/fillstyle: %s", name);
        return 0;
     }
 }
@@ -755,23 +1078,25 @@ void s_box(char*name, int width, int height, RGBA color, int linewidth, char*tex
 {
     SRECT r,r2;
     SHAPE* s;
-    int ls1,fs1=0;
+    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);
-    ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
-
+    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-linewidth/2;
-    r.ymin = r2.ymin-linewidth-linewidth/2;
-    r.xmax = r2.xmax+linewidth+linewidth/2;
-    r.ymax = r2.ymax+linewidth+linewidth/2;
+    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);
@@ -800,21 +1125,26 @@ void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*textu
 
     tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
     swf_ShapeNew(&s);
-    ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+    if(linewidth) {
+       linewidth = linewidth>=20?linewidth-20:0;
+        ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+    }
     if(texture)
        fs1 = addFillStyle(s, &r2, texture);
-    else 
-       syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
+    
     swf_SetU16(tag,id);
-    rect.xmin = r2.xmin-linewidth-linewidth/2;
-    rect.ymin = r2.ymin-linewidth-linewidth/2;
-    rect.xmax = r2.xmax+linewidth+linewidth/2;
-    rect.ymax = r2.ymax+linewidth+linewidth/2;
+    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_SetShapeBits(tag, outline->shape); //does not count bits!
-    swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
+    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);
@@ -825,21 +1155,24 @@ void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
 {
     SRECT rect,r2;
     SHAPE* s;
-    int ls1,fs1=0;
+    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);
-    ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+    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-linewidth/2;
-    rect.ymin = r2.ymin-linewidth-linewidth/2;
-    rect.xmax = r2.xmax+linewidth+linewidth/2;
-    rect.ymax = r2.ymax+linewidth+linewidth/2;
+    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);
@@ -906,23 +1239,50 @@ void s_text(char*name, char*fontname, char*text, int size, RGBA color)
        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_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
+void s_quicktime(char*name, char*url)
 {
-    SWFFONT*font;
+    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;
 
-    font = dictionary_lookup(&fonts, fontname);
-    if(!font)
-       syntaxerror("font \"%s\" not known!", fontname);
+    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 = 0;
+    layout.align = align;
     layout.leftmargin = 0;
     layout.rightmargin = 0;
     layout.indent = 0;
@@ -931,7 +1291,8 @@ void s_edittext(char*name, char*fontname, int size, int width, int height, char*
     r.ymin = 0;
     r.xmax = width;
     r.ymax = height;
-    swf_SetEditText(tag, flags|ET_USEOUTLINES, r, text, color, maxlength, font->id, size, &layout, variable);
+    
+    swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
 
     s_addcharacter(name, id, tag, r);
     incrementid();
@@ -948,13 +1309,8 @@ void s_image(char*name, char*type, char*filename, int quality)
     SRECT r;
     int imageID = id;
     int width, height;
-    if(type=="png") {
-       warning("image type \"png\" not supported yet!");
-       s_box(name, 0, 0, black, 20, 0);
-       return;
-    }
-    if(type=="jpeg") {
-#ifndef HAVE_LIBJPEG
+    if(!strcmp(type,"jpeg")) {
+#ifndef HAVE_JPEGLIB
        warning("no jpeg support compiled in");
        s_box(name, 0, 0, black, 20, 0);
        return;
@@ -976,6 +1332,32 @@ void s_image(char*name, char*type, char*filename, int quality)
        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 */
@@ -987,21 +1369,76 @@ void s_image(char*name, char*type, char*filename, int quality)
     incrementid();
 }
 
-void dumpSWF(SWF*swf)
+void s_getBitmapSize(char*name, int*width, int*height)
 {
-    TAG* tag = swf->firstTag;
-    printf("vvvvvvvvvvvvvvvvvvvvv\n");
-    while(tag) {
-       printf("%8d %s\n", tag->len, swf_TagGetName(tag));
-       tag = tag->next;
+    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;
     }
-    printf("^^^^^^^^^^^^^^^^^^^^^\n");
+    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_font(char*name, char*filename)
+
+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, char *glyphs)
+{
+    if(dictionary_lookup(&fonts, name))
+        syntaxerror("font %s defined twice", name);
+        
     SWFFONT* font;
-    font = swf_LoadFont(filename);
+    font = swf_LoadFont(filename, glyphs);
    
     if(font == 0) {
        warning("Couldn't open font file \"%s\"", filename);
@@ -1027,10 +1464,12 @@ void s_font(char*name, char*filename)
     font->id = id;
     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();
 
-    if(dictionary_lookup(&fonts, name))
-       syntaxerror("font %s defined twice", name);
     dictionary_put2(&fonts, name, font);
 }
 
@@ -1045,37 +1484,90 @@ typedef struct _sound_t
 void s_sound(char*name, char*filename)
 {
     struct WAV wav, wav2;
+    struct MP3 mp3;
     sound_t* sound;
-    U16*samples;
-    int numsamples;
+    U16*samples = NULL;
+    unsigned numsamples = 1;
+    unsigned blocksize = 1152;
+    int is_mp3 = 0;
 
-    if(!readWAV(filename, &wav)) {
-       warning("Couldn't read wav file \"%s\"", filename);
-       samples = 0;
-       numsamples = 0;
-    } else {
-       convertWAV2mono(&wav, &wav2, 44100);
-       samples = (U16*)wav2.data;
-       numsamples = wav2.size/2;
-       free(wav.data);
+    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
-    swf_SetSoundDefine(tag, samples, numsamples);
-   
+    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;
 
-    if(dictionary_lookup(&sounds, name))
-       syntaxerror("sound %s defined twice", name);
     dictionary_put2(&sounds, name, sound);
     
     incrementid();
 
-    if(samples)
-       free(samples);
+    if (samples)
+        free(samples);
 }
 
 static char* gradient_getToken(const char**p)
@@ -1101,51 +1593,160 @@ RGBA parseColor(char*str);
 GRADIENT parseGradient(const char*str)
 {
     GRADIENT gradient;
+    int lastpos = -1;
     const char* p = str;
     memset(&gradient, 0, sizeof(GRADIENT));
-    while(*p) {
-       char*posstr,*colorstr;
-       float pos;
-       RGBA color;
-       posstr = gradient_getToken(&p);
-       if(!*posstr)
-           break;
-       pos = parsePercent(posstr);
-       if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
-       colorstr = gradient_getToken(&p);
-       color = parseColor(colorstr);
-       if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
-           warning("gradient record too big- max size is 8, rest ignored");
-           break;
+    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;
        }
-       gradient.ratios[gradient.num] = (int)(pos*255.0);
-       gradient.rgba[gradient.num] = color;
-       gradient.num++;
-       free(posstr);
-       free(colorstr);
-    }
     return gradient;
 }
 
-void s_gradient(char*name, const char*text, int radial)
+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;
 
-    if(dictionary_lookup(&gradients, name))
-       syntaxerror("gradient %s defined twice", name);
     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);
+
+    composite = 1;
+
+    if(!g)
+       syntaxerror("unknown gradient %s", gradient);
+    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);
+
+    FILTER_BLUR* filter = rfx_calloc(sizeof(FILTER_BLUR));
+    filter->type = FILTERTYPE_BLUR;
+    filter->blurx = blurx;
+    filter->blury = blury;
+    filter->passes = passes;
+
+    dictionary_put2(&filters, name, filter);
+}
 
 void s_action(const char*text)
 {
     ActionTAG* a = 0;
     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
-    if(!a) {
-       syntaxerror("Couldn't compile ActionScript");
+    if(!a)
+    {
+        swf_ActionFree(a);
+        syntaxerror("Couldn't compile ActionScript");
     }
 
     tag = swf_InsertTag(tag, ST_DOACTION);
@@ -1155,11 +1756,34 @@ void s_action(const char*text)
     swf_ActionFree(a);
 }
 
+void s_initaction(const char*character, const char*text)
+{
+    ActionTAG* a = 0;
+    character_t*c = 0;
+    a = swf_ActionCompile(text, stack[0].swf->fileVersion);
+    if(!a) 
+    {
+        swf_ActionFree(a);
+        syntaxerror("Couldn't compile ActionScript");
+    }
+
+    c = (character_t*)dictionary_lookup(&characters, character);
+
+    tag = swf_InsertTag(tag, ST_DOINITACTION);
+    swf_SetU16(tag, c->id);
+    swf_ActionSet(tag, a);
+
+    swf_ActionFree(a);
+}
+
 int s_swf3action(char*name, char*action)
 {
     ActionTAG* a = 0;
-    instance_t* object = dictionary_lookup(&instances, name);
-    if(!object) {
+    instance_t* object = 0;
+    if(name) 
+       object = (instance_t*)dictionary_lookup(&instances, name);
+    if(!object && name && *name) {
+       /* we have a name, but couldn't find it. Abort. */
        return 0;
     }
     a = action_SetTarget(0, name);
@@ -1178,6 +1802,9 @@ int s_swf3action(char*name, char*action)
 
 void s_outline(char*name, char*format, char*source)
 {
+    if(dictionary_lookup(&outlines, name))
+        syntaxerror("outline %s defined twice", name);
+        
     outline_t* outline;
 
     drawer_t draw;
@@ -1185,30 +1812,29 @@ void s_outline(char*name, char*format, char*source)
     SHAPE2* shape2;
     SRECT bounds;
     
+    //swf_Shape10DrawerInit(&draw, 0);
     swf_Shape11DrawerInit(&draw, 0);
+
     draw_string(&draw, source);
     draw.finish(&draw);
     shape = swf_ShapeDrawerToShape(&draw);
-    //shape2 = swf_ShapeToShape2(shape);
-    //bounds = swf_GetShapeBoundingBox(shape2);
-    //swf_Shape2Free(shape2);
     bounds = swf_ShapeDrawerGetBBox(&draw);
     draw.dealloc(&draw);
     
-    outline = (outline_t*)malloc(sizeof(outline_t));
-    memset(outline, 0, sizeof(outline_t));
+    outline = (outline_t*)rfx_calloc(sizeof(outline_t));
     outline->shape = shape;
     outline->bbox = bounds;
     
-    if(dictionary_lookup(&outlines, name))
-       syntaxerror("outline %s defined twice", name);
     dictionary_put2(&outlines, name, outline);
 }
 
 int s_playsound(char*name, int loops, int nomultiple, int stop)
 {
-    sound_t* sound = dictionary_lookup(&sounds, name);
+    sound_t* sound;
     SOUNDINFO info;
+    if(!name)
+       return 0;
+    sound = dictionary_lookup(&sounds, name);
     if(!sound)
        return 0;
 
@@ -1253,7 +1879,7 @@ void s_includeswf(char*name, char*filename)
     
     s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
     swf_SetU16(tag, id);
-    swf_SetU16(tag, 0);
+    swf_SetU16(tag, swf.frameCount);
 
     swf_Relocate(&swf, idmap);
 
@@ -1272,13 +1898,17 @@ void s_includeswf(char*name, char*filename)
            level--;
        if(!level)
            break;
-       /* We simply dump all tags right after the sprite
-          header, relying on the fact that swf_OptimizeTagOrder() will
-          sort things out for us later. 
-          We also rely on the fact that the imported SWF is well-formed.
-        */
-       tag = swf_InsertTag(tag, ftag->id);
-       swf_SetBlock(tag, ftag->data, ftag->len);
+
+       if(ftag->id != ST_SETBACKGROUNDCOLOR) {
+           /* We simply dump all tags right after the sprite
+              header, relying on the fact that swf_OptimizeTagOrder() will
+              sort things out for us later. 
+              We also rely on the fact that the imported SWF is well-formed.
+            */
+           tag = swf_InsertTag(tag, ftag->id);
+           swf_SetBlock(tag, ftag->data, ftag->len);
+       }
+
        ftag = ftag->next;
     }
     if(!ftag)
@@ -1348,131 +1978,133 @@ void s_endClip()
     currentdepth++;
 }
 
+void setStartparameters(instance_t* i, parameters_t* p, TAG* tag)
+{
+    history_begin(i->history, "x", currentframe, tag, p->x);
+    history_begin(i->history, "y", currentframe, tag, p->y);
+    history_begin(i->history, "scalex", currentframe, tag, p->scalex);
+    history_begin(i->history, "scaley", currentframe, tag, p->scaley);
+    history_begin(i->history, "cxform.r0", currentframe, tag, p->cxform.r0);
+    history_begin(i->history, "cxform.g0", currentframe, tag, p->cxform.g0);
+    history_begin(i->history, "cxform.b0", currentframe, tag, p->cxform.b0);
+    history_begin(i->history, "cxform.a0", currentframe, tag, p->cxform.a0);
+    history_begin(i->history, "cxform.r1", currentframe, tag, p->cxform.r1);
+    history_begin(i->history, "cxform.g1", currentframe, tag, p->cxform.g1);
+    history_begin(i->history, "cxform.b1", currentframe, tag, p->cxform.b1);
+    history_begin(i->history, "cxform.a1", currentframe, tag, p->cxform.a1);
+    history_begin(i->history, "rotate", currentframe, tag, p->rotate);
+    history_begin(i->history, "shear", currentframe, tag, p->shear);
+    history_begin(i->history, "pivot.x", currentframe, tag, p->pivot.x);
+    history_begin(i->history, "pivot.y", currentframe, tag, p->pivot.y);
+    history_begin(i->history, "pin.x", currentframe, tag, p->pin.x);
+    history_begin(i->history, "pin.y", currentframe, tag, p->pin.y);
+    history_begin(i->history, "blendmode", currentframe, tag, p->blendmode);
+    history_beginFilter(i->history, currentframe, tag, p->filter);
+}
+
 void s_put(char*instance, char*character, parameters_t p)
 {
     character_t* c = dictionary_lookup(&characters, character);
     instance_t* i;
     MATRIX m;
-    if(!c) {
-       syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
-    }
+    if(!c) 
+        syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
     
     i = s_addinstance(instance, c, currentdepth);
     i->parameters = p;
     m = s_instancepos(i->character->size, &p);
-    
-    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
-    swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
+   
+    if(p.blendmode || p.filter)
+    {
+        if(stack[0].swf->fileVersion < 8) 
+        {
+            if(p.blendmode)
+                warning("blendmodes only supported for flash version>=8");
+            else
+                warning("filters only supported for flash version>=8");
+        }
+        tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
+       }
+    else
+        tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+    setPlacement(tag, c->id, currentdepth, m, instance, &p, 0);
+    setStartparameters(i, &p, tag);
     i->lastTag = tag;
     i->lastFrame = currentframe;
     currentdepth++;
 }
 
-void s_jump(char*instance, parameters_t p)
+void recordChanges(history_t* history, parameters_t p, int changeFunction, interpolation_t* inter)
 {
-    instance_t* i = dictionary_lookup(&instances, instance);
-    MATRIX m;
-    if(!i) {
-       syntaxerror("instance %s not known", instance);
+    if (p.set & SF_X)
+        history_remember(history, "x", currentframe, changeFunction, p.x, inter);
+    if (p.set & SF_Y)
+        history_remember(history, "y", currentframe, changeFunction, p.y, inter);
+    if (p.set & SF_SCALEX)
+        history_remember(history, "scalex", currentframe, changeFunction, p.scalex, inter);
+    if (p.set & SF_SCALEY)
+        history_remember(history, "scaley", currentframe, changeFunction, p.scaley, inter);
+    if (p.set & SF_CX_R)
+    {
+        history_remember(history, "cxform.r0", currentframe, changeFunction, p.cxform.r0, inter);
+        history_remember(history, "cxform.r1", currentframe, changeFunction, p.cxform.r1, inter);
     }
-
-    i->parameters = p;
-    m = s_instancepos(i->character->size, &p);
-
-    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
-    swf_ObjectMove(tag, i->depth, &m, &p.cxform);
-    i->lastTag = tag;
-    i->lastFrame = currentframe;
+    if (p.set & SF_CX_G)
+    {
+        history_remember(history, "cxform.g0", currentframe, changeFunction, p.cxform.g0, inter);
+        history_remember(history, "cxform.g1", currentframe, changeFunction, p.cxform.g1, inter);
+    }
+    if (p.set & SF_CX_B)
+    {
+        history_remember(history, "cxform.b0", currentframe, changeFunction, p.cxform.b0, inter);
+        history_remember(history, "cxform.b1", currentframe, changeFunction, p.cxform.b1, inter);
+    }
+    if (p.set & SF_CX_A)
+    {
+        history_remember(history, "cxform.a0", currentframe, changeFunction, p.cxform.a0, inter);
+        history_remember(history, "cxform.a1", currentframe, changeFunction, p.cxform.a1, inter);
+    }
+    if (p.set & SF_ROTATE)
+        history_remember(history, "rotate", currentframe, changeFunction, p.rotate, inter);
+    if (p.set & SF_SHEAR)
+        history_remember(history, "shear", currentframe, changeFunction, p.shear, inter);
+    if (p.set & SF_PIVOT)
+    {
+        history_remember(history, "pivot.x", currentframe, changeFunction, p.pivot.x, inter);
+        history_remember(history, "pivot.y", currentframe, changeFunction, p.pivot.y, inter);
+    }
+    if (p.set & SF_PIN)
+    {
+        history_remember(history, "pin.x", currentframe, changeFunction, p.pin.x, inter);
+        history_remember(history, "pin.y", currentframe, changeFunction, p.pin.y, inter);
+    }
+    if (p.set & SF_BLEND)
+        history_remember(history, "blendmode", currentframe, changeFunction, p.blendmode, inter);
+    if (p.set & SF_FILTER)
+        history_rememberFilter(history, currentframe, changeFunction, p.filter, inter);
 }
 
-parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
+void s_jump(char* instance, parameters_t p)
 {
-    parameters_t p;
-    float ratio;
-    if(num==0 || num==1)
-       return *p1;
-    ratio = (float)pos/(float)num;
-    
-    p.x = (p2->x-p1->x)*ratio + p1->x;
-    p.y = (p2->y-p1->y)*ratio + p1->y;
-    p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
-    p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
-    p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
-    p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
-
-    p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
-    p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
-    p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
-    p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
-
-    p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
-    p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
-    p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
-    p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
-
-    p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
-    p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
-    p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
-    p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
-    return p;
+    instance_t* i = dictionary_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p, CF_JUMP, 0);
 }
 
-void s_change(char*instance, parameters_t p2)
+void s_change(char*instance, parameters_t p2, interpolation_t* inter)
 {
     instance_t* i = dictionary_lookup(&instances, instance);
-    MATRIX m;
-    parameters_t p1;
-    TAG*t;
-    int frame, allframes;
-    if(!i) {
-       syntaxerror("instance %s not known", instance);
-    }
-    p1 = i->parameters;
-    
-    allframes = currentframe - i->lastFrame - 1;
-    if(allframes < 0) {
-       warning(".change ignored. can only .put/.change an object once per frame.");
-       return;
-    }
-    
-    m = s_instancepos(i->character->size, &p2);
-    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
-    swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
-    i->parameters = p2;
-
-    /* o.k., we got the start and end point set. Now iterate though all the
-       tags in between, inserting object changes after each new frame */
-    t = i->lastTag;
-    i->lastTag = tag;
-    if(!t) syntaxerror("internal error(6)");
-    frame = 0;
-    while(frame < allframes) {
-       if(t->id == ST_SHOWFRAME) {
-           parameters_t p;
-           MATRIX m;
-           TAG*lt;
-           frame ++;
-           p = s_interpolate(&p1, &p2, frame, allframes);
-           m = s_instancepos(i->character->size, &p); //needed?
-           lt = swf_InsertTag(t, ST_PLACEOBJECT2);
-           i->lastFrame = currentframe;
-           swf_ObjectMove(lt, i->depth, &m, &p.cxform);
-           t = lt;
-           if(frame == allframes)
-               break;
-       }
-       t = t->next;
-       if(!t) 
-           syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
-    }
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p2, CF_CHANGE, inter);
 }
 
 void s_delinstance(char*instance)
 {
     instance_t* i = dictionary_lookup(&instances, instance);
-    if(!i) {
-       syntaxerror("instance %s not known", instance);
-    }
+    if(!i)
+        syntaxerror("instance %s not known", instance);
     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
     swf_SetU16(tag, i->depth);
     dictionary_del(&instances, instance);
@@ -1480,21 +2112,33 @@ void s_delinstance(char*instance)
 
 void s_qchange(char*instance, parameters_t p)
 {
+    instance_t* i = dictionary_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p, CF_QCHANGE, 0);
 }
 
 void s_end()
 {
     if(!stackpos)
        syntaxerror(".end unexpected");
-    if(stack[stackpos-1].type == 0)
-       s_endSWF();
-    else if(stack[stackpos-1].type == 1)
-       s_endSprite();
-    else if(stack[stackpos-1].type == 2)
-       s_endClip();
-    else if(stack[stackpos-1].type == 3)
-       s_endButton();
-    else syntaxerror("internal error 1");
+    switch (stack[stackpos-1].type)
+    {
+        case 0:
+            s_endSWF();
+            break;
+        case 1:
+            s_endSprite();
+            break;
+        case 2:
+            s_endClip();
+            break;
+        case 3:
+            s_endButton();
+            break;
+        default:
+            syntaxerror("internal error 1");
+    }
 }
 
 // ------------------------------------------------------------------------
@@ -1503,7 +2147,7 @@ typedef int command_func_t(map_t*args);
 
 SRECT parseBox(char*str)
 {
-    SRECT r;
+    SRECT r = {0,0,0,0};
     float xmin, xmax, ymin, ymax;
     char*x = strchr(str, 'x');
     char*d1=0,*d2=0;
@@ -1573,20 +2217,29 @@ int parseTwip(char*str)
        int t;
        return sign*parseInt(str)*20;
     } else {
-       int l=strlen(++dot);
+       char* old = strdup(str);
+       int l=strlen(dot+1);
        char*s;
+       *dot++ = 0;
        for(s=str;s<dot-1;s++)
-           if(*s<'0' || *s>'9')
-               syntaxerror("Not a coordinate: \"%s\"", str);
-       for(s=dot;*s;s++) {
-           if(*s<'0' || *s>'9')
-               syntaxerror("Not a coordinate: \"%s\"", str);
-       }
-       if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
-           warning("precision loss: %s converted to twip", str);
+        if(*s<'0' || *s>'9')
+        {
+            free(old);
+            syntaxerror("Not a coordinate: \"%s\"", str);
+        }
+    for(s=dot;*s;s++)
+        if(*s<'0' || *s>'9')
+        {
+            free(old);
+            syntaxerror("Not a coordinate: \"%s\"", str);
+        }
+       if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
+           dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
            dot[2] = 0;
            l=2;
+           warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
        }
+       free(old);
        if(l==0)
            return sign*atoi(str)*20;
        if(l==1)
@@ -1650,12 +2303,18 @@ int parseColor2(char*str, RGBA*color)
        color->r = r; color->g = g; color->b = b; color->a = a;
        return 1;
     }
+    int len=strlen(str);
+    U8 alpha = 255;
+    if(strchr(str, '/')) {
+       len = strchr(str, '/')-str;
+       sscanf(str+len+1,"%02x", &alpha);
+    }
     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
-       if(!strcmp(str, colors[t].name)) {
+       if(!strncmp(str, colors[t].name, len)) {
            r = colors[t].r;
            g = colors[t].g;
            b = colors[t].b;
-           a = 255;
+           a = alpha;
            color->r = r; color->g = g; color->b = b; color->a = a;
            return 1;
        }
@@ -1718,6 +2377,19 @@ MULADD mergeMulAdd(MULADD m1, MULADD m2)
     return r;
 }
 
+float parsePxOrPercent(char*fontname, char*str)
+{
+    int l = strlen(str);
+    if(strchr(str, '%'))
+       return parsePercent(str);
+    if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
+       float p = atof(str);
+       return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
+    }
+    syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
+    return 0;
+}
+
 float parsePercent(char*str)
 {
     int l = strlen(str);
@@ -1759,25 +2431,37 @@ static char* lu(map_t* args, char*name)
 
 static int c_flash(map_t*args) 
 {
-    char* name = lu(args, "name");
+    char* filename = map_lookup(args, "filename");
     char* compressstr = lu(args, "compress");
     SRECT bbox = parseBox(lu(args, "bbox"));
     int version = parseInt(lu(args, "version"));
     int fps = (int)(parseFloat(lu(args, "fps"))*256);
     int compress = 0;
     RGBA color = parseColor(lu(args, "background"));
-    if(!strcmp(name, "!default!") || override_outputname)
-       name = outputname;
+
+    if(!filename || !*filename) {
+       /* for compatibility */
+       filename = map_lookup(args, "name");
+       if(!filename || !*filename) {
+           filename = 0;
+       } else {
+           //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
+           msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
+       }
+    }
+
+    if(!filename || override_outputname)
+       filename = outputname;
     
     if(!strcmp(compressstr, "default"))
-       compress = version==6;
+       compress = version>=6;
     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
        compress = 1;
     else if(!strcmp(compressstr, "no"))
        compress = 0;
     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
 
-    s_swf(name, bbox, version, fps, compress, color);
+    s_swf(filename, bbox, version, fps, compress, color);
     return 0;
 }
 int isRelative(char*str)
@@ -1807,6 +2491,34 @@ static dictionary_t points;
 static mem_t mpoints;
 int points_initialized = 0;
 
+static int c_interpolation(map_t *args)
+{
+    int i;
+    char* name = lu(args, "name");
+    if (dictionary_lookup(&interpolations, name))
+        syntaxerror("interpolation %s defined twice", name);
+    
+    interpolation_t* inter = (interpolation_t*)malloc(sizeof(interpolation_t));
+    char* functionstr = lu(args, "function");
+    inter->function = 0;
+    for (i = 0; i < sizeof(interpolationFunctions) / sizeof(interpolationFunctions[0]); i++)
+        if (!strcmp(functionstr,interpolationFunctions[i]))
+        {
+            inter->function = i + 1;
+            break;
+        }
+    if (!inter->function)
+        syntaxerror("unkown interpolation function %s", functionstr);
+    inter->speed = parseFloat(lu(args, "speed"));
+    inter->amplitude = parseFloat(lu(args, "amplitude"));
+    inter->growth = parseFloat(lu(args, "growth"));
+    inter->bounces = parseInt(lu(args, "bounces"));
+    inter->damping = parseInt(lu(args, "damping"));
+    
+    dictionary_put2(&interpolations, name, inter);
+    return 0;
+}
+
 SPOINT getPoint(SRECT r, char*name)
 {
     int l=0;
@@ -1825,18 +2537,210 @@ SPOINT getPoint(SRECT r, char*name)
     l--;
     return *(SPOINT*)&mpoints.buffer[l];
 }
+
+static int texture2(char*name, char*object, map_t*args, int errors) 
+{
+    SPOINT pos,size;
+    char*xstr = map_lookup(args, "x");
+    char*ystr = map_lookup(args, "y");
+    char*widthstr = map_lookup(args, "width");
+    char*heightstr = map_lookup(args, "height");
+    char*scalestr = map_lookup(args, "scale");
+    char*scalexstr = map_lookup(args, "scalex");
+    char*scaleystr = map_lookup(args, "scaley");
+    char*rotatestr = map_lookup(args, "rotate");
+    char* shearstr = map_lookup(args, "shear");
+    char* radiusstr = map_lookup(args, "r");
+    float x=0,y=0;
+    float scalex = 1.0, scaley = 1.0;
+    float rotate=0, shear=0;
+    float r = 0;
+    if(!*xstr && !*ystr) {
+       if(errors)
+           syntaxerror("x and y must be set");
+       return 0;
+    }
+    if(*scalestr && (*scalexstr || *scaleystr)) {
+       syntaxerror("scale and scalex/scaley can't both be set");
+       return 0;
+    }
+    if((*widthstr || *heightstr) && *radiusstr) {
+       syntaxerror("width/height and radius can't both be set");
+    }
+    if(*radiusstr) {
+       widthstr = radiusstr;
+       heightstr = radiusstr;
+    }
+    if(!*xstr) xstr="0";
+    if(!*ystr) ystr="0";
+    if(!*rotatestr) rotatestr="0";
+    if(!*shearstr) shearstr="0";
+
+    if(*scalestr) {
+       scalex = scaley = parsePercent(scalestr);
+    } else if(*scalexstr || *scaleystr) {
+       if(scalexstr) scalex = parsePercent(scalexstr);
+       if(scaleystr) scaley = parsePercent(scaleystr);
+    } else if(*widthstr || *heightstr) {
+       int width=0;
+       int height=0;
+       s_getBitmapSize(object, &width, &height);
+       if(*widthstr)
+           scalex = (float)parseTwip(widthstr)/(float)width;
+       if(*heightstr)
+           scaley = (float)parseTwip(heightstr)/(float)height;
+    }
+    x = parseTwip(xstr);
+    y = parseTwip(ystr);
+    rotate = parseFloat(rotatestr);
+    shear = parseFloat(shearstr);
+
+    s_texture(name, object, x,y,scalex,scaley,rotate, shear);
+
+    return 0;
+}
+
+static int c_texture(map_t*args) 
+{
+    char*name = lu(args, "instance");
+    char*object = lu(args, "character");
+    return texture2(name, object, args, 1);
+}
+
 static int c_gradient(map_t*args) 
 {
     char*name = lu(args, "name");
     int radial= strcmp(lu(args, "radial"), "radial")?0:1;
+    int rotate = parseInt(lu(args, "rotate"));
 
     readToken();
     if(type != RAWDATA)
-       syntaxerror("colon (:) expected");
+        syntaxerror("colon (:) expected");
+
+    if(dictionary_lookup(&gradients, name))
+        syntaxerror("gradient %s defined twice", name);
+
+    s_gradient(name, text, radial, rotate);
+
+    /* check whether we also have placement information,
+       which would make this a positioned gradient.
+       If there is placement information, texture2() will
+       add a texture, which has priority over the gradient.
+     */
+    texture2(name, name, args, 0);
+    return 0;
+}
+
+static int c_blur(map_t*args) 
+{
+    char*name = lu(args, "name");
+    char*blurstr = lu(args, "blur");
+    char*blurxstr = lu(args, "blurx");
+    char*blurystr = lu(args, "blury");
+    float blurx=1.0, blury=1.0;
+    if(blurstr[0]) {
+       blurx = parseFloat(blurstr);
+       blury = parseFloat(blurstr);
+    } 
+    if(blurxstr[0])
+       blurx = parseFloat(blurxstr);
+    if(blurystr[0])
+       blury = parseFloat(blurystr);
+    int passes = parseInt(lu(args, "passes"));
+    s_blur(name, blurx, blury, passes);
+    return 0;
+}
 
-    s_gradient(name, text, radial);
+static int c_gradientglow(map_t*args) 
+{
+    char*name = lu(args, "name");
+    char*gradient = lu(args, "gradient");
+    char*blurstr = lu(args, "blur");
+    char*blurxstr = lu(args, "blurx");
+    char*blurystr = lu(args, "blury");
+    float blurx=1.0, blury=1.0;
+    if(blurstr[0]) {
+       blurx = parseFloat(blurstr);
+       blury = parseFloat(blurstr);
+    } 
+    if(blurxstr[0])
+       blurx = parseFloat(blurxstr);
+    if(blurystr[0])
+       blury = parseFloat(blurystr);
+
+    float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
+    float distance = parseFloat(lu(args, "distance"));
+    float strength = parseFloat(lu(args, "strength"));
+    char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
+    char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
+    char composite = strcmp(lu(args, "composite"),"composite")?0:1;
+    char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
+    int passes = parseInt(lu(args, "passes"));
+
+    s_gradientglow(name, gradient, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
     return 0;
 }
+
+static int c_dropshadow(map_t*args) 
+{
+    char*name = lu(args, "name");
+    RGBA color = parseColor(lu(args, "color"));
+    char*blurstr = lu(args, "blur");
+    char*blurxstr = lu(args, "blurx");
+    char*blurystr = lu(args, "blury");
+    float blurx=1.0, blury=1.0;
+    if(blurstr[0]) {
+       blurx = parseFloat(blurstr);
+       blury = parseFloat(blurstr);
+    } 
+    if(blurxstr[0])
+       blurx = parseFloat(blurxstr);
+    if(blurystr[0])
+       blury = parseFloat(blurystr);
+
+    float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
+    float distance = parseFloat(lu(args, "distance"));
+    float strength = parseFloat(lu(args, "strength"));
+    char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
+    char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
+    char composite = strcmp(lu(args, "composite"),"composite")?0:1;
+    int passes = parseInt(lu(args, "passes"));
+
+    s_dropshadow(name, color, blurx, blury, angle, distance, strength, innershadow, knockout, composite, passes);
+    return 0;
+}
+
+static int c_bevel(map_t*args) 
+{
+    char*name = lu(args, "name");
+    RGBA shadow = parseColor(lu(args, "shadow"));
+    RGBA highlight = parseColor(lu(args, "highlight"));
+    char*blurstr = lu(args, "blur");
+    char*blurxstr = lu(args, "blurx");
+    char*blurystr = lu(args, "blury");
+    float blurx=1.0, blury=1.0;
+    if(blurstr[0]) {
+       blurx = parseFloat(blurstr);
+       blury = parseFloat(blurstr);
+    } 
+    if(blurxstr[0])
+       blurx = parseFloat(blurxstr);
+    if(blurystr[0])
+       blury = parseFloat(blurystr);
+
+    float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
+    float distance = parseFloat(lu(args, "distance"));
+    float strength = parseFloat(lu(args, "strength"));
+    char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
+    char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
+    char composite = strcmp(lu(args, "composite"),"composite")?0:1;
+    char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
+    int passes = parseInt(lu(args, "passes"));
+
+    s_bevel(name, shadow, highlight, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
+    return 0;
+}
+
 static int c_point(map_t*args) 
 {
     char*name = lu(args, "name");
@@ -1877,15 +2781,14 @@ static int c_play(map_t*args)
 
 static int c_stop(map_t*args) 
 {
-    char*name = lu(args, "name");
+    char*name = map_lookup(args, "name");
 
-    if(s_playsound(name, 0,0,1)) {
-       return 0;
-    } else if(s_swf3action(name, "stop")) {
-       return 0;
-    }
+    if(s_playsound(name, 0,0,1))
+        return 0;
+    else if(s_swf3action(name, "stop"))
+        return 0;
     syntaxerror("I don't know anything about sound/movie \"%s\"", name);
-    return 0;
+       return 0;
 }
 
 static int c_nextframe(map_t*args) 
@@ -1931,31 +2834,41 @@ static int c_placement(map_t*args, int type)
     char* astr = lu(args, "alpha");
     char* pinstr = lu(args, "pin");
     char* as = map_lookup(args, "as");
+    char* blendmode = lu(args, "blend");
+    char* filterstr = lu(args, "filter");
+    U8 blend;
     MULADD r,g,b,a;
     float oldwidth;
     float oldheight;
     SRECT oldbbox;
     MULADD luminance;
     parameters_t p;
+    U32 set = 0x00000000;
 
-    if(type==9) { // (?) .rotate  or .arcchange
-       pivotstr = lu(args, "pivot");
-       anglestr = lu(args, "angle");
-    } else {
-       xstr = lu(args, "x");
-       ystr = lu(args, "y");
+    if(type==9)
+    { // (?) .rotate  or .arcchange
+        pivotstr = lu(args, "pivot");
+        anglestr = lu(args, "angle");
     }
+    else
+    {
+        xstr = lu(args, "x");
+        ystr = lu(args, "y");
+    }
+    
     if(luminancestr[0])
-       luminance = parseMulAdd(luminancestr);
-    else {
-       luminance.add = 0;
-       luminance.mul = 256;
+        luminance = parseMulAdd(luminancestr);
+    else
+    {
+        luminance.add = 0;
+        luminance.mul = 256;
     }
 
-    if(scalestr[0]) {
-       if(scalexstr[0]||scaleystr[0])
-           syntaxerror("scalex/scaley and scale cannot both be set");
-       scalexstr = scaleystr = scalestr;
+    if(scalestr[0])
+    {
+        if(scalexstr[0]||scaleystr[0])
+            syntaxerror("scalex/scaley and scale cannot both be set");
+        scalexstr = scaleystr = scalestr;
     }
     
     if(type == 0 || type == 4)  {
@@ -1971,136 +2884,216 @@ static int c_placement(map_t*args, int type)
     }
 
     /* x,y position */
-    if(xstr[0]) {
-       if(isRelative(xstr)) {
-           if(type == 0 || type == 4)
-               syntaxerror("relative x values not allowed for initial put or startclip");
-           p.x += parseTwip(getOffset(xstr))*getSign(xstr);
-       } else {
-           p.x = parseTwip(xstr);
-       }
-    }
-    if(ystr[0]) {
-       if(isRelative(ystr)) {
-           if(type == 0 || type == 4)
-               syntaxerror("relative y values not allowed for initial put or startclip");
-           p.y += parseTwip(getOffset(ystr))*getSign(ystr);
-       } else {
-           p.y = parseTwip(ystr);
+    if(xstr[0]) 
+    {
+        if(isRelative(xstr))
+        {
+            if(type == 0 || type == 4)
+                syntaxerror("relative x values not allowed for initial put or startclip");
+            p.x += parseTwip(getOffset(xstr))*getSign(xstr);
+        }
+        else
+        {
+            p.x = parseTwip(xstr);
+        }
+        set = set | SF_X;
+     }
+    if(ystr[0])
+    {
+        if(isRelative(ystr))
+        {
+            if(type == 0 || type == 4)
+                syntaxerror("relative y values not allowed for initial put or startclip");
+            p.y += parseTwip(getOffset(ystr))*getSign(ystr);
+        }
+        else
+        {
+            p.y = parseTwip(ystr);
+        }
+        set = set | SF_Y;
        }
-    }
 
     /* scale, scalex, scaley */
-    if(character) {
-       oldbbox = s_getCharBBox(character);
-    } else {
-       oldbbox = s_getInstanceBBox(instance);
-    }
+    if(character)
+        oldbbox = s_getCharBBox(character);
+    else
+        oldbbox = s_getInstanceBBox(instance);
     oldwidth = oldbbox.xmax - oldbbox.xmin;
     oldheight = oldbbox.ymax - oldbbox.ymin;
-    if(scalexstr[0]) {
-       if(oldwidth==0) p.scalex = 1.0;
-       else {      
-           if(scalexstr[0])
-               p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
-       }
-    }
-    if(scaleystr[0]) {
-       if(oldheight==0) p.scaley = 1.0;
-       else {
-           if(scaleystr[0])
-               p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
-       }
-    }
+    if(scalexstr[0])
+    {
+        if(oldwidth==0)
+            p.scalex = 1.0;
+        else
+            if(scalexstr[0])
+                p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
+        set = set | SF_SCALEX;
+    }
+   if(scaleystr[0])
+   {
+        if(oldheight==0)
+            p.scaley = 1.0;
+        else
+            if(scaleystr[0])
+                p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
+        set = set | SF_SCALEY;
+   }
    
     /* rotation */
-    if(rotatestr[0]) {
-       if(isRelative(rotatestr)) {
-           p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
-       } else {
-           p.rotate = parseFloat(rotatestr);
+    if(rotatestr[0])
+    {
+        if(isRelative(rotatestr))
+            p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
+        else
+            p.rotate = parseFloat(rotatestr);
+        set = set | SF_ROTATE;
        }
-    }
 
     /* shearing */
-    if(shearstr[0]) {
-       if(isRelative(shearstr)) {
-           p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
-       } else {
-           p.shear = parseFloat(shearstr);
-       }
+    if(shearstr[0])
+    {
+        if(isRelative(shearstr))
+            p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
+        else
+            p.shear = parseFloat(shearstr);
+        set = set | SF_SHEAR;
     }
 
-    if(pivotstr[0]) {
-       if(isPoint(pivotstr)) 
-           p.pivot = parsePoint(pivotstr);
-       else 
-           p.pivot = getPoint(oldbbox, pivotstr);
+    if(pivotstr[0])
+    {
+        if(isPoint(pivotstr))
+            p.pivot = parsePoint(pivotstr);
+        else 
+            p.pivot = getPoint(oldbbox, pivotstr);
+        set = set | SF_PIVOT;
     }
-    if(pinstr[0]) {
-       if(isPoint(pinstr))
-           p.pin = parsePoint(pinstr);
-       else
-           p.pin = getPoint(oldbbox, pinstr);
+    
+    if(pinstr[0])
+    {
+        if(isPoint(pinstr))
+            p.pin = parsePoint(pinstr);
+        else
+            p.pin = getPoint(oldbbox, pinstr);
+        set = set | SF_PIN;
     }
        
     /* color transform */
 
-    if(rstr[0] || luminancestr[0]) {
-       MULADD r;
-       if(rstr[0])
-           r = parseMulAdd(rstr);
-       else {
-           r.add = p.cxform.r0;
-           r.mul = p.cxform.r1;
-       }
-       r = mergeMulAdd(r, luminance);
-       p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
-    }
-    if(gstr[0] || luminancestr[0]) {
-       MULADD g;
-       if(gstr[0])
-           g = parseMulAdd(gstr);
-       else {
-           g.add = p.cxform.g0;
-           g.mul = p.cxform.g1;
-       }
-       g = mergeMulAdd(g, luminance);
-       p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
-    }
-    if(bstr[0] || luminancestr[0]) {
-       MULADD b;
-       if(bstr[0])
-           b = parseMulAdd(bstr);
-       else {
-           b.add = p.cxform.b0;
-           b.mul = p.cxform.b1;
+    if(rstr[0] || luminancestr[0])
+    {
+        MULADD r;
+        if(rstr[0])
+            r = parseMulAdd(rstr);
+        else
+        {
+            r.add = p.cxform.r0;
+            r.mul = p.cxform.r1;
+        }
+        r = mergeMulAdd(r, luminance);
+        p.cxform.r0 = r.mul;
+        p.cxform.r1 = r.add;
+        set = set | SF_CX_R;
        }
-       b = mergeMulAdd(b, luminance);
-       p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
-    }
-    if(astr[0]) {
-       MULADD a = parseMulAdd(astr);
-       p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
-    }
-
-    if(type == 0)
-       s_put(instance, character, p);
-    else if(type == 1)
-       s_change(instance, p);
-    else if(type == 2)
-       s_qchange(instance, p);
-    else if(type == 3)
-       s_jump(instance, p);
-    else if(type == 4)
-       s_startclip(instance, character, p);
-    else if(type == 5) {
-       if(as && as[0]) {
-           s_buttonput(character, as, p);
-       } else {
-           s_buttonput(character, "shape", p);
+    if(gstr[0] || luminancestr[0])
+    {
+        MULADD g;
+        if(gstr[0])
+            g = parseMulAdd(gstr);
+        else
+        {
+            g.add = p.cxform.g0;
+            g.mul = p.cxform.g1;
+        }
+        g = mergeMulAdd(g, luminance);
+        p.cxform.g0 = g.mul;
+        p.cxform.g1 = g.add;
+        set = set | SF_CX_G;
+    }
+    if(bstr[0] || luminancestr[0])
+    {
+        MULADD b;
+        if(bstr[0])
+            b = parseMulAdd(bstr);
+        else
+        {
+            b.add = p.cxform.b0;
+            b.mul = p.cxform.b1;
+        }
+        b = mergeMulAdd(b, luminance);
+        p.cxform.b0 = b.mul;
+        p.cxform.b1 = b.add;
+        set = set | SF_CX_B;
+    }
+    if(astr[0])
+    {
+        MULADD a = parseMulAdd(astr);
+        p.cxform.a0 = a.mul;
+        p.cxform.a1 = a.add;
+        set = set | SF_CX_A;
+    }
+
+    if(blendmode[0])
+    {
+        int t;
+        blend = 255;
+        for(t = 0; blendModeNames[t]; t++)
+        {
+            if(!strcmp(blendModeNames[t], blendmode))
+            {
+                blend = t;
+                break;
+            }
+        }
+        if(blend == 255)
+        {
+            syntaxerror("unknown blend mode: '%s'", blendmode);
+        }
+        p.blendmode = blend;
+        set = set | SF_BLEND;
        }
+    
+    if(filterstr[0])
+    {
+        FILTER*f = dictionary_lookup(&filters, filterstr);
+        if(!f) 
+            syntaxerror("Unknown filter %s", filterstr);
+        p.filter = f;
+        set = set | SF_FILTER;
     }
+
+    p.set = set;
+
+    switch (type)
+    {
+        case 0:
+            s_put(instance, character, p);
+            break;
+        case 1:
+            {
+                char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
+                if (!inter)
+                    syntaxerror("unkown interpolation %s", interstr);
+                s_change(instance, p, inter);
+            }
+            break;
+        case 2:
+            s_qchange(instance, p);
+            break;
+        case 3:
+            s_jump(instance, p);
+            break;
+        case 4:
+            s_startclip(instance, character, p);
+            break;
+        case 5:
+            if(as && as[0])
+                s_buttonput(character, as, p);
+            else
+                s_buttonput(character, "shape", p);
+            break;
+//        default: 
+       }
     return 0;
 }
 static int c_put(map_t*args) 
@@ -2110,6 +3103,8 @@ static int c_put(map_t*args)
 }
 static int c_change(map_t*args) 
 {
+    if (currentframe == 0)
+        warning("change commands in frame 1 will be ignored, please use the put command to set object parameters");
     c_placement(args, 1);
     return 0;
 }
@@ -2159,7 +3154,14 @@ static int c_frame(map_t*args)
 {
     char*framestr = lu(args, "n");
     char*cutstr = lu(args, "cut");
+    
     char*name = lu(args, "name");
+    char*anchor = lu(args, "anchor");
+    char buf[40];
+
+    if(!strcmp(anchor, "anchor") && !*name)
+       name = framestr;
+
     int frame;
     int cut = 0;
     if(strcmp(cutstr, "no"))
@@ -2173,10 +3175,10 @@ static int c_frame(map_t*args)
     else {
        frame = parseInt(framestr);
        if(s_getframe() >= frame
-               && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
+               && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
            syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
     }
-    s_frame(frame, cut, name);
+    s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
     return 0;
 }
 static int c_primitive(map_t*args) 
@@ -2226,7 +3228,7 @@ static int c_textshape(map_t*args)
     char*name = lu(args, "name");
     char*text = lu(args, "text");
     char*font = lu(args, "font");
-    float size = parsePercent(lu(args, "size"));
+    float size = parsePxOrPercent(font, lu(args, "size"));
 
     s_textshape(name, font, size, text);
     return 0;
@@ -2247,7 +3249,17 @@ static int c_font(map_t*args)
 {
     char*name = lu(args, "name");
     char*filename = lu(args, "filename");
-    s_font(name, filename);
+    fontData* usage = getFontData(name);
+    char* glyphs = usage->glyphs;
+    if (usage->needsAll)
+        glyphs = "";
+    else
+        if (usage->notUsed)
+        {
+            printf("font %s was defined but not used\n", name);
+            return 0;
+        }
+    s_font(name, filename, glyphs);
     return 0;
 }
 
@@ -2264,7 +3276,7 @@ static int c_text(map_t*args)
     char*name = lu(args, "name");
     char*text = lu(args, "text");
     char*font = lu(args, "font");
-    float size = parsePercent(lu(args, "size"));
+    float size = parsePxOrPercent(font, lu(args, "size"));
     RGBA color = parseColor(lu(args, "color"));
     s_text(name, font, text, (int)(size*100), color);
     return 0;
@@ -2275,6 +3287,14 @@ static int c_soundtrack(map_t*args)
     return 0;
 }
 
+static int c_quicktime(map_t*args) 
+{
+    char*name = lu(args, "name");
+    char*url = lu(args, "url");
+    s_quicktime(name, url);
+    return 0;
+}
+
 static int c_image(map_t*args) 
 {
     char*command = lu(args, "commandname");
@@ -2434,10 +3454,10 @@ static int c_on_key(map_t*args)
 
 static int c_edittext(map_t*args) 
 {
- //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
+ //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @autosize=0"},
     char*name = lu(args, "name");
     char*font = lu(args, "font");
-    int size = (int)(1024*parsePercent(lu(args, "size")));
+    int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
     int width = parseTwip(lu(args, "width"));
     int height = parseTwip(lu(args, "height"));
     char*text  = lu(args, "text");
@@ -2451,6 +3471,9 @@ static int c_edittext(map_t*args)
     char*noselectstr = lu(args, "noselect");
     char*readonlystr = lu(args, "readonly");
     char*borderstr = lu(args, "border");
+    char*autosizestr = lu(args, "autosize");
+    char*alignstr = lu(args, "align");
+    int align = -1;
 
     int flags = 0;
     if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
@@ -2460,24 +3483,66 @@ static int c_edittext(map_t*args)
     if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
     if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
     if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
-
-    s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
+    if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
+    if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
+    else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
+    else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
+    else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
+    else syntaxerror("Unknown alignment: %s", alignstr);
+
+    s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
     return 0;
 }
 
 static int c_morphshape(map_t*args) {return fakechar(args);}
 static int c_movie(map_t*args) {return fakechar(args);}
 
-static int c_texture(map_t*args) {return 0;}
+static char* readfile(const char*filename)
+{
+    FILE*fi = fopen(filename, "rb");
+    int l;
+    char*text;
+    if(!fi) 
+       syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
+    fseek(fi, 0, SEEK_END);
+    l = ftell(fi);
+    fseek(fi, 0, SEEK_SET);
+    text = rfx_alloc(l+1);
+    fread(text, l, 1, fi);
+    text[l]=0;
+    fclose(fi);
+    return text;
+}
 
 static int c_action(map_t*args) 
 {
-    readToken();
-    if(type != RAWDATA) {
-       syntaxerror("colon (:) expected");
+    char* filename  = map_lookup(args, "filename");
+    if(!filename ||!*filename) {
+       readToken();
+       if(type != RAWDATA) {
+           syntaxerror("colon (:) expected");
+       }
+       s_action(text);
+    } else {
+       s_action(readfile(filename));
     }
+   
+    return 0;
+}
 
-    s_action(text);
+static int c_initaction(map_t*args) 
+{
+    char* character = lu(args, "name");
+    char* filename  = map_lookup(args, "filename");
+    if(!filename ||!*filename) {
+       readToken();
+       if(type != RAWDATA) {
+           syntaxerror("colon (:) expected");
+       }
+       s_initaction(character, text);
+    } else {
+       s_initaction(character, readfile(filename));
+    }
    
     return 0;
 }
@@ -2487,8 +3552,8 @@ static struct {
     command_func_t* func;
     char*arguments;
 } arguments[] =
-{{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
- {"frame", c_frame, "n=<plus>1 name= @cut=no"},
+{{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
+ {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
  // "import" type stuff
  {"swf", c_swf, "name filename"},
  {"shape", c_swf, "name filename"},
@@ -2498,14 +3563,22 @@ static struct {
  {"sound", c_sound, "name filename"},
  {"font", c_font, "name filename"},
  {"soundtrack", c_soundtrack, "filename"},
-
+ {"quicktime", c_quicktime, "url"},
     // generators of primitives
 
  {"point", c_point, "name x=0 y=0"},
- {"gradient", c_gradient, "name @radial=0"},
+ {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
+ {"interpolation", c_interpolation, "name function=linear speed=1.3 amplitude=0 bounces=2 growth=1.5, damping=2"},
  {"outline", c_outline, "name format=simple"},
  {"textshape", c_textshape, "name font size=100% text"},
 
+    // filters
+ {"blur", c_blur, "name blur= blurx= blury= passes=1"},
+ {"gradientglow", c_gradientglow, "name gradient blur= blurx= blury= angle=0.0 distance=0.0 strength=1.0 @innershadow=0 @knockout=0 @composite=0 @ontop=0 passes=1"},
+ {"dropshadow", c_dropshadow, "name color blur= blurx= blury= angle=0.0 distance=0.0 strength=1.0 @innershadow=0 @knockout=0 @composite=0 passes=1"},
+ {"bevel", c_bevel, "name shadow highlight blur= blurx= blury= angle=0.0 distance=0.0 strength=1.0 @innershadow=0 @knockout=0 @composite=0 @ontop=0 passes=1"},
+
     // character generators
  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
@@ -2513,10 +3586,10 @@ static struct {
 
  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
  {"text", c_text, "name text font size=100% color=white"},
- {"edittext", c_edittext, "name font size=100% width height text="" color=white maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @border=0"},
+ {"edittext", c_edittext, "name font= size=100% width height text="" color=white maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @border=0 @autosize=0 align="},
  {"morphshape", c_morphshape, "name start end"},
  {"button", c_button, "name"},
-    {"show", c_show,             "name x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below= as="},
+    {"show", c_show,             "name x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below= as="},
     {"on_press", c_on_press, "position=inside"},
     {"on_release", c_on_release, "position=anywhere"},
     {"on_move_in", c_on_move_in, "state=not_pressed"},
@@ -2525,25 +3598,26 @@ static struct {
  
     // control tags
  {"play", c_play, "name loop=0 @nomultiple=0"},
- {"stop", c_stop, "name"},
+ {"stop", c_stop, "name= "},
  {"nextframe", c_nextframe, "name"},
  {"previousframe", c_previousframe, "name"},
 
     // object placement tags
- {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
- {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
- {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
- {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
- {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
- {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
+ {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
+ {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
+ {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below= interpolation=linear"},
+ {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
+ {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
+ {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
  {"del", c_del, "name"},
     // virtual object placement
- {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
+ {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
 
     // commands which start a block
 //startclip (see above)
  {"sprite", c_sprite, "name"},
- {"action", c_action, ""},
+ {"action", c_action, "filename="},
+ {"initaction", c_initaction, "name filename="},
 
  {"end", c_end, ""}
 };
@@ -2682,7 +3756,7 @@ static map_t parseArguments(char*command, char*pattern)
            }
        }
        if(pos==len) {
-           syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
+           syntaxerror("Illegal argument \"%s\" to .%s", text, command);
        }
     }
 #if 0//def DEBUG
@@ -2785,7 +3859,7 @@ int main (int argc,char ** argv)
     
     file = generateTokens(filename);
     if(!file) {
-       printf("parser returned error.\n");
+       fprintf(stderr, "parser returned error.\n");
        return 1;
     }
     pos=0;