8547d37aa7c4c436855a9f9f7445aadd555b5814
[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 * catcombine(uchar*masterdata, int masterlength, char*_slavename, uchar*slavedata, int slavelength, int*newlength)
334 {
335         struct writer_t w;
336         u32*headlength;
337         u32 tmp32;
338         int length = masterlength + slavelength;
339         int pos = 0;
340         uchar*newdata = malloc(length);
341         if(!newdata) {
342             logf("<fatal> Couldn't allocate %d bytes of memory", length);
343             return 0;
344         }
345         writer_init(&w, newdata, length);
346         
347         do {
348             int tag = master.tags[pos].id;
349             if(is_defining_tag(tag)) {
350                 int defineid = getidfromtag(&master.tags[pos]);
351                 logf("<debug> tagid %02x defines object %d", tag, defineid);
352                 masterids[defineid] = 1;
353             }
354         }
355         while(master.tags[pos++].id != 0);
356         
357         swf_relocate (slavedata, slavelength, masterids);
358         read_swf(&slave, slavedata, slavelength);
359         
360         writer_write(&w, "FWS",3);
361         headlength = (u32*)(writer_getpos(&w) + 1);
362         writeheader(&w, master.header.headerdata, master.header.headerlength);
363
364         pos = 0;
365         do {
366             logf("<debug> [master] write tag %02x (%d bytes in body)", 
367                     master.tags[pos].id, master.tags[pos].length);
368             if(master.tags[pos].id != 0)
369                 writer_write(&w, master.tags[pos].fulldata, master.tags[pos].fulllength);
370         }
371         while(master.tags[pos++].id != 0);
372
373         pos = 0;
374         do {
375             logf("<debug> [slave] write tag %02x (%d bytes in body)", 
376                     slave.tags[pos].id, slave.tags[pos].length);
377             writer_write(&w, slave.tags[pos].fulldata, slave.tags[pos].fulllength);
378         }
379         while(slave.tags[pos++].id != 0);
380
381         tmp32 = (u8*)writer_getpos(&w) - (u8*)newdata; //length
382         *newlength = tmp32;
383         *headlength = tmp32; // set the header to the correct length
384
385         return newdata; //length
386 }
387
388 uchar * normalcombine(uchar*masterdata, int masterlength, char*_slavename, uchar*slavedata, int slavelength, int*newlength)
389 {
390         int length;
391         int pos=0;
392         u32 tmp32;
393         u32*headlength;
394         uchar*newdata;
395         int spriteid = -1;
396         int replaceddefine = -1;
397         struct writer_t w;
398         
399         length = masterlength + slavelength*2 + 128; // this is a guess, but a good guess.
400         newdata = malloc(length);
401         writer_init(&w, newdata, length);
402
403         if(!newdata) {
404             logf("<fatal> Couldn't allocate %d bytes of memory", length);
405             return 0;
406         }
407
408         // set the idtab
409         pos = 0;
410         do {
411             int tag = master.tags[pos].id;
412             if(is_defining_tag(tag)) {
413                 int defineid = getidfromtag(&master.tags[pos]);
414                 logf("<debug> tagid %02x defines object %d", tag, defineid);
415                 masterids[defineid] = 1;
416             } else if(tag == TAGID_PLACEOBJECT2) {
417                 char * name = tag_placeobject2_name(&master.tags[pos]);
418                 int id = tag_placeobject2_character(&master.tags[pos]);
419
420                 if(name)
421                   logf("<verbose> tagid %02x places object %d named \"%s\"", tag, id, name);
422                 else
423                   logf("<verbose> tagid %02x places object %d (no name)", tag, id);
424
425                 if ((name && slavename && !strcmp(name,slavename)) || 
426                     (!slavename && id==slaveid)) {
427                     if(id>=0) {
428                       spriteid = id;
429                       logf("<notice> Slave file attached to object %d.", id);
430                     }
431                 }
432             }
433         }
434         while(master.tags[pos++].id != 0);
435
436         if (spriteid<0)
437         {
438             if(slavename) {
439                 if(strcmp(slavename,"!!dummy!!"))
440                     logf("<warning> Didn't find anything named %s in file. No substitutions will occur.", slavename);
441             }
442             else
443                 logf("<warning> Didn't find id %d in file. No substitutions will occur.", slaveid);
444             spriteid = get_free_id();
445         }
446
447         swf_relocate (slavedata, slavelength, masterids);
448
449         read_swf(&slave, slavedata, slavelength);
450         
451         if(config.overlay)
452             replaceddefine = get_free_id();
453         
454         // write file 
455
456         writer_write(&w, "FWS",3);
457         headlength = (u32*)(writer_getpos(&w) + 1);
458         writeheader(&w, master.header.headerdata, master.header.headerlength);
459
460         if(config.antistream) {
461             write_sprite_defines(&w);
462             write_sprite(&w, spriteid, replaceddefine);
463             write_master(&w, spriteid, replaceddefine, FLAGS_WRITEDEFINES);
464             write_master(&w, spriteid, replaceddefine, FLAGS_WRITENONDEFINES);
465         } else {
466             write_master(&w, spriteid, replaceddefine, 
467                     FLAGS_WRITEDEFINES|FLAGS_WRITENONDEFINES|FLAGS_WRITESPRITE);
468         }
469
470         tmp32 = (u8*)writer_getpos(&w) - (u8*)newdata; //length
471         *newlength = tmp32;
472         *headlength = tmp32; // set the header to the correct length
473
474         return newdata; //length
475 }
476
477 uchar * combine(uchar*masterdata, int masterlength, char*_slavename, uchar*slavedata, int slavelength, int*newlength)
478 {
479     char master_flash = 0;
480     char slave_flash = 0;
481     slavename = _slavename;
482     if(slavename[0] == '#')
483     {
484         slaveid = atoi(&slavename[1]);
485         slavename = 0;
486     }
487
488     logf("<debug> move x (%d)", config.movex);
489     logf("<debug> move y (%d)", config.movey);
490     logf("<debug> scale x (%d)", config.scalex);
491     logf("<debug> scale y (%d)", config.scaley);
492     
493     memset(masterids, -1, sizeof(masterids));
494
495     if(masterlength < 3)
496     {
497         logf("<fatal> the master file is too small (%d bytes)", masterlength);
498         return 0;
499     }
500     if(slavelength < 3)
501     {
502         logf("<fatal> the slave file is too small (%d bytes)", slavelength);
503         return 0;
504     }
505     if(masterdata[2] == 'S' &&
506        masterdata[1] == 'W' &&
507        masterdata[0] == 'F')
508     {
509         logf("<notice> the master file is flash (swf) format\n");
510         master_flash = 1;
511     }
512     else
513         logf("<notice> the master file is not flash (swf) format!\n");
514
515     if(slavedata[2] == 'S' &&
516        slavedata[1] == 'W' &&
517        slavedata[0] == 'F')
518     {
519         logf("<notice> the slave file is flash (swf) format\n");
520         slave_flash = 1;
521     }
522     else
523         logf("<notice> the slave file is not flash (swf) format!\n");
524
525     if(master_flash && slave_flash) {
526         read_swf(&master, masterdata, masterlength);
527         if(config.cat) 
528             return catcombine(masterdata, masterlength, _slavename, slavedata, slavelength, newlength);
529         else
530             return normalcombine(masterdata, masterlength, _slavename, slavedata, slavelength, newlength);
531     }
532     
533     *newlength = 0;
534     return 0;
535 }