reworked compression handling logic
[swftools.git] / src / swfc.c
index a93fea5..c07a8a0 100644 (file)
@@ -154,6 +154,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
@@ -205,8 +218,9 @@ typedef struct _parameters {
     SPOINT pivot;
     SPOINT pin;
     U8 blendmode; //not interpolated
-    FILTER*filter;
+    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 {
@@ -219,8 +233,6 @@ 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;
 
@@ -325,8 +337,8 @@ static void free_outline(void* o)
 
 static void freeDictionaries()
 {
-   dictionary_free_all(&instances, free_instance);
-   dictionary_free_all(&characters, free);
+    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);
@@ -403,7 +415,7 @@ static void parameters_clear(parameters_t*p)
     p->rotate = 0;
     p->shear = 0;
     p->blendmode = 0;
-    p->filter = 0;
+    p->filters = 0;
     swf_GetCXForm(0, &p->cxform, 1);
 }
 
@@ -416,10 +428,10 @@ static void makeMatrix(MATRIX*m, parameters_t*p)
      *       \r0 sy/ \y/
      */
 
-    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;
+    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);
@@ -456,42 +468,54 @@ void initBuiltIns()
 
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_QUAD_IN;
+    new->slope = 0;
     dictionary_put2(&interpolations, "quadIn", new);
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_QUAD_OUT;
+    new->slope = 0;
     dictionary_put2(&interpolations, "quadOut", new);
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_QUAD_IN_OUT;
+    new->slope = 0;
     dictionary_put2(&interpolations, "quadInOut", new);
 
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_CUBIC_IN;
+    new->slope = 0;
     dictionary_put2(&interpolations, "cubicIn", new);
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_CUBIC_OUT;
+    new->slope = 0;
     dictionary_put2(&interpolations, "cubicOut", new);
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_CUBIC_IN_OUT;
+    new->slope = 0;
     dictionary_put2(&interpolations, "cubicInOut", new);
 
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_QUART_IN;
+    new->slope = 0;
     dictionary_put2(&interpolations, "quartIn", new);
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_QUART_OUT;
+    new->slope = 0;
     dictionary_put2(&interpolations, "quartOut", new);
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_QUART_IN_OUT;
+    new->slope = 0;
     dictionary_put2(&interpolations, "quartInOut", new);
 
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_QUINT_IN;
+    new->slope = 0;
     dictionary_put2(&interpolations, "quintIn", new);
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_QUINT_OUT;
+    new->slope = 0;
     dictionary_put2(&interpolations, "quintOut", new);
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_QUINT_IN_OUT;
+    new->slope = 0;
     dictionary_put2(&interpolations, "quintInOut", new);
 
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
@@ -538,17 +562,25 @@ void initBuiltIns()
     noGradient->rotate = 0;
     dictionary_put2(&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));
+    dictionary_put2(&filters, "no_filters", dummy);
     noBlur = (FILTER_BLUR*) swf_NewFilter(FILTERTYPE_BLUR);
     noBlur->passes = 1;
     dictionary_put2(&filters, "no_blur", noBlur);
     noBevel = (FILTER_BEVEL*) swf_NewFilter(FILTERTYPE_BEVEL);
     noBevel->passes = 1;
+    noBevel->composite = 1;
     dictionary_put2(&filters, "no_bevel", noBevel);
     noDropshadow = (FILTER_DROPSHADOW*) swf_NewFilter(FILTERTYPE_DROPSHADOW);
     noDropshadow->passes = 1;
+    noDropshadow->composite = 1;
     dictionary_put2(&filters, "no_dropshadow", noDropshadow);
     noGradientGlow = (FILTER_GRADIENTGLOW*) swf_NewFilter(FILTERTYPE_GRADIENTGLOW);
     noGradientGlow->passes = 1;
+    noGradientGlow->composite = 1;
     noGradientGlow->gradient = &noGradient->gradient;
     dictionary_put2(&filters, "no_gradientglow", noGradientGlow);
 }
@@ -808,6 +840,18 @@ static int parametersChange(history_t* history, int 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");
@@ -822,14 +866,14 @@ static void readParameters(history_t* history, parameters_t* p, int frame)
     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->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->filter = history_valueFilter(history, frame);
+    p->filters = history_filterValue(history, frame);
 }
 
 void setPlacement(TAG*tag, U16 id, U16 depth, MATRIX m, char*name, parameters_t*p, char move)
@@ -848,11 +892,8 @@ void setPlacement(TAG*tag, U16 id, U16 depth, MATRIX m, char*name, parameters_t*
     if(p->blendmode) {
     po.blendmode = p->blendmode;
     }
-    if(p->filter) {
-    flist.num = 1;
-    flist.filter[0] = p->filter;
-    po.filters = &flist;
-    }
+    if (p->filters)
+       po.filters = p->filters;
     swf_SetPlaceObject(tag, &po);
 }
 
@@ -862,6 +903,7 @@ static void writeInstance(instance_t* i)
     MATRIX m;
     int frame = i->history->firstFrame;
     TAG* tag = i->history->firstTag;
+    history_processFlags(i->history);
     while (frame < currentframe)
     {
         frame++;
@@ -870,20 +912,16 @@ static void writeInstance(instance_t* i)
         if (parametersChange(i->history, frame))
         {
             readParameters(i->history, &p, frame);
-        m = s_instancepos(i->character->size, &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);
-        setPlacement(tag, 0, i->depth, m, 0, &p, 1);
-        if (p.filter)
-            {
-               if (p.filter->type == FILTERTYPE_GRADIENTGLOW)
-                   gradient_free(((FILTER_GRADIENTGLOW*)p.filter)->gradient);
-            free(p.filter);
-    }
-}
+            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
             tag = tag->next;
     }
@@ -910,14 +948,15 @@ static void s_endSprite()
     stackpos--;
     instance_t *i;
     stringarray_t* index =dictionary_index(&instances);
-    int num = 0;
-    char* name = stringarray_at(index, num);
-    while (name)
+    int num;
+    for (num = 0; num < dictionary_count(&instances); num++)
     {
-        i = dictionary_lookup(&instances, name);
-        writeInstance(i);
-        num++;
-        name = stringarray_at(index, num);
+       char* name = stringarray_at(index, num);
+       if (name)
+       {
+            i = dictionary_lookup(&instances, name);
+            writeInstance(i);
+       }
     }
     while (tag->next)
        tag = tag->next;
@@ -950,14 +989,15 @@ static void s_endSWF()
 
     instance_t *i;
     stringarray_t* index = dictionary_index(&instances);
-    int num = 0;
-    char* name = stringarray_at(index, num);
-    while (name)
+    int num;
+    for (num = 0; num < dictionary_count(&instances); num++)
     {
-        i = dictionary_lookup(&instances, name);
-        writeInstance(i);
-        num++;
-        name = stringarray_at(index, num);
+       char* name = stringarray_at(index, num);
+       if (name)
+       {
+            i = dictionary_lookup(&instances, name);
+            writeInstance(i);
+       }
     }
 
     if(stack[stackpos].cut)
@@ -999,8 +1039,6 @@ 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");}
 
@@ -1091,8 +1129,8 @@ int addFillStyle(SHAPE*s, SRECT*r, char*name)
        MATRIX rot,m;
        double ccos,csin;
        swf_GetMatrix(0, &rot);
-    ccos = cos(-gradient->rotate*2*PI/360);
-    csin = sin(-gradient->rotate*2*PI/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;
@@ -1663,6 +1701,44 @@ GRADIENT parseGradient(const char*str)
     return gradient;
 }
 
+FILTERLIST* parseFilters(char* list)
+{
+    if (!strcmp(list, "no_filters"))
+       return 0;
+    FILTER* f;
+    FILTERLIST* f_list = (FILTERLIST*)malloc(sizeof(FILTERLIST));
+    f_list->num = 0;
+    char* f_start = list;
+    char* f_end;
+    while (f_start)
+    {
+       f_end = strchr(f_start, ',');
+       if (f_end)
+           *f_end = '\0';
+       f = dictionary_lookup(&filters, f_start);
+       if (!f)
+       {
+           free(f_list);
+           syntaxerror("unknown filter %s", f_start);
+       }
+       if (f_list->num == 8)
+       {
+           warning("too many filters in filterlist, no more than 8 please, rest ignored");
+           break;
+       }
+       f_list->filter[f_list->num] = f;
+       f_list->num++;
+       if (f_end)
+       {
+           *f_end = ',';
+           f_start = f_end + 1;
+       }
+       else
+           f_start = 0;
+    }
+    return f_list;
+}
+
 void s_gradient(char*name, const char*text, int radial, int rotate)
 {
     gradient_t* gradient;
@@ -1986,8 +2062,6 @@ void s_startclip(char*instance, char*character, parameters_t 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;
@@ -2029,7 +2103,8 @@ void setStartparameters(instance_t* i, parameters_t* p, TAG* tag)
     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);
+    history_beginFilter(i->history, currentframe, tag, p->filters);
+    history_begin(i->history, "flags", currentframe, tag, 0);
 }
 
 void s_put(char*instance, char*character, parameters_t p)
@@ -2044,7 +2119,7 @@ void s_put(char*instance, char*character, parameters_t p)
     i->parameters = p;
     m = s_instancepos(i->character->size, &p);
 
-    if(p.blendmode || p.filter)
+    if(p.blendmode || p.filters)
     {
         if(stack[0].swf->fileVersion < 8)
         {
@@ -2059,8 +2134,6 @@ void s_put(char*instance, char*character, parameters_t p)
         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++;
 }
 
@@ -2111,7 +2184,7 @@ void recordChanges(history_t* history, parameters_t p, int changeFunction, 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);
+        history_rememberFilter(history, currentframe, changeFunction, p.filters, inter);
 }
 
 void s_jump(char* instance, parameters_t p)
@@ -2122,13 +2195,31 @@ void s_jump(char* instance, parameters_t p)
     recordChanges(i->history, p, CF_JUMP, 0);
 }
 
-void s_change(char*instance, parameters_t p2, interpolation_t* inter)
+void s_change(char*instance, parameters_t p, interpolation_t* inter)
 {
     instance_t* i = dictionary_lookup(&instances, instance);
     if(!i)
         syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p, CF_CHANGE, inter);
+}
 
-    recordChanges(i->history, p2, CF_CHANGE, inter);
+void s_sweep(char* instance, parameters_t p, float radius, int clockwise, int short_arc, interpolation_t* inter)
+{
+    instance_t* i = dictionary_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_toggle(char* instance, U16 flagsOn, U16 flagsOff)
+{
+    instance_t* i = dictionary_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)
@@ -2136,17 +2227,18 @@ void s_delinstance(char*instance)
     instance_t* i = dictionary_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);
 }
 
-void s_qchange(char*instance, parameters_t p)
+void s_schange(char*instance, parameters_t p, interpolation_t* inter)
 {
     instance_t* i = dictionary_lookup(&instances, instance);
     if(!i)
         syntaxerror("instance %s not known", instance);
-    recordChanges(i->history, p, CF_QCHANGE, 0);
+    recordChanges(i->history, p, CF_SCHANGE, inter);
 }
 
 void s_end()
@@ -2281,6 +2373,26 @@ int parseTwip(char*str)
     return 0;
 }
 
+int parseArc(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(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(char*str)
 {
     if(strchr(str, '('))
@@ -2548,10 +2660,11 @@ static int c_interpolation(map_t *args)
     if (!inter->function)
         syntaxerror("unkown interpolation function %s", functionstr);
     inter->speed = parseFloat(lu(args, "speed"));
-    inter->amplitude = parseFloat(lu(args, "amplitude"));
+    inter->amplitude = parseTwip(lu(args, "amplitude"));
     inter->growth = parseFloat(lu(args, "growth"));
     inter->bounces = parseInt(lu(args, "bounces"));
-    inter->damping = parseInt(lu(args, "damping"));
+    inter->damping = parseFloat(lu(args, "damping"));
+    inter->slope = parseFloat(lu(args, "slope"));
 
     dictionary_put2(&interpolations, name, inter);
     return 0;
@@ -2561,21 +2674,71 @@ SPOINT getPoint(SRECT r, 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)dictionary_lookup(&points, name);
     if(l==0) {
-       syntaxerror("Invalid point: \"%s\".", name);
+        syntaxerror("Invalid point: \"%s\".", name);
     }
     l--;
     return *(SPOINT*)&mpoints.buffer[l];
 }
 
+
 static int texture2(char*name, char*object, map_t*args, int errors)
 {
     SPOINT pos,size;
@@ -2669,9 +2832,17 @@ static int c_gradient(map_t*args)
     return 0;
 }
 
+static char* checkFiltername(map_t* args)
+{
+    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)
 {
-    char*name = lu(args, "name");
+    char*name = checkFiltername(args);
     char*blurstr = lu(args, "blur");
     char*blurxstr = lu(args, "blurx");
     char*blurystr = lu(args, "blury");
@@ -2691,7 +2862,7 @@ static int c_blur(map_t*args)
 
 static int c_gradientglow(map_t*args)
 {
-    char*name = lu(args, "name");
+    char*name = checkFiltername(args);
     char*gradient = lu(args, "gradient");
     char*blurstr = lu(args, "blur");
     char*blurxstr = lu(args, "blurx");
@@ -2721,7 +2892,7 @@ static int c_gradientglow(map_t*args)
 
 static int c_dropshadow(map_t*args)
 {
-    char*name = lu(args, "name");
+    char*name = checkFiltername(args);
     RGBA color = parseColor(lu(args, "color"));
     char*blurstr = lu(args, "blur");
     char*blurxstr = lu(args, "blurx");
@@ -2750,7 +2921,7 @@ static int c_dropshadow(map_t*args)
 
 static int c_bevel(map_t*args)
 {
-    char*name = lu(args, "name");
+    char*name = checkFiltername(args);
     RGBA shadow = parseColor(lu(args, "shadow"));
     RGBA highlight = parseColor(lu(args, "highlight"));
     char*blurstr = lu(args, "blur");
@@ -2851,9 +3022,99 @@ static int c_previousframe(map_t*args)
     return 0;
 }
 
+static int c_movement(map_t*args, int type)
+{
+    char*instance = lu(args, "name");
+
+    char* xstr="";
+    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:
+            {
+                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 PT_SMOVE:
+            {
+                char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
+                if (!inter)
+                    syntaxerror("unkown interpolation %s", interstr);
+               s_schange(instance, p, inter);
+            }
+            break;
+        case PT_SWEEP:
+            {
+               char* rstr = lu(args, "r");
+               int radius = parseTwip(rstr);
+               if (radius <= 0)
+                       syntaxerror("sweep not possible: radius must be greater than 0.");
+               char* dirstr = lu(args, "dir");
+               int clockwise = parseDir(dirstr);
+               char* arcstr = lu(args, "arc");
+               int short_arc = parseArc(arcstr);
+                char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dictionary_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*instance = lu(args, (type==PT_PUT||type==PT_STARTCLIP)?"instance":"name");
     char*character = 0;
 
     char* luminancestr = lu(args, "luminance");
@@ -2909,11 +3170,11 @@ static int c_placement(map_t*args, int type)
         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
@@ -2926,7 +3187,7 @@ static int c_placement(map_t*args, int type)
     {
         if(isRelative(xstr))
         {
-            if(type == 0 || type == 4)
+            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);
         }
@@ -2940,7 +3201,7 @@ static int c_placement(map_t*args, int type)
     {
         if(isRelative(ystr))
         {
-            if(type == 0 || type == 4)
+            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);
         }
@@ -3092,23 +3353,26 @@ static int c_placement(map_t*args, int type)
 
     if(filterstr[0])
     {
-        FILTER*f = dictionary_lookup(&filters, filterstr);
-        if(!f)
-            syntaxerror("Unknown filter %s", filterstr);
-        p.filter = f;
+        p.filters = parseFilters(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 0:
+        case PT_PUT:
             s_put(instance, character, p);
             break;
-        case 1:
+        case PT_CHANGE:
             {
                 char* interstr = lu(args, "interpolation");
                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
@@ -3117,16 +3381,22 @@ static int c_placement(map_t*args, int type)
                 s_change(instance, p, inter);
             }
             break;
-        case 2:
-            s_qchange(instance, p);
+        case PT_SCHANGE:
+            {
+                char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
+                if (!inter)
+                    syntaxerror("unkown interpolation %s", interstr);
+               s_schange(instance, p, inter);
+            }
             break;
-        case 3:
+        case PT_JUMP:
             s_jump(instance, p);
             break;
-        case 4:
+        case PT_STARTCLIP:
             s_startclip(instance, character, p);
             break;
-        case 5:
+        case PT_BUTTON:
             if(as && as[0])
                 s_buttonput(character, as, p);
             else
@@ -3138,39 +3408,69 @@ static int c_placement(map_t*args, int type)
 }
 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)
 {
     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);
+    c_placement(args, PT_CHANGE);
+    return 0;
+}
+static int c_schange(map_t*args)
+{
+    c_placement(args, PT_SCHANGE);
+    return 0;
+}
+static int c_move(map_t* args)
+{
+    c_movement(args, PT_MOVE);
     return 0;
 }
-static int c_qchange(map_t*args)
+static int c_smove(map_t* args)
 {
-    c_placement(args, 2);
+    c_movement(args, PT_SMOVE);
+    return 0;
+}
+static int c_sweep(map_t* args)
+{
+    c_movement(args, PT_SWEEP);
     return 0;
 }
 static int c_arcchange(map_t*args)
 {
-    c_placement(args, 2);
+    c_placement(args, 0);
     return 0;
 }
 static int c_jump(map_t*args)
 {
-    c_placement(args, 3);
+    c_placement(args, PT_JUMP);
     return 0;
 }
 static int c_startclip(map_t*args)
 {
-    c_placement(args, 4);
+    c_placement(args, PT_STARTCLIP);
     return 0;
 }
 static int c_show(map_t*args)
 {
-    c_placement(args, 5);
+    c_placement(args, PT_BUTTON);
+    return 0;
+}
+static int c_toggle(map_t* args)
+{
+    char*instance = lu(args, "name");
+    U16 flagsOn = 0x0000, flagsOff = 0xffff;
+    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)
@@ -3591,7 +3891,7 @@ 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="},
  {"soundtrack", c_soundtrack, "filename"},
  {"quicktime", c_quicktime, "url"},
 
@@ -3599,7 +3899,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"},
+ {"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"},
 
@@ -3635,13 +3935,18 @@ 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="},
+ {"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="},
- {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
+ //{"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)
@@ -3915,6 +4220,7 @@ static void analyseArgumentsForCommand(char*command)
            font = (SWFFONT*)malloc(sizeof(SWFFONT));
            memset(font, 0, sizeof(SWFFONT));
        }
+       swf_FontUseUTF8(font, lu(&args, "glyphs"));
        swf_FontPrepareForEditText(font);
        dictionary_put2(&fonts, name, font);
     }