renamed png functions
[swftools.git] / src / swfc.c
index 3f08789..e34f9fc 100644 (file)
@@ -4,7 +4,7 @@
    Part of the swftools package.
 
    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
+
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
 #include "../lib/wav.h"
 #include "parser.h"
 #include "../lib/png.h"
+#include "swfc-feedback.h"
+#include "swfc-interpolation.h"
+#include "swfc-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 int change_sets_all = 0;
+static int do_exports = 0;
+static char * mainclass = "";
 
 static struct options_t options[] = {
 {"h", "help"},
 {"V", "version"},
+{"C", "cgi"},
 {"v", "verbose"},
 {"o", "output"},
 {0,0}
 };
-    
+
 int args_callback_option(char*name,char*val)
 {
     if(!strcmp(name, "V")) {
@@ -94,6 +99,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");
@@ -114,29 +120,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);
-    fprintf(stderr, "\"%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);
-    fprintf(stderr, "\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
-}
 
 static void readToken()
 {
@@ -173,6 +156,19 @@ static int noMoreTokens()
     return 0;
 }
 
+enum
+{
+    PT_PUT = 0,
+    PT_CHANGE = 1,
+    PT_SCHANGE = 2,
+    PT_MOVE = 3,
+    PT_SMOVE = 4,
+    PT_SWEEP = 5,
+    PT_JUMP = 6,
+    PT_STARTCLIP = 7,
+    PT_BUTTON = 8
+};
+
 // ------------------------------ swf routines ----------------------------
 struct _character;
 static struct level
@@ -181,26 +177,32 @@ static struct level
 
    /* for swf (0): */
    SWF*swf;
-   char*filename; 
+   char*filename;
+   char as3;
 
    /* for sprites (1): */
    TAG*tag;
    U16 id;
    char*name;
+   char*as3name;
    U16 olddepth;
    int oldframe;
-   dictionary_t oldinstances;
+   dict_t oldinstances;
    SRECT oldrect;
    TAG* cut;
 
+   SRECT scalegrid;
+
 } stack[256];
 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 dict_t characters;
+static dict_t images;
+static dict_t textures;
+static dict_t outlines;
+static dict_t gradients;
+static dict_t filters;
+static dict_t interpolations;
 static char idmap[65536];
 static TAG*tag = 0; //current tag
 
@@ -208,18 +210,23 @@ static int id; //current character id
 static int currentframe; //current frame in current level
 static SRECT currentrect; //current bounding box in current level
 static U16 currentdepth;
-static dictionary_t instances;
-static dictionary_t fonts;
-static dictionary_t sounds;
+static dict_t instances;
+static dict_t fonts;
+static dict_t sounds;
+static dict_t fontUsage;
 
 typedef struct _parameters {
-    int x,y; 
-    float scalex, scaley; 
+    int x,y;
+    float scalex, scaley;
     CXFORM cxform;
     float rotate;
     float shear;
     SPOINT pivot;
     SPOINT pin;
+    U8 blendmode; //not interpolated
+    FILTERLIST* filters;
+    U16 set; // bits indicating wether a parameter was set in the c_placement function
+    U16 flags; // bits to toggle anything you may care to implement as a toggle
 } parameters_t;
 
 typedef struct _character {
@@ -232,8 +239,7 @@ typedef struct _instance {
     character_t*character;
     U16 depth;
     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 {
@@ -247,14 +253,32 @@ typedef struct _gradient {
     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"};
+
 static void character_init(character_t*c)
 {
     memset(c, 0, sizeof(character_t));
 }
+
 static character_t* character_new()
 {
     character_t*c;
@@ -262,10 +286,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;
@@ -274,75 +307,125 @@ 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 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()
+{
+    dict_free_all(&instances, 1, free_instance);
+    dict_free_all(&characters, 1, free);
+    dict_free_all(&images, 1, free);
+    dict_free_all(&textures, 1, free);
+    dict_free_all(&outlines, 1, free_outline);
+    dict_free_all(&gradients, 1, free_gradient);
+    dict_free_all(&filters, 1, free);
+    dict_free_all(&fonts, 1, free_font);
+    dict_free_all(&sounds, 1, free);
+    dict_free_all(&interpolations, 1, free);
+    cleanUp = 0;
+}
+
+static void freeFontDictionary()
+{
+    dict_free_all(&fonts, 1, free_font);
+}
+
 static void incrementid()
 {
-    while(idmap[++id]) {
-       if(id==65535)
-           syntaxerror("Out of character ids.");
+    while(id<65536 && idmap[id]) {
+        id++;
     }
+    if(id>=65536)
+       syntaxerror("Out of character ids.");
     idmap[id] = 1;
 }
 
-static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
+static void s_addcharacter(const char*name, U16 id, TAG*ctag, SRECT r)
 {
+    if(dict_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);
+    dict_put(&characters, name, c);
 
-    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);
+    if(do_exports) {
+       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);
+    }
 }
-static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
+static void s_addimage(const char*name, U16 id, TAG*ctag, SRECT r)
 {
+    if(dict_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);
+    dict_put(&images, name, c);
 }
-static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
+static instance_t* s_addinstance(const char*name, character_t*c, U16 depth)
 {
+    if(dict_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);
+    dict_put(&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; 
+    p->x = 0; p->y = 0;
     p->scalex = 1.0; p->scaley = 1.0;
     p->pin.x = 0;  //1??
     p->pin.y = 0;
     p->pivot.x = 0; p->pivot.y = 0;
-    p->rotate = 0; 
-    p->shear = 0; 
+    p->rotate = 0;
+    p->shear = 0;
+    p->blendmode = 0;
+    p->filters = 0;
     swf_GetCXForm(0, &p->cxform, 1);
 }
 
@@ -351,14 +434,14 @@ static void makeMatrix(MATRIX*m, parameters_t*p)
     SPOINT h;
     float sx,r1,r0,sy;
 
-    /*       /sx r1\ /x\ 
+    /*       /sx r1\ /x\
      *       \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*M_PI);
+    r1 = -p->scalex*sin(p->rotate/360*2*M_PI)+sx*p->shear;
+    r0 =  p->scaley*sin(p->rotate/360*2*M_PI);
+    sy =  p->scaley*cos(p->rotate/360*2*M_PI)+r0*p->shear;
 
     m->sx = (int)(sx*65536+0.5);
     m->r1 = (int)(r1*65536+0.5);
@@ -366,7 +449,7 @@ static void makeMatrix(MATRIX*m, parameters_t*p)
     m->sy = (int)(sy*65536+0.5);
 
     m->tx = m->ty = 0;
-   
+
     h = swf_TurnPoint(p->pin, m);
     m->tx = p->x - h.x;
     m->ty = p->y - h.y;
@@ -378,7 +461,7 @@ static MATRIX s_instancepos(SRECT rect, parameters_t*p)
     SRECT r;
     makeMatrix(&m, p);
     r = swf_TurnRect(rect, &m);
-    if(currentrect.xmin == 0 && currentrect.ymin == 0 && 
+    if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
        currentrect.xmax == 0 && currentrect.ymax == 0)
        currentrect = r;
     else
@@ -386,12 +469,140 @@ static MATRIX s_instancepos(SRECT rect, parameters_t*p)
     return m;
 }
 
-void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
-{
-    SWF*swf = (SWF*)malloc(sizeof(SWF));
+void initBuiltIns()
+{
+    interpolation_t* new;
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_LINEAR;
+    dict_put(&interpolations, "linear", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_IN;
+    new->slope = 0;
+    dict_put(&interpolations, "quadIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_OUT;
+    new->slope = 0;
+    dict_put(&interpolations, "quadOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_IN_OUT;
+    new->slope = 0;
+    dict_put(&interpolations, "quadInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_IN;
+    new->slope = 0;
+    dict_put(&interpolations, "cubicIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_OUT;
+    new->slope = 0;
+    dict_put(&interpolations, "cubicOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_IN_OUT;
+    new->slope = 0;
+    dict_put(&interpolations, "cubicInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_IN;
+    new->slope = 0;
+    dict_put(&interpolations, "quartIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_OUT;
+    new->slope = 0;
+    dict_put(&interpolations, "quartOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_IN_OUT;
+    new->slope = 0;
+    dict_put(&interpolations, "quartInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_IN;
+    new->slope = 0;
+    dict_put(&interpolations, "quintIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_OUT;
+    new->slope = 0;
+    dict_put(&interpolations, "quintOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_IN_OUT;
+    new->slope = 0;
+    dict_put(&interpolations, "quintInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_IN;
+    dict_put(&interpolations, "circleIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_OUT;
+    dict_put(&interpolations, "circleOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_IN_OUT;
+    dict_put(&interpolations, "circleInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_IN;
+    dict_put(&interpolations, "exponentialIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_OUT;
+    dict_put(&interpolations, "exponentialOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_IN_OUT;
+    dict_put(&interpolations, "exponentialInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_IN;
+    dict_put(&interpolations, "sineIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_OUT;
+    dict_put(&interpolations, "sineOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_IN_OUT;
+    dict_put(&interpolations, "sineInOut", new);
 
+    RGBA c;
+    memset(&c, 0, sizeof(RGBA));
+    gradient_t* noGradient = (gradient_t*)malloc(sizeof(gradient_t));
+    noGradient->gradient.ratios = (U8*)malloc(16 * sizeof(U8));
+    noGradient->gradient.rgba = (RGBA*)malloc(16 * sizeof(RGBA));
+    noGradient->gradient.num = 2;
+    noGradient->gradient.rgba[0] = c;
+    noGradient->gradient.ratios[0] = 0;
+    noGradient->gradient.rgba[1] = c;
+    noGradient->gradient.ratios[1] = 255;
+    noGradient->radial = 0;
+    noGradient->rotate = 0;
+    dict_put(&gradients, "no_gradient", noGradient);
+
+    noFilters = 0;
+// put a no_filters entry in the filters dictionary to provoce a message when a user tries
+// to define a no_filters filter. The real filter=no_filters case is handled in parseFilters.
+    FILTER* dummy = (FILTER*)malloc(sizeof(FILTER));
+    dict_put(&filters, "no_filters", dummy);
+    noBlur = (FILTER_BLUR*) swf_NewFilter(FILTERTYPE_BLUR);
+    noBlur->passes = 1;
+    dict_put(&filters, "no_blur", noBlur);
+    noBevel = (FILTER_BEVEL*) swf_NewFilter(FILTERTYPE_BEVEL);
+    noBevel->passes = 1;
+    noBevel->composite = 1;
+    dict_put(&filters, "no_bevel", noBevel);
+    noDropshadow = (FILTER_DROPSHADOW*) swf_NewFilter(FILTERTYPE_DROPSHADOW);
+    noDropshadow->passes = 1;
+    noDropshadow->composite = 1;
+    dict_put(&filters, "no_dropshadow", noDropshadow);
+    noGradientGlow = (FILTER_GRADIENTGLOW*) swf_NewFilter(FILTERTYPE_GRADIENTGLOW);
+    noGradientGlow->passes = 1;
+    noGradientGlow->composite = 1;
+    noGradientGlow->gradient = &noGradient->gradient;
+    dict_put(&filters, "no_gradientglow", noGradientGlow);
+}
+
+void s_swf(const char*name, SRECT r, int version, int fps, int compress, RGBA background)
+{
     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;
@@ -401,17 +612,17 @@ 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(&instances);
-    dictionary_init(&fonts);
-    dictionary_init(&sounds);
+    dict_init(&characters, 16);
+    dict_init(&images, 16);
+    dict_init(&textures, 16);
+    dict_init(&outlines, 16);
+    dict_init(&gradients, 16);
+    dict_init(&filters, 16);
+    dict_init(&instances, 16);
+    dict_init(&sounds, 16);
+    dict_init(&interpolations, 16);
+    initBuiltIns();
+    cleanUp = &freeDictionaries;
 
     memset(&stack[stackpos], 0, sizeof(stack[0]));
     stack[stackpos].type = 0;
@@ -419,17 +630,18 @@ void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA backgrou
     stack[stackpos].swf = swf;
     stack[stackpos].oldframe = -1;
     stackpos++;
-    id = 0;
 
     currentframe = 0;
     memset(&currentrect, 0, sizeof(currentrect));
     currentdepth = 1;
-    
+
     memset(idmap, 0, sizeof(idmap));
+    idmap[0]=1; //main movie has ID 0
+
     incrementid();
 }
 
-void s_sprite(char*name)
+void s_sprite(const char*name, SRECT*scalegrid, const char*as3name)
 {
     tag = swf_InsertTag(tag, ST_DEFINESPRITE);
     swf_SetU16(tag, id); //id
@@ -444,9 +656,15 @@ void s_sprite(char*name)
     stack[stackpos].tag = tag;
     stack[stackpos].id = id;
     stack[stackpos].name = strdup(name);
-   
+    stack[stackpos].as3name = strdup(as3name);
+    if(scalegrid) {
+       stack[stackpos].scalegrid = *scalegrid;
+    } else {
+       memset(&stack[stackpos].scalegrid, 0, sizeof(SRECT));
+    }
+
     /* FIXME: those four fields should be bundled together */
-    dictionary_init(&instances);
+    dict_init(&instances, 16);
     currentframe = 0;
     currentdepth = 1;
     memset(&currentrect, 0, sizeof(currentrect));
@@ -472,7 +690,7 @@ typedef struct _button
 
 static button_t mybutton;
 
-void s_button(char*name)
+void s_button(const char*name, const char*as3name)
 {
     tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
     swf_SetU16(tag, id); //id
@@ -485,18 +703,19 @@ void s_button(char*name)
     stack[stackpos].tag = tag;
     stack[stackpos].id = id;
     stack[stackpos].name = strdup(name);
+    stack[stackpos].as3name = strdup(as3name);
     stack[stackpos].oldrect = currentrect;
     memset(&currentrect, 0, sizeof(currentrect));
 
     stackpos++;
     incrementid();
 }
-void s_buttonput(char*character, char*as, parameters_t p)
+void s_buttonput(const char*character, const char*as, parameters_t p)
 {
-    character_t* c = dictionary_lookup(&characters, character);
+    character_t* c = dict_lookup(&characters, character);
     MATRIX m;
     int flags = 0;
-    char*o = as,*s = as;
+    const char*o = as,*s = as;
     buttonrecord_t r;
     if(!stackpos || (stack[stackpos-1].type != 3))  {
        syntaxerror(".show may only appear in .button");
@@ -507,7 +726,7 @@ void s_buttonput(char*character, char*as, parameters_t p)
     if(mybutton.endofshapes) {
        syntaxerror("a .do may not precede a .show", character, character);
     }
-   
+
     m = s_instancepos(c->size, &p);
 
     r.id = c->id;
@@ -534,7 +753,7 @@ static void setbuttonrecords(TAG*tag)
     int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
     if(!mybutton.endofshapes) {
        int t;
-               
+
        if(!mybutton.records[3].set) {
            memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
        }
@@ -549,14 +768,18 @@ static void setbuttonrecords(TAG*tag)
     }
 }
 
-void s_buttonaction(int flags, char*action)
+void s_buttonaction(int flags, const char*action)
 {
     ActionTAG* a = 0;
     if(flags==0) {
        return;
     }
+    if(!stackpos || !stack[stackpos-1].tag ||
+            stack[stackpos-1].tag->id != ST_DEFINEBUTTON2) {
+        syntaxerror("Need to be inside a button for .on_* commands");
+    }
     setbuttonrecords(stack[stackpos-1].tag);
-    
+
     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
     if(!a) {
        syntaxerror("Couldn't compile ActionScript");
@@ -588,7 +811,7 @@ static void s_endButton()
     setbuttonrecords(stack[stackpos-1].tag);
     setactionend(stack[stackpos-1].tag);
     stackpos--;
-      
+
     swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
 
     r = currentrect;
@@ -597,6 +820,14 @@ static void s_endButton()
     currentrect = stack[stackpos].oldrect;
 
     s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
+
+    if(*stack[stackpos].as3name) {
+        tag = swf_InsertTag(tag, ST_SYMBOLCLASS);
+        swf_SetU16(tag, 1);
+        swf_SetU16(tag, stack[stackpos].id);
+        swf_SetString(tag, stack[stackpos].as3name);
+    }
+
     free(stack[stackpos].name);
 }
 
@@ -605,40 +836,193 @@ TAG* removeFromTo(TAG*from, TAG*to)
     TAG*save = from->prev;
     while(from!=to) {
        TAG*next = from->next;
-       swf_DeleteTag(from);
+       if(swf_isAllowedSpriteTag(from))
+           swf_DeleteTag(0, from);
        from = next;
     }
     save->next = 0;
     return save;
 }
 
+static int parametersChange(history_t* history, int frame)
+{
+    int willChange = 0;
+
+    willChange = willChange || history_change(history, frame, "x");
+    willChange = willChange || history_change(history, frame, "y");
+    willChange = willChange || history_change(history, frame, "scalex");
+    willChange = willChange || history_change(history, frame, "scaley");
+    willChange = willChange || history_change(history, frame, "cxform.r0");
+    willChange = willChange || history_change(history, frame, "cxform.g0");
+    willChange = willChange || history_change(history, frame, "cxform.b0");
+    willChange = willChange || history_change(history, frame, "cxform.a0");
+    willChange = willChange || history_change(history, frame, "cxform.r1");
+    willChange = willChange || history_change(history, frame, "cxform.g1");
+    willChange = willChange || history_change(history, frame, "cxform.b1");
+    willChange = willChange || history_change(history, frame, "cxform.a1");
+    willChange = willChange || history_change(history, frame, "rotate");
+    willChange = willChange || history_change(history, frame, "shear");
+    willChange = willChange || history_change(history, frame, "pivot.x");
+    willChange = willChange || history_change(history, frame, "pivot.y");
+    willChange = willChange || history_change(history, frame, "pin.x");
+    willChange = willChange || history_change(history, frame, "pin.y");
+    willChange = willChange || history_change(history, frame, "blendmode");
+    willChange = willChange || history_changeFilter(history, frame);
+
+    return willChange;
+}
+
+static void free_filterlist(FILTERLIST* f_list)
+{
+    int i;
+    for (i = 0; i < f_list->num; i++)
+    {
+        if(f_list->filter[i]->type == FILTERTYPE_GRADIENTGLOW)
+            gradient_free(((FILTER_GRADIENTGLOW*)f_list->filter[i])->gradient);
+        free(f_list->filter[i]);
+    }
+    free(f_list);
+}
+
+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_rotateValue(history, frame);
+    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->filters = history_filterValue(history, frame);
+}
+
+void setPlacement(TAG*tag, U16 id, U16 depth, MATRIX m, const 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 = (char*)name;
+    po.move = move;
+    if(move)
+    po.id = 0;
+    if(p->blendmode) {
+    po.blendmode = p->blendmode;
+    }
+    if(p->filters)
+       po.filters = p->filters;
+    swf_SetPlaceObject(tag, &po);
+}
+
+static void writeInstance(void* _i)
+{
+    instance_t*i = (instance_t*)_i;
+    parameters_t p;
+    MATRIX m;
+    int frame = i->history->firstFrame;
+    TAG* tag = i->history->firstTag;
+    history_processFlags(i->history);
+    while (tag && frame < currentframe)
+    {
+        frame++;
+        while (tag && tag->id != ST_SHOWFRAME)
+            tag = tag->next;
+        if(parametersChange(i->history, frame))
+        {
+            readParameters(i->history, &p, frame);
+            m = s_instancepos(i->character->size, &p);
+
+            if(p.blendmode || p.filters)
+               tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
+            else
+               tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+            setPlacement(tag, 0, i->depth, m, 0, &p, 1);
+            if(p.filters)
+               free_filterlist(p.filters);
+        } else if(tag) {
+            tag = tag->next;
+        }
+    }
+}
+
+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;
-    
+
+    stackpos--;
+    instance_t *i;
+
+    dict_foreach_value(&instances, writeInstance);
+
     if(stack[stackpos].cut)
        tag = removeFromTo(stack[stackpos].cut, tag);
 
-    stackpos--;
-   
+    // the writeInstance loop above may have inserted tags after what used to be the current tag,
+    // so let's make sure 'tag' point to the current tag again.
+    while (tag->next)
+       tag = tag->next;
+
+    tag = swf_InsertTag(tag, ST_SHOWFRAME);
+    tag = swf_InsertTag(tag, ST_END);
+
+    tag = stack[stackpos].tag;
+    swf_FoldSprite(tag);
+
+    if(stack[stackpos].scalegrid.xmin | stack[stackpos].scalegrid.ymin |
+       stack[stackpos].scalegrid.xmax | stack[stackpos].scalegrid.ymax) 
+    {
+       tag = swf_InsertTag(tag, ST_DEFINESCALINGGRID);
+       swf_SetU16(tag, stack[stackpos].id);
+       swf_SetRect(tag, &stack[stackpos].scalegrid);
+    }
+
+    if(tag->next != 0)
+        syntaxerror("internal error(7)");
     /* TODO: before clearing, prepend "<spritename>." to names and
              copy into old instances dict */
-    dictionary_clear(&instances);
+    dict_free_all(&instances, 1, free_instance);
 
     currentframe = stack[stackpos].oldframe;
     currentrect = stack[stackpos].oldrect;
     currentdepth = stack[stackpos].olddepth;
     instances = stack[stackpos].oldinstances;
 
-    tag = swf_InsertTag(tag, ST_SHOWFRAME);
-    tag = swf_InsertTag(tag, ST_END);
+    s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
+
+    if(*stack[stackpos].as3name) {
+        tag = swf_InsertTag(tag, ST_SYMBOLCLASS);
+        swf_SetU16(tag, 1);
+        swf_SetU16(tag, stack[stackpos].id);
+        swf_SetString(tag, stack[stackpos].as3name);
+    }   
 
-    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);
 }
 
@@ -647,38 +1031,66 @@ static void s_endSWF()
     int fi;
     SWF* swf;
     char*filename;
-    
+    char*mc="";
+
+    dict_foreach_value(&instances, writeInstance);
+
     if(stack[stackpos].cut)
        tag = removeFromTo(stack[stackpos].cut, tag);
 
     stackpos--;
-   
+
     swf = stack[stackpos].swf;
     filename = stack[stackpos].filename;
-  
+
+    // the writeInstance loop above may have inserted tags after what used yo be the current tag,
+    // so let's make sure 'tag' point to the current tag again.
+    while (tag->next)
+       tag = tag->next;
+
     //if(tag->prev && tag->prev->id != ST_SHOWFRAME)
     //    tag = swf_InsertTag(tag, ST_SHOWFRAME);
     tag = swf_InsertTag(tag, ST_SHOWFRAME);
 
+    if(stack[0].as3) {
+        TAG*tag = swf->firstTag;
+        tag = swf_InsertTag(tag, ST_DOABC);
+        void*code = as3_getcode();
+        swf_WriteABC(tag, code);
+        if(*mainclass)
+            mc = mainclass;
+        else if(as3_getglobalclass())
+            mc = as3_getglobalclass();
+        if(*mc) {
+            tag = swf_InsertTag(tag, ST_SYMBOLCLASS);
+            swf_SetU16(tag, 1);
+            swf_SetU16(tag, 0);
+            swf_SetString(tag, mc);
+        } else {
+            warning("no global public MovieClip subclass");
+        }
+        as3_destroy();
+    }
+
     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" */
     }
-    
+
     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");
     }
-    
-    if(do_cgi)
+
+    if(do_cgi || !strcmp(filename, "-"))
        fi = fileno(stdout);
     else
        fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
@@ -687,21 +1099,12 @@ static void s_endSWF()
     }
     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(&textures);
-    dictionary_clear(&outlines);
-    dictionary_clear(&gradients);
-    dictionary_clear(&fonts);
-    dictionary_clear(&sounds);
+
+    freeDictionaries();
 
     swf_FreeTags(swf);
     free(swf);
@@ -725,12 +1128,12 @@ int s_getframe()
     return currentframe+1;
 }
 
-void s_frame(int nr, int cut, char*name, char anchor)
+void s_frame(int nr, int cut, const char*name, char anchor)
 {
     int t;
     TAG*now = tag;
 
-    if(nr<1) 
+    if(nr<1)
        syntaxerror("Illegal frame number");
     nr--; // internally, frame 1 is frame 0
 
@@ -760,9 +1163,9 @@ void s_frame(int nr, int cut, char*name, char anchor)
     currentframe = nr;
 }
 
-int parseColor2(char*str, RGBA*color);
+int parseColor2(const char*str, RGBA*color);
 
-int addFillStyle(SHAPE*s, SRECT*r, char*name)
+int addFillStyle(SHAPE*s, SRECT*r, const char*name)
 {
     RGBA color;
     character_t*image;
@@ -771,9 +1174,9 @@ int addFillStyle(SHAPE*s, SRECT*r, char*name)
     if(name[0] == '#') {
        parseColor2(name, &color);
        return swf_ShapeAddSolidFillStyle(s, &color);
-    } else if ((texture = dictionary_lookup(&textures, name))) {
+    } else if((texture = dict_lookup(&textures, name))) {
        return swf_ShapeAddFillStyle2(s, &texture->fs);
-    } else if((image = dictionary_lookup(&images, name))) {
+    } else if((image = dict_lookup(&images, name))) {
        MATRIX m;
        swf_GetMatrix(0, &m);
        m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
@@ -781,13 +1184,13 @@ int addFillStyle(SHAPE*s, SRECT*r, char*name)
        m.tx = r->xmin;
        m.ty = r->ymin;
        return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
-    }  else if ((gradient = dictionary_lookup(&gradients, name))) {
+    }  else if((gradient = dict_lookup(&gradients, name))) {
        SRECT r2;
        MATRIX rot,m;
        double ccos,csin;
        swf_GetMatrix(0, &rot);
-       ccos = cos(-gradient->rotate*2*3.14159265358979/360);
-       csin = sin(-gradient->rotate*2*3.14159265358979/360);
+       ccos = cos(-gradient->rotate*2*M_PI/360);
+       csin = sin(-gradient->rotate*2*M_PI/360);
        rot.sx =  ccos*65536;
        rot.r1 = -csin*65536;
        rot.r0 =  csin*65536;
@@ -801,16 +1204,16 @@ int addFillStyle(SHAPE*s, SRECT*r, char*name)
        m.tx = r->xmin + (r->xmax - r->xmin)/2;
        m.ty = r->ymin + (r->ymax - r->ymin)/2;
        return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
-    }  else if (parseColor2(name, &color)) {
+    }  else if(parseColor2(name, &color)) {
        return swf_ShapeAddSolidFillStyle(s, &color);
     } else {
        syntaxerror("not a color/fillstyle: %s", name);
        return 0;
     }
 }
-       
+
 RGBA black={r:0,g:0,b:0,a:0};
-void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
+void s_box(const char*name, int width, int height, RGBA color, int linewidth, const char*texture)
 {
     SRECT r,r2;
     SHAPE* s;
@@ -842,18 +1245,18 @@ void s_box(char*name, int width, int height, RGBA color, int linewidth, char*tex
     swf_ShapeSetLine(tag,s,0,-height);
     swf_ShapeSetEnd(tag);
     swf_ShapeFree(s);
-   
+
     s_addcharacter(name, id, tag, r);
     incrementid();
 }
 
-void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
+void s_filled(const char*name, const char*outlinename, RGBA color, int linewidth, const char*texture)
 {
     SRECT rect,r2;
     SHAPE* s;
     int ls1,fs1=0;
     outline_t* outline;
-    outline = dictionary_lookup(&outlines, outlinename);
+    outline = dict_lookup(&outlines, outlinename);
     if(!outline) {
        syntaxerror("outline %s not defined", outlinename);
     }
@@ -867,7 +1270,7 @@ void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*textu
     }
     if(texture)
        fs1 = addFillStyle(s, &r2, texture);
-    
+
     swf_SetU16(tag,id);
     rect.xmin = r2.xmin-linewidth/2;
     rect.ymin = r2.ymin-linewidth/2;
@@ -877,7 +1280,7 @@ void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*textu
     swf_SetRect(tag,&rect);
     swf_SetShapeStyles(tag, s);
     swf_ShapeCountBits(s,0,0);
-    swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line, 
+    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);
@@ -887,7 +1290,7 @@ void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*textu
     incrementid();
 }
 
-void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
+void s_circle(const char*name, int r, RGBA color, int linewidth, const char*texture)
 {
     SRECT rect,r2;
     SHAPE* s;
@@ -916,22 +1319,22 @@ void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
     swf_ShapeSetCircle(tag, s, r,r,r,r);
     swf_ShapeSetEnd(tag);
     swf_ShapeFree(s);
-   
+
     s_addcharacter(name, id, tag, rect);
     incrementid();
 }
 
-void s_textshape(char*name, char*fontname, float size, char*_text)
+void s_textshape(const char*name, const char*fontname, float size, const char*_text)
 {
     int g;
     U8*text = (U8*)_text;
     outline_t* outline;
 
     SWFFONT*font;
-    font = dictionary_lookup(&fonts, fontname);
+    font = dict_lookup(&fonts, fontname);
     if(!font)
        syntaxerror("font \"%s\" not known!", fontname);
-    
+
     if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
        warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
        s_box(name, 0, 0, black, 20, 0);
@@ -943,59 +1346,86 @@ void s_textshape(char*name, char*fontname, float size, char*_text)
     memset(outline, 0, sizeof(outline_t));
     outline->shape = font->glyph[g].shape;
     outline->bbox = font->layout->bounds[g];
-    
+
     {
        drawer_t draw;
        swf_Shape11DrawerInit(&draw, 0);
-       swf_DrawText(&draw, font, (int)(size*100), _text);
+       swf_DrawText(&draw, font, (int)(size*100), (char*)_text);
        draw.finish(&draw);
        outline->shape = swf_ShapeDrawerToShape(&draw);
        outline->bbox = swf_ShapeDrawerGetBBox(&draw);
        draw.dealloc(&draw);
     }
 
-    if(dictionary_lookup(&outlines, name))
+    if(dict_lookup(&outlines, name))
        syntaxerror("outline %s defined twice", name);
-    dictionary_put2(&outlines, name, outline);
+    dict_put(&outlines, name, outline);
 }
 
-void s_text(char*name, char*fontname, char*text, int size, RGBA color)
+void s_text(const char*name, const char*fontname, const char*text, int size, RGBA color)
 {
     SRECT r;
     MATRIX _m,*m=0;
     SWFFONT*font;
-    font = dictionary_lookup(&fonts, fontname);
+    font = dict_lookup(&fonts, fontname);
     if(!font)
        syntaxerror("font \"%s\" not known!", fontname);
-    
+
     tag = swf_InsertTag(tag, ST_DEFINETEXT2);
     swf_SetU16(tag, id);
     if(!font->numchars) {
        s_box(name, 0, 0, black, 20, 0);
        return;
     }
-    r = swf_SetDefineText(tag, font, &color, text, size);
-   
+    r = swf_SetDefineText(tag, font, &color, (char*)text, size);
+
+    if(stack[0].swf->fileVersion >= 8) {
+       tag = swf_InsertTag(tag, ST_CSMTEXTSETTINGS);
+       swf_SetU16(tag, id);
+       swf_SetU8(tag, /*grid*/(1<<3)|/*flashtype*/0x40);
+       swf_SetU32(tag, 0);//thickness
+       swf_SetU32(tag, 0);//sharpness
+       swf_SetU8(tag, 0);//reserved
+    }
+
     s_addcharacter(name, id, tag, r);
     incrementid();
 }
 
-void s_quicktime(char*name, char*url)
+void s_quicktime(const char*name, const char*url)
 {
     SRECT r;
     MATRIX _m,*m=0;
 
     memset(&r, 0, sizeof(r));
-    
+
     tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
     swf_SetU16(tag, id);
     swf_SetString(tag, url);
-    
+
+    s_addcharacter(name, id, tag, r);
+    incrementid();
+}
+
+void s_video(const char *name, int width, int height)
+{
+    SRECT r;
+
+    memset(&r, 0, sizeof(r));
+
+    tag = swf_InsertTag(tag, ST_DEFINEVIDEOSTREAM);
+    swf_SetU16(tag, id);
+    swf_SetU16(tag, 0); // numframes
+    swf_SetU16(tag, width);
+    swf_SetU16(tag, height);
+    swf_SetU8(tag, 0); // videoflags
+    swf_SetU8(tag, 0); // codecid
+
     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)
+void s_edittext(const char*name, const char*fontname, int size, int width, int height, const char*text, RGBA*color, int maxlength, const char*variable, int flags, int align)
 {
     SWFFONT*font = 0;
     EditTextLayout layout;
@@ -1003,7 +1433,7 @@ void s_edittext(char*name, char*fontname, int size, int width, int height, char*
 
     if(fontname && *fontname) {
        flags |= ET_USEOUTLINES;
-       font = dictionary_lookup(&fonts, fontname);
+       font = dict_lookup(&fonts, fontname);
        if(!font)
            syntaxerror("font \"%s\" not known!", fontname);
     }
@@ -1018,8 +1448,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, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
+
+    swf_SetEditText(tag, flags, r, (char*)text, color, maxlength, font?font->id:0, size, &layout, (char*)variable);
 
     s_addcharacter(name, id, tag, r);
     incrementid();
@@ -1027,17 +1457,17 @@ void s_edittext(char*name, char*fontname, int size, int width, int height, char*
 
 /* type: either "jpeg" or "png"
  */
-void s_image(char*name, char*type, char*filename, int quality)
+void s_image(const char*name, const char*type, const char*filename, int quality)
 {
     /* an image is actually two folded: 1st bitmap, 2nd character.
        Both of them can be used separately */
-    
+
     /* step 1: the bitmap */
     SRECT r;
     int imageID = id;
-    int width, height;
+    unsigned width, height;
     if(!strcmp(type,"jpeg")) {
-#ifndef HAVE_LIBJPEG
+#ifndef HAVE_JPEGLIB
        warning("no jpeg support compiled in");
        s_box(name, 0, 0, black, 20, 0);
        return;
@@ -1045,7 +1475,7 @@ void s_image(char*name, char*type, char*filename, int quality)
        tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
        swf_SetU16(tag, imageID);
 
-       if(swf_SetJPEGBits(tag, filename, quality) < 0) {
+       if(swf_SetJPEGBits(tag, (char*)filename, quality) < 0) {
            syntaxerror("Image \"%s\" not found, or contains errors", filename);
        }
 
@@ -1063,7 +1493,7 @@ void s_image(char*name, char*type, char*filename, int quality)
        RGBA*data = 0;
        swf_SetU16(tag, imageID);
 
-       getPNG(filename, &width, &height, (unsigned char**)&data);
+       png_load(filename, &width, &height, (unsigned char**)&data);
 
        if(!data) {
            syntaxerror("Image \"%s\" not found, or contains errors", filename);
@@ -1073,6 +1503,7 @@ void s_image(char*name, char*type, char*filename, int 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;
@@ -1095,10 +1526,10 @@ void s_image(char*name, char*type, char*filename, int quality)
     incrementid();
 }
 
-void s_getBitmapSize(char*name, int*width, int*height)
+void s_getBitmapSize(const char*name, int*width, int*height)
 {
-    character_t* image = dictionary_lookup(&images, name);
-    gradient_t* gradient = dictionary_lookup(&gradients,name);
+    character_t* image = dict_lookup(&images, name);
+    gradient_t* gradient = dict_lookup(&gradients,name);
     if(image) {
        *width = image->size.xmax;
        *height = image->size.ymax;
@@ -1118,10 +1549,12 @@ void s_getBitmapSize(char*name, int*width, int*height)
     syntaxerror("No such bitmap/gradient: %s", name);
 }
 
-void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
+void s_texture(const char*name, const char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
 {
-    gradient_t* gradient = dictionary_lookup(&gradients, object);
-    character_t* bitmap = dictionary_lookup(&images, object);
+    if(dict_lookup(&textures, name))
+        syntaxerror("texture %s defined twice", name);
+    gradient_t* gradient = dict_lookup(&gradients, object);
+    character_t* bitmap = dict_lookup(&images, object);
     texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
     parameters_t p;
     FILLSTYLE*fs = &texture->fs;
@@ -1148,61 +1581,65 @@ void s_texture(char*name, char*object, int x, int y, float scalex, float scaley,
        fs->m.tx += p2.x;
        fs->m.ty += p2.y;
     }
+    if(bitmap) {
+       fs->m.sx *= 20;
+       fs->m.sy *= 20;
+    }
 
-    if(dictionary_lookup(&textures, name))
-       syntaxerror("texture %s defined twice", name);
-    dictionary_put2(&textures, name, texture);
+    dict_put(&textures, name, texture);
 }
 
-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");
-}
-    
-void s_font(char*name, char*filename)
+void s_createfont(const char*name, const char*filename, const char*glyphs, char flashtype)
 {
-    SWFFONT* font;
-    font = swf_LoadFont(filename);
-   
+    if(dict_lookup(&fonts, name))
+       syntaxerror("font %s defined twice", name);
+
+    SWFFONT* font = swf_LoadFont(filename, flashtype);
     if(font == 0) {
        warning("Couldn't open font file \"%s\"", filename);
        font = (SWFFONT*)malloc(sizeof(SWFFONT));
        memset(font, 0, sizeof(SWFFONT));
-       dictionary_put2(&fonts, name, font);
+       dict_put(&fonts, name, font);
        return;
     }
+    swf_FontPrepareForEditText(font);
 
-    if(0)
-    {
-       /* fix the layout. Only needed for old fonts */
-       int t;
-       for(t=0;t<font->numchars;t++) {
-           font->glyph[t].advance = 0;
+    if(!strcmp(glyphs, "all")) {
+       swf_FontUseAll(font);
+       font->use->glyphs_specified = 1;
+    } else {
+       if(!glyphs[0]) {
+           swf_FontInitUsage(font);
+       } else {
+           swf_FontUseUTF8(font, (const U8*)glyphs, 0xffff);
+           font->use->glyphs_specified = 1;
        }
-       font->layout = 0;
-       swf_FontCreateLayout(font);
     }
-    /* just in case this thing is used in .edittext later on */
-    swf_FontPrepareForEditText(font);
+    dict_put(&fonts, name, font);
+}
 
+void s_font(const char*name, const char*filename)
+{
+    SWFFONT* font;
+    font = dict_lookup(&fonts, name);
     font->id = id;
-    tag = swf_InsertTag(tag, ST_DEFINEFONT2);
+    swf_FontReduce_swfc(font);
+       
+    if(font->version>=3 && stack[0].swf->fileVersion < 8) {
+       warning("flashtype not supported for flash versions 8 and below");
+    }
+
+    tag = swf_InsertTag(tag, font->version==3?ST_DEFINEFONT3: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);
+    if(do_exports) {
+       tag = swf_InsertTag(tag, ST_EXPORTASSETS);
+       swf_SetU16(tag, 1);
+       swf_SetU16(tag, id);
+       swf_SetString(tag, name);
+    }
+
+    incrementid();
 }
 
 
@@ -1213,52 +1650,59 @@ typedef struct _sound_t
     TAG*tag;
 } sound_t;
 
-void s_sound(char*name, char*filename)
+void s_sound(const char*name, const char*filename)
 {
     struct WAV wav, wav2;
     struct MP3 mp3;
     sound_t* sound;
     U16*samples = NULL;
-    unsigned numsamples;
+    unsigned numsamples = 1;
     unsigned blocksize = 1152;
     int is_mp3 = 0;
 
-    if(wav_read(&wav, filename)) {
+    if(dict_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);
+        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;
-       }
+        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(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;
-       samples = samples2;
+        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);
@@ -1270,33 +1714,31 @@ void s_sound(char*name, char*filename)
                 mp3.SampRate,
                 mp3.Channels,
                 mp3.NumFrames);
-       mp3_clear(&mp3);
+        mp3_clear(&mp3);
     }
     else
-    {
         swf_SetSoundDefine(tag, samples, numsamples);
+
+    if(do_exports) {
+       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);
     }
-    
-    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);
-    
+    dict_put(&sounds, name, sound);
+
     incrementid();
 
     if(samples)
-       free(samples);
+        free(samples);
 }
 
 static char* gradient_getToken(const char**p)
@@ -1305,7 +1747,7 @@ static char* gradient_getToken(const char**p)
     char*result;
     while(**p && strchr(" \t\n\r", **p)) {
        (*p)++;
-    } 
+    }
     start = *p;
     while(**p && !strchr(" \t\n\r", **p)) {
        (*p)++;
@@ -1316,8 +1758,8 @@ static char* gradient_getToken(const char**p)
     return result;
 }
 
-float parsePercent(char*str);
-RGBA parseColor(char*str);
+float parsePercent(const char*str);
+RGBA parseColor(const char*str);
 
 GRADIENT parseGradient(const char*str)
 {
@@ -1325,34 +1767,86 @@ GRADIENT parseGradient(const char*str)
     int lastpos = -1;
     const char* p = str;
     memset(&gradient, 0, sizeof(GRADIENT));
-    while(*p) {
-       char*posstr,*colorstr;
-       int pos;
-       RGBA color;
-       posstr = gradient_getToken(&p);
-       if(!*posstr)
-           break;
-       pos = (int)(parsePercent(posstr)*255.0);
-       if(pos == lastpos)
-           pos++;
-       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] = pos;
-       gradient.rgba[gradient.num] = color;
-       gradient.num++;
-       free(posstr);
-       free(colorstr);
-       lastpos = pos;
-    }
     return gradient;
 }
 
-void s_gradient(char*name, const char*text, int radial, int rotate)
+FILTERLIST* parseFilters(char* list)
+{
+    if(!strcmp(list, "no_filters"))
+       return 0;
+    FILTER* f;
+    FILTERLIST* f_list = (FILTERLIST*)malloc(sizeof(FILTERLIST));
+    f_list->num = 0;
+    char* f_start = list;
+    char* f_end;
+    while (f_start)
+    {
+       f_end = strchr(f_start, ',');
+       if(f_end)
+           *f_end = '\0';
+       f = dict_lookup(&filters, f_start);
+       if(!f)
+       {
+           free(f_list);
+           syntaxerror("unknown filter %s", f_start);
+       }
+       if(f_list->num == 8)
+       {
+           warning("too many filters in filterlist, no more than 8 please, rest ignored");
+           break;
+       }
+       f_list->filter[f_list->num] = f;
+       f_list->num++;
+       if(f_end)
+       {
+           *f_end = ',';
+           f_start = f_end + 1;
+       }
+       else
+           f_start = 0;
+    }
+    return f_list;
+}
+
+void s_gradient(const char*name, const char*text, int radial, int rotate)
 {
     gradient_t* gradient;
     gradient = malloc(sizeof(gradient_t));
@@ -1361,24 +1855,115 @@ void s_gradient(char*name, const char*text, int radial, int rotate)
     gradient->radial = radial;
     gradient->rotate = rotate;
 
-    if(dictionary_lookup(&gradients, name))
-       syntaxerror("gradient %s defined twice", name);
-    dictionary_put2(&gradients, name, gradient);
+    dict_put(&gradients, name, gradient);
 }
 
-void s_action(const char*text)
+void s_gradientglow(const char*name, const char*gradient, float blurx, float blury,
+                   float angle, float distance, float strength, char innershadow,
+                   char knockout, char composite, char ontop, int passes)
 {
-    ActionTAG* a = 0;
-    a = swf_ActionCompile(text, stack[0].swf->fileVersion);
-    if(!a) {
-       syntaxerror("Couldn't compile ActionScript");
-    }
+    if(dict_lookup(&filters, name))
+        syntaxerror("filter %s defined twice", name);
 
-    tag = swf_InsertTag(tag, ST_DOACTION);
+    gradient_t* g = dict_lookup(&gradients, gradient);
+    if(!g)
+       syntaxerror("unknown gradient %s", gradient);
 
-    swf_ActionSet(tag, a);
+    composite = 1;
 
-    swf_ActionFree(a);
+    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;
+
+    dict_put(&filters, name, filter);
+}
+
+void s_dropshadow(const char*name, RGBA color, double blurx, double blury, double angle, double distance, double strength, char innershadow, char knockout, char composite, int passes)
+{
+    if(dict_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;
+
+    dict_put(&filters, name, filter);
+}
+
+void s_bevel(const 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(dict_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;
+
+    dict_put(&filters, name, filter);
+}
+
+void s_blur(const char*name, double blurx, double blury, int passes)
+{
+    if(dict_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;
+
+    dict_put(&filters, name, filter);
+}
+
+void s_action(const char*text)
+{
+    if(stack[0].swf->fileVersion < 9) {
+        ActionTAG* a = 0;
+        a = swf_ActionCompile(text, stack[0].swf->fileVersion);
+        if(!a) {
+            swf_ActionFree(a);
+            syntaxerror("Couldn't compile ActionScript");
+        }
+        tag = swf_InsertTag(tag, ST_DOACTION);
+        swf_ActionSet(tag, a);
+        swf_ActionFree(a);
+    } else {
+        as3_parse_bytearray(stack[0].filename, text, strlen(text));
+        stack[0].as3 = 1;
+    }
 }
 
 void s_initaction(const char*character, const char*text)
@@ -1386,11 +1971,13 @@ 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) {
-       syntaxerror("Couldn't compile ActionScript");
+    if(!a)
+    {
+        swf_ActionFree(a);
+        syntaxerror("Couldn't compile ActionScript");
     }
 
-    c = (character_t*)dictionary_lookup(&characters, character);
+    c = (character_t*)dict_lookup(&characters, character);
 
     tag = swf_InsertTag(tag, ST_DOINITACTION);
     swf_SetU16(tag, c->id);
@@ -1399,12 +1986,12 @@ void s_initaction(const char*character, const char*text)
     swf_ActionFree(a);
 }
 
-int s_swf3action(char*name, char*action)
+int s_swf3action(const char*name, const char*action)
 {
     ActionTAG* a = 0;
     instance_t* object = 0;
-    if(name) 
-       object = (instance_t*)dictionary_lookup(&instances, name);
+    if(name)
+       object = (instance_t*)dict_lookup(&instances, name);
     if(!object && name && *name) {
        /* we have a name, but couldn't find it. Abort. */
        return 0;
@@ -1423,15 +2010,18 @@ int s_swf3action(char*name, char*action)
     return 1;
 }
 
-void s_outline(char*name, char*format, char*source)
+void s_outline(const char*name, const char*format, const char*source)
 {
+    if(dict_lookup(&outlines, name))
+        syntaxerror("outline %s defined twice", name);
+
     outline_t* outline;
 
     drawer_t draw;
     SHAPE* shape;
     SHAPE2* shape2;
     SRECT bounds;
-    
+
     //swf_Shape10DrawerInit(&draw, 0);
     swf_Shape11DrawerInit(&draw, 0);
 
@@ -1440,23 +2030,21 @@ void s_outline(char*name, char*format, char*source)
     shape = swf_ShapeDrawerToShape(&draw);
     bounds = swf_ShapeDrawerGetBBox(&draw);
     draw.dealloc(&draw);
-    
+
     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);
+
+    dict_put(&outlines, name, outline);
 }
 
-int s_playsound(char*name, int loops, int nomultiple, int stop)
+int s_playsound(const char*name, int loops, int nomultiple, int stop)
 {
     sound_t* sound;
     SOUNDINFO info;
     if(!name)
        return 0;
-    sound = dictionary_lookup(&sounds, name);
+    sound = dict_lookup(&sounds, name);
     if(!sound)
        return 0;
 
@@ -1470,7 +2058,7 @@ int s_playsound(char*name, int loops, int nomultiple, int stop)
     return 1;
 }
 
-void s_includeswf(char*name, char*filename)
+void s_includeswf(const char*name, const char*filename)
 {
     int f;
     SWF swf;
@@ -1480,25 +2068,25 @@ void s_includeswf(char*name, char*filename)
     int level = 0;
     U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
     f = open(filename,O_RDONLY|O_BINARY);
-    if (f<0) { 
+    if(f<0) {
        warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
        s_box(name, 0, 0, black, 20, 0);
        return;
     }
-    if (swf_ReadSWF(f,&swf)<0) { 
+    if(swf_ReadSWF(f,&swf)<0) {
        warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
        s_box(name, 0, 0, black, 20, 0);
        return;
     }
     close(f);
 
-    /* FIXME: The following sets the bounding Box for the character. 
+    /* FIXME: The following sets the bounding Box for the character.
               It is wrong for two reasons:
              a) It may be too small (in case objects in the movie clip at the borders)
              b) it may be too big (because the poor movie never got autocropped)
     */
     r = swf.movieSize;
-    
+
     s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
     swf_SetU16(tag, id);
     swf_SetU16(tag, swf.frameCount);
@@ -1524,7 +2112,7 @@ void s_includeswf(char*name, char*filename)
        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. 
+              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);
@@ -1542,30 +2130,60 @@ void s_includeswf(char*name, char*filename)
     s_addcharacter(name, id, tag, r);
     incrementid();
 }
-SRECT s_getCharBBox(char*name)
+SRECT s_getCharBBox(const char*name)
 {
-    character_t* c = dictionary_lookup(&characters, name);
+    character_t* c = dict_lookup(&characters, name);
     if(!c) syntaxerror("character '%s' unknown(2)", name);
     return c->size;
 }
-SRECT s_getInstanceBBox(char*name)
+SRECT s_getInstanceBBox(const char*name)
 {
-    instance_t * i = dictionary_lookup(&instances, name);
+    instance_t * i = dict_lookup(&instances, name);
     character_t * c;
     if(!i) syntaxerror("instance '%s' unknown(4)", name);
     c = i->character;
     if(!c) syntaxerror("internal error(5)");
     return c->size;
 }
-parameters_t s_getParameters(char*name)
-{
-    instance_t * i = dictionary_lookup(&instances, name);
-    if(!i) syntaxerror("instance '%s' unknown(10)", name);
-    return i->parameters;
-}
-void s_startclip(char*instance, char*character, parameters_t p)
+void s_getParameters(const char*name, parameters_t* p)
 {
-    character_t* c = dictionary_lookup(&characters, character);
+    instance_t * i = dict_lookup(&instances, name);
+    if(!i)
+       syntaxerror("instance '%s' unknown(10)", name);
+    if(change_sets_all)
+        readParameters(i->history, p, currentframe);
+    else
+       *p = i->parameters;
+}
+
+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->filters);
+    history_begin(i->history, "flags", currentframe, tag, 0);
+}
+
+void s_startclip(const char*instance, const char*character, parameters_t p)
+{
+    character_t* c = dict_lookup(&characters, character);
     instance_t* i;
     MATRIX m;
     if(!c) {
@@ -1574,17 +2192,16 @@ void s_startclip(char*instance, char*character, parameters_t p)
     i = s_addinstance(instance, c, currentdepth);
     i->parameters = p;
     m = s_instancepos(i->character->size, &p);
-    
+
     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
     /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
-    i->lastTag = tag;
-    i->lastFrame= currentframe;
 
     stack[stackpos].tag = tag;
     stack[stackpos].type = 2;
     stackpos++;
 
+    setStartparameters(i, &p, tag);
     currentdepth++;
 }
 void s_endClip()
@@ -1594,168 +2211,177 @@ void s_endClip()
     swf_SetTagPos(stack[stackpos].tag, 0);
     swf_GetPlaceObject(stack[stackpos].tag, &p);
     p.clipdepth = currentdepth;
-    p.name = 0;
+    //p.name = 0;
     swf_ClearTag(stack[stackpos].tag);
     swf_SetPlaceObject(stack[stackpos].tag, &p);
     currentdepth++;
 }
 
-void s_put(char*instance, char*character, parameters_t p)
+void s_put(const char*instance, const char*character, parameters_t p)
 {
-    character_t* c = dictionary_lookup(&characters, character);
+    character_t* c = dict_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);
-    i->lastTag = tag;
-    i->lastFrame = currentframe;
+
+    if(p.blendmode || p.filters)
+    {
+        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);
     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);
+    }
+    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.filters, inter);
+}
 
-    i->parameters = p;
-    m = s_instancepos(i->character->size, &p);
+void s_jump(const char* instance, parameters_t p)
+{
+    instance_t* i = dict_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p, CF_JUMP, 0);
+}
 
-    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
-    swf_ObjectMove(tag, i->depth, &m, &p.cxform);
-    i->lastTag = tag;
-    i->lastFrame = currentframe;
+void s_change(const char*instance, parameters_t p, interpolation_t* inter)
+{
+    instance_t* i = dict_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p, CF_CHANGE, inter);
 }
 
-parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
+void s_sweep(const char* instance, parameters_t p, float radius, int clockwise, int short_arc, interpolation_t* inter)
 {
-    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 = dict_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    history_rememberSweep(i->history, currentframe, p.x, p.y, radius, clockwise, short_arc, inter);
 }
 
-void s_change(char*instance, parameters_t p2)
+void s_toggle(const char* instance, U16 flagsOn, U16 flagsOff)
 {
-    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);
-    }
+    instance_t* i = dict_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    U16 flags = (U16)history_value(i->history, currentframe, "flags");
+    flags |= flagsOn;
+    flags &= flagsOff;
+    history_remember(i->history, "flags", currentframe, CF_JUMP, flags, 0);
 }
 
-void s_delinstance(char*instance)
+void s_delinstance(const char*instance)
 {
-    instance_t* i = dictionary_lookup(&instances, instance);
-    if(!i) {
-       syntaxerror("instance %s not known", instance);
-    }
+    instance_t* i = dict_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    writeInstance(i);
     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
     swf_SetU16(tag, i->depth);
-    dictionary_del(&instances, instance);
+    dict_del(&instances, instance);
+    free(i);
 }
 
-void s_qchange(char*instance, parameters_t p)
+void s_schange(const char*instance, parameters_t p, interpolation_t* inter)
 {
+    instance_t* i = dict_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p, CF_SCHANGE, inter);
 }
 
 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");
+    }
 }
 
 // ------------------------------------------------------------------------
 
 typedef int command_func_t(map_t*args);
 
-SRECT parseBox(char*str)
+SRECT parseBox(const 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;
@@ -1793,11 +2419,11 @@ error:
     syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
     return r;
 }
-float parseFloat(char*str)
+float parseFloat(const char*str)
 {
     return atof(str);
 }
-int parseInt(char*str)
+int parseInt(const char*str)
 {
     int t;
     int l=strlen(str);
@@ -1810,7 +2436,7 @@ int parseInt(char*str)
            syntaxerror("Not an Integer: \"%s\"", str);
     return atoi(str);
 }
-int parseTwip(char*str)
+static double parseRawTwip(const char*str)
 {
     char*dot;
     int sign=1;
@@ -1823,19 +2449,26 @@ int parseTwip(char*str)
     if(!dot) {
        int l=strlen(str);
        int t;
-       return sign*parseInt(str)*20;
+       return sign*parseInt(str);
     } else {
        char* old = strdup(str);
        int l=strlen(dot+1);
-       char*s;
+       const 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);
-       }
+       for(s=str;s<dot-1;s++) {
+            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;
@@ -1844,16 +2477,167 @@ int parseTwip(char*str)
        }
        free(old);
        if(l==0)
-           return sign*atoi(str)*20;
+           return sign*(atoi(str));
        if(l==1)
-           return sign*atoi(str)*20+atoi(dot)*2;
+           return sign*(atoi(str)+0.1*atoi(dot));
        if(l==2)
-           return sign*atoi(str)*20+atoi(dot)/5;
+           return sign*(atoi(str)+0.01*atoi(dot));
     }
     return 0;
 }
 
-int isPoint(char*str)
+static dict_t defines;
+static int defines_initialized = 0;
+static mem_t define_values;
+
+static double parseNameOrTwip(const char*s)
+{
+    int l = 0;
+    double v;
+    if(defines_initialized) {
+        l = (int)dict_lookup(&defines, s);
+    }
+    if(l) {
+        return *(int*)&define_values.buffer[l-1];
+    } else {
+        return parseRawTwip(s);
+    }
+}
+
+/* automatically generated by yiyiyacc, http://www.quiss.org/yiyiyacc/ */
+static double parseExpression(char*s)
+{
+    int chr2index[256];
+    memset(chr2index, -1, sizeof(chr2index));
+    chr2index['+'] = 0;
+    chr2index['-'] = 1;
+    chr2index['*'] = 2;
+    chr2index['/'] = 3;
+    chr2index['('] = 5;
+    chr2index[')'] = 6;
+    chr2index['\0'] = 7;
+
+    int stackpos = 1;
+    int stack[256];
+    double values[256];
+    stack[0]=0;
+    values[0]=0;
+    int accept = 18;
+    int left[10]={11,8,8,8,8,9,9,9,10,10}; //production left side
+    int plen[10]={1,3,2,3,1,3,3,1,1,3}; //production size
+    int table[18][12] = {
+        {0, 4, 0, 0, 5, 6, 0, 0, 1, 2, 3, 0},
+        {7, 8, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0},
+        {-4, -4, 9, 10, 0, 0, -4, -4, 0, 0, 0, 0},
+        {-7, -7, -7, -7, 0, 0, -7, -7, 0, 0, 0, 0},
+        {0, 0, 0, 0, 5, 6, 0, 0, 0, 11, 3, 0},
+        {-8, -8, -8, -8, 0, 0, -8, -8, 0, 0, 0, 0},
+        {0, 4, 0, 0, 5, 6, 0, 0, 12, 2, 3, 0},
+        {0, 0, 0, 0, 5, 6, 0, 0, 0, 13, 3, 0},
+        {0, 0, 0, 0, 5, 6, 0, 0, 0, 14, 3, 0},
+        {0, 0, 0, 0, 5, 6, 0, 0, 0, 0, 15, 0},
+        {0, 0, 0, 0, 5, 6, 0, 0, 0, 0, 16, 0},
+        {-2, -2, 9, 10, 0, 0, -2, -2, 0, 0, 0, 0},
+        {7, 8, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0},
+        {-1, -1, 9, 10, 0, 0, -1, -1, 0, 0, 0, 0},
+        {-3, -3, 9, 10, 0, 0, -3, -3, 0, 0, 0, 0},
+        {-5, -5, -5, -5, 0, 0, -5, -5, 0, 0, 0, 0},
+        {-6, -6, -6, -6, 0, 0, -6, -6, 0, 0, 0, 0},
+        {-9, -9, -9, -9, 0, 0, -9, -9, 0, 0, 0, 0}};
+
+    char*p = s;
+    while(1) {
+        char*pnext = p+1;
+        int action;
+        double value = 0;
+        if(!stackpos) {
+            fprintf(stderr, "Error in expression\n");
+            return 0.0;
+        }
+
+        if(chr2index[*p]<0) {
+            action = table[stack[stackpos-1]][4];
+            if(action>0) {
+                while(chr2index[*pnext]<0) 
+                    pnext++;
+                char save = *pnext;
+                *pnext = 0;
+                value = parseNameOrTwip(p);
+                *pnext = save;
+            }
+        } else {
+            action = table[stack[stackpos-1]][chr2index[*p]];
+        }
+
+        if(action == accept) {
+            return values[stack[stackpos-1]];
+        } else if(action>0) { // shift
+            if(stackpos>254) {
+                fprintf(stderr, "Stack overflow while parsing expression\n");
+                return 0.0;
+            }
+            values[stackpos]=value;
+            stack[stackpos++]=action;
+            p=pnext;
+        } else if(action<0) { // reduce
+            stackpos-=plen[-action];
+            stack[stackpos] = table[stack[stackpos-1]][left[-action]];
+            switch(-action) {
+              case 1:
+                values[stackpos] = values[stackpos+0] + values[stackpos+2];
+              break;
+              case 2:
+                values[stackpos] = 0 - values[stackpos+1];
+              break;
+              case 3:
+                values[stackpos] = values[stackpos+0] - values[stackpos+2];
+              break;
+              case 5:
+                values[stackpos] = values[stackpos+0] * values[stackpos+2];
+              break;
+              case 6:
+                values[stackpos] = values[stackpos+0] / values[stackpos+2];
+              break;
+              case 9:
+                values[stackpos] = values[stackpos+1];
+              break;
+            }
+            stackpos++;
+        } else {
+            fprintf(stderr, "Syntax error in expression\n");
+            return 0.0;
+        }
+    }
+}
+
+int parseTwip(const char*str)
+{
+    char*str2 = (char*)str;
+    int v = (int)(parseExpression(str2)*20);
+    return v;
+}
+
+int parseArc(const char* str)
+{
+    if(!strcmp(str, "short"))
+       return 1;
+    if(!strcmp(str, "long"))
+       return 0;
+    syntaxerror("invalid value for the arc parameter: %s", str);
+    return 1;
+}
+
+int parseDir(const char* str)
+{
+    if(!strcmp(str, "clockwise"))
+       return 1;
+    if(!strcmp(str, "counterclockwise"))
+       return 0;
+    syntaxerror("invalid value for the dir parameter: %s", str);
+    return 1;
+}
+
+int isPoint(const char*str)
 {
     if(strchr(str, '('))
        return 1;
@@ -1861,7 +2645,7 @@ int isPoint(char*str)
        return 0;
 }
 
-SPOINT parsePoint(char*str)
+SPOINT parsePoint(const char*str)
 {
     SPOINT p;
     char tmp[80];
@@ -1876,7 +2660,7 @@ SPOINT parsePoint(char*str)
     return p;
 }
 
-int parseColor2(char*str, RGBA*color)
+int parseColor2(const char*str, RGBA*color)
 {
     int l = strlen(str);
     int r,g,b,a;
@@ -1906,19 +2690,25 @@ int parseColor2(char*str, RGBA*color)
        color->r = r; color->g = g; color->b = b; color->a = a;
        return 1;
     }
+    int len=strlen(str);
+    int 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;
        }
     return 0;
 
 }
-RGBA parseColor(char*str)
+RGBA parseColor(const char*str)
 {
     RGBA c;
     if(!parseColor2(str, &c))
@@ -1931,7 +2721,7 @@ typedef struct _muladd {
     S16 add;
 } MULADD;
 
-MULADD parseMulAdd(char*str)
+MULADD parseMulAdd(const char*str)
 {
     float add, mul;
     char* str2 = (char*)malloc(strlen(str)+5);
@@ -1974,7 +2764,7 @@ MULADD mergeMulAdd(MULADD m1, MULADD m2)
     return r;
 }
 
-float parsePxOrPercent(char*fontname, char*str)
+float parsePxOrPercent(const char*fontname, const char*str)
 {
     int l = strlen(str);
     if(strchr(str, '%'))
@@ -1987,22 +2777,22 @@ float parsePxOrPercent(char*fontname, char*str)
     return 0;
 }
 
-float parsePercent(char*str)
+float parsePercent(const char*str)
 {
     int l = strlen(str);
     if(!l)
        return 1.0;
     if(str[l-1]=='%') {
-       return atoi(str)/100.0;
+       return atof(str)/100.0;
     }
     syntaxerror("Expression '%s' is not a percentage", str);
     return 0;
 }
-int isPercent(char*str)
+int isPercent(const char*str)
 {
     return str[strlen(str)-1]=='%';
 }
-int parseNewSize(char*str, int size)
+int parseNewSize(const char*str, int size)
 {
     if(isPercent(str))
        return parsePercent(str)*size;
@@ -2016,9 +2806,9 @@ int isColor(char*str)
     return parseColor2(str, &c);
 }
 
-static char* lu(map_t* args, char*name)
+static const char* lu(map_t* args, char*name)
 {
-    char* value = map_lookup(args, name);
+    const char* value = map_lookup(args, name);
     if(!value) {
        map_dump(args, stdout, "");
        syntaxerror("internal error 2: value %s should be set", name);
@@ -2026,15 +2816,17 @@ static char* lu(map_t* args, char*name)
     return value;
 }
 
-static int c_flash(map_t*args) 
+static int c_flash(map_t*args)
 {
-    char* filename = map_lookup(args, "filename");
-    char* compressstr = lu(args, "compress");
+    const char* filename = map_lookup(args, "filename");
+    const char* compressstr = lu(args, "compress");
+    const char* change_modestr = lu(args, "change-sets-all");
+    const char* exportstr = lu(args, "export");
     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"));
+    int compress = 0;
 
     if(!filename || !*filename) {
        /* for compatibility */
@@ -2049,24 +2841,33 @@ static int c_flash(map_t*args)
 
     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);
 
+    if(!strcmp(change_modestr, "yes"))
+       change_sets_all = 1;
+    else
+       if(strcmp(change_modestr, "no"))
+           syntaxerror("value \"%s\" not supported for the change-sets-all argument", change_modestr);
+
+    do_exports=atoi(exportstr);
+    mainclass=strdup(lu(args, "mainclass"));
+
     s_swf(filename, bbox, version, fps, compress, color);
     return 0;
 }
-int isRelative(char*str)
+int isRelative(const char*str)
 {
     return !strncmp(str, "<plus>", 6) ||
           !strncmp(str, "<minus>", 7);
 }
-char* getOffset(char*str)
+const char* getOffset(const char*str)
 {
     if(!strncmp(str, "<plus>", 6))
        return str+6;
@@ -2075,7 +2876,7 @@ char* getOffset(char*str)
     syntaxerror("internal error (347)");
     return 0;
 }
-int getSign(char*str)
+int getSign(const char*str)
 {
     if(!strncmp(str, "<plus>", 6))
        return 1;
@@ -2084,42 +2885,121 @@ int getSign(char*str)
     syntaxerror("internal error (348)");
     return 0;
 }
-static dictionary_t points;
+
+static dict_t points;
 static mem_t mpoints;
-int points_initialized = 0;
+static int points_initialized = 0;
+
+static int c_interpolation(map_t *args)
+{
+    int i;
+    const char* name = lu(args, "name");
+    if(dict_lookup(&interpolations, name))
+        syntaxerror("interpolation %s defined twice", name);
+
+    interpolation_t* inter = (interpolation_t*)malloc(sizeof(interpolation_t));
+    const 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 = parseTwip(lu(args, "amplitude"));
+    inter->growth = parseFloat(lu(args, "growth"));
+    inter->bounces = parseInt(lu(args, "bounces"));
+    inter->damping = parseFloat(lu(args, "damping"));
+    inter->slope = parseFloat(lu(args, "slope"));
+
+    dict_put(&interpolations, name, inter);
+    return 0;
+}
 
-SPOINT getPoint(SRECT r, char*name)
+SPOINT getPoint(SRECT r, const char*name)
 {
     int l=0;
     if(!strcmp(name, "center")) {
-       SPOINT p;
-       p.x = (r.xmin + r.xmax)/2;
-       p.y = (r.ymin + r.ymax)/2;
-       return p;
+        SPOINT p;
+        p.x = (r.xmin + r.xmax)/2;
+        p.y = (r.ymin + r.ymax)/2;
+        return p;
+    }
+    if(!strcmp(name, "bottom-center")) {
+        SPOINT p;
+        p.x = (r.xmin + r.xmax)/2;
+        p.y = r.ymax;
+        return p;
+    }
+    if(!strcmp(name, "top-center")) {
+        SPOINT p;
+        p.x = (r.xmin + r.xmax)/2;
+        p.y = r.ymin;
+        return p;
+    }
+    if(!strcmp(name, "top-left")) {
+        SPOINT p;
+        p.x = r.xmin;
+        p.y = r.ymin;
+        return p;
+    }
+    if(!strcmp(name, "top-right")) {
+        SPOINT p;
+        p.x = r.xmax;
+        p.y = r.ymin;
+        return p;
+    }
+    if(!strcmp(name, "bottom-right")) {
+        SPOINT p;
+        p.x = r.xmax;
+        p.y = r.ymax;
+        return p;
+    }
+    if(!strcmp(name, "bottom-left")) {
+        SPOINT p;
+        p.x = r.xmin;
+        p.y = r.ymax;
+        return p;
+    }
+    if(!strcmp(name, "left-center")) {
+        SPOINT p;
+        p.x = r.xmin;
+        p.y = (r.ymin + r.ymax)/2;
+        return p;
+    }
+    if(!strcmp(name, "right-center")) {
+        SPOINT p;
+        p.x = r.xmax;
+        p.y = (r.ymin + r.ymax)/2;
+        return p;
     }
 
+
     if(points_initialized)
-       l = (int)dictionary_lookup(&points, name);
+        l = (int)dict_lookup(&points, name);
     if(l==0) {
-       syntaxerror("Invalid point: \"%s\".", name);
+        syntaxerror("Invalid point: \"%s\".", name);
     }
-    l--;
-    return *(SPOINT*)&mpoints.buffer[l];
+    return *(SPOINT*)&mpoints.buffer[l-1];
 }
 
-static int texture2(char*name, char*object, map_t*args, int errors) 
+
+static int texture2(const char*name, const 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");
+    const char*xstr = map_lookup(args, "x");
+    const char*ystr = map_lookup(args, "y");
+    const char*widthstr = map_lookup(args, "width");
+    const char*heightstr = map_lookup(args, "height");
+    const char*scalestr = map_lookup(args, "scale");
+    const char*scalexstr = map_lookup(args, "scalex");
+    const char*scaleystr = map_lookup(args, "scaley");
+    const char*rotatestr = map_lookup(args, "rotate");
+    const char* shearstr = map_lookup(args, "shear");
+    const char* radiusstr = map_lookup(args, "r");
     float x=0,y=0;
     float scalex = 1.0, scaley = 1.0;
     float rotate=0, shear=0;
@@ -2169,22 +3049,25 @@ static int texture2(char*name, char*object, map_t*args, int errors)
     return 0;
 }
 
-static int c_texture(map_t*args) 
+static int c_texture(map_t*args)
 {
-    char*name = lu(args, "instance");
-    char*object = lu(args, "character");
+    const char*name = lu(args, "instance");
+    const char*object = lu(args, "character");
     return texture2(name, object, args, 1);
 }
 
-static int c_gradient(map_t*args) 
+static int c_gradient(map_t*args)
 {
-    char*name = lu(args, "name");
+    const 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(dict_lookup(&gradients, name))
+        syntaxerror("gradient %s defined twice", name);
 
     s_gradient(name, text, radial, rotate);
 
@@ -2196,30 +3079,161 @@ static int c_gradient(map_t*args)
     texture2(name, name, args, 0);
     return 0;
 }
-static int c_point(map_t*args) 
+
+static const char* checkFiltername(map_t* args)
+{
+    const char* name = lu(args, "name");
+    if(strchr(name, ','))
+       syntaxerror("the comma (,) is used to separate filters in filterlists. Please do not use in filternames.");
+    return name;
+}
+
+static int c_blur(map_t*args)
+{
+    const char*name = checkFiltername(args);
+    const char*blurstr = lu(args, "blur");
+    const char*blurxstr = lu(args, "blurx");
+    const 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;
+}
+
+static int c_gradientglow(map_t*args)
+{
+    const char*name = checkFiltername(args);
+    const char*gradient = lu(args, "gradient");
+    const char*blurstr = lu(args, "blur");
+    const char*blurxstr = lu(args, "blurx");
+    const 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)
+{
+    const char*name = checkFiltername(args);
+    RGBA color = parseColor(lu(args, "color"));
+    const char*blurstr = lu(args, "blur");
+    const char*blurxstr = lu(args, "blurx");
+    const 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)
+{
+    const char*name = checkFiltername(args);
+    RGBA shadow = parseColor(lu(args, "shadow"));
+    RGBA highlight = parseColor(lu(args, "highlight"));
+    const char*blurstr = lu(args, "blur");
+    const char*blurxstr = lu(args, "blurx");
+    const 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_define(map_t*args)
+{
+    const char*name = lu(args, "name");
+    const char*value = lu(args, "value");
+    
+    if(!defines_initialized) {
+       dict_init(&defines, 16);
+       mem_init(&define_values);
+       defines_initialized = 1;
+    }
+    int val = parseTwip(value);
+    int pos = mem_put(&define_values, &val, sizeof(val));
+    dict_put(&defines, name, (void*)(pos+1));
+    return 0;
+}
+static int c_point(map_t*args)
 {
-    char*name = lu(args, "name");
+    const char*name = lu(args, "name");
     int pos;
-    string_t s1;
     SPOINT p;
     if(!points_initialized) {
-       dictionary_init(&points);
+       dict_init(&points, 16);
        mem_init(&mpoints);
        points_initialized = 1;
     }
     p.x = parseTwip(lu(args, "x"));
     p.y = parseTwip(lu(args, "y"));
     pos = mem_put(&mpoints, &p, sizeof(p));
-    string_set(&s1, name);
-    pos++;
-    dictionary_put(&points, s1, (void*)pos);
+    dict_put(&points, name, (void*)(pos+1));
     return 0;
 }
-static int c_play(map_t*args) 
+static int c_play(map_t*args)
 {
-    char*name = lu(args, "name");
-    char*loop = lu(args, "loop");
-    char*nomultiple = lu(args, "nomultiple");
+    const char*name = lu(args, "name");
+    const char*loop = lu(args, "loop");
+    const char*nomultiple = lu(args, "nomultiple");
     int nm = 0;
     if(!strcmp(nomultiple, "nomultiple"))
        nm = 1;
@@ -2234,22 +3248,21 @@ static int c_play(map_t*args)
     return 0;
 }
 
-static int c_stop(map_t*args) 
+static int c_stop(map_t*args)
 {
-    char*name = map_lookup(args, "name");
+    const 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) 
+static int c_nextframe(map_t*args)
 {
-    char*name = lu(args, "name");
+    const char*name = lu(args, "name");
 
     if(s_swf3action(name, "nextframe")) {
        return 0;
@@ -2258,9 +3271,9 @@ static int c_nextframe(map_t*args)
     return 0;
 }
 
-static int c_previousframe(map_t*args) 
+static int c_previousframe(map_t*args)
 {
-    char*name = lu(args, "name");
+    const char*name = lu(args, "name");
 
     if(s_swf3action(name, "previousframe")) {
        return 0;
@@ -2269,258 +3282,489 @@ static int c_previousframe(map_t*args)
     return 0;
 }
 
+static int c_movement(map_t*args, int type)
+{
+    const char*instance = lu(args, "name");
+
+    const char* xstr="";
+    const char* ystr="";
+    SRECT oldbbox;
+    parameters_t p;
+    U16 set = 0x0000;
+
+    xstr = lu(args, "x");
+    ystr = lu(args, "y");
+
+    s_getParameters(instance, &p);
+
+    /* x,y position */
+    if(xstr[0])
+    {
+        if(isRelative(xstr))
+        {
+            if(type == PT_PUT || type == PT_STARTCLIP)
+                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 == PT_PUT || type == PT_STARTCLIP)
+                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;
+    }
+
+    if(change_sets_all)
+       set = SF_ALL;
+    p.set = set;
+
+    switch (type)
+    {
+        case PT_MOVE:
+            {
+                const char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dict_lookup(&interpolations, interstr);
+                if(!inter)
+                    syntaxerror("unkown interpolation %s", interstr);
+                s_change(instance, p, inter);
+            }
+            break;
+        case PT_SMOVE:
+            {
+                const char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dict_lookup(&interpolations, interstr);
+                if(!inter)
+                    syntaxerror("unkown interpolation %s", interstr);
+               s_schange(instance, p, inter);
+            }
+            break;
+        case PT_SWEEP:
+            {
+               const char* rstr = lu(args, "r");
+               int radius = parseTwip(rstr);
+               if(radius <= 0)
+                       syntaxerror("sweep not possible: radius must be greater than 0.");
+               const char* dirstr = lu(args, "dir");
+               int clockwise = parseDir(dirstr);
+               const char* arcstr = lu(args, "arc");
+               int short_arc = parseArc(arcstr);
+                const char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dict_lookup(&interpolations, interstr);
+                if(!inter)
+                    syntaxerror("unkown interpolation %s", interstr);
+               s_sweep(instance, p, radius, clockwise, short_arc, inter);
+            }
+            break;
+       }
+    return 0;
+}
+
 static int c_placement(map_t*args, int type)
 {
-    char*instance = lu(args, (type==0||type==4)?"instance":"name");
-    char*character = 0;
-
-    char* luminancestr = lu(args, "luminance");
-    char* scalestr = lu(args, "scale");
-    char* scalexstr = lu(args, "scalex");
-    char* scaleystr = lu(args, "scaley");
-    char* rotatestr = lu(args, "rotate");
-    char* shearstr = lu(args, "shear");
-    char* xstr="", *pivotstr="";
-    char* ystr="", *anglestr="";
-    char*above = lu(args, "above"); /*FIXME*/
-    char*below = lu(args, "below");
-    char* rstr = lu(args, "red");
-    char* gstr = lu(args, "green");
-    char* bstr = lu(args, "blue");
-    char* astr = lu(args, "alpha");
-    char* pinstr = lu(args, "pin");
-    char* as = map_lookup(args, "as");
+    const char*instance = lu(args, (type==PT_PUT||type==PT_STARTCLIP)?"instance":"name");
+    const char*character = 0;
+
+    const char* luminancestr = lu(args, "luminance");
+    const char* scalestr = lu(args, "scale");
+    const char* scalexstr = lu(args, "scalex");
+    const char* scaleystr = lu(args, "scaley");
+    const char* rotatestr = lu(args, "rotate");
+    const char* shearstr = lu(args, "shear");
+    const char* xstr="", *pivotstr="";
+    const char* ystr="", *anglestr="";
+    const char*above = lu(args, "above"); /*FIXME*/
+    const char*below = lu(args, "below");
+    const char* rstr = lu(args, "red");
+    const char* gstr = lu(args, "green");
+    const char* bstr = lu(args, "blue");
+    const char* astr = lu(args, "alpha");
+    const char* pinstr = lu(args, "pin");
+    const char* as = map_lookup(args, "as");
+    const char* blendmode = lu(args, "blend");
+    const char* filterstr = lu(args, "filter");
+    U8 blend;
     MULADD r,g,b,a;
     float oldwidth;
     float oldheight;
     SRECT oldbbox;
     MULADD luminance;
     parameters_t p;
+    U16 set = 0x0000;
 
-    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)  {
+
+    if(type == PT_PUT || type == PT_STARTCLIP)  {
        // put or startclip
        character = lu(args, "character");
        parameters_clear(&p);
-    } else if (type == 5) {
+    } else if(type == PT_BUTTON) {
        character = lu(args, "name");
        parameters_clear(&p);
        // button's show
     } else {
-       p = s_getParameters(instance);
+       s_getParameters(instance, &p);
     }
 
     /* 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 == PT_PUT || type == PT_STARTCLIP)
+                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 == PT_PUT || type == PT_STARTCLIP)
+                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])
+    {
+        p.filters = parseFilters((char*)filterstr);
+        set = set | SF_FILTER;
     }
+
+    if(type == PT_CHANGE && set & (SF_X | SF_Y))
+       warning("As of version 0.8.2 using the .change command to modify an \
+object's position on the stage is considered deprecated. Future \
+versions may consider x and y parameters for the .change command \
+to be illegal; please use the .move command.");
+
+    if(change_sets_all)
+       set = SF_ALL;
+    p.set = set;
+
+    switch (type)
+    {
+        case PT_PUT:
+            s_put(instance, character, p);
+            break;
+        case PT_CHANGE:
+            {
+                const char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dict_lookup(&interpolations, interstr);
+                if(!inter)
+                    syntaxerror("unkown interpolation %s", interstr);
+                s_change(instance, p, inter);
+            }
+            break;
+        case PT_SCHANGE:
+            {
+                const char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dict_lookup(&interpolations, interstr);
+                if(!inter)
+                    syntaxerror("unkown interpolation %s", interstr);
+               s_schange(instance, p, inter);
+            }
+            break;
+        case PT_JUMP:
+            s_jump(instance, p);
+            break;
+        case PT_STARTCLIP:
+            s_startclip(instance, character, p);
+            break;
+        case PT_BUTTON:
+            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) 
+static int c_put(map_t*args)
 {
-    c_placement(args, 0);
+    c_placement(args, PT_PUT);
     return 0;
 }
-static int c_change(map_t*args) 
+static int c_change(map_t*args)
 {
-    c_placement(args, 1);
+    if(currentframe == 0)
+        warning("change commands in frame 1 will be ignored, please use the put command to set object parameters");
+    c_placement(args, PT_CHANGE);
     return 0;
 }
-static int c_qchange(map_t*args) 
+static int c_schange(map_t*args)
 {
-    c_placement(args, 2);
+    c_placement(args, PT_SCHANGE);
     return 0;
 }
-static int c_arcchange(map_t*args) 
+static int c_move(map_t* args)
 {
-    c_placement(args, 2);
+    c_movement(args, PT_MOVE);
     return 0;
 }
-static int c_jump(map_t*args) 
+static int c_smove(map_t* args)
 {
-    c_placement(args, 3);
+    c_movement(args, PT_SMOVE);
     return 0;
 }
-static int c_startclip(map_t*args) 
+static int c_sweep(map_t* args)
 {
-    c_placement(args, 4);
+    c_movement(args, PT_SWEEP);
     return 0;
 }
-static int c_show(map_t*args) 
+static int c_arcchange(map_t*args)
 {
-    c_placement(args, 5);
+    c_placement(args, 0);
+    return 0;
+}
+static int c_jump(map_t*args)
+{
+    c_placement(args, PT_JUMP);
+    return 0;
+}
+static int c_startclip(map_t*args)
+{
+    c_placement(args, PT_STARTCLIP);
+    return 0;
+}
+static int c_show(map_t*args)
+{
+    c_placement(args, PT_BUTTON);
+    return 0;
+}
+static int c_toggle(map_t* args)
+{
+    const char*instance = lu(args, "name");
+    U16 flagsOn = 0x0000, flagsOff = 0xffff;
+    const char* alignstr = lu(args, "fixed_alignment");
+    if(!strcmp(alignstr, "on"))
+       flagsOn += IF_FIXED_ALIGNMENT;
+    else
+       if(!strcmp(alignstr, "off"))
+           flagsOff -= IF_FIXED_ALIGNMENT;
+       else
+           syntaxerror("values for toggle must be \"on\" or \"off\". %s is not legal.", alignstr);
+    s_toggle(instance, flagsOn, flagsOff);
     return 0;
 }
-static int c_del(map_t*args) 
+static int c_del(map_t*args)
 {
-    char*instance = lu(args, "name");
+    const char*instance = lu(args, "name");
     s_delinstance(instance);
     return 0;
 }
-static int c_end(map_t*args) 
+static int c_end(map_t*args)
 {
     s_end();
     return 0;
 }
-static int c_sprite(map_t*args) 
+static int c_sprite(map_t*args)
 {
-    char* name = lu(args, "name");
-    s_sprite(name);
+    const char* name = lu(args, "name");
+    const char* scalinggrid = lu(args, "scalinggrid");
+    const char* as3name = lu(args, "as3name");
+
+    if(scalinggrid && *scalinggrid) {
+       SRECT r = parseBox(scalinggrid);
+       s_sprite(name, &r, as3name);
+    } else {
+       s_sprite(name, 0, as3name);
+    }
     return 0;
 }
-static int c_frame(map_t*args) 
+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");
+    const char*framestr = lu(args, "n");
+    const char*cutstr = lu(args, "cut");
+
+    const char*name = lu(args, "name");
+    const char*anchor = lu(args, "anchor");
     char buf[40];
 
     if(!strcmp(anchor, "anchor") && !*name)
@@ -2545,32 +3789,32 @@ static int c_frame(map_t*args)
     s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
     return 0;
 }
-static int c_primitive(map_t*args) 
+static int c_primitive(map_t*args)
 {
-    char*name = lu(args, "name");
-    char*command = lu(args, "commandname");
+    const char*name = lu(args, "name");
+    const char*command = lu(args, "commandname");
     int width=0, height=0, r=0;
     int linewidth = parseTwip(lu(args, "line"));
-    char*colorstr = lu(args, "color");
+    const char*colorstr = lu(args, "color");
     RGBA color = parseColor(colorstr);
-    char*fillstr = lu(args, "fill");
+    const char*fillstr = lu(args, "fill");
     int dofill = 1;
     int type=0;
-    char* font;
-    char* text;
-    char* outline=0;
+    const char* font;
+    const char* text;
+    const char* outline=0;
     RGBA fill;
     if(!strcmp(command, "circle"))
        type = 1;
     else if(!strcmp(command, "filled"))
        type = 2;
-   
+
     if(type==0) {
        width = parseTwip(lu(args, "width"));
        height = parseTwip(lu(args, "height"));
-    } else if (type==1) {
+    } else if(type==1) {
        r = parseTwip(lu(args, "r"));
-    } else if (type==2) {
+    } else if(type==2) {
        outline = lu(args, "outline");
     }
 
@@ -2580,80 +3824,89 @@ static int c_primitive(map_t*args)
        fillstr = 0;
     if(width<0 || height<0 || linewidth<0 || r<0)
        syntaxerror("values width, height, line, r must be positive");
-    
+
     if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
     else if(type==1) s_circle(name, r, color, linewidth, fillstr);
     else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
     return 0;
 }
 
-static int c_textshape(map_t*args) 
+static int c_textshape(map_t*args)
 {
-    char*name = lu(args, "name");
-    char*text = lu(args, "text");
-    char*font = lu(args, "font");
+    const char*name = lu(args, "name");
+    const char*text = lu(args, "text");
+    const char*font = lu(args, "font");
     float size = parsePxOrPercent(font, lu(args, "size"));
 
     s_textshape(name, font, size, text);
     return 0;
 }
 
-static int c_swf(map_t*args) 
+static int c_swf(map_t*args)
 {
-    char*name = lu(args, "name");
-    char*filename = lu(args, "filename");
-    char*command = lu(args, "commandname");
+    const char*name = lu(args, "name");
+    const char*filename = lu(args, "filename");
+    const char*command = lu(args, "commandname");
     if(!strcmp(command, "shape"))
        warning("Please use .swf instead of .shape");
     s_includeswf(name, filename);
     return 0;
 }
 
-static int c_font(map_t*args) 
+static int c_font(map_t*args)
 {
-    char*name = lu(args, "name");
-    char*filename = lu(args, "filename");
+    const char*name = lu(args, "name");
+    const char*filename = lu(args, "filename");
     s_font(name, filename);
     return 0;
 }
 
-static int c_sound(map_t*args) 
+static int c_sound(map_t*args)
 {
-    char*name = lu(args, "name");
-    char*filename = lu(args, "filename");
+    const char*name = lu(args, "name");
+    const char*filename = lu(args, "filename");
     s_sound(name, filename);
     return 0;
 }
 
-static int c_text(map_t*args) 
+static int c_text(map_t*args)
 {
-    char*name = lu(args, "name");
-    char*text = lu(args, "text");
-    char*font = lu(args, "font");
+    const char*name = lu(args, "name");
+    const char*text = lu(args, "text");
+    const char*font = lu(args, "font");
     float size = parsePxOrPercent(font, lu(args, "size"));
     RGBA color = parseColor(lu(args, "color"));
     s_text(name, font, text, (int)(size*100), color);
     return 0;
 }
 
-static int c_soundtrack(map_t*args) 
+static int c_soundtrack(map_t*args)
 {
     return 0;
 }
 
-static int c_quicktime(map_t*args) 
+static int c_quicktime(map_t*args)
 {
-    char*name = lu(args, "name");
-    char*url = lu(args, "url");
+    const char*name = lu(args, "name");
+    const char*url = lu(args, "url");
     s_quicktime(name, url);
     return 0;
 }
 
-static int c_image(map_t*args) 
+static int c_video(map_t*args)
+{
+    const char*name = lu(args, "name");
+    int width = parseInt(lu(args, "width"));
+    int height = parseInt(lu(args, "height"));
+    s_video(name, width, height);
+    return 0;
+}
+
+static int c_image(map_t*args)
 {
-    char*command = lu(args, "commandname");
-    char*name = lu(args, "name");
-    char*filename = lu(args, "filename");
+    const char*command = lu(args, "commandname");
+    const char*name = lu(args, "name");
+    const char*filename = lu(args, "filename");
     if(!strcmp(command,"jpeg")) {
        int quality = (int)(parsePercent(lu(args, "quality"))*100);
        s_image(name, "jpeg", filename, quality);
@@ -2663,10 +3916,10 @@ static int c_image(map_t*args)
     return 0;
 }
 
-static int c_outline(map_t*args) 
+static int c_outline(map_t*args)
 {
-    char*name = lu(args, "name");
-    char*format = lu(args, "format");
+    const char*name = lu(args, "name");
+    const char*format = lu(args, "format");
 
     readToken();
     if(type != RAWDATA)
@@ -2678,22 +3931,23 @@ static int c_outline(map_t*args)
 
 int fakechar(map_t*args)
 {
-    char*name = lu(args, "name");
+    const char*name = lu(args, "name");
     s_box(name, 0, 0, black, 20, 0);
     return 0;
 }
 
 static int c_egon(map_t*args) {return fakechar(args);}
 static int c_button(map_t*args) {
-    char*name = lu(args, "name");
-    s_button(name);
+    const char*name = lu(args, "name");
+    const char*as3name = lu(args, "as3name");
+    s_button(name, as3name);
     return 0;
 }
 static int current_button_flags = 0;
 static int c_on_press(map_t*args)
 {
-    char*position = lu(args, "position");
-    char*action = "";
+    const char*position = lu(args, "position");
+    const char*action = "";
     if(!strcmp(position, "inside")) {
        current_button_flags |= BC_OVERUP_OVERDOWN;
     } else if(!strcmp(position, "outside")) {
@@ -2714,8 +3968,8 @@ static int c_on_press(map_t*args)
 }
 static int c_on_release(map_t*args)
 {
-    char*position = lu(args, "position");
-    char*action = "";
+    const char*position = lu(args, "position");
+    const char*action = "";
     if(!strcmp(position, "inside")) {
        current_button_flags |= BC_OVERDOWN_OVERUP;
     } else if(!strcmp(position, "outside")) {
@@ -2735,8 +3989,8 @@ static int c_on_release(map_t*args)
 }
 static int c_on_move_in(map_t*args)
 {
-    char*position = lu(args, "state");
-    char*action = "";
+    const char*position = lu(args, "state");
+    const char*action = "";
     if(!strcmp(position, "pressed")) {
        current_button_flags |= BC_OUTDOWN_OVERDOWN;
     } else if(!strcmp(position, "not_pressed")) {
@@ -2756,8 +4010,8 @@ static int c_on_move_in(map_t*args)
 }
 static int c_on_move_out(map_t*args)
 {
-    char*position = lu(args, "state");
-    char*action = "";
+    const char*position = lu(args, "state");
+    const char*action = "";
     if(!strcmp(position, "pressed")) {
        current_button_flags |= BC_OVERDOWN_OUTDOWN;
     } else if(!strcmp(position, "not_pressed")) {
@@ -2777,8 +4031,8 @@ static int c_on_move_out(map_t*args)
 }
 static int c_on_key(map_t*args)
 {
-    char*key = lu(args, "key");
-    char*action = "";
+    const char*key = lu(args, "key");
+    const char*action = "";
     if(strlen(key)==1) {
        /* ascii */
        if(key[0]>=32) {
@@ -2788,7 +4042,7 @@ static int c_on_key(map_t*args)
            return 1;
        }
     } else {
-       /* TODO: 
+       /* TODO:
           <ctrl-x> = 0x200*(x-'a')
           esc = = 0x3600
           space = = 0x4000;
@@ -2806,27 +4060,27 @@ static int c_on_key(map_t*args)
     return 0;
 }
 
-static int c_edittext(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 @autosize=0"},
-    char*name = lu(args, "name");
-    char*font = lu(args, "font");
+    const char*name = lu(args, "name");
+    const char*font = lu(args, "font");
     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");
+    const char*text  = lu(args, "text");
     RGBA color = parseColor(lu(args, "color"));
     int maxlength = parseInt(lu(args, "maxlength"));
-    char*variable = lu(args, "variable");
-    char*passwordstr = lu(args, "password");
-    char*wordwrapstr = lu(args, "wordwrap");
-    char*multilinestr = lu(args, "multiline");
-    char*htmlstr = lu(args, "html");
-    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");
+    const char*variable = lu(args, "variable");
+    const char*passwordstr = lu(args, "password");
+    const char*wordwrapstr = lu(args, "wordwrap");
+    const char*multilinestr = lu(args, "multiline");
+    const char*htmlstr = lu(args, "html");
+    const char*noselectstr = lu(args, "noselect");
+    const char*readonlystr = lu(args, "readonly");
+    const char*borderstr = lu(args, "border");
+    const char*autosizestr = lu(args, "autosize");
+    const char*alignstr = lu(args, "align");
     int align = -1;
 
     int flags = 0;
@@ -2851,12 +4105,12 @@ static int c_edittext(map_t*args)
 static int c_morphshape(map_t*args) {return fakechar(args);}
 static int c_movie(map_t*args) {return fakechar(args);}
 
-static char* readfile(const char*filename)
+static char* readfile(char*filename)
 {
     FILE*fi = fopen(filename, "rb");
     int l;
     char*text;
-    if(!fi) 
+    if(!fi)
        syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
     fseek(fi, 0, SEEK_END);
     l = ftell(fi);
@@ -2868,9 +4122,9 @@ static char* readfile(const char*filename)
     return text;
 }
 
-static int c_action(map_t*args) 
+static int c_action(map_t*args)
 {
-    char* filename  = map_lookup(args, "filename");
+    const char* filename  = map_lookup(args, "filename");
     if(!filename ||!*filename) {
        readToken();
        if(type != RAWDATA) {
@@ -2878,16 +4132,16 @@ static int c_action(map_t*args)
        }
        s_action(text);
     } else {
-       s_action(readfile(filename));
+       s_action(readfile((char*)filename));
     }
-   
+
     return 0;
 }
 
-static int c_initaction(map_t*args) 
+static int c_initaction(map_t*args)
 {
-    char* character = lu(args, "name");
-    char* filename  = map_lookup(args, "filename");
+    const char* character = lu(args, "name");
+    const char* filename  = map_lookup(args, "filename");
     if(!filename ||!*filename) {
        readToken();
        if(type != RAWDATA) {
@@ -2895,9 +4149,9 @@ static int c_initaction(map_t*args)
        }
        s_initaction(character, text);
     } else {
-       s_initaction(character, readfile(filename));
+       s_initaction(character, readfile((char*)filename));
     }
-   
+
     return 0;
 }
 
@@ -2906,7 +4160,7 @@ static struct {
     command_func_t* func;
     char*arguments;
 } arguments[] =
-{{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
+{{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default @change-sets-all=no @export=1 @mainclass="},
  {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
  // "import" type stuff
  {"swf", c_swf, "name filename"},
@@ -2915,17 +4169,26 @@ static struct {
  {"png", c_image, "name filename"},
  {"movie", c_movie, "name filename"},
  {"sound", c_sound, "name filename"},
- {"font", c_font, "name filename"},
+ {"font", c_font, "name filename glyphs= @flashtype="},
  {"soundtrack", c_soundtrack, "filename"},
  {"quicktime", c_quicktime, "url"},
+ {"video", c_video, "name width= height="},
 
     // generators of primitives
 
+ {"define", c_define, "name value=0"},
  {"point", c_point, "name x=0 y=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 slope=0"},
  {"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"},
@@ -2935,14 +4198,14 @@ static struct {
  {"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 @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="},
+ {"button", c_button, "name as3name="},
+    {"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"},
     {"on_move_out", c_on_move_out, "state=not_pressed"},
     {"on_key", c_on_key, "key=any"},
+
     // control tags
  {"play", c_play, "name loop=0 @nomultiple=0"},
  {"stop", c_stop, "name= "},
@@ -2950,19 +4213,24 @@ static struct {
  {"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="},
+ {"move", c_move,      "name x= y= interpolation=linear"},
+ {"smove", c_smove,    "name x= y= interpolation=linear"},
+ {"sweep", c_sweep,    "name x= y= r= dir=counterclockwise arc=short interpolation=linear"},
+ {"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="},
+ {"schange", c_schange, "name red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below= interpolation=linear"},
+ {"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 width= height= scale= scalex= scaley= r= shear= rotate="},
+    // switching
+ {"toggle", c_toggle, "name fixed_alignment="},
 
     // commands which start a block
 //startclip (see above)
- {"sprite", c_sprite, "name"},
+ {"sprite", c_sprite, "name scalinggrid= as3name="},
  {"action", c_action, "filename="},
  {"initaction", c_initaction, "name filename="},
 
@@ -2974,7 +4242,7 @@ static map_t parseArguments(char*command, char*pattern)
 {
     char*x;
     char*d,*e;
-   
+
     string_t name[64];
     string_t value[64];
     int set[64];
@@ -3034,7 +4302,7 @@ static map_t parseArguments(char*command, char*pattern)
            d=&x[strlen(x)];
        set[pos] = 0;
 
-       if(!e || d<e) { 
+       if(!e || d<e) {
            // no default
            name[pos].str = x;
            name[pos].len = d-x;
@@ -3129,7 +4397,7 @@ static map_t parseArguments(char*command, char*pattern)
 
     /* ok, now construct the dictionary from the parameters */
 
-    for(t=0;t<len;t++) 
+    for(t=0;t<len;t++)
     {
        map_put(&result, name[t], value[t]);
     }
@@ -3148,7 +4416,7 @@ static void parseArgumentsForCommand(char*command)
            /* ugly hack- will be removed soon (once documentation and .sc generating
               utilities have been changed) */
            if(!strcmp(command, "swf") && !stackpos) {
-               warning("Please use .flash instead of .swf- this will be mandatory soon");      
+               warning("Please use .flash instead of .swf- this will be mandatory soon");
                command = "flash";
                t = 0;
            }
@@ -3160,41 +4428,122 @@ static void parseArgumentsForCommand(char*command)
     }
     if(nr<0)
        syntaxerror("command %s not known", command);
-   
+
+#ifndef EMPTY
     // catch missing .flash directives at the beginning of a file
     if(strcmp(command, "flash") && !stackpos)
     {
-       syntaxerror("No movie defined- use .flash first");
+        syntaxerror("No movie defined- use .flash first");
     }
+#endif
 
 #ifdef DEBUG
     printf(".%s\n", command);fflush(stdout);
     map_dump(&args, stdout, "\t");fflush(stdout);
 #endif
 
+#ifndef EMPTY
     (*arguments[nr].func)(&args);
-
-    /*if(!strcmp(command, "button") ||
-       !strcmp(command, "action")) {
-       while(1) {
-           readToken();
-           if(type == COMMAND) {
-               if(!strcmp(text, "end"))
-                   break;
-               else {
-                   pushBack();
-                   break;
-               }
-           }
+#else
+    if(!strcmp(command, "action")  || !strcmp(command, "initaction") ||
+       !strcmp(command, "outline") || !strcmp(command, "gradient")) {
+       readToken();
+       if(type != RAWDATA) {
+           syntaxerror("colon (:) expected");
        }
-    }*/
+    }
+#endif
+    map_clear(&args);
+    return;
+}
+
 
+/* for now only intended to find what glyphs of each font are to be included in the swf file.
+ * Therefore some knowledge about the command is assumed i.e. it is font/text-related
+ * No syntax checking is done */
+static void analyseArgumentsForCommand(char*command)
+{
+    int t;
+    map_t args;
+    int nr = -1;
+    U8* glyphs_to_include;
+    msg("<verbose> analyse Command: %s (line %d)", command, line);
+
+    for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++)
+    {
+        if(!strcmp(arguments[t].command, command))
+        {
+            args = parseArguments(command, arguments[t].arguments);
+            nr = t;
+            break;
+        }
+    }
+#ifdef DEBUG
+    printf(".%s\n", command);fflush(stdout);
+    map_dump(&args, stdout, "\t");fflush(stdout);
+#endif
+    const char* name = lu(&args, "name");
+    if(!strcmp(command, "font"))
+    {
+       const char* fontfile = lu(&args, "filename");
+       const char* glyphs = lu(&args, "glyphs");
+       const char* flashtype = lu(&args, "flashtype");
+       s_createfont(name, fontfile, glyphs, flashtype[0]);
+    } else {
+        SWFFONT* font = dict_lookup(&fonts, lu(&args, "font"));
+        if(!font) {
+           //that's ok... it might be an edittext with a system font
+            //syntaxerror("font %s is not known in line %d", lu(&args, "font"), line);
+       } else
+            if(font->use && !font->use->glyphs_specified) {
+               if(!strcmp(command, "edittext"))
+               {
+                   swf_FontUseAll(font);
+                   font->use->glyphs_specified = 1;
+               }
+               else
+                   swf_FontUseUTF8(font, (U8*)lu(&args, "text"), 0xffff);
+            }
+    }
     map_clear(&args);
     return;
 }
 
+void skipParameters()
+{
+       do
+               readToken();
+       while (type != COMMAND);
+       pushBack();
+}
+
+void findFontUsage()
+{
+    char* fontRelated = "font;text;textshape;edittext;";
+    while(!noMoreTokens())
+    {
+        readToken();
+        if(type != COMMAND)
+            syntaxerror("command expected");
+        if(strstr(fontRelated, text))
+            analyseArgumentsForCommand(text);
+        else
+            if(strcmp(text, "end"))
+                skipParameters();
+    }
+}
+
+void firstPass()
+{
+    pos = 0;
+    id = 0;
+    dict_init(&fonts, 16);
+    cleanUp = &freeFontDictionary;
+    findFontUsage();
+}
+
 int main (int argc,char ** argv)
-{ 
+{
     int t;
     processargs(argc, argv);
     initLog(0,-1,0,0,-1,verbose);
@@ -3203,12 +4552,13 @@ int main (int argc,char ** argv)
        args_callback_usage(argv[0]);
        exit(1);
     }
-    
+
     file = generateTokens(filename);
     if(!file) {
        fprintf(stderr, "parser returned error.\n");
        return 1;
     }
+    firstPass();
     pos=0;
     t=0;