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