implement --merge, --frame and --stack1 options.
[swftools.git] / src / combine.c
index 10b8efc..02e714c 100644 (file)
@@ -19,7 +19,9 @@
 // TODO:
 // * readers should be object-oriented
 
-static char* slavename;
+static char* slavename = 0;
+static int slaveid = -1;
+static int slaveframe = -1;
 
 static char* tag_placeobject2_name (struct swf_tag* tag)
 {
@@ -157,8 +159,15 @@ void write_sprite_defines(struct writer_t*w)
                         * Anyway we can't throw it out, so we just pass it
                         * through.
                         */
+                       writer_write(w, tag->fulldata, tag->fulllength);
                        break;
                    }
+                case TAGID_JPEGTABLES:
+                       /* according to the flash specs, there may only 
+                          be one JPEGTABLES tag per swf. This is maybe
+                          a big FIXME */
+                       writer_write(w, tag->fulldata, tag->fulllength);
+                   break;
                 case TAGID_EXPORTASSETS:
                    logf("<debug> deliberately ignoring EXPORTASSETS tag");
                    break;
@@ -198,12 +207,6 @@ void write_sprite(struct writer_t*w, int spriteid, int replaceddefine)
     
     startpos = (u8*)writer_getpos(w);
 
-    if (spriteid<0)
-    {
-       logf("<warning> Didn't find anything named %s in file. No substitutions will occur.", slavename);
-       spriteid = get_free_id();
-    }
-
     logf ("<notice> sprite id is %d", spriteid);
     tmp = spriteid;
     writer_write(w, &tmp, 2);
@@ -228,7 +231,7 @@ void write_sprite(struct writer_t*w, int spriteid, int replaceddefine)
        writer_write(w, &tmp,2);
     }
 
-    if(config.overlay) {
+    if(config.overlay && !config.isframe) {
        tmp = 5 + (TAGID_PLACEOBJECT2 << 6);
        writer_write(w, &tmp, 2);
        tmp = 2; //flags: character
@@ -261,18 +264,47 @@ void write_sprite(struct writer_t*w, int spriteid, int replaceddefine)
     logf("<verbose> sprite length is %d",*tagidpos);
 }
 
+static char tag_ok_for_slave(int id)
+{
+    if(id == TAGID_BACKGROUNDCOLOR)
+       return 0;
+    return 1;
+}
+
 #define FLAGS_WRITEDEFINES 1
 #define FLAGS_WRITENONDEFINES 2
 #define FLAGS_WRITESPRITE 4
+#define FLAGS_WRITESLAVE 8
 void write_master(struct writer_t*w, int spriteid, int replaceddefine, int flags)
 {
     int pos = 0;
-    do {
-       if(is_defining_tag(master.tags[pos].id) && (flags&1))
+    int spos = 0;
+    int outputslave = 0;
+    int frame = 0;
+    int sframe = 0;
+    int slavewritten = 0;
+    while(master.tags[pos].id != 0)
+    {
+       if(master.tags[pos].id == TAGID_SHOWFRAME && outputslave)
+       {
+           while(slave.tags[spos].id) {
+               if(slave.tags[spos].id == TAGID_SHOWFRAME) {
+                   spos++;
+                   sframe++;
+                   break;
+               }
+               if(tag_ok_for_slave(slave.tags[spos].id))
+                   writer_write(w, slave.tags[spos].fulldata, slave.tags[spos].fulllength);
+               spos++;
+           }
+           frame ++;
+       }
+
+       if(is_defining_tag(master.tags[pos].id) && (flags&FLAGS_WRITEDEFINES))
        {
            logf("<debug> [master] write tag %02x (%d bytes in body)", 
                    master.tags[pos].id, master.tags[pos].length);
-           if( getidfromtag(&master.tags[pos]) == spriteid) 
+           if(getidfromtag(&master.tags[pos]) == spriteid && !config.isframe)
            {
                if(config.overlay)
                {
@@ -284,23 +316,84 @@ void write_master(struct writer_t*w, int spriteid, int replaceddefine, int flags
                            ,spriteid);
                }
 
-               if(flags&4)
+               if(flags&FLAGS_WRITESPRITE)
                {
                    write_sprite_defines(w);
                    write_sprite(w, spriteid, replaceddefine);
                }
+               if(flags&FLAGS_WRITESLAVE)
+               {
+                   outputslave = 1;
+               }
            } else { 
                writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
            }
        }
-       if(!is_defining_tag(master.tags[pos].id) && (flags&2))
+       if(frame == slaveframe)
        {
-           logf("<debug> [master] write tag %02x (%d bytes in body)", 
-                   master.tags[pos].id, master.tags[pos].length);
-           writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
+           if(flags&FLAGS_WRITESLAVE) {
+               outputslave = 1;
+               slavewritten = 1;
+           }
+           if((flags&FLAGS_WRITESPRITE) && !slavewritten)
+           {
+               int id = get_free_id();
+               int depth = 0;
+               char data[7];
+               if(config.clip) {
+                   logf("<fatal> Can't combine --clip and --frame");
+               }
+               *(u16*)&data[0] = (u16)(TAGID_PLACEOBJECT2<<6) + 5 ;
+               *(u8*)&data[2]= 2; //flags: id
+               *(u16*)&data[3]= depth; // depth
+               *(u16*)&data[5]= id;
+               write_sprite_defines(w);
+               write_sprite(w, id, -1);
+               writer_write(w,data,7);
+               slavewritten = 1;
+           }
        }
+       if(!is_defining_tag(master.tags[pos].id) && (flags&FLAGS_WRITENONDEFINES))
+       {
+           int dontwrite = 0;
+           switch(master.tags[pos].id) {
+               case TAGID_PLACEOBJECT:
+               case TAGID_PLACEOBJECT2:
+                   if(frame == slaveframe && !config.overlay)
+                       dontwrite = 1;
+               case TAGID_REMOVEOBJECT:
+//             case TAGID_REMOVEOBJECT2:
+                   /* place/removetags for the object we replaced
+                      should be discarded, too, as the object to insert 
+                      isn't a sprite 
+                    */
+                   if(spriteid>=0 && getidfromtag(&master.tags[pos]) == spriteid && !config.isframe)
+                       dontwrite = 1;
+               break;
+           }
+           if(!dontwrite) {
+               logf("<debug> [master] write tag %02x (%d bytes in body)", 
+                       master.tags[pos].id, master.tags[pos].length);
+               writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
+           }
+       }
+       pos++;
+    }
+   
+    if(outputslave) 
+    while(slave.tags[spos].id)
+    {
+           if(tag_ok_for_slave(slave.tags[spos].id))
+               writer_write(w, slave.tags[spos].fulldata, slave.tags[spos].fulllength);
+           spos++;
     }
-    while(master.tags[pos++].id != 0);
+    if(!slavewritten && config.isframe && (flags&(FLAGS_WRITESLAVE|FLAGS_WRITESPRITE)))
+    {
+       logf("<warning> Frame %d doesn't exist in file. No substitution will occur",
+               slaveframe);
+    }
+    //write END tag: 
+    writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
 }
 
 void writeheader(struct writer_t*w, u8*data, int length)
@@ -328,46 +421,108 @@ void writeheader(struct writer_t*w, u8*data, int length)
     writer_write(w, data, length);
 }
 
-uchar * combine(uchar*masterdata, int masterlength, char*_slavename, uchar*slavedata, int slavelength, int*newlength)
+uchar * catcombine(uchar*masterdata, int masterlength, char*_slavename, uchar*slavedata, int slavelength, int*newlength)
 {
-    char master_flash = 0;
-    char slave_flash = 0;
-    slavename = _slavename;
+       struct writer_t w;
+       u32*headlength;
+       u32 tmp32;
+       int length = masterlength*2 + slavelength;
+       int pos = 0;
+       int t;
+       char* depths;
+       uchar*newdata = malloc(length);
+       if(!newdata) {
+           logf("<fatal> Couldn't allocate %d bytes of memory", length);
+           return 0;
+       }
+       if(config.isframe) {
+           logf("<fatal> Can't combine --cat and --frame");
+           exit(1);
+       }
+       writer_init(&w, newdata, length);
+       
+       do {
+           int tag = master.tags[pos].id;
+           if(is_defining_tag(tag)) {
+               int defineid = getidfromtag(&master.tags[pos]);
+               logf("<debug> tagid %02x defines object %d", tag, defineid);
+               masterids[defineid] = 1;
+           }
+       }
+       while(master.tags[pos++].id != 0);
+       
+       swf_relocate (slavedata, slavelength, masterids);
+       read_swf(&slave, slavedata, slavelength);
+       
+       writer_write(&w, "FWS",3);
+       headlength = (u32*)(writer_getpos(&w) + 1);
+       writeheader(&w, master.header.headerdata, master.header.headerlength);
 
-    logf("<debug> move x (%d)", config.movex);
-    logf("<debug> move y (%d)", config.movey);
-    logf("<debug> scale x (%d)", config.scalex);
-    logf("<debug> scale y (%d)", config.scaley);
-    
-    memset(masterids, -1, sizeof(masterids));
+       depths = malloc(65536);
+       if(!depths) {
+           logf("<fatal> Couldn't allocate %d bytes of memory", 65536);
+           return 0;
+       }
+       memset(depths, 0, 65536);
+       pos = 0;
+       do {
+           int num=1;
+           u16 depth;
+           logf("<debug> [master] write tag %02x (%d bytes in body)", 
+                   master.tags[pos].id, master.tags[pos].length);
+           switch(master.tags[pos].id) {
+               case TAGID_PLACEOBJECT2:
+                   num++;
+               case TAGID_PLACEOBJECT:
+                  reader_init (master.tags[pos].data, master.tags[pos].length);
+                  if(num>=2)
+                       readu8();
+                  depth = readu16();
+                  depths[depth] = 1;
+               break;
+               case TAGID_REMOVEOBJECT:
+                  reader_init (master.tags[pos].data, master.tags[pos].length);
+                  readu16();
+                  depths[readu16()] = 0;
+               break;
+               case TAGID_REMOVEOBJECT2:
+                  reader_init (master.tags[pos].data, master.tags[pos].length);
+                  depths[readu16()] = 0;
+               break;
+           }
+           if(master.tags[pos].id != 0)
+               writer_write(&w, master.tags[pos].fulldata, master.tags[pos].fulllength);
+       }
+       while(master.tags[pos++].id != 0);
 
-    if(masterlength < 3)
-    {
-       logf("<fatal> the master file is too small (%d bytes)", masterlength);
-       return 0;
-    }
-    if(slavelength < 3)
-    {
-       logf("<fatal> the slave file is too small (%d bytes)", slavelength);
-       return 0;
-    }
-    if(masterdata[2] == 'S' &&
-       masterdata[1] == 'W' &&
-       masterdata[0] == 'F')
-    {
-       logf("<notice> the master file is flash (swf) format\n");
-       master_flash = 1;
-    }
-    if(slavedata[2] == 'S' &&
-       slavedata[1] == 'W' &&
-       slavedata[0] == 'F')
-    {
-       logf("<notice> the slave file is flash (swf) format\n");
-       slave_flash = 1;
-    }
+       for(t=0;t<65536;t++) 
+       if(depths[t])
+       {
+           char data[16];
+           int len;
+           *(u16*)(&data[0]) = (TAGID_REMOVEOBJECT2<<6) + 2;
+           *(u16*)(&data[2]) = t;
+           writer_write(&w, data, 4);
+       }
+       free(depths);
 
-    if(master_flash && slave_flash)
-    {
+       pos = 0;
+       do {
+           logf("<debug> [slave] write tag %02x (%d bytes in body)", 
+                   slave.tags[pos].id, slave.tags[pos].length);
+           writer_write(&w, slave.tags[pos].fulldata, slave.tags[pos].fulllength);
+       }
+       while(slave.tags[pos++].id != 0);
+
+       tmp32 = (u8*)writer_getpos(&w) - (u8*)newdata; //length
+       *newlength = tmp32;
+       *headlength = tmp32; // set the header to the correct length
+
+       return newdata; //length
+}
+
+uchar * normalcombine(uchar*masterdata, int masterlength, char*_slavename, uchar*slavedata, int slavelength, int*newlength)
+{
        int length;
        int pos=0;
        u32 tmp32;
@@ -376,9 +531,9 @@ uchar * combine(uchar*masterdata, int masterlength, char*_slavename, uchar*slave
        int spriteid = -1;
        int replaceddefine = -1;
        struct writer_t w;
+       int frame;
+       char*framelabel;
        
-       read_swf(&master, masterdata, masterlength);
-
        length = masterlength + slavelength*2 + 128; // this is a guess, but a good guess.
        newdata = malloc(length);
        writer_init(&w, newdata, length);
@@ -405,21 +560,43 @@ uchar * combine(uchar*masterdata, int masterlength, char*_slavename, uchar*slave
                else
                  logf("<verbose> tagid %02x places object %d (no name)", tag, id);
 
-               if (name && !strcmp(name,slavename)) {
+               if ((name && slavename && !strcmp(name,slavename)) || 
+                   (!slavename && id==slaveid)) {
                    if(id>=0) {
                      spriteid = id;
                      logf("<notice> Slave file attached to object %d.", id);
                    }
                }
+           } else if(tag == TAGID_SHOWFRAME) {
+               if(slaveframe>=0 && frame==slaveframe) {
+                   logf("<notice> Slave file attached to frame %d.", frame);
+               }
+               frame++;
+           } else if(tag == TAGID_FRAMELABEL) {
+               char * name = master.tags[pos].data;
+               if(name && slavename && config.isframe && !strcmp(name, slavename)) {
+                   slaveframe = frame;
+                   logf("<notice> Slave file attached to frame %d (%s).", frame, name);
+               }
            }
        }
        while(master.tags[pos++].id != 0);
 
+       if (spriteid<0 && !config.isframe) {
+           if(slavename) {
+               if(strcmp(slavename,"!!dummy!!"))
+                   logf("<warning> Didn't find anything named %s in file. No substitutions will occur.", slavename);
+           }
+           else
+               logf("<warning> Didn't find id %d in file. No substitutions will occur.", slaveid);
+           spriteid = get_free_id();
+       }
+
        swf_relocate (slavedata, slavelength, masterids);
 
        read_swf(&slave, slavedata, slavelength);
        
-       if(config.overlay)
+       if (config.overlay)
            replaceddefine = get_free_id();
        
        // write file 
@@ -428,13 +605,20 @@ uchar * combine(uchar*masterdata, int masterlength, char*_slavename, uchar*slave
        headlength = (u32*)(writer_getpos(&w) + 1);
        writeheader(&w, master.header.headerdata, master.header.headerlength);
 
-       if(config.antistream) {
+       if (config.antistream) {
+           if (config.merge) {
+               logf("<fatal> Can't combine --antistream and --merge");
+           }
            write_sprite_defines(&w);
            write_sprite(&w, spriteid, replaceddefine);
            write_master(&w, spriteid, replaceddefine, FLAGS_WRITEDEFINES);
            write_master(&w, spriteid, replaceddefine, FLAGS_WRITENONDEFINES);
        } else {
-           write_master(&w, spriteid, replaceddefine, 
+           if (config.merge)
+               write_master(&w, spriteid, replaceddefine, 
+                   FLAGS_WRITEDEFINES|FLAGS_WRITENONDEFINES|FLAGS_WRITESLAVE);
+           else
+               write_master(&w, spriteid, replaceddefine, 
                    FLAGS_WRITEDEFINES|FLAGS_WRITENONDEFINES|FLAGS_WRITESPRITE);
        }
 
@@ -443,6 +627,72 @@ uchar * combine(uchar*masterdata, int masterlength, char*_slavename, uchar*slave
        *headlength = tmp32; // set the header to the correct length
 
        return newdata; //length
+}
+
+uchar * combine(uchar*masterdata, int masterlength, char*_slavename, uchar*slavedata, int slavelength, int*newlength)
+{
+    char master_flash = 0;
+    char slave_flash = 0;
+    slavename = _slavename;
+
+    slaveid = -1;
+    slaveframe = -1;
+
+    if(slavename[0] == '#')
+    {
+       slaveid = atoi(&slavename[1]);
+       slavename = 0;
+    }
+    if(config.isframe)
+    {
+       slaveframe = slaveid;
+       slaveid = -1;
+    }
+
+    logf("<debug> move x (%d)", config.movex);
+    logf("<debug> move y (%d)", config.movey);
+    logf("<debug> scale x (%d)", config.scalex);
+    logf("<debug> scale y (%d)", config.scaley);
+    logf("<debug> is frame (%d)", config.isframe);
+    
+    memset(masterids, -1, sizeof(masterids));
+
+    if(masterlength < 3)
+    {
+       logf("<fatal> the master file is too small (%d bytes)", masterlength);
+       return 0;
+    }
+    if(slavelength < 3)
+    {
+       logf("<fatal> the slave file is too small (%d bytes)", slavelength);
+       return 0;
+    }
+    if(masterdata[2] == 'S' &&
+       masterdata[1] == 'W' &&
+       masterdata[0] == 'F')
+    {
+       logf("<notice> the master file is flash (swf) format\n");
+       master_flash = 1;
+    }
+    else
+       logf("<notice> the master file is not flash (swf) format!\n");
+
+    if(slavedata[2] == 'S' &&
+       slavedata[1] == 'W' &&
+       slavedata[0] == 'F')
+    {
+       logf("<notice> the slave file is flash (swf) format\n");
+       slave_flash = 1;
+    }
+    else
+       logf("<notice> the slave file is not flash (swf) format!\n");
+
+    if(master_flash && slave_flash) {
+       read_swf(&master, masterdata, masterlength);
+       if(config.cat) 
+           return catcombine(masterdata, masterlength, _slavename, slavedata, slavelength, newlength);
+       else
+           return normalcombine(masterdata, masterlength, _slavename, slavedata, slavelength, newlength);
     }
     
     *newlength = 0;