added font patch from Huub Schaeks
[swftools.git] / src / swfc.c
index 9e08623..41e5b93 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;
@@ -54,7 +55,7 @@ static struct options_t options[] = {
 {"o", "output"},
 {0,0}
 };
-    
+
 int args_callback_option(char*name,char*val)
 {
     if(!strcmp(name, "V")) {
@@ -116,29 +117,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()
 {
@@ -183,7 +161,7 @@ static struct level
 
    /* for swf (0): */
    SWF*swf;
-   char*filename; 
+   char*filename;
 
    /* for sprites (1): */
    TAG*tag;
@@ -204,6 +182,7 @@ static dictionary_t textures;
 static dictionary_t outlines;
 static dictionary_t gradients;
 static dictionary_t filters;
+static dictionary_t interpolations;
 static char idmap[65536];
 static TAG*tag = 0; //current tag
 
@@ -214,10 +193,11 @@ static U16 currentdepth;
 static dictionary_t instances;
 static dictionary_t fonts;
 static dictionary_t sounds;
+static dictionary_t fontUsage;
 
 typedef struct _parameters {
-    int x,y; 
-    float scalex, scaley; 
+    int x,y;
+    float scalex, scaley;
     CXFORM cxform;
     float rotate;
     float shear;
@@ -225,6 +205,7 @@ typedef struct _parameters {
     SPOINT pin;
     U8 blendmode; //not interpolated
     FILTER*filter;
+    U16 set; // bits indicating wether a parameter was set in the c_placement function
 } parameters_t;
 
 typedef struct _character {
@@ -239,6 +220,7 @@ typedef struct _instance {
     parameters_t parameters;
     TAG* lastTag; //last tag which set the object
     U16 lastFrame; //frame lastTag is in
+    history_t* history;
 } instance_t;
 
 typedef struct _outline {
@@ -260,10 +242,24 @@ 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;
@@ -271,10 +267,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;
@@ -283,6 +288,59 @@ 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()
+{
+   dictionary_free_all(&instances, free_instance);
+   dictionary_free_all(&characters, free);
+    dictionary_free_all(&images, free);
+    dictionary_free_all(&textures, free);
+    dictionary_free_all(&outlines, free_outline);
+    dictionary_free_all(&gradients, free_gradient);
+    dictionary_free_all(&filters, free);
+    dictionary_free_all(&fonts, free_font);
+    dictionary_free_all(&sounds, free);
+    dictionary_free_all(&interpolations, free);
+}
+
+static void freeFontDictionary()
+{
+    dictionary_free_all(&fonts, free_font);
+}
+
 static void incrementid()
 {
     while(idmap[++id]) {
@@ -294,13 +352,13 @@ static void incrementid()
 
 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
 {
+    if(dictionary_lookup(&characters, name))
+        syntaxerror("character %s defined twice", name);
     character_t* c = character_new();
-    
+
     c->definingTag = ctag;
     c->id = id;
     c->size = r;
-    if(dictionary_lookup(&characters, name))
-       syntaxerror("character %s defined twice", name);
     dictionary_put2(&characters, name, c);
 
     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
@@ -313,36 +371,36 @@ static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
 }
 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
 {
+    if(dictionary_lookup(&images, name))
+        syntaxerror("image %s defined twice", name);
+
     character_t* c = character_new();
     c->definingTag = ctag;
     c->id = id;
     c->size = r;
-
-    if(dictionary_lookup(&images, name))
-       syntaxerror("image %s defined twice", name);
     dictionary_put2(&images, name, c);
 }
 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
 {
+    if(dictionary_lookup(&instances, name))
+        syntaxerror("object %s defined twice", name);
     instance_t* i = instance_new();
     i->character = c;
     i->depth = depth;
     //swf_GetMatrix(0, &i->matrix);
-    if(dictionary_lookup(&instances, name))
-       syntaxerror("object %s defined twice", name);
     dictionary_put2(&instances, name, i);
     return i;
 }
 
 static void parameters_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->filter = 0;
     swf_GetCXForm(0, &p->cxform, 1);
@@ -353,14 +411,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*PI);
+    r1 = -p->scalex*sin(p->rotate/360*2*PI)+sx*p->shear;
+    r0 =  p->scaley*sin(p->rotate/360*2*PI);
+    sy =  p->scaley*cos(p->rotate/360*2*PI)+r0*p->shear;
 
     m->sx = (int)(sx*65536+0.5);
     m->r1 = (int)(r1*65536+0.5);
@@ -368,7 +426,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;
@@ -380,7 +438,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
@@ -388,12 +446,92 @@ static MATRIX s_instancepos(SRECT rect, parameters_t*p)
     return m;
 }
 
+void builtInInterpolations()
+{
+    interpolation_t* new;
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_LINEAR;
+    dictionary_put2(&interpolations, "linear", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_IN;
+    dictionary_put2(&interpolations, "quadIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_OUT;
+    dictionary_put2(&interpolations, "quadOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_IN_OUT;
+    dictionary_put2(&interpolations, "quadInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_IN;
+    dictionary_put2(&interpolations, "cubicIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_OUT;
+    dictionary_put2(&interpolations, "cubicOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_IN_OUT;
+    dictionary_put2(&interpolations, "cubicInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_IN;
+    dictionary_put2(&interpolations, "quartIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_OUT;
+    dictionary_put2(&interpolations, "quartOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_IN_OUT;
+    dictionary_put2(&interpolations, "quartInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_IN;
+    dictionary_put2(&interpolations, "quintIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_OUT;
+    dictionary_put2(&interpolations, "quintOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_IN_OUT;
+    dictionary_put2(&interpolations, "quintInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_IN;
+    dictionary_put2(&interpolations, "circleIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_OUT;
+    dictionary_put2(&interpolations, "circleOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_IN_OUT;
+    dictionary_put2(&interpolations, "circleInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_IN;
+    dictionary_put2(&interpolations, "exponentialIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_OUT;
+    dictionary_put2(&interpolations, "exponentialOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_IN_OUT;
+    dictionary_put2(&interpolations, "exponentialInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_IN;
+    dictionary_put2(&interpolations, "sineIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_OUT;
+    dictionary_put2(&interpolations, "sineOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_IN_OUT;
+    dictionary_put2(&interpolations, "sineInOut", new);
+}
+
 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
 {
-    SWF*swf = (SWF*)malloc(sizeof(SWF));
-
     if(stackpos)
-       syntaxerror(".swf blocks can't be nested");
+        syntaxerror(".swf blocks can't be nested");
+    if(stackpos==sizeof(stack)/sizeof(stack[0]))
+        syntaxerror("too many levels of recursion");
+
+    SWF*swf = (SWF*)malloc(sizeof(SWF));
 
     memset(swf, 0, sizeof(swf));
     swf->fileVersion = version;
@@ -403,9 +541,6 @@ 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);
@@ -413,8 +548,10 @@ void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA backgrou
     dictionary_init(&gradients);
     dictionary_init(&filters);
     dictionary_init(&instances);
-    dictionary_init(&fonts);
     dictionary_init(&sounds);
+    dictionary_init(&interpolations);
+    builtInInterpolations();
+    cleanUp = &freeDictionaries;
 
     memset(&stack[stackpos], 0, sizeof(stack[0]));
     stack[stackpos].type = 0;
@@ -422,12 +559,11 @@ 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));
     incrementid();
 }
@@ -447,7 +583,7 @@ void s_sprite(char*name)
     stack[stackpos].tag = tag;
     stack[stackpos].id = id;
     stack[stackpos].name = strdup(name);
-   
+
     /* FIXME: those four fields should be bundled together */
     dictionary_init(&instances);
     currentframe = 0;
@@ -510,7 +646,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;
@@ -537,7 +673,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));
        }
@@ -559,7 +695,7 @@ void s_buttonaction(int flags, char*action)
        return;
     }
     setbuttonrecords(stack[stackpos-1].tag);
-    
+
     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
     if(!a) {
        syntaxerror("Couldn't compile ActionScript");
@@ -591,7 +727,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;
@@ -615,23 +751,108 @@ TAG* removeFromTo(TAG*from, TAG*to)
     return save;
 }
 
+static void readParameters(history_t* history, parameters_t* p, int frame)
+{
+    p->x = history_value(history, frame, "x");
+    p->y = history_value(history, frame, "y");
+    p->scalex = history_value(history, frame, "scalex");
+    p->scaley = history_value(history, frame, "scaley");
+    p->cxform.r0 = history_value(history, frame, "cxform.r0");
+    p->cxform.g0 = history_value(history, frame, "cxform.g0");
+    p->cxform.b0 = history_value(history, frame, "cxform.b0");
+    p->cxform.a0 = history_value(history, frame, "cxform.a0");
+    p->cxform.r1 = history_value(history, frame, "cxform.r1");
+    p->cxform.g1 = history_value(history, frame, "cxform.g1");
+    p->cxform.b1 = history_value(history, frame, "cxform.b1");
+    p->cxform.a1 = history_value(history, frame, "cxform.a1");
+    p->rotate = history_value(history, frame, "rotate");
+    p->shear = history_value(history, frame, "shear");
+    p->pivot.x = history_value(history, frame, "pivot.x");
+    p->pivot.y = history_value(history, frame, "pivot.y");
+    p->pin.x = history_value(history, frame, "pin.x");
+    p->pin.y = history_value(history, frame, "pin.y");
+    p->blendmode = history_value(history, frame, "blendmode");
+    p->filter = history_valueFilter(history, frame);
+}
+
+void setPlacement(TAG*tag, U16 id, U16 depth, MATRIX m, char*name, parameters_t*p, char move)
+{
+    SWFPLACEOBJECT po;
+    FILTERLIST flist;
+    swf_GetPlaceObject(NULL, &po);
+    po.id = id;
+    po.depth = depth;
+    po.matrix = m;
+    po.cxform = p->cxform;
+    po.name = name;
+    po.move = move;
+    if(move)
+    po.id = 0;
+    if(p->blendmode) {
+    po.blendmode = p->blendmode;
+    }
+    if(p->filter) {
+    flist.num = 1;
+    flist.filter[0] = p->filter;
+    po.filters = &flist;
+    }
+    swf_SetPlaceObject(tag, &po);
+}
+
+static void writeInstance(instance_t* i)
+{
+    parameters_t p;
+    MATRIX m;
+    int frame = i->history->firstFrame;
+    TAG* tag = i->history->firstTag;
+    while (frame < currentframe)
+    {
+        frame++;
+        readParameters(i->history, &p, frame);
+        while (tag->id != ST_SHOWFRAME)
+            tag = tag->next;
+        m = s_instancepos(i->character->size, &p);
+
+        if(p.blendmode || p.filter)
+            tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
+        else
+            tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+        setPlacement(tag, 0, i->depth, m, 0, &p, 1);
+        if (p.filter)
+            free(p.filter);
+    }
+}
+
+void dumpSWF(SWF*swf)
+{
+    TAG* tag = swf->firstTag;
+    printf("vvvvvvvvvvvvvvvvvvvvv\n");
+    while(tag) {
+    printf("%8d %s\n", tag->len, swf_TagGetName(tag));
+    tag = tag->next;
+    }
+    printf("^^^^^^^^^^^^^^^^^^^^^\n");
+}
+
 static void s_endSprite()
 {
     SRECT r = currentrect;
-    
+
     if(stack[stackpos].cut)
        tag = removeFromTo(stack[stackpos].cut, tag);
 
     stackpos--;
-   
-    /* TODO: before clearing, prepend "<spritename>." to names and
-             copy into old instances dict */
-    dictionary_clear(&instances);
-
-    currentframe = stack[stackpos].oldframe;
-    currentrect = stack[stackpos].oldrect;
-    currentdepth = stack[stackpos].olddepth;
-    instances = stack[stackpos].oldinstances;
+    instance_t *i;
+    stringarray_t* index =dictionary_index(&instances);
+    int num = 0;
+    char* name = stringarray_at(index, num);
+    while (name)
+    {
+        i = dictionary_lookup(&instances, name);
+        writeInstance(i);
+        num++;
+        name = stringarray_at(index, num);
+    }
 
     tag = swf_InsertTag(tag, ST_SHOWFRAME);
     tag = swf_InsertTag(tag, ST_END);
@@ -639,7 +860,15 @@ static void s_endSprite()
     tag = stack[stackpos].tag;
     swf_FoldSprite(tag);
     if(tag->next != 0)
-       syntaxerror("internal error(7)");
+        syntaxerror("internal error(7)");
+    /* TODO: before clearing, prepend "<spritename>." to names and
+             copy into old instances dict */
+   dictionary_free_all(&instances, free_instance);
+
+    currentframe = stack[stackpos].oldframe;
+    currentrect = stack[stackpos].oldrect;
+    currentdepth = stack[stackpos].olddepth;
+    instances = stack[stackpos].oldinstances;
 
     s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
     free(stack[stackpos].name);
@@ -650,15 +879,27 @@ static void s_endSWF()
     int fi;
     SWF* swf;
     char*filename;
-    
+
+    instance_t *i;
+    stringarray_t* index = dictionary_index(&instances);
+    int num = 0;
+    char* name = stringarray_at(index, num);
+    while (name)
+    {
+        i = dictionary_lookup(&instances, name);
+        writeInstance(i);
+        num++;
+        name = stringarray_at(index, num);
+    }
+
     if(stack[stackpos].cut)
        tag = removeFromTo(stack[stackpos].cut, tag);
 
     stackpos--;
-   
+
     swf = stack[stackpos].swf;
     filename = stack[stackpos].filename;
-  
+
     //if(tag->prev && tag->prev->id != ST_SHOWFRAME)
     //    tag = swf_InsertTag(tag, ST_SHOWFRAME);
     tag = swf_InsertTag(tag, ST_SHOWFRAME);
@@ -666,21 +907,21 @@ static void s_endSWF()
     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 || !strcmp(filename, "-"))
        fi = fileno(stdout);
     else
@@ -690,22 +931,14 @@ static void s_endSWF()
     }
     if(do_cgi)
        {if(swf_WriteCGI(swf)<0) syntaxerror("WriteCGI() failed.\n");}
-    else if(swf->compressed) 
+    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); // mem leak
-    dictionary_clear(&filters);
-    dictionary_clear(&fonts);
-    dictionary_clear(&sounds);
+
+    freeDictionaries();
 
     swf_FreeTags(swf);
     free(swf);
@@ -734,7 +967,7 @@ void s_frame(int nr, int cut, 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
 
@@ -790,8 +1023,8 @@ int addFillStyle(SHAPE*s, SRECT*r, char*name)
        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*PI/360);
+    csin = sin(-gradient->rotate*2*PI/360);
        rot.sx =  ccos*65536;
        rot.r1 = -csin*65536;
        rot.r0 =  csin*65536;
@@ -812,7 +1045,7 @@ int addFillStyle(SHAPE*s, SRECT*r, char*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)
 {
@@ -846,7 +1079,7 @@ 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();
 }
@@ -871,7 +1104,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;
@@ -881,7 +1114,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);
@@ -920,7 +1153,7 @@ 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();
 }
@@ -935,7 +1168,7 @@ void s_textshape(char*name, char*fontname, float size, char*_text)
     font = dictionary_lookup(&fonts, fontname);
     if(!font)
        syntaxerror("font \"%s\" not known!", fontname);
-    
+
     if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
        warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
        s_box(name, 0, 0, black, 20, 0);
@@ -947,7 +1180,7 @@ 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);
@@ -971,7 +1204,7 @@ void s_text(char*name, char*fontname, char*text, int size, RGBA color)
     font = dictionary_lookup(&fonts, fontname);
     if(!font)
        syntaxerror("font \"%s\" not known!", fontname);
-    
+
     tag = swf_InsertTag(tag, ST_DEFINETEXT2);
     swf_SetU16(tag, id);
     if(!font->numchars) {
@@ -979,7 +1212,16 @@ void s_text(char*name, char*fontname, char*text, int size, RGBA color)
        return;
     }
     r = swf_SetDefineText(tag, font, &color, text, size);
-   
+
+    if(stack[0].swf->fileVersion >= 8) {
+       tag = swf_InsertTag(tag, ST_CSMTEXTSETTINGS);
+       swf_SetU16(tag, id);
+       swf_SetU8(tag, /*grid*/(1<<3)|/*flashtype*/0x40);
+       swf_SetU32(tag, 0);//thickness
+       swf_SetU32(tag, 0);//sharpness
+       swf_SetU8(tag, 0);//reserved
+    }
+
     s_addcharacter(name, id, tag, r);
     incrementid();
 }
@@ -990,11 +1232,11 @@ void s_quicktime(char*name, char*url)
     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();
 }
@@ -1022,7 +1264,7 @@ 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);
 
     s_addcharacter(name, id, tag, r);
@@ -1035,7 +1277,7 @@ void s_image(char*name, char*type, char*filename, int quality)
 {
     /* an image is actually two folded: 1st bitmap, 2nd character.
        Both of them can be used separately */
-    
+
     /* step 1: the bitmap */
     SRECT r;
     int imageID = id;
@@ -1077,6 +1319,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;
@@ -1124,6 +1367,8 @@ void s_getBitmapSize(char*name, int*width, int*height)
 
 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
 {
+    if(dictionary_lookup(&textures, name))
+        syntaxerror("texture %s defined twice", name);
     gradient_t* gradient = dictionary_lookup(&gradients, object);
     character_t* bitmap = dictionary_lookup(&images, object);
     texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
@@ -1157,36 +1402,13 @@ void s_texture(char*name, char*object, int x, int y, float scalex, float scaley,
        fs->m.sy *= 20;
     }
 
-
-    if(dictionary_lookup(&textures, name))
-       syntaxerror("texture %s defined twice", name);
     dictionary_put2(&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)
 {
     SWFFONT* font;
-    font = swf_LoadFont(filename);
-   
-    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);
-       return;
-    }
-
+    font = dictionary_lookup(&fonts, name);
     if(0)
     {
        /* fix the layout. Only needed for old fonts */
@@ -1197,21 +1419,16 @@ void s_font(char*name, char*filename)
        font->layout = 0;
        swf_FontCreateLayout(font);
     }
-    /* just in case this thing is used in .edittext later on */
-    swf_FontPrepareForEditText(font);
-
     font->id = id;
+       swf_FontReduce_swfc(font);
     tag = swf_InsertTag(tag, ST_DEFINEFONT2);
     swf_FontSetDefine2(tag, font);
     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
     swf_SetU16(tag, 1);
     swf_SetU16(tag, id);
     swf_SetString(tag, name);
-    incrementid();
 
-    if(dictionary_lookup(&fonts, name))
-       syntaxerror("font %s defined twice", name);
-    dictionary_put2(&fonts, name, font);
+    incrementid();
 }
 
 
@@ -1228,46 +1445,53 @@ void s_sound(char*name, char*filename)
     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(dictionary_lookup(&sounds, name))
+        syntaxerror("sound %s defined twice", name);
+
+    if(wav_read(&wav, filename))
+    {
         int t;
-       wav_convert2mono(&wav, &wav2, 44100);
-       samples = (U16*)wav2.data;
-       numsamples = wav2.size/2;
-       free(wav.data);
+        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);
@@ -1279,13 +1503,11 @@ 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);
-    }
-    
+
     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
     swf_SetU16(tag, id);
     swf_SetString(tag, name);
@@ -1298,14 +1520,12 @@ void s_sound(char*name, char*filename)
     sound->tag = tag;
     sound->id = id;
 
-    if(dictionary_lookup(&sounds, name))
-       syntaxerror("sound %s defined twice", name);
     dictionary_put2(&sounds, name, sound);
-    
+
     incrementid();
 
-    if(samples)
-       free(samples);
+    if (samples)
+        free(samples);
 }
 
 static char* gradient_getToken(const char**p)
@@ -1314,7 +1534,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)++;
@@ -1336,30 +1556,42 @@ GRADIENT parseGradient(const char*str)
     memset(&gradient, 0, sizeof(GRADIENT));
     gradient.ratios = rfx_calloc(16*sizeof(U8));
     gradient.rgba = rfx_calloc(16*sizeof(RGBA));
-    while(*p) {
-       char*posstr,*colorstr;
-       int pos;
-       RGBA color;
-       posstr = gradient_getToken(&p);
-       if(!*posstr)
-           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 == 16) {
-           warning("gradient record too big- max size is 16, rest ignored");
-           break;
+
+    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;
 }
 
@@ -1372,15 +1604,16 @@ 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);
 }
-    
-void s_gradientglow(char*name, char*gradient, float blurx, float blury, 
-                   float angle, float distance, float strength, char innershadow, 
+
+void s_gradientglow(char*name, char*gradient, float blurx, float blury,
+                   float angle, float distance, float strength, char innershadow,
                    char knockout, char composite, char ontop, int passes)
 {
+    if(dictionary_lookup(&filters, name))
+        syntaxerror("filter %s defined twice", name);
+
     gradient_t* g = dictionary_lookup(&gradients, gradient);
 
     composite = 1;
@@ -1401,13 +1634,14 @@ void s_gradientglow(char*name, char*gradient, float blurx, float blury,
     filter->ontop = ontop;
     filter->passes = passes;
 
-    if(dictionary_lookup(&filters, name))
-       syntaxerror("filter %s defined twice", name);
     dictionary_put2(&filters, name, filter);
 }
 
 void s_dropshadow(char*name, RGBA color, double blurx, double blury, double angle, double distance, double strength, char innershadow, char knockout, char composite, int passes)
 {
+    if(dictionary_lookup(&filters, name))
+        syntaxerror("filter %s defined twice", name);
+
     composite = 1;
     FILTER_DROPSHADOW* filter = rfx_calloc(sizeof(FILTER_DROPSHADOW));
     filter->type = FILTERTYPE_DROPSHADOW;
@@ -1422,13 +1656,14 @@ void s_dropshadow(char*name, RGBA color, double blurx, double blury, double angl
     filter->composite = composite;
     filter->passes = passes;
 
-    if(dictionary_lookup(&filters, name))
-       syntaxerror("filter %s defined twice", name);
     dictionary_put2(&filters, name, filter);
 }
 
 void s_bevel(char*name, RGBA shadow, RGBA highlight, double blurx, double blury, double angle, double distance, double strength, char innershadow, char knockout, char composite, char ontop, int passes)
 {
+    if(dictionary_lookup(&filters, name))
+        syntaxerror("filter %s defined twice", name);
+
     composite = 1;
     FILTER_BEVEL* filter = rfx_calloc(sizeof(FILTER_BEVEL));
     filter->type = FILTERTYPE_BEVEL;
@@ -1445,21 +1680,20 @@ void s_bevel(char*name, RGBA shadow, RGBA highlight, double blurx, double blury,
     filter->ontop = ontop;
     filter->passes = passes;
 
-    if(dictionary_lookup(&filters, name))
-       syntaxerror("filter %s defined twice", name);
     dictionary_put2(&filters, name, filter);
 }
 
 void s_blur(char*name, double blurx, double blury, int passes)
 {
+    if(dictionary_lookup(&filters, name))
+        syntaxerror("filter %s defined twice", name);
+
     FILTER_BLUR* filter = rfx_calloc(sizeof(FILTER_BLUR));
     filter->type = FILTERTYPE_BLUR;
     filter->blurx = blurx;
     filter->blury = blury;
     filter->passes = passes;
 
-    if(dictionary_lookup(&filters, name))
-       syntaxerror("filter %s defined twice", name);
     dictionary_put2(&filters, name, filter);
 }
 
@@ -1467,8 +1701,10 @@ void s_action(const char*text)
 {
     ActionTAG* a = 0;
     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
-    if(!a) {
-       syntaxerror("Couldn't compile ActionScript");
+    if(!a)
+    {
+        swf_ActionFree(a);
+        syntaxerror("Couldn't compile ActionScript");
     }
 
     tag = swf_InsertTag(tag, ST_DOACTION);
@@ -1483,8 +1719,10 @@ 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);
@@ -1500,7 +1738,7 @@ int s_swf3action(char*name, char*action)
 {
     ActionTAG* a = 0;
     instance_t* object = 0;
-    if(name) 
+    if(name)
        object = (instance_t*)dictionary_lookup(&instances, name);
     if(!object && name && *name) {
        /* we have a name, but couldn't find it. Abort. */
@@ -1522,13 +1760,16 @@ int s_swf3action(char*name, char*action)
 
 void s_outline(char*name, char*format, char*source)
 {
+    if(dictionary_lookup(&outlines, name))
+        syntaxerror("outline %s defined twice", name);
+
     outline_t* outline;
 
     drawer_t draw;
     SHAPE* shape;
     SHAPE2* shape2;
     SRECT bounds;
-    
+
     //swf_Shape10DrawerInit(&draw, 0);
     swf_Shape11DrawerInit(&draw, 0);
 
@@ -1537,13 +1778,11 @@ 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);
 }
 
@@ -1577,25 +1816,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);
@@ -1621,7 +1860,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);
@@ -1671,7 +1910,7 @@ 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);
@@ -1697,28 +1936,28 @@ void s_endClip()
     currentdepth++;
 }
 
-void setPlacement(TAG*tag, U16 id, U16 depth, MATRIX m, char*name, parameters_t*p, char move)
-{
-    SWFPLACEOBJECT po;
-    FILTERLIST flist;
-    swf_GetPlaceObject(NULL, &po);
-    po.id = id;
-    po.depth = depth;
-    po.matrix = m;
-    po.cxform = p->cxform;
-    po.name = name;
-    po.move = move;
-    if(move)
-       po.id = 0;
-    if(p->blendmode) {
-       po.blendmode = p->blendmode;
-    }
-    if(p->filter) {
-       flist.num = 1;
-       flist.filter[0] = p->filter;
-       po.filters = &flist;
-    }
-    swf_SetPlaceObject(tag, &po);
+void setStartparameters(instance_t* i, parameters_t* p, TAG* tag)
+{
+    history_begin(i->history, "x", currentframe, tag, p->x);
+    history_begin(i->history, "y", currentframe, tag, p->y);
+    history_begin(i->history, "scalex", currentframe, tag, p->scalex);
+    history_begin(i->history, "scaley", currentframe, tag, p->scaley);
+    history_begin(i->history, "cxform.r0", currentframe, tag, p->cxform.r0);
+    history_begin(i->history, "cxform.g0", currentframe, tag, p->cxform.g0);
+    history_begin(i->history, "cxform.b0", currentframe, tag, p->cxform.b0);
+    history_begin(i->history, "cxform.a0", currentframe, tag, p->cxform.a0);
+    history_begin(i->history, "cxform.r1", currentframe, tag, p->cxform.r1);
+    history_begin(i->history, "cxform.g1", currentframe, tag, p->cxform.g1);
+    history_begin(i->history, "cxform.b1", currentframe, tag, p->cxform.b1);
+    history_begin(i->history, "cxform.a1", currentframe, tag, p->cxform.a1);
+    history_begin(i->history, "rotate", currentframe, tag, p->rotate);
+    history_begin(i->history, "shear", currentframe, tag, p->shear);
+    history_begin(i->history, "pivot.x", currentframe, tag, p->pivot.x);
+    history_begin(i->history, "pivot.y", currentframe, tag, p->pivot.y);
+    history_begin(i->history, "pin.x", currentframe, tag, p->pin.x);
+    history_begin(i->history, "pin.y", currentframe, tag, p->pin.y);
+    history_begin(i->history, "blendmode", currentframe, tag, p->blendmode);
+    history_beginFilter(i->history, currentframe, tag, p->filter);
 }
 
 void s_put(char*instance, char*character, parameters_t p)
@@ -1726,224 +1965,104 @@ void s_put(char*instance, char*character, parameters_t p)
     character_t* c = dictionary_lookup(&characters, character);
     instance_t* i;
     MATRIX m;
-    if(!c) {
-       syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
-    }
-    
+    if(!c)
+        syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
+
     i = s_addinstance(instance, c, currentdepth);
     i->parameters = p;
     m = s_instancepos(i->character->size, &p);
-   
-    if(p.blendmode || p.filter) {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
-    } else {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
-    }
+
+    if(p.blendmode || p.filter)
+    {
+        if(stack[0].swf->fileVersion < 8)
+        {
+            if(p.blendmode)
+                warning("blendmodes only supported for flash version>=8");
+            else
+                warning("filters only supported for flash version>=8");
+        }
+        tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
+       }
+    else
+        tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
     setPlacement(tag, c->id, currentdepth, m, instance, &p, 0);
-    
+    setStartparameters(i, &p, tag);
     i->lastTag = tag;
     i->lastFrame = currentframe;
     currentdepth++;
-    
 }
 
-void s_jump(char*instance, parameters_t p)
+void recordChanges(history_t* history, parameters_t p, int changeFunction, interpolation_t* inter)
 {
-    instance_t* i = dictionary_lookup(&instances, instance);
-    MATRIX m;
-    if(!i) {
-       syntaxerror("instance %s not known", instance);
+    if (p.set & SF_X)
+        history_remember(history, "x", currentframe, changeFunction, p.x, inter);
+    if (p.set & SF_Y)
+        history_remember(history, "y", currentframe, changeFunction, p.y, inter);
+    if (p.set & SF_SCALEX)
+        history_remember(history, "scalex", currentframe, changeFunction, p.scalex, inter);
+    if (p.set & SF_SCALEY)
+        history_remember(history, "scaley", currentframe, changeFunction, p.scaley, inter);
+    if (p.set & SF_CX_R)
+    {
+        history_remember(history, "cxform.r0", currentframe, changeFunction, p.cxform.r0, inter);
+        history_remember(history, "cxform.r1", currentframe, changeFunction, p.cxform.r1, inter);
     }
-
-    i->parameters = p;
-    m = s_instancepos(i->character->size, &p);
-
-    if(p.blendmode || p.filter) {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
-    } else {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+    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);
     }
-    setPlacement(tag, 0, i->depth, m, 0, &p, 1);
-
-    i->lastTag = tag;
-    i->lastFrame = currentframe;
-}
-
-RGBA interpolateColor(RGBA c1, RGBA c2, float ratio)
-{
-    RGBA c;
-    c.r = c1.r * (1-ratio) + c2.r * ratio;
-    c.g = c1.g * (1-ratio) + c2.g * ratio;
-    c.b = c1.b * (1-ratio) + c2.b * ratio;
-    c.a = c1.a * (1-ratio) + c2.a * ratio;
-    return c;
-}
-
-FILTER* interpolateFilter(FILTER*filter1,FILTER*filter2, float ratio)
-{
-    if(!filter1 && !filter2)
-       return 0;
-    if(!filter1)
-       return interpolateFilter(filter2,filter1,1-ratio);
-
-    if(filter2 && filter2->type != filter1->type)
-       syntaxerror("can't interpolate between %s and %s filters yet", filtername[filter1->type], filtername[filter2->type]);
-   
-    if(filter1->type == FILTERTYPE_BLUR) {
-       FILTER_BLUR*f1 = (FILTER_BLUR*)filter1;
-       FILTER_BLUR*f2 = (FILTER_BLUR*)filter2;
-       if(f2 && f1->blurx == f2->blurx && f1->blury == f2->blury)
-           return 0;
-       FILTER_BLUR*f = (FILTER_BLUR*)swf_NewFilter(FILTERTYPE_BLUR);
-       f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
-       f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
-       f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
-       return (FILTER*)f;
-    } else if (filter1->type == FILTERTYPE_DROPSHADOW) {
-       FILTER_DROPSHADOW*f1 = (FILTER_DROPSHADOW*)filter1;
-       FILTER_DROPSHADOW*f2 = (FILTER_DROPSHADOW*)filter2;
-       if(f2 && !memcmp(&f1->color,&f2->color,sizeof(RGBA)) && f1->strength == f2->strength && 
-          f1->blurx == f2->blurx && f1->blury == f2->blury && 
-          f1->angle == f2->angle && f1->distance == f2->distance)
-           return 0;
-       FILTER_DROPSHADOW*f = (FILTER_DROPSHADOW*)swf_NewFilter(FILTERTYPE_DROPSHADOW);
-       memcpy(f, f1, sizeof(FILTER_DROPSHADOW));
-       f->color = interpolateColor(f1->color, f2->color, ratio);
-       f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
-       f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
-       f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
-       f->angle= (f1->angle)*(1-ratio) + (f2?f2->angle:0)*ratio;
-       f->distance= (f1->distance)*(1-ratio) + (f2?f2->distance:0)*ratio;
-       f->strength= (f1->strength)*(1-ratio) + (f2?f2->strength:0)*ratio;
-       return (FILTER*)f;
-    } else if (filter1->type == FILTERTYPE_BEVEL) {
-       FILTER_BEVEL*f1 = (FILTER_BEVEL*)filter1;
-       FILTER_BEVEL*f2 = (FILTER_BEVEL*)filter2;
-       if(f2 && !memcmp(&f1->shadow,&f2->shadow,sizeof(RGBA)) && 
-          !memcmp(&f1->highlight,&f2->highlight,sizeof(RGBA)) && 
-          f1->blurx == f2->blurx && f1->blury == f2->blury && f1->angle == f2->angle && f1->strength == f2->strength && f1->distance == f2->distance)
-           return 0;
-       FILTER_BEVEL*f = (FILTER_BEVEL*)swf_NewFilter(FILTERTYPE_BEVEL);
-       memcpy(f, f1, sizeof(FILTER_BEVEL));
-       f->shadow = interpolateColor(f1->shadow, f2->shadow, ratio);
-       f->highlight = interpolateColor(f1->highlight, f2->highlight, ratio);
-       f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
-       f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
-       f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
-       f->angle= (f1->angle)*(1-ratio) + (f2?f2->angle:0)*ratio;
-       f->distance= (f1->distance)*(1-ratio) + (f2?f2->distance:0)*ratio;
-       f->strength= (f1->strength)*(1-ratio) + (f2?f2->strength:0)*ratio;
-       return (FILTER*)f;
-    } /*else if (filter1->type == FILTERTYPE_GRADIENTGLOW) {
-       FILTER_GRADIENTGLOW*f = (FILTER_GRADIENTGLOW*)swf_NewFilter(FILTERTYPE_GRADIENTGLOW);
-       // can't interpolate gradients
-       memcpy(f, filter1, sizeof(FILTER_GRADIENTGLOW));
-       return (FILTER*)f;
-    }*/ else {
-       syntaxerror("can't interpolate %s filters yet", filtername[filter1->type]);
+    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);
     }
-    return 0;
+    if (p.set & SF_CX_A)
+    {
+        history_remember(history, "cxform.a0", currentframe, changeFunction, p.cxform.a0, inter);
+        history_remember(history, "cxform.a1", currentframe, changeFunction, p.cxform.a1, inter);
+    }
+    if (p.set & SF_ROTATE)
+        history_remember(history, "rotate", currentframe, changeFunction, p.rotate, inter);
+    if (p.set & SF_SHEAR)
+        history_remember(history, "shear", currentframe, changeFunction, p.shear, inter);
+    if (p.set & SF_PIVOT)
+    {
+        history_remember(history, "pivot.x", currentframe, changeFunction, p.pivot.x, inter);
+        history_remember(history, "pivot.y", currentframe, changeFunction, p.pivot.y, inter);
+    }
+    if (p.set & SF_PIN)
+    {
+        history_remember(history, "pin.x", currentframe, changeFunction, p.pin.x, inter);
+        history_remember(history, "pin.y", currentframe, changeFunction, p.pin.y, inter);
+    }
+    if (p.set & SF_BLEND)
+        history_remember(history, "blendmode", currentframe, changeFunction, p.blendmode, inter);
+    if (p.set & SF_FILTER)
+        history_rememberFilter(history, currentframe, changeFunction, p.filter, inter);
 }
 
-parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
+void s_jump(char* instance, parameters_t p)
 {
-    parameters_t p;
-    float ratio;
-    if(num==0 || num==1)
-       return *p1;
-    ratio = (float)pos/(float)num;
-    
-    p.x = (p2->x-p1->x)*ratio + p1->x;
-    p.y = (p2->y-p1->y)*ratio + p1->y;
-    p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
-    p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
-    p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
-    p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
-
-    p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
-    p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
-    p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
-    p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
-
-    p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
-    p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
-    p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
-    p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
-
-    p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
-    p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
-    p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
-    p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
-
-    p.filter = interpolateFilter(p1->filter, p2->filter, ratio);
-    return p;
+    instance_t* i = dictionary_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p, CF_JUMP, 0);
 }
 
-void s_change(char*instance, parameters_t p2)
+void s_change(char*instance, parameters_t p2, interpolation_t* inter)
 {
     instance_t* i = dictionary_lookup(&instances, instance);
-    MATRIX m;
-    parameters_t p1;
-    TAG*t;
-    int frame, allframes;
-    if(!i) {
-       syntaxerror("instance %s not known", instance);
-    }
-    p1 = i->parameters;
-    
-    allframes = currentframe - i->lastFrame - 1;
-    if(allframes < 0) {
-       warning(".change ignored. can only .put/.change an object once per frame.");
-       return;
-    }
-    
-    m = s_instancepos(i->character->size, &p2);
-    if(p2.blendmode || p2.filter) {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
-    } else {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
-    }
-    setPlacement(tag, 0, i->depth, m, 0, &p2, 1);
-    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?
-
-           i->lastFrame = currentframe;
-           if(p.blendmode || p.filter) {
-               lt = swf_InsertTag(t, ST_PLACEOBJECT3);
-           } else {
-               lt = swf_InsertTag(t, ST_PLACEOBJECT2);
-           }
-           setPlacement(lt, 0, i->depth, m, 0, &p, 1);
-           t = lt;
-           if(frame == allframes)
-               break;
-       }
-       t = t->next;
-       if(!t) 
-           syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
-    }
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p2, CF_CHANGE, inter);
 }
 
 void s_delinstance(char*instance)
 {
     instance_t* i = dictionary_lookup(&instances, instance);
-    if(!i) {
-       syntaxerror("instance %s not known", instance);
-    }
+    if(!i)
+        syntaxerror("instance %s not known", instance);
     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
     swf_SetU16(tag, i->depth);
     dictionary_del(&instances, instance);
@@ -1951,21 +2070,33 @@ void s_delinstance(char*instance)
 
 void s_qchange(char*instance, parameters_t p)
 {
+    instance_t* i = dictionary_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p, CF_QCHANGE, 0);
 }
 
 void s_end()
 {
     if(!stackpos)
        syntaxerror(".end unexpected");
-    if(stack[stackpos-1].type == 0)
-       s_endSWF();
-    else if(stack[stackpos-1].type == 1)
-       s_endSprite();
-    else if(stack[stackpos-1].type == 2)
-       s_endClip();
-    else if(stack[stackpos-1].type == 3)
-       s_endButton();
-    else syntaxerror("internal error 1");
+    switch (stack[stackpos-1].type)
+    {
+        case 0:
+            s_endSWF();
+            break;
+        case 1:
+            s_endSprite();
+            break;
+        case 2:
+            s_endClip();
+            break;
+        case 3:
+            s_endButton();
+            break;
+        default:
+            syntaxerror("internal error 1");
+    }
 }
 
 // ------------------------------------------------------------------------
@@ -2049,12 +2180,17 @@ int parseTwip(char*str)
        char*s;
        *dot++ = 0;
        for(s=str;s<dot-1;s++)
-           if(*s<'0' || *s>'9')
-               syntaxerror("Not a coordinate: \"%s\"", str);
-       for(s=dot;*s;s++) {
-           if(*s<'0' || *s>'9')
-               syntaxerror("Not a coordinate: \"%s\"", str);
-       }
+        if(*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;
@@ -2251,7 +2387,7 @@ 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");
@@ -2274,7 +2410,7 @@ static int c_flash(map_t*args)
 
     if(!filename || override_outputname)
        filename = outputname;
-    
+
     if(!strcmp(compressstr, "default"))
        compress = version>=6;
     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
@@ -2313,6 +2449,34 @@ static dictionary_t points;
 static mem_t mpoints;
 int points_initialized = 0;
 
+static int c_interpolation(map_t *args)
+{
+    int i;
+    char* name = lu(args, "name");
+    if (dictionary_lookup(&interpolations, name))
+        syntaxerror("interpolation %s defined twice", name);
+
+    interpolation_t* inter = (interpolation_t*)malloc(sizeof(interpolation_t));
+    char* functionstr = lu(args, "function");
+    inter->function = 0;
+    for (i = 0; i < sizeof(interpolationFunctions) / sizeof(interpolationFunctions[0]); i++)
+        if (!strcmp(functionstr,interpolationFunctions[i]))
+        {
+            inter->function = i + 1;
+            break;
+        }
+    if (!inter->function)
+        syntaxerror("unkown interpolation function %s", functionstr);
+    inter->speed = parseFloat(lu(args, "speed"));
+    inter->amplitude = parseFloat(lu(args, "amplitude"));
+    inter->growth = parseFloat(lu(args, "growth"));
+    inter->bounces = parseInt(lu(args, "bounces"));
+    inter->damping = parseInt(lu(args, "damping"));
+
+    dictionary_put2(&interpolations, name, inter);
+    return 0;
+}
+
 SPOINT getPoint(SRECT r, char*name)
 {
     int l=0;
@@ -2332,7 +2496,7 @@ SPOINT getPoint(SRECT r, char*name)
     return *(SPOINT*)&mpoints.buffer[l];
 }
 
-static int texture2(char*name, char*object, map_t*args, int errors) 
+static int texture2(char*name, char*object, map_t*args, int errors)
 {
     SPOINT pos,size;
     char*xstr = map_lookup(args, "x");
@@ -2394,14 +2558,14 @@ 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");
     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");
     int radial= strcmp(lu(args, "radial"), "radial")?0:1;
@@ -2409,7 +2573,10 @@ static int c_gradient(map_t*args)
 
     readToken();
     if(type != RAWDATA)
-       syntaxerror("colon (:) expected");
+        syntaxerror("colon (:) expected");
+
+    if(dictionary_lookup(&gradients, name))
+        syntaxerror("gradient %s defined twice", name);
 
     s_gradient(name, text, radial, rotate);
 
@@ -2422,7 +2589,7 @@ static int c_gradient(map_t*args)
     return 0;
 }
 
-static int c_blur(map_t*args) 
+static int c_blur(map_t*args)
 {
     char*name = lu(args, "name");
     char*blurstr = lu(args, "blur");
@@ -2432,7 +2599,7 @@ static int c_blur(map_t*args)
     if(blurstr[0]) {
        blurx = parseFloat(blurstr);
        blury = parseFloat(blurstr);
-    } 
+    }
     if(blurxstr[0])
        blurx = parseFloat(blurxstr);
     if(blurystr[0])
@@ -2442,7 +2609,7 @@ static int c_blur(map_t*args)
     return 0;
 }
 
-static int c_gradientglow(map_t*args) 
+static int c_gradientglow(map_t*args)
 {
     char*name = lu(args, "name");
     char*gradient = lu(args, "gradient");
@@ -2453,7 +2620,7 @@ static int c_gradientglow(map_t*args)
     if(blurstr[0]) {
        blurx = parseFloat(blurstr);
        blury = parseFloat(blurstr);
-    } 
+    }
     if(blurxstr[0])
        blurx = parseFloat(blurxstr);
     if(blurystr[0])
@@ -2472,7 +2639,7 @@ static int c_gradientglow(map_t*args)
     return 0;
 }
 
-static int c_dropshadow(map_t*args) 
+static int c_dropshadow(map_t*args)
 {
     char*name = lu(args, "name");
     RGBA color = parseColor(lu(args, "color"));
@@ -2483,7 +2650,7 @@ static int c_dropshadow(map_t*args)
     if(blurstr[0]) {
        blurx = parseFloat(blurstr);
        blury = parseFloat(blurstr);
-    } 
+    }
     if(blurxstr[0])
        blurx = parseFloat(blurxstr);
     if(blurystr[0])
@@ -2501,7 +2668,7 @@ static int c_dropshadow(map_t*args)
     return 0;
 }
 
-static int c_bevel(map_t*args) 
+static int c_bevel(map_t*args)
 {
     char*name = lu(args, "name");
     RGBA shadow = parseColor(lu(args, "shadow"));
@@ -2513,7 +2680,7 @@ static int c_bevel(map_t*args)
     if(blurstr[0]) {
        blurx = parseFloat(blurstr);
        blury = parseFloat(blurstr);
-    } 
+    }
     if(blurxstr[0])
        blurx = parseFloat(blurxstr);
     if(blurystr[0])
@@ -2532,7 +2699,7 @@ static int c_bevel(map_t*args)
     return 0;
 }
 
-static int c_point(map_t*args) 
+static int c_point(map_t*args)
 {
     char*name = lu(args, "name");
     int pos;
@@ -2551,7 +2718,7 @@ static int c_point(map_t*args)
     dictionary_put(&points, s1, (void*)pos);
     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");
@@ -2570,20 +2737,19 @@ 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");
 
-    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");
 
@@ -2594,7 +2760,7 @@ 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");
 
@@ -2627,7 +2793,7 @@ static int c_placement(map_t*args, int type)
     char* pinstr = lu(args, "pin");
     char* as = map_lookup(args, "as");
     char* blendmode = lu(args, "blend");
-    char*filterstr = lu(args, "filter");
+    char* filterstr = lu(args, "filter");
     U8 blend;
     MULADD r,g,b,a;
     float oldwidth;
@@ -2635,27 +2801,34 @@ static int c_placement(map_t*args, int type)
     SRECT oldbbox;
     MULADD luminance;
     parameters_t p;
+    U32 set = 0x00000000;
 
-    if(type==9) { // (?) .rotate  or .arcchange
-       pivotstr = lu(args, "pivot");
-       anglestr = lu(args, "angle");
-    } else {
-       xstr = lu(args, "x");
-       ystr = lu(args, "y");
+    if(type==9)
+    { // (?) .rotate  or .arcchange
+        pivotstr = lu(args, "pivot");
+        anglestr = lu(args, "angle");
     }
+    else
+    {
+        xstr = lu(args, "x");
+        ystr = lu(args, "y");
+    }
+
     if(luminancestr[0])
-       luminance = parseMulAdd(luminancestr);
-    else {
-       luminance.add = 0;
-       luminance.mul = 256;
+        luminance = parseMulAdd(luminancestr);
+    else
+    {
+        luminance.add = 0;
+        luminance.mul = 256;
     }
 
-    if(scalestr[0]) {
-       if(scalexstr[0]||scaleystr[0])
-           syntaxerror("scalex/scaley and scale cannot both be set");
-       scalexstr = scaleystr = scalestr;
+    if(scalestr[0])
+    {
+        if(scalexstr[0]||scaleystr[0])
+            syntaxerror("scalex/scaley and scale cannot both be set");
+        scalexstr = scaleystr = scalestr;
     }
-    
+
     if(type == 0 || type == 4)  {
        // put or startclip
        character = lu(args, "character");
@@ -2669,218 +2842,277 @@ static int c_placement(map_t*args, int type)
     }
 
     /* x,y position */
-    if(xstr[0]) {
-       if(isRelative(xstr)) {
-           if(type == 0 || type == 4)
-               syntaxerror("relative x values not allowed for initial put or startclip");
-           p.x += parseTwip(getOffset(xstr))*getSign(xstr);
-       } else {
-           p.x = parseTwip(xstr);
-       }
-    }
-    if(ystr[0]) {
-       if(isRelative(ystr)) {
-           if(type == 0 || type == 4)
-               syntaxerror("relative y values not allowed for initial put or startclip");
-           p.y += parseTwip(getOffset(ystr))*getSign(ystr);
-       } else {
-           p.y = parseTwip(ystr);
+    if(xstr[0])
+    {
+        if(isRelative(xstr))
+        {
+            if(type == 0 || type == 4)
+                syntaxerror("relative x values not allowed for initial put or startclip");
+            p.x += parseTwip(getOffset(xstr))*getSign(xstr);
+        }
+        else
+        {
+            p.x = parseTwip(xstr);
+        }
+        set = set | SF_X;
+     }
+    if(ystr[0])
+    {
+        if(isRelative(ystr))
+        {
+            if(type == 0 || type == 4)
+                syntaxerror("relative y values not allowed for initial put or startclip");
+            p.y += parseTwip(getOffset(ystr))*getSign(ystr);
+        }
+        else
+        {
+            p.y = parseTwip(ystr);
+        }
+        set = set | SF_Y;
        }
-    }
 
     /* scale, scalex, scaley */
-    if(character) {
-       oldbbox = s_getCharBBox(character);
-    } else {
-       oldbbox = s_getInstanceBBox(instance);
-    }
+    if(character)
+        oldbbox = s_getCharBBox(character);
+    else
+        oldbbox = s_getInstanceBBox(instance);
     oldwidth = oldbbox.xmax - oldbbox.xmin;
     oldheight = oldbbox.ymax - oldbbox.ymin;
-    if(scalexstr[0]) {
-       if(oldwidth==0) p.scalex = 1.0;
-       else {      
-           if(scalexstr[0])
-               p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
-       }
-    }
-    if(scaleystr[0]) {
-       if(oldheight==0) p.scaley = 1.0;
-       else {
-           if(scaleystr[0])
-               p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
-       }
-    }
-   
+    if(scalexstr[0])
+    {
+        if(oldwidth==0)
+            p.scalex = 1.0;
+        else
+            if(scalexstr[0])
+                p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
+        set = set | SF_SCALEX;
+    }
+   if(scaleystr[0])
+   {
+        if(oldheight==0)
+            p.scaley = 1.0;
+        else
+            if(scaleystr[0])
+                p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
+        set = set | SF_SCALEY;
+   }
+
     /* rotation */
-    if(rotatestr[0]) {
-       if(isRelative(rotatestr)) {
-           p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
-       } else {
-           p.rotate = parseFloat(rotatestr);
+    if(rotatestr[0])
+    {
+        if(isRelative(rotatestr))
+            p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
+        else
+            p.rotate = parseFloat(rotatestr);
+        set = set | SF_ROTATE;
        }
-    }
 
     /* shearing */
-    if(shearstr[0]) {
-       if(isRelative(shearstr)) {
-           p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
-       } else {
-           p.shear = parseFloat(shearstr);
-       }
+    if(shearstr[0])
+    {
+        if(isRelative(shearstr))
+            p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
+        else
+            p.shear = parseFloat(shearstr);
+        set = set | SF_SHEAR;
     }
 
-    if(pivotstr[0]) {
-       if(isPoint(pivotstr)) 
-           p.pivot = parsePoint(pivotstr);
-       else 
-           p.pivot = getPoint(oldbbox, pivotstr);
+    if(pivotstr[0])
+    {
+        if(isPoint(pivotstr))
+            p.pivot = parsePoint(pivotstr);
+        else
+            p.pivot = getPoint(oldbbox, pivotstr);
+        set = set | SF_PIVOT;
     }
-    if(pinstr[0]) {
-       if(isPoint(pinstr))
-           p.pin = parsePoint(pinstr);
-       else
-           p.pin = getPoint(oldbbox, pinstr);
+
+    if(pinstr[0])
+    {
+        if(isPoint(pinstr))
+            p.pin = parsePoint(pinstr);
+        else
+            p.pin = getPoint(oldbbox, pinstr);
+        set = set | SF_PIN;
     }
-       
+
     /* color transform */
 
-    if(rstr[0] || luminancestr[0]) {
-       MULADD r;
-       if(rstr[0])
-           r = parseMulAdd(rstr);
-       else {
-           r.add = p.cxform.r0;
-           r.mul = p.cxform.r1;
-       }
-       r = mergeMulAdd(r, luminance);
-       p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
-    }
-    if(gstr[0] || luminancestr[0]) {
-       MULADD g;
-       if(gstr[0])
-           g = parseMulAdd(gstr);
-       else {
-           g.add = p.cxform.g0;
-           g.mul = p.cxform.g1;
-       }
-       g = mergeMulAdd(g, luminance);
-       p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
-    }
-    if(bstr[0] || luminancestr[0]) {
-       MULADD b;
-       if(bstr[0])
-           b = parseMulAdd(bstr);
-       else {
-           b.add = p.cxform.b0;
-           b.mul = p.cxform.b1;
+    if(rstr[0] || luminancestr[0])
+    {
+        MULADD r;
+        if(rstr[0])
+            r = parseMulAdd(rstr);
+        else
+        {
+            r.add = p.cxform.r0;
+            r.mul = p.cxform.r1;
+        }
+        r = mergeMulAdd(r, luminance);
+        p.cxform.r0 = r.mul;
+        p.cxform.r1 = r.add;
+        set = set | SF_CX_R;
        }
-       b = mergeMulAdd(b, luminance);
-       p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
-    }
-    if(astr[0]) {
-       MULADD a = parseMulAdd(astr);
-       p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
+    if(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;
-    }
-    
-    if(filterstr[0]) {
-       FILTER*f = dictionary_lookup(&filters, filterstr);
-       if(!f) 
-           syntaxerror("Unknown filter %s", filterstr);
-       p.filter = f;
-    }
-
-
-    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(blendmode[0])
+    {
+        int t;
+        blend = 255;
+        for(t = 0; blendModeNames[t]; t++)
+        {
+            if(!strcmp(blendModeNames[t], blendmode))
+            {
+                blend = t;
+                break;
+            }
+        }
+        if(blend == 255)
+        {
+            syntaxerror("unknown blend mode: '%s'", blendmode);
+        }
+        p.blendmode = blend;
+        set = set | SF_BLEND;
        }
+
+    if(filterstr[0])
+    {
+        FILTER*f = dictionary_lookup(&filters, filterstr);
+        if(!f)
+            syntaxerror("Unknown filter %s", filterstr);
+        p.filter = f;
+        set = set | SF_FILTER;
     }
+
+    p.set = set;
+
+    switch (type)
+    {
+        case 0:
+            s_put(instance, character, p);
+            break;
+        case 1:
+            {
+                char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
+                if (!inter)
+                    syntaxerror("unkown interpolation %s", interstr);
+                s_change(instance, p, inter);
+            }
+            break;
+        case 2:
+            s_qchange(instance, p);
+            break;
+        case 3:
+            s_jump(instance, p);
+            break;
+        case 4:
+            s_startclip(instance, character, p);
+            break;
+        case 5:
+            if(as && as[0])
+                s_buttonput(character, as, p);
+            else
+                s_buttonput(character, "shape", p);
+            break;
+//        default:
+       }
     return 0;
 }
-static int c_put(map_t*args) 
+static int c_put(map_t*args)
 {
     c_placement(args, 0);
     return 0;
 }
-static int c_change(map_t*args) 
+static int c_change(map_t*args)
 {
+    if (currentframe == 0)
+        warning("change commands in frame 1 will be ignored, please use the put command to set object parameters");
     c_placement(args, 1);
     return 0;
 }
-static int c_qchange(map_t*args) 
+static int c_qchange(map_t*args)
 {
     c_placement(args, 2);
     return 0;
 }
-static int c_arcchange(map_t*args) 
+static int c_arcchange(map_t*args)
 {
     c_placement(args, 2);
     return 0;
 }
-static int c_jump(map_t*args) 
+static int c_jump(map_t*args)
 {
     c_placement(args, 3);
     return 0;
 }
-static int c_startclip(map_t*args) 
+static int c_startclip(map_t*args)
 {
     c_placement(args, 4);
     return 0;
 }
-static int c_show(map_t*args) 
+static int c_show(map_t*args)
 {
     c_placement(args, 5);
     return 0;
 }
-static int c_del(map_t*args) 
+static int c_del(map_t*args)
 {
     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);
     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");
     char buf[40];
@@ -2907,7 +3139,7 @@ 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");
@@ -2926,7 +3158,7 @@ static int c_primitive(map_t*args)
        type = 1;
     else if(!strcmp(command, "filled"))
        type = 2;
-   
+
     if(type==0) {
        width = parseTwip(lu(args, "width"));
        height = parseTwip(lu(args, "height"));
@@ -2942,14 +3174,14 @@ 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");
@@ -2960,7 +3192,7 @@ static int c_textshape(map_t*args)
     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");
@@ -2971,7 +3203,7 @@ static int c_swf(map_t*args)
     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");
@@ -2979,7 +3211,7 @@ static int c_font(map_t*args)
     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");
@@ -2987,7 +3219,7 @@ static int c_sound(map_t*args)
     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");
@@ -2998,12 +3230,12 @@ static int c_text(map_t*args)
     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");
@@ -3011,7 +3243,7 @@ static int c_quicktime(map_t*args)
     return 0;
 }
 
-static int c_image(map_t*args) 
+static int c_image(map_t*args)
 {
     char*command = lu(args, "commandname");
     char*name = lu(args, "name");
@@ -3025,7 +3257,7 @@ 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");
@@ -3150,7 +3382,7 @@ static int c_on_key(map_t*args)
            return 1;
        }
     } else {
-       /* TODO: 
+       /* TODO:
           <ctrl-x> = 0x200*(x-'a')
           esc = = 0x3600
           space = = 0x4000;
@@ -3168,7 +3400,7 @@ 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");
@@ -3218,7 +3450,7 @@ static char* readfile(const 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);
@@ -3230,7 +3462,7 @@ 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");
     if(!filename ||!*filename) {
@@ -3242,11 +3474,11 @@ static int c_action(map_t*args)
     } else {
        s_action(readfile(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");
@@ -3259,7 +3491,7 @@ static int c_initaction(map_t*args)
     } else {
        s_initaction(character, readfile(filename));
     }
-   
+
     return 0;
 }
 
@@ -3285,6 +3517,7 @@ static struct {
 
  {"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"},
  {"outline", c_outline, "name format=simple"},
  {"textshape", c_textshape, "name font size=100% text"},
 
@@ -3310,7 +3543,7 @@ static struct {
     {"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= "},
@@ -3320,7 +3553,7 @@ static struct {
     // object placement tags
  {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
  {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
- {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
+ {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below= interpolation=linear"},
  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
@@ -3342,7 +3575,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];
@@ -3402,7 +3635,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;
@@ -3497,7 +3730,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]);
     }
@@ -3516,7 +3749,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;
            }
@@ -3528,7 +3761,7 @@ static void parseArgumentsForCommand(char*command)
     }
     if(nr<0)
        syntaxerror("command %s not known", command);
-   
+
     // catch missing .flash directives at the beginning of a file
     if(strcmp(command, "flash") && !stackpos)
     {
@@ -3561,8 +3794,98 @@ static void parseArgumentsForCommand(char*command)
     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;
+    char* fontfile;
+    int nr = -1;
+    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
+    char* name = lu(&args, "name");
+    if (!strcmp(command, "font"))
+    {
+       if(dictionary_lookup(&fonts, name))
+            syntaxerror("font %s defined twice", name);
+
+       SWFFONT* font;
+       fontfile = lu(&args, "filename");
+       font = swf_LoadFont(fontfile);
+       if(font == 0) {
+           warning("Couldn't open font file \"%s\"", fontfile);
+           font = (SWFFONT*)malloc(sizeof(SWFFONT));
+           memset(font, 0, sizeof(SWFFONT));
+       }
+       swf_FontPrepareForEditText(font);
+       dictionary_put2(&fonts, name, font);
+    }
+    else
+    {
+        SWFFONT* font = dictionary_lookup(&fonts, lu(&args, "font"));
+        if (!font)
+            syntaxerror("font %s is not known in line %d", lu(&args, "font"), line);
+        else
+            if (!strcmp(command, "edittext"))
+               swf_FontUseAll(font);
+            else
+               swf_FontUseUTF8(font, lu(&args, "text"));
+    }
+    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;
+    dictionary_init(&fonts);
+    cleanUp = &freeFontDictionary;
+    findFontUsage();
+}
+
 int main (int argc,char ** argv)
-{ 
+{
     int t;
     processargs(argc, argv);
     initLog(0,-1,0,0,-1,verbose);
@@ -3571,12 +3894,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;