brought up to date.
[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 static int slaveid;
24
25 static char* tag_placeobject2_name (struct swf_tag* tag)
26 {
27     struct PlaceObject2 plo2;
28     placeobject2_init (&plo2, tag);
29     return plo2.name;
30 }
31
32 static u16 tag_placeobject2_character (struct swf_tag* tag)
33 {
34     struct PlaceObject2 plo2;
35     placeobject2_init (&plo2, tag);
36     return plo2.id;
37 }
38
39 static struct swffile master;
40 static struct swffile slave;
41
42 static int masterids[65536];
43
44 static int get_free_id()
45 {
46     int t;
47     for (t=1;t<65536;t++)
48     {
49         if(masterids[t] == -1)
50         {
51             masterids[t] = 1;
52             return t;
53         }
54     }
55     return -1;
56 }
57
58 void changedepth(struct swf_tag*tag, int add)
59 {
60     if(tag->id == TAGID_PLACEOBJECT)
61         (*(u16*)&tag->data[2]) += add;
62     if(tag->id == TAGID_PLACEOBJECT2)
63         (*(u16*)&tag->data[1]) += add;
64     if(tag->id == TAGID_REMOVEOBJECT)
65         (*(u16*)&tag->data[2]) += add;
66     if(tag->id == TAGID_REMOVEOBJECT2)
67         (*(u16*)&tag->data[0]) += add;
68 }
69
70 /* applies the config move and scale parameters to
71  * a matrix. (If settings would provide a rotation,
72  * this would be a matrix concatenation/multiplication
73  * routine. In this case, it's just element-wise multiplication.
74  */
75 void matrix_adjust(struct MATRIX*m)
76 {
77     if(config.scalex != 1 || config.scaley != 1)
78     {
79         if(!m->hasscale) {
80             m->hasscale = 1;
81             m->a[0][0] = config.scalex;
82             m->a[1][1] = config.scaley;
83         } else {
84             m->a[0][0] *= config.scalex;
85             m->a[1][1] *= config.scaley;
86         }
87         if(m->hasrotate) {
88             m->a[0][1] *= config.scalex;
89             m->a[1][0] *= config.scaley;
90         }
91         m->b[0] *= config.scalex;
92         m->b[1] *= config.scaley;
93     }
94 /*    printf("hasscale: %d\n",m->hasscale);
95     printf("hasrotate: %d\n", m->hasrotate);
96     printf("move: %d %d\n", m->b[0],m->b[1]);
97     printf("rot: %f %f\n",m->a[0][0],m->a[0][1]);
98     printf("     %f %f\n",m->a[1][0],m->a[1][1]);*/
99     m->b[0] += config.movex;
100     m->b[1] += config.movey;
101 }
102
103 void write_changepos(struct swf_tag*tag, struct writer_t*w)
104 {
105     if(config.movex || config.movey || config.scalex != 1 || config.scaley != 1)
106     {
107         switch(tag->id)
108         {
109             case TAGID_PLACEOBJECT: {
110                 struct PlaceObject p;
111                 placeobject_init(&p, tag);
112                 matrix_adjust(&p.matrix);
113                 placeobject_write(&p, w);
114                 break;
115             }
116             case TAGID_PLACEOBJECT2: {
117                 struct PlaceObject2 p;
118                 placeobject2_init(&p, tag);
119                 if(!p.hasmatrix) {
120                     p.hasmatrix = 1;
121                     MATRIX_init(&p.matrix);
122                 }
123                 matrix_adjust(&p.matrix);
124                 placeobject2_write(&p, w);
125                 break;
126             }
127             default:
128             writer_write(w, tag->fulldata, tag->fulllength);
129         }
130     } 
131     else 
132     {
133             writer_write(w, tag->fulldata, tag->fulllength);
134     }
135 }
136
137 void write_sprite_defines(struct writer_t*w)
138 {
139     int pos = 0;
140     while(slave.tags[pos].id != 0) {
141         struct swf_tag * tag = &slave.tags[pos];
142         if(!is_sprite_tag(tag->id)) {
143             logf("<debug> processing sprite tag %02x", slave.tags[pos].id);
144             if(is_defining_tag(tag->id))
145             {
146                 logf("<debug> [sprite defs] write tag %02x (%d bytes in body)", 
147                         tag->id, tag->length);
148                 writer_write(w, tag->fulldata, tag->fulllength);
149             }
150             else
151             {
152                 switch(tag->id)
153                 {case TAGID_DEFINEFONTINFO:
154                     {
155                         /* define font info is not a defining tag, in that
156                          * it doesn't define a new id, but rather extends
157                          * an existing one. It also isn't a sprite tag. 
158                          * Anyway we can't throw it out, so we just pass it
159                          * through.
160                          */
161                         writer_write(w, tag->fulldata, tag->fulllength);
162                         break;
163                     }
164                  case TAGID_JPEGTABLES:
165                         /* according to the flash specs, there may only 
166                            be one JPEGTABLES tag per swf. This is maybe
167                            a big FIXME */
168                         writer_write(w, tag->fulldata, tag->fulllength);
169                     break;
170                  case TAGID_EXPORTASSETS:
171                     logf("<debug> deliberately ignoring EXPORTASSETS tag");
172                     break;
173                  case TAGID_ENABLEDEBUGGER:
174                     logf("<debug> deliberately ignoring ENABLEDEBUGGER tag");
175                     break;
176                  case TAGID_BACKGROUNDCOLOR:
177                     logf("<debug> deliberately ignoring BACKGROUNDCOLOR tag");
178                     break;
179                  case 40:
180                  case 49:
181                  case 51:
182                     logf("<notice> found tag %d. This is a Generator template, isn't it?", slave.tags[pos].id);
183                     break;
184                  default:
185                     logf("<notice> funny tag: %d is neither defining nor sprite", slave.tags[pos].id);
186                 }
187             }
188         }
189         pos++;
190     }
191 }
192
193
194 void write_sprite(struct writer_t*w, int spriteid, int replaceddefine)
195 {
196     u16 tmp;
197     u32 tmp32;
198     u32*tagidpos;
199     u8*startpos;
200     int pos = 0;
201     // write slave(2) (header)
202     tmp = 0x3f + (TAGID_DEFINESPRITE << 6);
203     writer_write(w, &tmp, 2);
204     tagidpos = (u32*)writer_getpos(w);
205     writer_write(w, &tmp32, 4);
206     
207     startpos = (u8*)writer_getpos(w);
208
209     logf ("<notice> sprite id is %d", spriteid);
210     tmp = spriteid;
211     writer_write(w, &tmp, 2);
212     tmp = slave.header.count;
213     writer_write(w, &tmp, 2);
214
215
216     // write slave(2) (body)
217     tmp = slave.header.count;
218     logf("<debug> %d frames to go",tmp);
219
220     if(config.clip) {
221         tmp = 7 + (TAGID_PLACEOBJECT2 << 6);
222         writer_write(w, &tmp, 2);
223         tmp = 2+64; //flags: character + clipaction
224         writer_write(w, &tmp, 1);
225         tmp = 0; //depth
226         writer_write(w, &tmp,2);
227         tmp = replaceddefine; //id
228         writer_write(w, &tmp,2);
229         tmp = 65535; //clipdepth
230         writer_write(w, &tmp,2);
231     }
232
233     if(config.overlay) {
234         tmp = 5 + (TAGID_PLACEOBJECT2 << 6);
235         writer_write(w, &tmp, 2);
236         tmp = 2; //flags: character
237         writer_write(w, &tmp, 1);
238         tmp = 0; //depth
239         writer_write(w, &tmp,2);
240         tmp = replaceddefine; //id
241         writer_write(w, &tmp,2);
242     }
243
244     do {
245         struct swf_tag * tag = &slave.tags[pos];
246         if (is_sprite_tag(tag->id)) {
247
248             changedepth(tag, +1);
249             logf("<debug> [sprite main] write tag %02x (%d bytes in body)", 
250                     slave.tags[pos].id, slave.tags[pos].length);
251             write_changepos(tag, w);
252
253             if(tag->id == TAGID_SHOWFRAME)
254             {
255                 tmp--;
256                 logf("<debug> %d frames to go",tmp);
257             }
258         }
259     }
260     while(slave.tags[pos++].id != TAGID_END);
261
262     *tagidpos = (u8*)writer_getpos(w) - startpos; // set length of sprite (in header)
263     logf("<verbose> sprite length is %d",*tagidpos);
264 }
265
266 #define FLAGS_WRITEDEFINES 1
267 #define FLAGS_WRITENONDEFINES 2
268 #define FLAGS_WRITESPRITE 4
269 void write_master(struct writer_t*w, int spriteid, int replaceddefine, int flags)
270 {
271     int pos = 0;
272     do {
273         if(is_defining_tag(master.tags[pos].id) && (flags&1))
274         {
275             logf("<debug> [master] write tag %02x (%d bytes in body)", 
276                     master.tags[pos].id, master.tags[pos].length);
277             if( getidfromtag(&master.tags[pos]) == spriteid) 
278             {
279                 if(config.overlay)
280                 {
281                     *(u16*)master.tags[pos].data = replaceddefine;
282                     writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
283                 } else {
284                     /* don't write this tag */
285                     logf("<verbose> replacing tag %d id %d with sprite", master.tags[pos].id
286                             ,spriteid);
287                 }
288
289                 if(flags&4)
290                 {
291                     write_sprite_defines(w);
292                     write_sprite(w, spriteid, replaceddefine);
293                 }
294             } else { 
295                 writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
296             }
297         }
298         if(!is_defining_tag(master.tags[pos].id) && (flags&2))
299         {
300             logf("<debug> [master] write tag %02x (%d bytes in body)", 
301                     master.tags[pos].id, master.tags[pos].length);
302             writer_write(w, master.tags[pos].fulldata, master.tags[pos].fulllength);
303         }
304     }
305     while(master.tags[pos++].id != 0);
306 }
307
308 void writeheader(struct writer_t*w, u8*data, int length)
309 {
310     if(config.hassizex || config.hassizey || config.framerate)
311     {
312         struct flash_header head;
313         swf_init(data-3, length+3);
314         head = swf_read_header();
315         if(config.hassizex)
316         {
317             head.boundingBox.x2 = head.boundingBox.x1 + config.sizex;
318         }
319         if(config.hassizey)
320         {
321             head.boundingBox.y2 = head.boundingBox.y1 + config.sizey;
322         }
323         if(config.framerate)
324         {
325             head.rate = config.framerate;
326         }
327         swf_write_header(w, &head);
328     }
329     else
330     writer_write(w, data, length);
331 }
332
333 uchar * combine(uchar*masterdata, int masterlength, char*_slavename, uchar*slavedata, int slavelength, int*newlength)
334 {
335     char master_flash = 0;
336     char slave_flash = 0;
337     slavename = _slavename;
338     if(slavename[0] == '#')
339     {
340         slaveid = atoi(&slavename[1]);
341         slavename = 0;
342     }
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 && slavename && !strcmp(name,slavename)) || 
421                     (!slavename && id==slaveid)) {
422                     if(id>=0) {
423                       spriteid = id;
424                       logf("<notice> Slave file attached to object %d.", id);
425                     }
426                 }
427             }
428         }
429         while(master.tags[pos++].id != 0);
430
431         if (spriteid<0)
432         {
433             if(slavename) {
434                 if(strcmp(slavename,"!!dummy!!"))
435                     logf("<warning> Didn't find anything named %s in file. No substitutions will occur.", slavename);
436             }
437             else
438                 logf("<warning> Didn't find id %d in file. No substitutions will occur.", slaveid);
439             spriteid = get_free_id();
440         }
441
442         swf_relocate (slavedata, slavelength, masterids);
443
444         read_swf(&slave, slavedata, slavelength);
445         
446         if(config.overlay)
447             replaceddefine = get_free_id();
448         
449         // write file 
450
451         writer_write(&w, "FWS",3);
452         headlength = (u32*)(writer_getpos(&w) + 1);
453         writeheader(&w, master.header.headerdata, master.header.headerlength);
454
455         if(config.antistream) {
456             write_sprite_defines(&w);
457             write_sprite(&w, spriteid, replaceddefine);
458             write_master(&w, spriteid, replaceddefine, FLAGS_WRITEDEFINES);
459             write_master(&w, spriteid, replaceddefine, FLAGS_WRITENONDEFINES);
460         } else {
461             write_master(&w, spriteid, replaceddefine, 
462                     FLAGS_WRITEDEFINES|FLAGS_WRITENONDEFINES|FLAGS_WRITESPRITE);
463         }
464
465         tmp32 = (u8*)writer_getpos(&w) - (u8*)newdata; //length
466         *newlength = tmp32;
467         *headlength = tmp32; // set the header to the correct length
468
469         return newdata; //length
470     }
471     
472     *newlength = 0;
473     return 0;
474 }