added handling for jpegtables.
[swftools.git] / src / combine.c
1 /* combine.c 
2    Implements combine(), which merges two swfs in memory.
3
4    Part of the swftools package.
5    
6    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org> 
7
8    This file is distributed under the GPL, see file COPYING for details */
9
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <memory.h>
14 #include "../lib/log.h"
15 #include "./flash.h"
16 #include "./reloc.h"
17 #include "./settings.h"
18
19 // TODO:
20 // * readers should be object-oriented
21
22 static char* slavename;
23
24 static char* tag_placeobject2_name (struct swf_tag* tag)
25 {
26     struct PlaceObject2 plo2;
27     placeobject2_init (&plo2, tag);
28     return plo2.name;
29 }
30
31 static u16 tag_placeobject2_character (struct swf_tag* tag)
32 {
33     struct PlaceObject2 plo2;
34     placeobject2_init (&plo2, tag);
35     return plo2.id;
36 }
37
38 static struct swffile master;
39 static struct swffile slave;
40
41 static int masterids[65536];
42
43 static int get_free_id()
44 {
45     int t;
46     for (t=1;t<65536;t++)
47     {
48         if(masterids[t] == -1)
49         {
50             masterids[t] = 1;
51             return t;
52         }
53     }
54     return -1;
55 }
56
57 void changedepth(struct swf_tag*tag, int add)
58 {
59     if(tag->id == TAGID_PLACEOBJECT)
60         (*(u16*)&tag->data[2]) += add;
61     if(tag->id == TAGID_PLACEOBJECT2)
62         (*(u16*)&tag->data[1]) += add;
63     if(tag->id == TAGID_REMOVEOBJECT)
64         (*(u16*)&tag->data[2]) += add;
65     if(tag->id == TAGID_REMOVEOBJECT2)
66         (*(u16*)&tag->data[0]) += add;
67 }
68
69 /* applies the config move and scale parameters to
70  * a matrix. (If settings would provide a rotation,
71  * this would be a matrix concatenation/multiplication
72  * routine. In this case, it's just element-wise multiplication.
73  */
74 void matrix_adjust(struct MATRIX*m)
75 {
76     if(config.scalex != 1 || config.scaley != 1)
77     {
78         if(!m->hasscale) {
79             m->hasscale = 1;
80             m->a[0][0] = config.scalex;
81             m->a[1][1] = config.scaley;
82         } else {
83             m->a[0][0] *= config.scalex;
84             m->a[1][1] *= config.scaley;
85         }
86         if(m->hasrotate) {
87             m->a[0][1] *= config.scalex;
88             m->a[1][0] *= config.scaley;
89         }
90         m->b[0] *= config.scalex;
91         m->b[1] *= config.scaley;
92     }
93 /*    printf("hasscale: %d\n",m->hasscale);
94     printf("hasrotate: %d\n", m->hasrotate);
95     printf("move: %d %d\n", m->b[0],m->b[1]);
96     printf("rot: %f %f\n",m->a[0][0],m->a[0][1]);
97     printf("     %f %f\n",m->a[1][0],m->a[1][1]);*/
98     m->b[0] += config.movex;
99     m->b[1] += config.movey;
100 }
101
102 void write_changepos(struct swf_tag*tag, struct writer_t*w)
103 {
104     if(config.movex || config.movey || config.scalex != 1 || config.scaley != 1)
105     {
106         switch(tag->id)
107         {
108             case TAGID_PLACEOBJECT: {
109                 struct PlaceObject p;
110                 placeobject_init(&p, tag);
111                 matrix_adjust(&p.matrix);
112                 placeobject_write(&p, w);
113                 break;
114             }
115             case TAGID_PLACEOBJECT2: {
116                 struct PlaceObject2 p;
117                 placeobject2_init(&p, tag);
118                 if(!p.hasmatrix) {
119                     p.hasmatrix = 1;
120                     MATRIX_init(&p.matrix);
121                 }
122                 matrix_adjust(&p.matrix);
123                 placeobject2_write(&p, w);
124                 break;
125             }
126             default:
127             writer_write(w, tag->fulldata, tag->fulllength);
128         }
129     } 
130     else 
131     {
132             writer_write(w, tag->fulldata, tag->fulllength);
133     }
134 }
135
136 void write_sprite_defines(struct writer_t*w)
137 {
138     int pos = 0;
139     while(slave.tags[pos].id != 0) {
140         struct swf_tag * tag = &slave.tags[pos];
141         if(!is_sprite_tag(tag->id)) {
142             logf("<debug> processing sprite tag %02x", slave.tags[pos].id);
143             if(is_defining_tag(tag->id))
144             {
145                 logf("<debug> [sprite defs] write tag %02x (%d bytes in body)", 
146                         tag->id, tag->length);
147                 writer_write(w, tag->fulldata, tag->fulllength);
148             }
149             else
150             {
151                 switch(tag->id)
152                 {case TAGID_DEFINEFONTINFO:
153                     {
154                         /* define font info is not a defining tag, in that
155                          * it doesn't define a new id, but rather extends
156                          * an existing one. It also isn't a sprite tag. 
157                          * Anyway we can't throw it out, so we just pass it
158                          * through.
159                          */
160                         writer_write(w, tag->fulldata, tag->fulllength);
161                         break;
162                     }
163                  case TAGID_JPEGTABLES:
164                         /* according to the flash specs, there may only 
165                            be one JPEGTABLES tag per swf. This is maybe
166                            a big FIXME */
167                         writer_write(w, tag->fulldata, tag->fulllength);
168                     break;
169                  case TAGID_EXPORTASSETS:
170                     logf("<debug> deliberately ignoring EXPORTASSETS tag");
171                     break;
172                  case TAGID_ENABLEDEBUGGER:
173                     logf("<debug> deliberately ignoring ENABLEDEBUGGER tag");
174                     break;
175                  case TAGID_BACKGROUNDCOLOR:
176                     logf("<debug> deliberately ignoring BACKGROUNDCOLOR tag");
177                     break;
178                  case 40:
179                  case 49:
180                  case 51:
181                     logf("<notice> found tag %d. This is a Generator template, isn't it?", slave.tags[pos].id);
182                     break;
183                  default:
184                     logf("<notice> funny tag: %d is neither defining nor sprite", slave.tags[pos].id);
185                 }
186             }
187         }
188         pos++;
189     }
190 }
191
192
193 void write_sprite(struct writer_t*w, int spriteid, int replaceddefine)
194 {
195     u16 tmp;
196     u32 tmp32;
197     u32*tagidpos;
198     u8*startpos;
199     int pos = 0;
200     // write slave(2) (header)
201     tmp = 0x3f + (TAGID_DEFINESPRITE << 6);
202     writer_write(w, &tmp, 2);
203     tagidpos = (u32*)writer_getpos(w);
204     writer_write(w, &tmp32, 4);
205     
206     startpos = (u8*)writer_getpos(w);
207
208     if (spriteid<0)
209     {
210         logf("<warning> Didn't find anything named %s in file. No substitutions will occur.", slavename);
211         spriteid = get_free_id();
212     }
213
214     logf ("<notice> sprite id is %d", spriteid);
215     tmp = spriteid;
216     writer_write(w, &tmp, 2);
217     tmp = slave.header.count;
218     writer_write(w, &tmp, 2);
219
220
221     // write slave(2) (body)
222     tmp = slave.header.count;
223     logf("<debug> %d frames to go",tmp);
224
225     if(config.clip) {
226         tmp = 7 + (TAGID_PLACEOBJECT2 << 6);
227         writer_write(w, &tmp, 2);
228         tmp = 2+64; //flags: character + clipaction
229         writer_write(w, &tmp, 1);
230         tmp = 0; //depth
231         writer_write(w, &tmp,2);
232         tmp = replaceddefine; //id
233         writer_write(w, &tmp,2);
234         tmp = 65535; //clipdepth
235         writer_write(w, &tmp,2);
236     }
237
238     if(config.overlay) {
239         tmp = 5 + (TAGID_PLACEOBJECT2 << 6);
240         writer_write(w, &tmp, 2);
241         tmp = 2; //flags: character
242         writer_write(w, &tmp, 1);
243         tmp = 0; //depth
244         writer_write(w, &tmp,2);
245         tmp = replaceddefine; //id
246         writer_write(w, &tmp,2);
247     }
248
249     do {
250         struct swf_tag * tag = &slave.tags[pos];
251         if (is_sprite_tag(tag->id)) {
252
253             changedepth(tag, +1);
254             logf("<debug> [sprite main] write tag %02x (%d bytes in body)", 
255                     slave.tags[pos].id, slave.tags[pos].length);
256             write_changepos(tag, w);
257
258             if(tag->id == TAGID_SHOWFRAME)
259             {
260                 tmp--;
261                 logf("<debug> %d frames to go",tmp);
262             }
263         }
264     }
265     while(slave.tags[pos++].id != TAGID_END);
266
267     *tagidpos = (u8*)writer_getpos(w) - startpos; // set length of sprite (in header)
268     logf("<verbose> sprite length is %d",*tagidpos);
269 }
270
271 #define FLAGS_WRITEDEFINES 1
272 #define FLAGS_WRITENONDEFINES 2
273 #define FLAGS_WRITESPRITE 4
274 void write_master(struct writer_t*w, int spriteid, int replaceddefine, int flags)
275 {
276     int pos = 0;
277     do {
278         if(is_defining_tag(master.tags[pos].id) && (flags&1))
279         {
280             logf("<debug> [master] write tag %02x (%d bytes in body)", 
281                     master.tags[pos].id, master.tags[pos].length);
282             if( getidfromtag(&master.tags[pos]) == spriteid) 
283             {
284                 if(config.overlay)
285                 {
286                     *(u16*)master.tags[pos].data = replaceddefine;
287                     writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
288                 } else {
289                     /* don't write this tag */
290                     logf("<verbose> replacing tag %d id %d with sprite", master.tags[pos].id
291                             ,spriteid);
292                 }
293
294                 if(flags&4)
295                 {
296                     write_sprite_defines(w);
297                     write_sprite(w, spriteid, replaceddefine);
298                 }
299             } else { 
300                 writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
301             }
302         }
303         if(!is_defining_tag(master.tags[pos].id) && (flags&2))
304         {
305             logf("<debug> [master] write tag %02x (%d bytes in body)", 
306                     master.tags[pos].id, master.tags[pos].length);
307             writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
308         }
309     }
310     while(master.tags[pos++].id != 0);
311 }
312
313 void writeheader(struct writer_t*w, u8*data, int length)
314 {
315     if(config.hassizex || config.hassizey || config.framerate)
316     {
317         struct flash_header head;
318         swf_init(data-3, length+3);
319         head = swf_read_header();
320         if(config.hassizex)
321         {
322             head.boundingBox.x2 = head.boundingBox.x1 + config.sizex;
323         }
324         if(config.hassizey)
325         {
326             head.boundingBox.y2 = head.boundingBox.y1 + config.sizey;
327         }
328         if(config.framerate)
329         {
330             head.rate = config.framerate;
331         }
332         swf_write_header(w, &head);
333     }
334     else
335     writer_write(w, data, length);
336 }
337
338 uchar * combine(uchar*masterdata, int masterlength, char*_slavename, uchar*slavedata, int slavelength, int*newlength)
339 {
340     char master_flash = 0;
341     char slave_flash = 0;
342     slavename = _slavename;
343
344     logf("<debug> move x (%d)", config.movex);
345     logf("<debug> move y (%d)", config.movey);
346     logf("<debug> scale x (%d)", config.scalex);
347     logf("<debug> scale y (%d)", config.scaley);
348     
349     memset(masterids, -1, sizeof(masterids));
350
351     if(masterlength < 3)
352     {
353         logf("<fatal> the master file is too small (%d bytes)", masterlength);
354         return 0;
355     }
356     if(slavelength < 3)
357     {
358         logf("<fatal> the slave file is too small (%d bytes)", slavelength);
359         return 0;
360     }
361     if(masterdata[2] == 'S' &&
362        masterdata[1] == 'W' &&
363        masterdata[0] == 'F')
364     {
365         logf("<notice> the master file is flash (swf) format\n");
366         master_flash = 1;
367     }
368     else
369         logf("<notice> the master file is not flash (swf) format!\n");
370
371     if(slavedata[2] == 'S' &&
372        slavedata[1] == 'W' &&
373        slavedata[0] == 'F')
374     {
375         logf("<notice> the slave file is flash (swf) format\n");
376         slave_flash = 1;
377     }
378     else
379         logf("<notice> the slave file is not flash (swf) format!\n");
380
381     if(master_flash && slave_flash)
382     {
383         int length;
384         int pos=0;
385         u32 tmp32;
386         u32*headlength;
387         uchar*newdata;
388         int spriteid = -1;
389         int replaceddefine = -1;
390         struct writer_t w;
391         
392         read_swf(&master, masterdata, masterlength);
393
394         length = masterlength + slavelength*2 + 128; // this is a guess, but a good guess.
395         newdata = malloc(length);
396         writer_init(&w, newdata, length);
397
398         if(!newdata) {
399             logf("<fatal> Couldn't allocate %d bytes of memory", length);
400             return 0;
401         }
402
403         // set the idtab
404         pos = 0;
405         do {
406             int tag = master.tags[pos].id;
407             if(is_defining_tag(tag)) {
408                 int defineid = getidfromtag(&master.tags[pos]);
409                 logf("<debug> tagid %02x defines object %d", tag, defineid);
410                 masterids[defineid] = 1;
411             } else if(tag == TAGID_PLACEOBJECT2) {
412                 char * name = tag_placeobject2_name(&master.tags[pos]);
413                 int id = tag_placeobject2_character(&master.tags[pos]);
414
415                 if(name)
416                   logf("<verbose> tagid %02x places object %d named \"%s\"", tag, id, name);
417                 else
418                   logf("<verbose> tagid %02x places object %d (no name)", tag, id);
419
420                 if (name && !strcmp(name,slavename)) {
421                     if(id>=0) {
422                       spriteid = id;
423                       logf("<notice> Slave file attached to object %d.", id);
424                     }
425                 }
426             }
427         }
428         while(master.tags[pos++].id != 0);
429
430         swf_relocate (slavedata, slavelength, masterids);
431
432         read_swf(&slave, slavedata, slavelength);
433         
434         if(config.overlay)
435             replaceddefine = get_free_id();
436         
437         // write file 
438
439         writer_write(&w, "FWS",3);
440         headlength = (u32*)(writer_getpos(&w) + 1);
441         writeheader(&w, master.header.headerdata, master.header.headerlength);
442
443         if(config.antistream) {
444             write_sprite_defines(&w);
445             write_sprite(&w, spriteid, replaceddefine);
446             write_master(&w, spriteid, replaceddefine, FLAGS_WRITEDEFINES);
447             write_master(&w, spriteid, replaceddefine, FLAGS_WRITENONDEFINES);
448         } else {
449             write_master(&w, spriteid, replaceddefine, 
450                     FLAGS_WRITEDEFINES|FLAGS_WRITENONDEFINES|FLAGS_WRITESPRITE);
451         }
452
453         tmp32 = (u8*)writer_getpos(&w) - (u8*)newdata; //length
454         *newlength = tmp32;
455         *headlength = tmp32; // set the header to the correct length
456
457         return newdata; //length
458     }
459     
460     *newlength = 0;
461     return 0;
462 }