fixed graphics bug
[swftools.git] / lib / rfxswf.c
index 9b96235..944418d 100644 (file)
@@ -1,5 +1,4 @@
 /* vi: set sts=2 sw=2 :*/
-
 /* rfxswf.c 
 
    Library for creating and reading SWF files or parts of it.
@@ -8,33 +7,42 @@
 
    Part of the swftools package.
 
-   Copyright (c) 2000, 2001 Rainer Böhme <rfxswf@reflex-studio.de>
-   This file is distributed under the GPL, see file COPYING for details 
+   Copyright (c) 2000-2003 Rainer Böhme <rfxswf@reflex-studio.de>
+   Copyright (c) 2003 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
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-*/
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
 #include "rfxswf.h"
 
-#ifdef HAVE_LIBJPEG
-#ifdef HAVE_JPEGLIB_H
+#ifdef HAVE_JPEGLIB
 #define HAVE_BOOLEAN
 #include <jpeglib.h>
-#define _JPEGLIB_INCLUDED_
-#endif // HAVE_JPEGLIB_H
-#endif // HAVE_LIBJPEG
+#endif // HAVE_JPEGLIB
 
-#ifdef HAVE_LIBZ
-#ifdef HAVE_ZLIB_H
+#ifdef HAVE_ZLIB
 #include <zlib.h>
-#define _ZLIB_INCLUDED_
-#endif // HAVE_ZLIB_H
-#endif // HAVE_LIBZ
+#endif // HAVE_ZLIB
 
-#define LAME
+#ifndef RFXSWF_DISABLESOUND
+#ifdef HAVE_LAME
 #include "lame/lame.h"
+#endif
+#endif
 
 #include "./bitio.h"
+#include "./MD5.h"
 
 // internal constants
 
@@ -48,7 +56,6 @@
 
 TAG * swf_NextTag(TAG * t) { return t->next; }
 TAG * swf_PrevTag(TAG * t) { return t->prev; }
-int   swf_GetFrameNo(TAG * t)  { return t->frame; }
 U16   swf_GetTagID(TAG * t)    { return t->id; }
 U32   swf_GetTagLen(TAG * t) { return t->len; }
 U8*   swf_GetTagLenPtr(TAG * t) { return &(t->data[t->len]); }
@@ -72,6 +79,13 @@ void swf_SetTagPos(TAG * t,U32 pos)
   #endif
 }
 
+char* swf_GetString(TAG*t)
+{
+    char* str = ((char*)(&(t)->data[(t)->pos]));
+    while(swf_GetU8(t));
+    return str;
+}
+
 U8 swf_GetU8(TAG * t)
 { swf_ResetReadBits(t);
   #ifdef DEBUG_RFXSWF
@@ -249,7 +263,7 @@ int swf_SetRGB(TAG * t,RGBA * col)
 void swf_GetRGB(TAG * t, RGBA * col)
 {
     RGBA dummy;
-    if(!col);
+    if(!col)
        col = &dummy;
     col->r = swf_GetU8(t);
     col->g = swf_GetU8(t);
@@ -282,6 +296,10 @@ void swf_GetGradient(TAG * tag, GRADIENT * gradient, char alpha)
 {
     GRADIENT dummy;
     int t;
+    if(!tag) {
+      memset(gradient, 0, sizeof(GRADIENT));
+      return;
+    }
     if(!gradient)
        gradient = &dummy;
     gradient->num = swf_GetU8(tag);
@@ -298,22 +316,53 @@ void swf_GetGradient(TAG * tag, GRADIENT * gradient, char alpha)
     }
 }
 
+void swf_SetGradient(TAG * tag, GRADIENT * gradient, char alpha)
+{
+    int t;
+    if(!tag) {
+      memset(gradient, 0, sizeof(GRADIENT));
+      return;
+    }
+    swf_SetU8(tag, gradient->num);
+    for(t=0; t<8 && t<gradient->num; t++)
+    {
+       swf_SetU8(tag, gradient->ratios[t]);
+       if(!alpha)
+           swf_SetRGB(tag, &gradient->rgba[t]);
+       else
+           swf_SetRGBA(tag, &gradient->rgba[t]);
+    }
+}
+
+int swf_CountUBits(U32 v,int nbits)
+{ int n = 32;
+  U32 m = 0x80000000;
+  if(v == 0x00000000) n = 0; 
+  else
+    while (!(v&m))
+    { n--;
+      m>>=1;
+    } 
+  return (n>nbits)?n:nbits;
+}
+
 int swf_CountBits(U32 v,int nbits)
 { int n = 33;
   U32 m = 0x80000000;
-  if (!v) n = 0; else
   if (v&m)
-  { while (v&m)
+  { if(v == 0xffffffff) n = 1;
+    else 
+    while (v&m)
     { n--;
       m>>=1;
-      if (!m) break;
     } 
   }
   else
-  { while (!(v&m))
+  { if(v == 0x00000000) n = 0; 
+    else
+    while (!(v&m))
     { n--;
       m>>=1;
-      if (!m) break;
     } 
   }
   return (n>nbits)?n:nbits;
@@ -322,6 +371,7 @@ int swf_CountBits(U32 v,int nbits)
 int swf_GetRect(TAG * t,SRECT * r)
 { int nbits;
   SRECT dummy;
+  if(!t) {r->xmin=r->xmax=r->ymin=r->ymax=0;return 0;}
   if (!r) r = &dummy;
   nbits = (int) swf_GetBits(t,5);
   r->xmin = swf_GetSBits(t,nbits);
@@ -364,6 +414,58 @@ int swf_SetRect(TAG * t,SRECT * r)
   return 0;
 }
 
+void swf_ExpandRect(SRECT*src, SPOINT add)
+{
+    if(add.x < src->xmin)
+       src->xmin = add.x;
+    if(add.x > src->xmax)
+       src->xmax = add.x;
+    if(add.y < src->ymin)
+       src->ymin = add.y;
+    if(add.y > src->ymax)
+       src->ymax = add.y;
+}
+void swf_ExpandRect2(SRECT*src, SRECT*add)
+{
+    if((add->xmin | add->ymin | add->xmax | add->ymax)==0)
+       return;
+    if(add->xmin < src->xmin)
+       src->xmin = add->xmin;
+    if(add->ymin < src->ymin)
+       src->ymin = add->ymin;
+    if(add->xmax > src->xmax)
+       src->xmax = add->xmax;
+    if(add->ymax > src->ymax)
+       src->ymax = add->ymax;
+}
+SPOINT swf_TurnPoint(SPOINT p, MATRIX* m)
+{
+    SPOINT r;
+    r.x = (int)(m->sx*(1/65536.0)*p.x + m->r1*(1/65536.0)*p.y + 0.5) + m->tx;
+    r.y = (int)(m->r0*(1/65536.0)*p.x + m->sy*(1/65536.0)*p.y + 0.5) + m->ty;
+    return r;
+}
+SRECT swf_TurnRect(SRECT r, MATRIX* m)
+{
+    SRECT g;
+    SPOINT p1,p2,p3,p4,pp1,pp2,pp3,pp4;
+    p1.x = r.xmin;p1.y = r.ymin;
+    p2.x = r.xmax;p2.y = r.ymin;
+    p3.x = r.xmin;p3.y = r.ymax;
+    p4.x = r.xmax;p4.y = r.ymax;
+    pp1 = swf_TurnPoint(p1, m);
+    pp2 = swf_TurnPoint(p2, m);
+    pp3 = swf_TurnPoint(p3, m);
+    pp4 = swf_TurnPoint(p4, m);
+    g.xmin = g.xmax = pp1.x;
+    g.ymin = g.ymax = pp1.y;
+    swf_ExpandRect(&g, pp2);
+    swf_ExpandRect(&g, pp3);
+    swf_ExpandRect(&g, pp4);
+    return g;
+}
+       
+
 int swf_GetMatrix(TAG * t,MATRIX * m)
 { MATRIX dummy;
   int nbits;
@@ -549,23 +651,46 @@ int swf_SetCXForm(TAG * t,CXFORM * cx,U8 alpha)
   return 0;
 }
 
-int swf_GetPoint(TAG * t,SPOINT * p) { return 0; }
-int swf_SetPoint(TAG * t,SPOINT * p) { return 0; }
+//int swf_GetPoint(TAG * t,SPOINT * p) { return 0; }
+//int swf_SetPoint(TAG * t,SPOINT * p) { return 0; }
 
-// Tag List Manipulating Functions
+void  swf_SetPassword(TAG * t, const char * password)
+{
+    /* WARNING: crypt_md5 is not reentrant */
+    char* md5string = crypt_md5(password, "salt"); /* FIXME- get random salt */
+    swf_SetString(t, md5string);
+}
 
-int swf_UpdateFrame(TAG * t,S8 delta)
-// returns number of frames
-{ int res = -1;
-  while (t)
-  { t->frame+=delta;
-    res = t->frame;
-    t = t->next;
-  }
-  return res;
+int swf_VerifyPassword(TAG * t, const char * password)
+{
+    char*md5string1, *md5string2;
+    char*x;
+    char*md5, *salt;
+    int n;
+
+    md5string1 = swf_GetString(t);
+
+    if(!strncmp(md5string1, "$1$",3 )) {
+       return 0;
+    }
+    x = strchr(md5string1+3, '$');
+    if(!x)
+       return 0;
+    n = x-(md5string1+3);
+    salt = (char*)malloc(n+1);
+    memcpy(salt, md5string1+3, n);
+    salt[n] = 0;
+
+    md5string2 = crypt_md5(password, salt);
+    free(salt);
+    if(strcmp(md5string1, md5string2) != 0)
+       return 0;
+    return 1;
 }
 
-TAG * swf_InsertTag(TAG * after,U16 id)     // updates frames, if nescessary
+// Tag List Manipulating Functions
+
+TAG * swf_InsertTag(TAG * after,U16 id)
 { TAG * t;
 
   t = (TAG *)malloc(sizeof(TAG));
@@ -574,23 +699,58 @@ TAG * swf_InsertTag(TAG * after,U16 id)     // updates frames, if nescessary
     t->id = id;
     
     if (after)
-    { t->frame = after->frame;
+    {
       t->prev  = after;
       t->next  = after->next;
       after->next = t;
       if (t->next) t->next->prev = t;
-      
-      if (id==ST_SHOWFRAME) swf_UpdateFrame(t->next,+1);
     }
   }
   return t;
 }
 
+TAG * swf_InsertTagBefore(SWF* swf, TAG * before,U16 id)
+{ TAG * t;
+
+  t = (TAG *)malloc(sizeof(TAG));
+  if (t)
+  { memset(t,0x00,sizeof(TAG));
+    t->id = id;
+    
+    if (before)
+    {
+      t->next  = before;
+      t->prev  = before->prev;
+      before->prev = t;
+      if (t->prev) t->prev->next = t;
+    }
+  }
+  if(swf && swf->firstTag == before) {
+    swf->firstTag = t;
+  }
+  return t;
+}
+
+void swf_ClearTag(TAG * t)
+{
+  if (t->data) free(t->data);
+  t->data = 0;
+  t->pos = 0;
+  t->len = 0;
+  t->readBit = 0;
+  t->writeBit = 0;
+  t->memsize = 0;
+}
+
+void swf_ResetTag(TAG*tag, U16 id)
+{
+    tag->len = tag->pos = tag->readBit = tag->writeBit = 0;
+    tag->id = id;
+}
+
 int swf_DeleteTag(TAG * t)
 { if (!t) return -1;
 
-  if (t->id==ST_SHOWFRAME) swf_UpdateFrame(t->next,-1);
-    
   if (t->prev) t->prev->next = t->next;
   if (t->next) t->next->prev = t->prev;
 
@@ -618,7 +778,7 @@ TAG * swf_ReadTag(struct reader_t*reader, TAG * prev)
   }
 
   if (id==ST_DEFINESPRITE) len = 2*sizeof(U16);
-  // Sprite handling fix: Flaten sprite tree
+  // Sprite handling fix: Flatten sprite tree
 
   t = (TAG *)malloc(sizeof(TAG));
   
@@ -649,7 +809,7 @@ TAG * swf_ReadTag(struct reader_t*reader, TAG * prev)
   }
 
   if (prev)
-  { t->frame = prev->frame+((prev->id==ST_SHOWFRAME)?1:0);
+  {
     t->prev  = prev;
     prev->next = t;
   }
@@ -738,16 +898,70 @@ int swf_DefineSprite_GetRealSize(TAG * t)
   }
   do
   { t = swf_NextTag(t);
-    if (t->id!=ST_DEFINESPRITE) len += swf_WriteTag(-1, t);
+    if (t && t->id!=ST_DEFINESPRITE) len += swf_WriteTag(-1, t);
     else t = NULL;
   } while (t&&(t->id!=ST_END));
   return len;
 }
 
+void swf_UnFoldSprite(TAG * t)
+{
+  U16 id,tmp;
+  U32 len;
+  TAG*next = t;
+  U16 spriteid,spriteframes;
+  int level;
+  if(t->id!=ST_DEFINESPRITE)
+    return;
+  if(t->len<=4) // not folded
+    return;
+
+  swf_SetTagPos(t,0);
+
+  spriteid = swf_GetU16(t); //id
+  spriteframes = swf_GetU16(t); //frames
+
+  level = 1;
+
+  while(1)
+  {
+    TAG*it = 0;
+    tmp = swf_GetU16(t);
+    len = tmp&0x3f;
+    id  = tmp>>6;
+    if(id == ST_END)
+       level--;
+    if(id == ST_DEFINESPRITE && len<=4)
+       level++;
+
+    if (len==0x3f)
+       len = swf_GetU32(t);
+    it = swf_InsertTag(next, id);
+    next = it;
+    it->len = len;
+    it->id  = id;
+    if (it->len)
+    { it->data = (U8*)malloc(it->len);
+      it->memsize = it->len;
+      swf_GetBlock(t, it->data, it->len);
+    }
+
+    if(!level)
+       break;
+  }
+  
+  free(t->data); t->data = 0;
+  t->memsize = t->len = t->pos = 0;
+
+  swf_SetU16(t, spriteid);
+  swf_SetU16(t, spriteframes);
+}
+
 void swf_FoldSprite(TAG * t)
 {
   TAG*sprtag=t,*tmp;
   U16 id,frames,tmpid;
+  int level;
   if(t->id!=ST_DEFINESPRITE)
       return;
   if(!t->len) {
@@ -761,26 +975,35 @@ void swf_FoldSprite(TAG * t)
 
   t->pos = 0;
   id = swf_GetU16(t);
-  //frames = swf_GetU16(t);
   free(t->data);
   t->len = t->pos = t->memsize = 0;
   t->data = 0;
 
   frames = 0;
 
+  t = swf_NextTag(sprtag);
+  level = 1;
+
   do 
   { 
     if(t->id==ST_SHOWFRAME) frames++;
+    if(t->id == ST_DEFINESPRITE && t->len<=4)
+       level++;
+    if(t->id == ST_END)
+       level--;
     t = swf_NextTag(t);
-  } while(t&&t!=ST_END);
+  } while(t && level);
+  if(level)
+    fprintf(stderr, "rfxswf error: sprite doesn't end(1)\n");
 
-  t = swf_NextTag(sprtag);
   swf_SetU16(sprtag, id);
   swf_SetU16(sprtag, frames);
 
+  t = swf_NextTag(sprtag);
+  level = 1;
+
   do
   { 
-    tmpid= t->id;
     if(t->len<0x3f) {
        swf_SetU16(sprtag,t->len|(t->id<<6));
     } else {
@@ -790,25 +1013,104 @@ void swf_FoldSprite(TAG * t)
     if(t->len)
        swf_SetBlock(sprtag,t->data, t->len);
     tmp = t;
+    if(t->id == ST_DEFINESPRITE && t->len<=4)
+       level++;
+    if(t->id == ST_END)
+       level--;
     t = swf_NextTag(t);
     swf_DeleteTag(tmp);
   } 
-  while (t&&(tmpid!=ST_END));
+  while (t && level);
+  if(level)
+    fprintf(stderr, "rfxswf error: sprite doesn't end(2)\n");
 
 //  sprtag->next = t;
 //  t->prev = sprtag;
 }
 
+int swf_IsFolded(TAG * t)
+{
+    return (t->id == ST_DEFINESPRITE && t->len>4);
+}
+
 void swf_FoldAll(SWF*swf)
 {
     TAG*tag = swf->firstTag;
+    //swf_DumpSWF(stdout, swf);
     while(tag) {
-       if(tag->id == ST_DEFINESPRITE)
+       if(tag->id == ST_DEFINESPRITE) {
            swf_FoldSprite(tag);
+           //swf_DumpSWF(stdout, swf);
+       }
        tag = swf_NextTag(tag);
     }
 }
 
+void swf_UnFoldAll(SWF*swf)
+{
+    TAG*tag = swf->firstTag;
+    while(tag) {
+       if(tag->id == ST_DEFINESPRITE)
+           swf_UnFoldSprite(tag);
+       tag = tag->next;
+    }
+}
+
+void swf_OptimizeTagOrder(SWF*swf)
+{
+  TAG*tag,*next;
+  TAG*level0;
+  int level;
+  int changes;
+  swf_UnFoldAll(swf);
+  /* at the moment, we don't actually do optimizing,
+     only fixing of non-spec-conformant things like
+     sprite tags */
+
+  do {
+    changes = 0;
+    level = 0;
+    level0 = 0;
+    tag = swf->firstTag;
+    while(tag) {
+      next = tag->next;
+      if(tag->id == ST_DEFINESPRITE) {
+       if(tag->len>4) {
+         /* ??? all sprites are supposed to be unfolded */
+         fprintf(stderr, "librfxswf error - internal error in OptimizeTagOrder/UnfoldAll\n");
+       }
+       level++;
+       if(level==1) {
+         level0 = tag;
+         tag = next;
+         continue;
+       }
+      }
+      if(level>=1) {
+       /* move non-sprite tags out of sprite */
+       if(!swf_isAllowedSpriteTag(tag) || level>=2) {
+         /* remove tag from current position */
+         tag->prev->next = tag->next;
+         if(tag->next)
+           tag->next->prev = tag->prev;
+
+         /* insert before tag level0 */
+         tag->next = level0;
+         tag->prev = level0->prev;
+         level0->prev = tag;
+         tag->prev->next = tag;
+         changes = 1;
+       }
+      }
+      if(tag->id == ST_END) {
+       level--;
+      }
+
+      tag = next;
+    }
+  } while(changes);
+}
+
 // Movie Functions
 
 int swf_ReadSWF2(struct reader_t*reader, SWF * swf)   // Reads SWF to memory (malloc'ed), returns length or <0 if fails
@@ -909,29 +1211,37 @@ int  swf_WriteSWF2(struct writer_t*writer, SWF * swf)     // Writes SWF to file,
       swf_SetU16(&t2, swf->frameCount);
       l = swf_GetTagLen(&t2)+8;
     }
+    if(swf->compressed == 8) {
+      l -= 8;
+    }
 
     fileSize = l+len;
     if(len) {// don't touch headers without tags
        swf->fileSize = fileSize;
        swf->frameCount = frameCount;
     }
-   
-    if(swf->compressed) {
-      char*id = "CWS";
-      writer->write(writer, id, 3);
-    }
-    else {
-      char*id = "FWS";
-      writer->write(writer, id, 3);
-    }
 
-    writer->write(writer, &swf->fileVersion, 1);
-    PUT32(b4, swf->fileSize);
-    writer->write(writer, b4, 4);
+    if(swf->compressed != 8) {
+    /* compressed flag set to 8 means "skip first 8 
+       header bytes". This is necessary if the caller wants to
+       create compressed SWFs himself */
+      if(swf->compressed) {
+       char*id = "CWS";
+       writer->write(writer, id, 3);
+      }
+      else {
+       char*id = "FWS";
+       writer->write(writer, id, 3);
+      }
 
-    if(swf->compressed) {
-      writer_init_zlibdeflate(&zwriter, writer);
-      writer = &zwriter;
+      writer->write(writer, &swf->fileVersion, 1);
+      PUT32(b4, swf->fileSize);
+      writer->write(writer, b4, 4);
+      
+      if(swf->compressed) {
+       writer_init_zlibdeflate(&zwriter, writer);
+       writer = &zwriter;
+      }
     }
 
     swf_SetRect(&t1,&swf->movieSize);
@@ -944,7 +1254,7 @@ int  swf_WriteSWF2(struct writer_t*writer, SWF * swf)     // Writes SWF to file,
       if (ret!=swf_GetTagLen(&t1))
       {
         #ifdef DEBUG_RFXSWF
-          printf("ret:%d\n",ret);
+          fprintf(stderr, "ret:%d\n",ret);
           perror("write:");
           fprintf(stderr,"WriteSWF() failed: Header.\n");
         #endif
@@ -956,7 +1266,8 @@ int  swf_WriteSWF2(struct writer_t*writer, SWF * swf)     // Writes SWF to file,
       { if (swf_WriteTag2(writer, t)<0) return -1;
         t = swf_NextTag(t);
       }
-      writer->finish(writer); //e.g. flush zlib buffers
+      if(swf->compressed != 8)
+       writer->finish(writer); // flush zlib buffers - only if _we_ initialized that writer.
     }
   }
   return (int)fileSize;
@@ -966,8 +1277,10 @@ int  swf_WriteSWF(int handle, SWF * swf)     // Writes SWF to file, returns leng
 {
   struct writer_t writer;
   swf->compressed = 0;
-  if(handle<0)
+  if(handle<0) {
+    writer_init_nullwriter(&writer);
     return swf_WriteSWF2(&writer, swf);
+  }
   writer_init_filewriter(&writer, handle);
   return swf_WriteSWF2(&writer, swf);
 }
@@ -976,8 +1289,10 @@ int  swf_WriteSWC(int handle, SWF * swf)     // Writes SWF to file, returns leng
 {
   struct writer_t writer;
   swf->compressed = 1;
-  if(handle<0)
+  if(handle<0) {
+    writer_init_nullwriter(&writer);
     return swf_WriteSWF2(&writer, swf);
+  }
   writer_init_filewriter(&writer, handle);
   return swf_WriteSWF2(&writer, swf);
 }
@@ -1032,6 +1347,7 @@ void swf_FreeTags(SWF * swf)                 // Frees all malloc'ed memory for t
 #include "modules/swfdump.c"
 #include "modules/swfshape.c"
 #include "modules/swftext.c"
+#include "modules/swffont.c"
 #include "modules/swfobject.c"
 #include "modules/swfbutton.c"
 #include "modules/swftools.c"
@@ -1039,4 +1355,4 @@ void swf_FreeTags(SWF * swf)                 // Frees all malloc'ed memory for t
 #include "modules/swfbits.c"
 #include "modules/swfaction.c"
 #include "modules/swfsound.c"
-
+#include "modules/swfdraw.c"