DEFINEBINARY defines an id
[swftools.git] / lib / modules / swftools.c
index 5526d31..1dc66d8 100644 (file)
 
 // Matrix & Math tools for SWF files
 
+#include "../rfxswf.h"
+
 #define S64 long long
 SFIXED RFXSWF_SP(SFIXED a1,SFIXED a2,SFIXED b1,SFIXED b2)
-{ S64 a;
-  a = (S64)a1*(S64)b1+(S64)a2*(S64)b2;
-  return (SFIXED)(a>>16);
+{ S64 a = ((S64)a1*(S64)b1+(S64)a2*(S64)b2)>>16;
+  SFIXED result = (SFIXED)(a);
+  if(a!=result) 
+      fprintf(stderr, "Warning: overflow in matrix multiplication\n");
+  return result;
 }
 SFIXED RFXSWF_QFIX(int zaehler,int nenner) // bildet Quotient von zwei INTs in SFIXED
 { S64 z = zaehler<<16;
@@ -42,13 +46,14 @@ MATRIX * swf_MatrixJoin(MATRIX * d,MATRIX * s1,MATRIX * s2)
   if (!s1) return (s2)?(MATRIX *)memcpy(d,s2,sizeof(MATRIX)):NULL;
   if (!s2) return (MATRIX *)memcpy(d,s1,sizeof(MATRIX));
   
-  d->tx = s1->tx + s2->tx;
-  d->ty = s1->ty + s2->ty;
+  d->tx = s1->tx + RFXSWF_SP(s1->sx,s1->r1,s2->tx,s2->ty);
+  d->ty = s1->ty + RFXSWF_SP(s1->r0,s1->sy,s2->tx,s2->ty);
   
   d->sx = RFXSWF_SP(s1->sx,s1->r1,s2->sx,s2->r0);
-  d->sy = RFXSWF_SP(s1->r0,s1->sy,s2->r1,s2->sy);
   d->r0 = RFXSWF_SP(s1->r0,s1->sy,s2->sx,s2->r0);
+
   d->r1 = RFXSWF_SP(s1->sx,s1->r1,s2->r1,s2->sy);
+  d->sy = RFXSWF_SP(s1->r0,s1->sy,s2->r1,s2->sy);
 
   //DumpMatrix(NULL,d);
   
@@ -95,22 +100,30 @@ U16 swf_GetDefineID(TAG * t)
   { case ST_DEFINESHAPE:
     case ST_DEFINESHAPE2:
     case ST_DEFINESHAPE3:
+    case ST_DEFINESHAPE4:
     case ST_DEFINEMORPHSHAPE:
+    case ST_DEFINEMORPHSHAPE2:
     case ST_DEFINEEDITTEXT:
     case ST_DEFINEBITS:
     case ST_DEFINEBITSJPEG2:
     case ST_DEFINEBITSJPEG3:
     case ST_DEFINEBITSLOSSLESS:
     case ST_DEFINEBITSLOSSLESS2:
+    case ST_DEFINESCALINGGRID: //pseudodefine
     case ST_DEFINEBUTTON:
     case ST_DEFINEBUTTON2:
     case ST_DEFINEBUTTONCXFORM: //pseudodefine
     case ST_DEFINEBUTTONSOUND: //pseudodefine
+    case ST_CSMTEXTSETTINGS: //pseudodefine
     case ST_DEFINEFONT:
     case ST_DEFINEFONT2:
+    case ST_DEFINEFONT3:
     case ST_DEFINEFONTINFO: //pseudodefine
     case ST_DEFINEFONTINFO2: //pseudodefine
+    case ST_DEFINEFONTALIGNZONES: //pseudodefine
+    case ST_DEFINEFONTNAME: //pseudodefine
     case ST_DEFINETEXT:
+    case ST_DEFINEBINARY:
     case ST_DEFINETEXT2:
     case ST_DEFINESOUND:
     case ST_DEFINESPRITE:
@@ -147,6 +160,7 @@ SRECT swf_GetDefineBBox(TAG * t)
   { case ST_DEFINESHAPE:
     case ST_DEFINESHAPE2:
     case ST_DEFINESHAPE3:
+    case ST_DEFINESHAPE4:
     case ST_DEFINEEDITTEXT:
     case ST_DEFINETEXT:
     case ST_DEFINETEXT2:
@@ -195,6 +209,12 @@ U16 swf_GetPlaceID(TAG * t)
       U16 d = swf_GetU16(t);
       id = (flags&PF_CHAR)?swf_GetU16(t):id;
     } break;
+    case ST_PLACEOBJECT3:
+    { U8 flags = swf_GetU8(t);
+      U8 flags2 = swf_GetU8(t);
+      U16 d = swf_GetU16(t);
+      id = (flags&PF_CHAR)?swf_GetU16(t):id;
+    } break;
 
   }
 
@@ -207,9 +227,12 @@ static int swf_definingtagids[] =
 {ST_DEFINESHAPE,
  ST_DEFINESHAPE2,
  ST_DEFINESHAPE3,
+ ST_DEFINESHAPE4,
  ST_DEFINEMORPHSHAPE,
+ ST_DEFINEMORPHSHAPE2,
  ST_DEFINEFONT,
  ST_DEFINEFONT2,
+ ST_DEFINEFONT3,
  ST_DEFINETEXT,
  ST_DEFINETEXT2,
  ST_DEFINEEDITTEXT,
@@ -224,6 +247,7 @@ static int swf_definingtagids[] =
  ST_DEFINEBUTTON2,
  ST_DEFINESOUND,
  ST_DEFINEVIDEOSTREAM,
+ ST_DEFINEBINARY,
  -1
 };
 
@@ -232,9 +256,11 @@ static int swf_spritetagids[] =
 {ST_SHOWFRAME,
  ST_PLACEOBJECT,
  ST_PLACEOBJECT2,
+ ST_PLACEOBJECT3,
  ST_REMOVEOBJECT,
- ST_REMOVEOBJECT2, //?
+ ST_REMOVEOBJECT2,
  ST_DOACTION,
+ ST_DOABC,
  ST_STARTSOUND,
  ST_FRAMELABEL,
  ST_SOUNDSTREAMHEAD,
@@ -244,12 +270,17 @@ static int swf_spritetagids[] =
  -1
 };
 
+/* tags which add content or information to a character with a given ID */
 static int swf_pseudodefiningtagids[] = 
 {
  ST_DEFINEFONTINFO,
  ST_DEFINEFONTINFO2,
+ ST_DEFINEFONTALIGNZONES,
+ ST_DEFINEFONTNAME,
  ST_DEFINEBUTTONCXFORM,
  ST_DEFINEBUTTONSOUND,
+ ST_DEFINESCALINGGRID,
+ ST_CSMTEXTSETTINGS,
  ST_NAMECHARACTER,
  ST_DOINITACTION,
  ST_VIDEOFRAME,
@@ -316,6 +347,11 @@ int swf_GetDepth(TAG * t)
     { U8 flags = swf_GetU8(t);
       depth = swf_GetU16(t);
     } break;
+    case ST_PLACEOBJECT3:
+    { U8 flags = swf_GetU8(t);
+      U8 flags2 = swf_GetU8(t);
+      depth = swf_GetU16(t);
+    } break;
     case ST_SETTABINDEX:
     {
       depth = swf_GetU16(t);
@@ -357,10 +393,13 @@ char* swf_GetName(TAG * t)
     switch(swf_GetTagID(t))
     {
         case ST_FRAMELABEL:
-            name = &t->data[swf_GetTagPos(t)];
+            name = (char*)&t->data[swf_GetTagPos(t)];
         break;
+        case ST_PLACEOBJECT3:
         case ST_PLACEOBJECT2: {   
             U8 flags = swf_GetU8(t);
+           if(t->id == ST_PLACEOBJECT3)
+               swf_GetU8(t);
             swf_GetU16(t); //depth;
             if(flags&PF_CHAR) 
               swf_GetU16(t); //id
@@ -370,11 +409,11 @@ char* swf_GetName(TAG * t)
               swf_GetCXForm(t, &c, 1);
             if(flags&PF_RATIO)
               swf_GetU16(t);
-            if(flags&PF_CLIPACTION)
+            if(flags&PF_CLIPDEPTH)
               swf_GetU16(t);
             if(flags&PF_NAME) {
               swf_ResetReadBits(t);
-              name = &t->data[swf_GetTagPos(t)];
+              name = (char*)&t->data[swf_GetTagPos(t)];
             }
         }
         break;
@@ -386,92 +425,152 @@ char* swf_GetName(TAG * t)
 /* used in enumerateUsedIDs */
 void swf_GetMorphGradient(TAG * tag, GRADIENT * gradient1, GRADIENT * gradient2)
 {
-    GRADIENT dummy1;
-    GRADIENT dummy2;
     int t;
-    if(!gradient1)
-       gradient1 = &dummy1;
-    if(!gradient2)
-       gradient2 = &dummy2;
-    gradient1->num = 
-    gradient2->num = swf_GetU8(tag);
-    for(t=0;t<gradient1->num;t++)
+    int num = swf_GetU8(tag) & 15;
+    if(gradient1) gradient1->num = num;
+    if(gradient2) gradient2->num = num;
+    
+    if(gradient1) {
+       gradient1->num = num;
+       gradient1->rgba = (RGBA*)rfx_calloc(sizeof(RGBA)*gradient1->num);
+       gradient1->ratios = (U8*)rfx_calloc(sizeof(gradient1->ratios[0])*gradient1->num);
+    }
+    if(gradient2) {
+       gradient2->num = num;
+       gradient2->rgba = (RGBA*)rfx_calloc(sizeof(RGBA)*gradient2->num);
+       gradient2->ratios = (U8*)rfx_calloc(sizeof(gradient2->ratios[0])*gradient2->num);
+    }
+    for(t=0;t<num;t++)
     {
-       int s=t;
-       if(s>=8) //FIXME
-           s=7;
-       gradient1->ratios[t] = swf_GetU8(tag);
-       swf_GetRGBA(tag, &gradient1->rgba[t]);
-       gradient2->ratios[t] = swf_GetU8(tag);
-       swf_GetRGBA(tag, &gradient2->rgba[t]);
+       U8 ratio;
+       RGBA color;
+       
+       ratio = swf_GetU8(tag);
+       swf_GetRGBA(tag, &color);
+       if(gradient1) {
+           gradient1->ratios[t] = ratio;
+           gradient1->rgba[t] = color;
+       }
+
+       ratio = swf_GetU8(tag);
+       swf_GetRGBA(tag, &color);
+       if(gradient2) {
+           gradient2->ratios[t] = ratio;
+           gradient2->rgba[t] = color;
+       }
     }
 }
 
 #define DEBUG_ENUMERATE if(0)
+//#define DEBUG_ENUMERATE
 
-static void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
+void enumerateUsedIDs_fillstyle(TAG * tag, int t, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
 {
-    U16 count;
-    int t;
-    count = swf_GetU8(tag);
-    if(count == 0xff && num>1) // defineshape2,3 only
-       count = swf_GetU16(tag);
-
-    for(t=0;t<count;t++)
+    int type;
+    type = swf_GetU8(tag); //type
+    DEBUG_ENUMERATE printf("fill style %d) type=%02x (tagpos=%d)\n", t, type, tag->pos);
+    if(type == 0) {
+       RGBA color;
+       if(num >= 3)
+           {swf_GetRGBA(tag, &color);if(morph) swf_GetRGBA(tag, NULL);}
+       else 
+           {swf_GetRGB(tag, &color);if(morph) swf_GetRGB(tag, NULL);}
+       DEBUG_ENUMERATE printf("               %02x%02x%02x%02x\n", color.r,color.g,color.b,color.a);
+    }
+    else if(type == 0x10 || type == 0x12 || type == 0x13)
     {
-       int type;
-       U8*pos;
        swf_ResetReadBits(tag);
-       type = swf_GetU8(tag); //type
-       if(type == 0) {
-           if(num == 3)
-               {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
-           else 
-               {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
+       MATRIX m;
+       swf_GetMatrix(tag, &m);
+       DEBUG_ENUMERATE swf_DumpMatrix(stdout, &m);
+       if(morph) {
+           swf_GetMatrix(tag, &m);
+           DEBUG_ENUMERATE swf_DumpMatrix(stdout, &m);
        }
-       else if(type == 0x10 || type == 0x12)
-       {
-           swf_ResetReadBits(tag);
-           swf_GetMatrix(tag, NULL);
-           if(morph)
-               swf_GetMatrix(tag, NULL);
-           swf_ResetReadBits(tag);
-           if(morph)
-               swf_GetMorphGradient(tag, NULL, NULL);
-           else
-               swf_GetGradient(tag, NULL, /*alpha*/ num>=3?1:0);
+       swf_ResetReadBits(tag);
+       if(morph) {
+           swf_GetMorphGradient(tag, NULL, NULL);
+           if(type == 0x13) {
+               swf_GetU16(tag);
+               swf_GetU16(tag);
+           }
+       } else {
+           GRADIENT g;
+           swf_GetGradient(tag, &g, /*alpha*/ num>=3?1:0);
+           DEBUG_ENUMERATE swf_DumpGradient(stdout, &g);
+           if(type == 0x13)
+               swf_GetU16(tag);
        }
-       else if(type == 0x40 || type == 0x41)
-       {
-           swf_ResetReadBits(tag);
-           // we made it.
-           if(tag->data[tag->pos] != 0xff ||
-              tag->data[tag->pos+1] != 0xff)
-           (callback)(tag, tag->pos, callback_data);
+    }
+    else if(type == 0x40 || type == 0x41 || type == 0x42 || type == 0x43)
+    {
+       swf_ResetReadBits(tag);
+       if(tag->data[tag->pos] != 0xff ||
+          tag->data[tag->pos+1] != 0xff)
+       (callback)(tag, tag->pos, callback_data);
 
-           swf_GetU16(tag);
-           swf_ResetReadBits(tag);
+       swf_GetU16(tag);
+       swf_ResetReadBits(tag);
+       swf_GetMatrix(tag, NULL);
+       if(morph)
            swf_GetMatrix(tag, NULL);
-           if(morph)
-               swf_GetMatrix(tag, NULL);
+    }
+    else {
+       fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x in tag %02d\n",type, tag->id);
+    }
+}
+
+void enumerateUsedIDs_linestyle(TAG * tag, int t, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
+{
+    U16  width;
+    RGBA color;
+    width = swf_GetU16(tag);
+    char fill=0;
+    if(morph)
+       swf_GetU16(tag);
+    if(num >= 4) {
+       U16 flags = swf_GetU16(tag);
+       DEBUG_ENUMERATE printf("line style %d) flags: %08x\n", t, flags);
+       if((flags & 0x30) == 0x20) {
+           U16 miter = swf_GetU16(tag); // miter limit
+           DEBUG_ENUMERATE printf("line style %d) miter join: %08x\n", t, miter);
        }
-       else {
-           fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x\n",type);
+       if(flags & 0x08) {
+           fill = 1;
        }
     }
+    if(!fill) {
+       if(num >= 3)
+           {swf_GetRGBA(tag, &color);if(morph) swf_GetRGBA(tag, NULL);}
+       else
+           {swf_GetRGB(tag, &color);if(morph) swf_GetRGB(tag, NULL);}
+    } else {
+       enumerateUsedIDs_fillstyle(tag, t, callback, callback_data, num, morph);
+    }
+    DEBUG_ENUMERATE printf("line style %d) width=%.2f color=%02x%02x%02x%02x \n", t, width/20.0, color.r,color.g,color.b,color.a);
+}
+
+void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
+{
+    U16 count;
+    int t;
+    count = swf_GetU8(tag);
+    if(count == 0xff && num>1) // defineshape2,3,4 only
+       count = swf_GetU16(tag);
+
+    DEBUG_ENUMERATE printf("%d fill styles\n", count);
+    for(t=0;t<count;t++)
+    {
+       enumerateUsedIDs_fillstyle(tag, t, callback, callback_data, num, morph);
+    }
     swf_ResetReadBits(tag);
     count = swf_GetU8(tag); // line style array
     if(count == 0xff)
        count = swf_GetU16(tag);
+    DEBUG_ENUMERATE printf("%d line styles\n", count);
     for(t=0;t<count;t++) 
     {
-       swf_GetU16(tag);
-       if(morph)
-           swf_GetU16(tag);
-       if(num == 3)
-           {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
-       else
-           {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
+       enumerateUsedIDs_linestyle(tag, t, callback, callback_data, num, morph);
     }
 }
 
@@ -482,7 +581,7 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
     tag->pos = 0;
     switch(tag->id)
     {
-       case ST_DEFINEBUTTONCXFORM: {
+       case ST_DEFINEBUTTONSOUND: {
            int t;
            callback(tag, tag->pos + base, callback_data);
            for(t=0;t<4;t++) {
@@ -509,10 +608,11 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
                }
            }
         } break;
-       case ST_DEFINEBUTTONSOUND:
+       case ST_DEFINEBUTTONCXFORM:
            callback(tag, tag->pos + base, callback_data); //button id
        break;
 
+       case ST_SYMBOLCLASS:
        case ST_EXPORTASSETS: {
            int num =  swf_GetU16(tag);
            int t;
@@ -523,8 +623,27 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
            }
        } break;
 
+       case ST_IMPORTASSETS: 
+       case ST_IMPORTASSETS2: {
+           swf_GetString(tag); //count
+           swf_GetU8(tag); //reserved
+           swf_GetU8(tag); //reserved
+           int num =  swf_GetU16(tag); //url
+           int t;
+           for(t=0;t<num;t++) {
+               callback(tag, tag->pos + base, callback_data); //button id
+               swf_GetU16(tag); //id
+               while(swf_GetU8(tag)); //name
+           }
+       } break;
+
+        case ST_DOABC:
+        case ST_RAWABC:
+        break;
+
        case ST_FREECHARACTER: /* unusual tags, which all start with an ID */
        case ST_NAMECHARACTER:
+       case ST_DEFINEFONTNAME:
        case ST_GENERATORTEXT:
            callback(tag, tag->pos + base, callback_data);
         break;
@@ -537,6 +656,12 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
                break;
            callback(tag, 3 + base, callback_data);
         break;
+       case ST_PLACEOBJECT3:
+           // only if placeflaghascharacter
+           if(!(tag->data[0]&2))
+               break;
+           callback(tag, 4 + base, callback_data);
+        break;
        case ST_REMOVEOBJECT:
            callback(tag, tag->pos + base, callback_data);
        break;
@@ -561,12 +686,12 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
                if(id == ST_END)
                    break;
                tag2->len = tag2->memsize = len;
-               tag2->data = rfx_alloc(len);
+               tag2->data = (U8*)rfx_alloc(len);
                memcpy(tag2->data, &tag->data[tag->pos], len);
                /* I never saw recursive sprites, but they are (theoretically) 
                   possible, so better add base here again */
                enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
-               swf_DeleteTag(tag2);
+               swf_DeleteTag(0, tag2);
                swf_GetBlock(tag, NULL, len);
            }
        } 
@@ -584,8 +709,8 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
            }
            while(1)
            {
-               U16 charid;
-               if(!swf_GetU8(tag)) //flags
+               U8 flags = swf_GetU8(tag);
+               if(!flags) //flags
                    break; 
                callback(tag, tag->pos + base, callback_data);
                swf_GetU16(tag); //char
@@ -596,6 +721,16 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
                  swf_ResetReadBits(tag);
                  swf_GetCXForm(tag, NULL, 1);
                }
+               if(flags&0x10) {
+                   U8 num = swf_GetU8(tag);
+                   int t;
+                   for(t=0;t<num;t++) {
+                       swf_DeleteFilter(swf_GetFilter(tag));
+                   }
+               }
+               if(flags&0x20) {
+                   U8 blendmode = swf_GetU8(tag);
+               }
            }
            // ...
        }
@@ -625,44 +760,47 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
            advancebits = swf_GetU8(tag); //advancebits
            while(1) {
                U16 flags;
+               int t;
                swf_ResetReadBits(tag);
                flags = swf_GetBits(tag, 8);
                if(!flags) break;
-               if(flags & 128) // text style record
-               {
+               
+               swf_ResetReadBits(tag);
+               if(flags & 8) { // hasfont
+                   callback(tag, tag->pos + base, callback_data);
+                   id = swf_GetU16(tag);
+               }
+               if(flags & 4) { // hascolor
+                   if(num==1) swf_GetRGB(tag, NULL);
+                   else       swf_GetRGBA(tag, NULL);
+               }
+               if(flags & 2) { //has x offset
                    swf_ResetReadBits(tag);
-                   if(flags & 8) { // hasfont
-                       callback(tag, tag->pos + base, callback_data);
-                       id = swf_GetU16(tag);
-                   }
-                   if(flags & 4) { // hascolor
-                       if(num==1) swf_GetRGB(tag, NULL);
-                       else       swf_GetRGBA(tag, NULL);
-                   }
-                   if(flags & 2) { //has x offset
-                       swf_ResetReadBits(tag);
-                       swf_GetU16(tag);
-                   }
-                   if(flags & 1) { //has y offset
-                       swf_ResetReadBits(tag);
-                       swf_GetU16(tag);
-                   }
-                   if(flags & 8) { //has height
-                       swf_ResetReadBits(tag);
-                       swf_GetU16(tag);
-                   }
-               } else { // glyph record
-                   int t;
+                   swf_GetU16(tag);
+               }
+               if(flags & 1) { //has y offset
                    swf_ResetReadBits(tag);
-                   for(t=0;t<flags;t++) {
-                       swf_GetBits(tag, glyphbits);
-                       swf_GetBits(tag, advancebits);
-                   }
+                   swf_GetU16(tag);
+               }
+               if(flags & 8) { //has height
+                   swf_ResetReadBits(tag);
+                   swf_GetU16(tag);
+               }
+               
+               flags = swf_GetBits(tag, 8);
+               if(!flags) break;
+               swf_ResetReadBits(tag);
+               for(t=0;t<flags;t++) {
+                   swf_GetBits(tag, glyphbits);
+                   swf_GetBits(tag, advancebits);
                }
            }
            break;
        }
+       case ST_DEFINEFONTALIGNZONES:
+       case ST_DEFINESCALINGGRID:
        case ST_GLYPHNAMES:
+       case ST_CSMTEXTSETTINGS:
        case ST_DEFINEFONTINFO:
        case ST_DEFINEFONTINFO2:
        case ST_VIDEOFRAME:
@@ -675,6 +813,9 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
            callback(tag, tag->pos + base, callback_data);
        break;
 
+       case ST_DEFINEMORPHSHAPE2:
+       case ST_DEFINESHAPE4:
+       num++;
        case ST_DEFINEMORPHSHAPE:
        case ST_DEFINESHAPE3:
        num++; //fallthrough
@@ -686,30 +827,48 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
            int id; 
            int numshapes = 1;
            int morph = 0;
-           if(tag->id == ST_DEFINEMORPHSHAPE) {
+           if(tag->id == ST_DEFINEMORPHSHAPE || tag->id==ST_DEFINEMORPHSHAPE2) {
                numshapes = 2;
                morph = 1;
            }
 
            id = swf_GetU16(tag); // id;
-           swf_GetRect(tag, NULL); // bounds
+           SRECT r={0,0,0,0},r2={0,0,0,0};
+           swf_GetRect(tag, &r); // shape bounds
            if(morph) {
                swf_ResetReadBits(tag);
-               swf_GetRect(tag, NULL); // bounds2
+               swf_GetRect(tag, NULL); // shape bounds2
+               if(num>=4) {
+                   swf_ResetReadBits(tag);
+                   swf_GetRect(tag, NULL); // edge bounds1
+               }
+           }
+           if(num>=4) {
+               swf_ResetReadBits(tag);
+               swf_GetRect(tag, &r2); // edge bounds
+               U8 flags = swf_GetU8(tag); // flags, &1: contains scaling stroke, &2: contains non-scaling stroke
+               DEBUG_ENUMERATE printf("flags: %02x (1=scaling strokes, 2=non-scaling strokes)\n", flags);
+           }
+           if(morph) {
                swf_GetU32(tag); //offset to endedges
            }
    
            DEBUG_ENUMERATE printf("Tag:%d Name:%s ID:%d\n", tag->id, swf_TagGetName(tag), id);
+           DEBUG_ENUMERATE printf("BBox %.2f %.2f %.2f %.2f\n", r.xmin/20.0,r.ymin/20.0,r.xmax/20.0,r.ymax/20.0);
+           DEBUG_ENUMERATE printf("BBox %.2f %.2f %.2f %.2f\n", r2.xmin/20.0,r2.ymin/20.0,r2.xmax/20.0,r2.ymax/20.0);
 
+           DEBUG_ENUMERATE printf("style tag pos: %d\n", tag->pos);
            enumerateUsedIDs_styles(tag, callback, callback_data, num, morph);
            DEBUG_ENUMERATE printf("-------\n");
+           swf_ResetReadBits(tag);
            while(--numshapes>=0) /* morph shapes define two shapes */
            {
                DEBUG_ENUMERATE printf("shape:%d\n", numshapes);
                fillbits = swf_GetBits(tag, 4);
                linebits = swf_GetBits(tag, 4);
-               DEBUG_ENUMERATE printf("%d %d\n", fillbits, linebits);
+               DEBUG_ENUMERATE printf("fillbits=%d linebits=%d\n", fillbits, linebits);
                swf_ResetReadBits(tag);
+               int x=0,y=0;
                while(1) {
                    int flags;
                    flags = swf_GetBits(tag, 1);
@@ -719,10 +878,9 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
                            break;
                        if(flags&1) { //move
                            int n = swf_GetBits(tag, 5); 
-                           int x,y;
                            x = swf_GetBits(tag, n); //x
                            y = swf_GetBits(tag, n); //y
-                           DEBUG_ENUMERATE printf("move %f %f\n",x/20.0,y/20.0);
+                           DEBUG_ENUMERATE printf("moveTo %.2f %.2f\n",x/20.0,y/20.0);
                        }
                        if(flags&2) { //fill0
                            int fill0;
@@ -750,15 +908,18 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
                        if(flags) { //straight edge
                            int n = swf_GetBits(tag, 4) + 2;
                            if(swf_GetBits(tag, 1)) { //line flag
-                               int x,y;
-                               x = swf_GetSBits(tag, n); //delta x
-                               y = swf_GetSBits(tag, n); //delta y
-                               DEBUG_ENUMERATE printf("line %f %f\n",x/20.0,y/20.0);
+                               x += swf_GetSBits(tag, n); //delta x
+                               y += swf_GetSBits(tag, n); //delta y
+                               DEBUG_ENUMERATE printf("lineTo %.2f %.2f\n",x/20.0,y/20.0);
                            } else {
                                int v=swf_GetBits(tag, 1);
                                int d;
                                d = swf_GetSBits(tag, n); //vert/horz
-                               DEBUG_ENUMERATE printf("%s %f\n",v?"vertical":"horizontal", d/20.0);
+                               if(!v)
+                                   x += d;
+                               else
+                                   y += d;
+                               DEBUG_ENUMERATE printf("lineTo %.2f %.2f (%s)\n",x/20.0,y/20.0, v?"vertical":"horizontal");
                            }
                        } else { //curved edge
                            int n = swf_GetBits(tag, 4) + 2;
@@ -767,7 +928,7 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
                            y1 = swf_GetSBits(tag, n);
                            x2 = swf_GetSBits(tag, n);
                            y2 = swf_GetSBits(tag, n);
-                           DEBUG_ENUMERATE printf("curve %f %f %f %f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
+                           DEBUG_ENUMERATE printf("splineTo %.2f %.2f %.2f %.2f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
                        }
                    }
                }
@@ -805,17 +966,31 @@ void swf_GetUsedIDs(TAG * t, int * positions)
     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
 }
 
-void swf_Relocate (SWF*swf, char*bitmap)
+char swf_Relocate (SWF*swf, char*bitmap)
 {
     TAG*tag;
     int slaveids[65536];
     memset(slaveids, -1, sizeof(slaveids));
     tag = swf->firstTag;
+    char ok = 1;
+
+    int current_id=0;
+#define NEW_ID(n) \
+               for(current_id++;current_id<65536;current_id++) { \
+                   if(!bitmap[current_id]) { \
+                       n = current_id; \
+                       break; \
+                   } \
+               } \
+                if(current_id==65536) { \
+                    fprintf(stderr, "swf_Relocate: Couldn't relocate: Out of IDs\n"); \
+                    return 0; \
+                }
+
     while(tag)
     {
        int num; 
        int *ptr;
-       int t;
 
        if(swf_isDefiningTag(tag))
        {
@@ -826,18 +1001,12 @@ void swf_Relocate (SWF*swf, char*bitmap)
 
            if(!bitmap[id]) { //free
                newid = id;
+           } else if(slaveids[id]>0) {
+               newid = slaveids[id];
+           } else {
+               NEW_ID(newid);
            }
-           else {
-               newid = 0;
-               for (t=1;t<65536;t++)
-               {
-                   if(!bitmap[t])
-                   {
-                       newid = t;
-                       break;
-                   }
-               }
-           }
+
            bitmap[newid] = 1;
            slaveids[id] = newid;
 
@@ -846,21 +1015,76 @@ void swf_Relocate (SWF*swf, char*bitmap)
        
        num = swf_GetNumUsedIDs(tag);
        if(num) {
-           ptr = rfx_alloc(sizeof(int)*num);
+           ptr = (int*)rfx_alloc(sizeof(int)*num);
            swf_GetUsedIDs(tag, ptr);
-
+           int t;
            for(t=0;t<num;t++) {
                int id = GET16(&tag->data[ptr[t]]);
                if(slaveids[id]<0) {
-                   fprintf(stderr, "swf_Relocate: Mapping id never encountered before: %d\n", id);
-                   return ;
+                   if(!id && bitmap[id]) {
+                       /* id 0 is only used in SWF versions >=9. It's the ID of
+                          the main timeline. It's used in e.g. SYMBOLTAG tags, but
+                          never defined, so if we're asked to reallocate it, we have 
+                          to allocate an ID for it on the fly. */
+                       int newid;
+                       NEW_ID(newid);
+                       bitmap[newid] = 1;
+                       slaveids[id] = newid;
+                       id = newid;
+                   } else if(!bitmap[id]) {
+                       /* well- we don't know this id, but it's not reserved anyway, so just
+                          leave it alone */
+                   } else {
+                       /* this actually happens with files created with Flash CS4 and never.
+                          Apparently e.g. DefineButton tags are able to use forward declarations of objects. */
+                       fprintf(stderr, "warning: Mapping id (%d) never encountered before in %s\n", id,
+                               swf_TagGetName(tag));
+                       int newid;
+                       NEW_ID(newid);
+                       id = slaveids[id] = newid;
+                       ok = 0;
+                   }
+               } else {
+                   id = slaveids[id];
                }
-               id = slaveids[id];
                PUT16(&tag->data[ptr[t]], id);
            }
+            free(ptr);
        }
        tag=tag->next;
     }
+    return ok;
+}
+
+/* untested */
+void swf_Relocate2(SWF*swf, int*id2id)
+{
+    TAG*tag;
+    tag = swf->firstTag;
+    while(tag) {
+       if(swf_isDefiningTag(tag)) {
+           int id = swf_GetDefineID(tag);
+           id = id2id[id];
+           if(id>=0) {
+               swf_SetDefineID(tag, id);
+           }
+       }
+       int num = swf_GetNumUsedIDs(tag);
+       if(num) {
+           int *ptr;
+           int t;
+           ptr = (int*)rfx_alloc(sizeof(int)*num);
+           swf_GetUsedIDs(tag, ptr);
+           for(t=0;t<num;t++) {
+               int id = GET16(&tag->data[ptr[t]]);
+               id = id2id[id];
+               if(id>=0) {
+                   PUT16(&tag->data[ptr[t]], id);
+               }
+           }
+            free(ptr);
+       }
+    }
 }
 
 void swf_RelocateDepth(SWF*swf, char*bitmap)
@@ -878,10 +1102,27 @@ void swf_RelocateDepth(SWF*swf, char*bitmap)
 
     while(tag)
     {
+       int depth;
        /* TODO * clip depths 
                * sprites
         */
-       int depth = swf_GetDepth(tag);
+       if(tag->id == ST_PLACEOBJECT2) {
+           SWFPLACEOBJECT obj;
+           swf_GetPlaceObject(tag, &obj);
+           if(obj.clipdepth) {
+               int newdepth = obj.clipdepth+nr;
+               if(newdepth>65535) {
+                   fprintf(stderr, "Couldn't relocate depths: too large values\n");
+                   newdepth = 65535;
+               }
+               obj.clipdepth = newdepth;
+               swf_ResetTag(tag, ST_PLACEOBJECT2);
+               swf_SetPlaceObject(tag, &obj);
+           }
+           swf_PlaceObjectFree(&obj);
+       }
+
+       depth = swf_GetDepth(tag);
        if(depth>=0) {
            int newdepth = depth+nr;
            if(newdepth>65535) {
@@ -898,7 +1139,34 @@ U8 swf_isShapeTag(TAG*tag)
 {
     if(tag->id == ST_DEFINESHAPE ||
        tag->id == ST_DEFINESHAPE2 ||
-       tag->id == ST_DEFINESHAPE3) 
+       tag->id == ST_DEFINESHAPE3 ||
+       tag->id == ST_DEFINESHAPE4) 
+        return 1;
+    return 0;
+}
+
+U8 swf_isPlaceTag(TAG*tag)
+{
+    if(tag->id == ST_PLACEOBJECT ||
+       tag->id == ST_PLACEOBJECT2 ||
+       tag->id == ST_PLACEOBJECT3)
+        return 1;
+    return 0;
+}
+U8 swf_isTextTag(TAG*tag)
+{
+    if(tag->id == ST_DEFINETEXT ||
+       tag->id == ST_DEFINETEXT2)
+        return 1;
+    return 0;
+}
+
+U8 swf_isFontTag(TAG*tag)
+{
+    if(tag->id == ST_DEFINEFONT ||
+       tag->id == ST_DEFINEFONT2 ||
+       tag->id == ST_DEFINEFONT3 ||
+       tag->id == ST_DEFINEFONTINFO)
         return 1;
     return 0;
 }
@@ -973,10 +1241,10 @@ static int tagHash(TAG*tag)
 void swf_Optimize(SWF*swf)
 {
     const int hash_size = 131072;
-    char* dontremap = rfx_calloc(sizeof(char)*65536);
-    U16* remap = rfx_alloc(sizeof(U16)*65536);
-    TAG* id2tag = rfx_calloc(sizeof(TAG*)*65536);
-    TAG** hashmap = rfx_calloc(sizeof(TAG*)*hash_size);
+    char* dontremap = (char*)rfx_calloc(sizeof(char)*65536);
+    U16* remap = (U16*)rfx_alloc(sizeof(U16)*65536);
+    TAG* id2tag = (TAG*)rfx_calloc(sizeof(TAG*)*65536);
+    TAG** hashmap = (TAG**)rfx_calloc(sizeof(TAG*)*hash_size);
     TAG* tag;
     int t;
     for(t=0;t<65536;t++) {
@@ -1000,13 +1268,11 @@ void swf_Optimize(SWF*swf)
     }
     tag = swf->firstTag;
     while(tag) {
-        int doremap=1;
-        
         TAG*next = tag->next;
 
         /* remap the tag */
         int num = swf_GetNumUsedIDs(tag);
-        int*positions = rfx_alloc(sizeof(int)*num);
+        int*positions = (int*)rfx_alloc(sizeof(int)*num);
         int t;
         swf_GetUsedIDs(tag, positions);
         for(t=0;t<num;t++) {
@@ -1015,7 +1281,6 @@ void swf_Optimize(SWF*swf)
             PUT16(&tag->data[positions[t]], id);
         }
         rfx_free(positions);
-        tag = tag->next;
 
         /* now look for previous tags with the same
            content */
@@ -1026,22 +1291,11 @@ void swf_Optimize(SWF*swf)
             int match=0;
             if(!dontremap[id]) 
             while((tag2 = hashmap[hash%hash_size])) {
-                if(tag2 != (TAG*)(-1) && tag->len == tag2->len) {
-                    int t;
-                    /* start at pos 2, as 0 and 1 are the id */
-                    for(t=2;t<tag->len;t++) {
-                        if(tag->data[t] != tag2->data[t])
-                            break;
-                    }
-                    if(t == tag->len) {
-                        match=1;
-                    }
-                }
-                if(match) {
-                    /* we found two identical tags- remap one
-                       of them */
-                    remap[id] = swf_GetDefineID(tag2);
-                    break;
+                if(tag2 != (TAG*)0 && tag->len == tag2->len) {
+                   if(memcmp(&tag->data[2],&tag2->data[2],tag->len-2) == 0) {
+                       match = 1;
+                       break;
+                   }
                 }
                 hash++;
             }
@@ -1049,10 +1303,10 @@ void swf_Optimize(SWF*swf)
                 while(hashmap[hash%hash_size]) hash++;
                 hashmap[hash%hash_size] = tag;
             } else {
-                swf_DeleteTag(tag);
-                if(tag == swf->firstTag)
-                    swf->firstTag = next;
-                doremap = 0;
+               /* we found two identical tags- remap one
+                  of them */
+                remap[id] = swf_GetDefineID(tag2);
+                swf_DeleteTag(swf, tag);
             }
         } else if(swf_isPseudoDefiningTag(tag)) {
             int id = swf_GetDefineID(tag);
@@ -1060,17 +1314,72 @@ void swf_Optimize(SWF*swf)
                 /* if this tag was remapped, we don't
                    need the helper tag anymore. Discard
                    it. */
-                swf_DeleteTag(tag);
-                if(tag == swf->firstTag)
-                    swf->firstTag = next;
-                doremap = 0;
+                swf_DeleteTag(swf, tag);
             }
         }
 
         tag = next;
     }
+    
     rfx_free(dontremap);
     rfx_free(remap);
     rfx_free(id2tag);
     rfx_free(hashmap);
 }
+
+void swf_SetDefineBBox(TAG * tag, SRECT newbbox)
+{
+    U16 id = 0;
+    SRECT b1;
+    swf_SetTagPos(tag,0);
+
+    switch (swf_GetTagID(tag))
+    { 
+       case ST_DEFINESHAPE:
+       case ST_DEFINESHAPE2:
+       case ST_DEFINESHAPE3:
+       case ST_DEFINEEDITTEXT:
+       case ST_DEFINETEXT:
+       case ST_DEFINETEXT2:
+       case ST_DEFINEVIDEOSTREAM: {
+             U32 after_bbox_offset = 0, len;
+             U8*data;
+             id = swf_GetU16(tag);
+             swf_GetRect(tag, &b1);
+             swf_ResetReadBits(tag);
+             after_bbox_offset = tag->pos;
+             len = tag->len - after_bbox_offset;
+             data = (U8*)malloc(len);
+             memcpy(data, &tag->data[after_bbox_offset], len);
+             tag->writeBit = 0;
+             tag->len = 2;
+             swf_SetRect(tag, &newbbox);
+             swf_SetBlock(tag, data, len);
+             free(data);
+             tag->pos = tag->readBit = 0;
+
+       } break;
+       default:
+           fprintf(stderr, "rfxswf: Tag %d (%s) has no bbox\n", tag->id, swf_TagGetName(tag));
+    }
+}
+
+RGBA swf_GetSWFBackgroundColor(SWF*swf)
+{
+    TAG*t=swf->firstTag;
+    RGBA color;
+    color.r = color.b = color.g = 0;
+    color.a = 255;
+    while(t) {
+       if(t->id == ST_SETBACKGROUNDCOLOR) {
+           swf_SetTagPos(t, 0);
+           color.r = swf_GetU8(t);
+           color.g = swf_GetU8(t);
+           color.b = swf_GetU8(t);
+           break;
+       }
+       t=t->next;
+    }
+    return color;
+}
+