fixed BUTTONSOUND/BUTTONCXFORM parsing
[swftools.git] / lib / modules / swftools.c
index 35cd13c..1099bc3 100644 (file)
 #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);
+  a = ((S64)a1*(S64)b1+(S64)a2*(S64)b2)>>16;
+  SFIXED result = (SFIXED)(a);
+  if(a!=result) 
+      fprintf(stderr, "Warning: overflow in matrix multiplication");
+  return result;
 }
 SFIXED RFXSWF_QFIX(int zaehler,int nenner) // bildet Quotient von zwei INTs in SFIXED
 { S64 z = zaehler<<16;
@@ -42,13 +45,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);
   
@@ -116,11 +120,14 @@ U16 swf_GetDefineID(TAG * t)
     case ST_DEFINESPRITE:
     case ST_DEFINEMOVIE:
     case ST_DEFINEVIDEOSTREAM:
-  { case ST_GLYPHNAMES: //pseudodefine
+    case ST_GLYPHNAMES: //pseudodefine
     case ST_VIDEOFRAME: //pseudodefine
     case ST_NAMECHARACTER: //pseudodefine
+    case ST_DOINITACTION: //pseudodefine
       id = swf_GetU16(t);
       break;
+    default:
+      fprintf(stderr, "rfxswf: Error: tag %d (%s) has no id\n", t->id, swf_TagGetName(t));
   }
 
   swf_SetTagPos(t,oldTagPos);
@@ -133,6 +140,7 @@ SRECT swf_GetDefineBBox(TAG * t)
   U32 oldTagPos;
   U16 id = 0;
   SRECT b1,b2;
+  memset(&b1, 0, sizeof(b1));
 
   oldTagPos = swf_GetTagPos(t);
   swf_SetTagPos(t,0);
@@ -405,7 +413,7 @@ void swf_GetMorphGradient(TAG * tag, GRADIENT * gradient1, GRADIENT * gradient2)
 
 #define DEBUG_ENUMERATE if(0)
 
-static void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
+void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
 {
     U16 count;
     int t;
@@ -478,7 +486,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++) {
@@ -505,7 +513,7 @@ 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;
 
@@ -557,7 +565,7 @@ void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), v
                if(id == ST_END)
                    break;
                tag2->len = tag2->memsize = len;
-               tag2->data = malloc(len);
+               tag2->data = 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 */
@@ -841,17 +849,20 @@ void swf_Relocate (SWF*swf, char*bitmap)
        } 
        
        num = swf_GetNumUsedIDs(tag);
-       ptr = malloc(sizeof(int)*num);
-       swf_GetUsedIDs(tag, ptr);
-
-       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(num) {
+           ptr = rfx_alloc(sizeof(int)*num);
+           swf_GetUsedIDs(tag, ptr);
+
+           for(t=0;t<num;t++) {
+               int id = GET16(&tag->data[ptr[t]]);
+               if(slaveids[id]<0) {
+                   fprintf(stderr, "swf_Relocate: Mapping id (%d) never encountered before in %s\n", id,
+                           swf_TagGetName(tag));
+                   return ;
+               }
+               id = slaveids[id];
+               PUT16(&tag->data[ptr[t]], id);
            }
-           id = slaveids[id];
-           PUT16(&tag->data[ptr[t]], id);
        }
        tag=tag->next;
     }
@@ -872,6 +883,9 @@ void swf_RelocateDepth(SWF*swf, char*bitmap)
 
     while(tag)
     {
+       /* TODO * clip depths 
+               * sprites
+        */
        int depth = swf_GetDepth(tag);
        if(depth>=0) {
            int newdepth = depth+nr;
@@ -885,6 +899,50 @@ void swf_RelocateDepth(SWF*swf, char*bitmap)
     }
 }
 
+U8 swf_isShapeTag(TAG*tag)
+{
+    if(tag->id == ST_DEFINESHAPE ||
+       tag->id == ST_DEFINESHAPE2 ||
+       tag->id == ST_DEFINESHAPE3) 
+        return 1;
+    return 0;
+}
+
+U8 swf_isPlaceTag(TAG*tag)
+{
+    if(tag->id == ST_PLACEOBJECT ||
+       tag->id == ST_PLACEOBJECT2)
+        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_DEFINEFONTINFO)
+        return 1;
+    return 0;
+}
+
+U8  swf_isImageTag(TAG*tag)
+{
+    if(tag->id == ST_DEFINEBITSJPEG || 
+       tag->id == ST_DEFINEBITSJPEG2 || 
+       tag->id == ST_DEFINEBITSJPEG3 ||
+       tag->id == ST_DEFINEBITSLOSSLESS || 
+       tag->id == ST_DEFINEBITSLOSSLESS2)
+        return 1;
+    return 0;
+}
+
 TAG* swf_Concatenate (TAG*list1,TAG*list2)
 {
     TAG*tag=0,*lasttag=0;
@@ -927,3 +985,178 @@ TAG* swf_Concatenate (TAG*list1,TAG*list2)
 
     return swf1.firstTag;
 }
+
+static int tagHash(TAG*tag)
+{
+    int t, h=0;
+    unsigned int a = 0x6b973e5a;
+    /* start at pos 2, as 0 and 1 are the id */
+    for(t=2;t<tag->len;t++) {
+        unsigned int b = a;
+        a >>= 8;
+        a += tag->data[t]*0xefbc35a5*b*(t+1);
+    }
+    return a&0x7fffffff; //always return positive number
+}
+
+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);
+    TAG* tag;
+    int t;
+    for(t=0;t<65536;t++) {
+        remap[t] = t;
+    }
+
+    swf_FoldAll(swf);
+
+    tag = swf->firstTag;
+    while(tag) {
+        /* make sure we don't remap to this tag,
+           as it might have different "helper tags" 
+           FIXME: a better way would be to compare
+                  the helper tags, too.
+         */
+        if(swf_isPseudoDefiningTag(tag) &&
+           tag->id != ST_NAMECHARACTER) {
+            dontremap[swf_GetDefineID(tag)] = 1;
+        }
+        tag=tag->next;
+    }
+    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 t;
+        swf_GetUsedIDs(tag, positions);
+        for(t=0;t<num;t++) {
+            int id = GET16(&tag->data[positions[t]]);
+            id = remap[id];
+            PUT16(&tag->data[positions[t]], id);
+        }
+        rfx_free(positions);
+        tag = tag->next;
+
+        /* now look for previous tags with the same
+           content */
+        if(swf_isDefiningTag(tag)) {
+            TAG*tag2;
+            int id = swf_GetDefineID(tag);
+            int hash = tagHash(tag);
+            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;
+                }
+                hash++;
+            }
+            if(!match) {
+                while(hashmap[hash%hash_size]) hash++;
+                hashmap[hash%hash_size] = tag;
+            } else {
+                swf_DeleteTag(tag);
+                if(tag == swf->firstTag)
+                    swf->firstTag = next;
+                doremap = 0;
+            }
+        } else if(swf_isPseudoDefiningTag(tag)) {
+            int id = swf_GetDefineID(tag);
+            if(remap[id]!=id) {
+                /* 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;
+            }
+        }
+
+        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 = 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;
+}
+