more solaris fixes
[swftools.git] / src / combine.c
index 727f858..4847a58 100644 (file)
 #include <string.h>
 #include <memory.h>
 #include "../lib/log.h"
+#include "../lib/rfxswf.h"
 #include "./flash.h"
 #include "./reloc.h"
 #include "./settings.h"
 
-// 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)
 {
@@ -56,14 +56,58 @@ static int get_free_id()
 
 void changedepth(struct swf_tag*tag, int add)
 {
+    /* fucking big endian byte order */
     if(tag->id == TAGID_PLACEOBJECT)
-       (*(u16*)&tag->data[2]) += add;
+       PUT16(&tag->data[2],GET16(&tag->data[2])+add);
     if(tag->id == TAGID_PLACEOBJECT2)
-       (*(u16*)&tag->data[1]) += add;
+       PUT16(&tag->data[1],GET16(&tag->data[1])+add);
     if(tag->id == TAGID_REMOVEOBJECT)
-       (*(u16*)&tag->data[2]) += add;
+       PUT16(&tag->data[2],GET16(&tag->data[2])+add);
     if(tag->id == TAGID_REMOVEOBJECT2)
-       (*(u16*)&tag->data[0]) += add;
+       PUT16(&tag->data[0],GET16(&tag->data[0])+add);
+}
+
+void jpeg_assert()
+{
+    /* TODO: if there's a jpegtable found, store it
+       and handle it together with the flash file
+       headers */
+    /* check that master and slave don't have both
+       jpegtables (which would be fatal) */
+    int pos;
+    int mpos=-1, spos=-1;
+    pos = 0;
+    while(master.tags[pos].id != 0)
+    {
+       if(master.tags[pos].id == TAGID_JPEGTABLES)
+           mpos = pos;
+       pos++;
+    }
+    pos = 0;
+    while(master.tags[pos].id != 0)
+    {
+       if(slave.tags[pos].id == TAGID_JPEGTABLES)
+           spos = pos;
+       pos++;
+    }
+    if(spos>=0 && mpos>=0)
+    {
+       if(slave.tags[pos].length ==
+          master.tags[pos].length &&
+       !memcmp(slave.tags[pos].data, master.tags[pos].data,
+           master.tags[pos].length))
+       {
+           // ok, both have jpegtables, but they're identical.
+           // delete one and don't make an error
+           for(;spos<slave.tagnum-1;spos++)
+               slave.tags[spos] =
+                   slave.tags[spos+1];
+           spos = -1;
+       }
+    }
+    if(spos>=0 && mpos>=0) {
+       logf("<error> Master and slave have incompatible JPEGTABLES.");
+    }
 }
 
 /* applies the config move and scale parameters to
@@ -157,8 +201,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:
+                       /* if we get here, jpeg_assert has already run,
+                          ensuring this is the only one of it's kind,
+                          so we may safely write it out */
+                       writer_write(w, tag->fulldata, tag->fulllength);
+                   break;
                 case TAGID_EXPORTASSETS:
                    logf("<debug> deliberately ignoring EXPORTASSETS tag");
                    break;
@@ -182,7 +233,6 @@ void write_sprite_defines(struct writer_t*w)
     }
 }
 
-
 void write_sprite(struct writer_t*w, int spriteid, int replaceddefine)
 {
     u16 tmp;
@@ -191,23 +241,17 @@ void write_sprite(struct writer_t*w, int spriteid, int replaceddefine)
     u8*startpos;
     int pos = 0;
     // write slave(2) (header)
-    tmp = 0x3f + (TAGID_DEFINESPRITE << 6);
+    tmp = SWAP16(0x3f + (TAGID_DEFINESPRITE << 6));
     writer_write(w, &tmp, 2);
     tagidpos = (u32*)writer_getpos(w);
     writer_write(w, &tmp32, 4);
     
     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;
+    tmp = SWAP16(spriteid);
     writer_write(w, &tmp, 2);
-    tmp = slave.header.count;
+    tmp = SWAP16(slave.header.count);
     writer_write(w, &tmp, 2);
 
 
@@ -216,26 +260,26 @@ void write_sprite(struct writer_t*w, int spriteid, int replaceddefine)
     logf("<debug> %d frames to go",tmp);
 
     if(config.clip) {
-       tmp = 7 + (TAGID_PLACEOBJECT2 << 6);
+       tmp = SWAP16(7 + (TAGID_PLACEOBJECT2 << 6));
        writer_write(w, &tmp, 2);
-       tmp = 2+64; //flags: character + clipaction
+       tmp = SWAP16(2+64); //flags: character + clipaction
        writer_write(w, &tmp, 1);
-       tmp = 0; //depth
+       tmp = SWAP16(0); //depth
        writer_write(w, &tmp,2);
-       tmp = replaceddefine; //id
+       tmp = SWAP16(replaceddefine); //id
        writer_write(w, &tmp,2);
-       tmp = 65535; //clipdepth
+       tmp = SWAP16(65535); //clipdepth
        writer_write(w, &tmp,2);
     }
 
-    if(config.overlay) {
-       tmp = 5 + (TAGID_PLACEOBJECT2 << 6);
+    if(config.overlay && !config.isframe) {
+       tmp = SWAP16(5 + (TAGID_PLACEOBJECT2 << 6));
        writer_write(w, &tmp, 2);
-       tmp = 2; //flags: character
+       tmp = SWAP16(2); //flags: character
        writer_write(w, &tmp, 1);
-       tmp = 0; //depth
+       tmp = SWAP16(0); //depth
        writer_write(w, &tmp,2);
-       tmp = replaceddefine; //id
+       tmp = SWAP16(replaceddefine); //id
        writer_write(w, &tmp,2);
     }
 
@@ -257,26 +301,55 @@ void write_sprite(struct writer_t*w, int spriteid, int replaceddefine)
     }
     while(slave.tags[pos++].id != TAGID_END);
 
-    *tagidpos = (u8*)writer_getpos(w) - startpos; // set length of sprite (in header)
-    logf("<verbose> sprite length is %d",*tagidpos);
+    PUT32(tagidpos, (u8*)writer_getpos(w) - startpos); // set length of sprite (in header)
+    logf("<verbose> sprite length is %d",GET32(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)
                {
-                   *(u16*)master.tags[pos].data = replaceddefine;
+                   PUT16(master.tags[pos].data, replaceddefine);
                    writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
                } else {
                    /* don't write this tag */
@@ -284,23 +357,85 @@ 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");
+               }
+               PUT16(&data[0], (u16)(TAGID_PLACEOBJECT2<<6) + 5);
+               *(u8*)&data[2]= 2; //flags: id
+               PUT16(&data[3], depth);
+               PUT16(&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 && config.merge)
+                       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)
@@ -308,8 +443,9 @@ void writeheader(struct writer_t*w, u8*data, int length)
     if(config.hassizex || config.hassizey || config.framerate)
     {
        struct flash_header head;
-       swf_init(data-3, length+3);
-       head = swf_read_header();
+       struct reader_t reader;
+       swf_init(&reader, data-3, length+3);
+       head = swf_read_header(&reader);
        if(config.hassizex)
        {
            head.boundingBox.x2 = head.boundingBox.x1 + config.sizex;
@@ -328,51 +464,115 @@ 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);
+       jpeg_assert();
+       
+       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: {
+                  struct reader_t r;
+                  reader_init (&r, master.tags[pos].data, master.tags[pos].length);
+                  if(num>=2)
+                       reader_readu8(&r);
+                  depth = reader_readu16(&r);
+                  depths[depth] = 1;
+               }
+               break;
+               case TAGID_REMOVEOBJECT: {
+                  struct reader_t r;
+                  reader_init (&r, master.tags[pos].data, master.tags[pos].length);
+                  reader_readu16(&r);
+                  depths[reader_readu16(&r)] = 0;
+               }
+               break;
+               case TAGID_REMOVEOBJECT2: {
+                  struct reader_t r;
+                  reader_init (&r, master.tags[pos].data, master.tags[pos].length);
+                  depths[reader_readu16(&r)] = 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;
-    }
-    else
-       logf("<notice> the master file is not flash (swf) format!\n");
+       for(t=0;t<65536;t++) 
+       if(depths[t])
+       {
+           char data[16];
+           int len;
+           PUT16(&data[0], (TAGID_REMOVEOBJECT2<<6) + 2);
+           PUT16(&data[2], t);
+           writer_write(&w, data, 4);
+       }
+       free(depths);
 
-    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");
+       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);
 
-    if(master_flash && slave_flash)
-    {
+       tmp32 = (u8*)writer_getpos(&w) - (u8*)newdata; //length
+       *newlength = tmp32;
+       PUT32(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;
@@ -381,9 +581,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);
@@ -410,21 +610,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);
 
-       swf_relocate (slavedata, slavelength, masterids);
+       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);
+       jpeg_assert();
        
-       if(config.overlay)
+       if (config.overlay)
            replaceddefine = get_free_id();
        
        // write file 
@@ -433,21 +655,94 @@ 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);
        }
 
        tmp32 = (u8*)writer_getpos(&w) - (u8*)newdata; //length
        *newlength = tmp32;
-       *headlength = tmp32; // set the header to the correct length
+       PUT32(headlength, tmp32);
 
        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 (%f)", config.scalex);
+    logf("<debug> scale y (%f)", 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;