* renamed logf to msg
[swftools.git] / src / swfcombine.c
1 /* swfcombine.c
2    main routine for swfcombine(1), a tool for merging .swf-files.
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 "../lib/rfxswf.h"
14 #include "../lib/args.h"
15 #include "../lib/log.h"
16 #include "../config.h"
17
18 struct config_t
19 {
20    char overlay;
21    char alloctest;
22    char clip;
23    char stack;
24    char stack1;
25    char antistream;
26    char dummy;
27    char zlib;
28    char cat;
29    char merge;
30    char isframe;
31    int loglevel;
32    int sizex;
33    char hassizex;
34    int sizey;
35    char hassizey;
36    int framerate;
37    int movex;
38    int movey;
39    float scalex;
40    float scaley;
41    int mastermovex;
42    int mastermovey;
43    float masterscalex;
44    float masterscaley;
45 };
46 struct config_t config;
47
48 char * master_filename = 0;
49 char * master_name = 0;
50 char * slave_filename[128];
51 char * slave_name[128];
52 int slave_movex[128];
53 int slave_movey[128];
54 float slave_scalex[128];
55 float slave_scaley[128];
56 char slave_isframe[128];
57 int numslaves = 0;
58
59 char * outputname = "output.swf";
60
61 int args_callback_option(char*name,char*val) {
62     if(!strcmp(name,"c"))
63     {
64         config.clip = 1;
65         return 0;
66     }
67     else if(!strcmp(name,"l"))
68     {
69         config.overlay = 1;
70         return 0;
71     }
72     else if (!strcmp(name, "o"))
73     {
74         outputname = val;
75         return 1;
76     }
77     else if (!strcmp(name, "v"))
78     {
79         config.loglevel ++;
80         return 0;
81     }
82     else if (!strcmp(name, "a"))
83     {
84         config.cat = 1;
85         return 0;
86     }
87     else if (!strcmp(name, "A"))
88     {
89         config.alloctest = 1;
90         return 0;
91     }
92     else if (!strcmp(name, "x"))
93     {
94         config.movex = atoi(val);
95         return 1;
96     }
97     else if (!strcmp(name, "y"))
98     {
99         config.movey = atoi(val);
100         return 1;
101     }
102     else if (!strcmp(name, "m"))
103     {
104         config.merge = 1;
105         return 0;
106     }
107     else if (!strcmp(name, "f"))
108     {
109         config.isframe = 1;
110         return 0;
111     }
112     else if (!strcmp(name, "d"))
113     {
114         config.dummy = 1;
115         return 0;
116     }
117     else if (!strcmp(name, "z"))
118     {
119         config.zlib = 1;
120         return 0;
121     }
122     else if (!strcmp(name, "r"))
123     {
124         config.framerate = atoi(val)*256/100;
125         return 1;
126     }
127     else if (!strcmp(name, "X"))
128     {
129         config.sizex = atoi(val)*20;
130         config.hassizex = 1;
131         return 1;
132     }
133     else if (!strcmp(name, "Y"))
134     {
135         config.sizey = atoi(val)*20;
136         config.hassizey = 1;
137         return 1;
138     }
139     else if (!strcmp(name, "s"))
140     {
141         config.scalex = config.scaley = atoi(val)/100.0;
142         return 1;
143     }
144     else if (!strcmp(name, "t") || !strcmp(name, "T"))
145     {
146         if(master_filename) {
147             fprintf(stderr, "error with arguments. Try --help.\n");
148             exit(1);
149         }
150         config.stack = 1;
151         if(!strcmp(name,"T"))
152             config.stack1 = 1;
153         master_filename = "__none__";
154         return 0;
155     }
156     else if (!strcmp(name, "V"))
157     {   
158         printf("swfcombine - part of %s %s\n", PACKAGE, VERSION);
159         exit(0);
160     }
161     else 
162     {
163         fprintf(stderr, "Unknown option: -%s\n", name);
164         exit(1);
165     }
166 }
167
168 struct options_t options[] =
169 {{"o","output"},
170  {"s","scale"},
171  {"d","dummy"},
172  {"x","xpos"},
173  {"y","ypos"},
174  {"X","width"},
175  {"Y","height"},
176  {"r","rate"},
177  {"f","frame"},
178  {"l","overlay"},
179  {"m","merge"},
180  {"t","stack"},
181  {"T","stack1"},
182  {"v","verbose"},
183  {"V","version"},
184  {"c","clip"},
185  {"a","cat"},
186  {"z","zlib"},
187  {0,0}
188 };
189
190 int args_callback_longoption(char*name,char*val) {
191     return args_long2shortoption(options, name, val);
192 }
193
194 int args_callback_command(char*name, char*val) {
195     char*myname = strdup(name);
196     char*filename;
197     filename = strchr(myname, '=');
198     if(filename) {
199         *filename = 0;
200         filename++;
201     } else {
202         // argument has no explicit name field. guess one from the file name
203         char*path = strrchr(myname, '/');
204         char*ext = strrchr(myname, '.');
205         if(!path) path = myname;
206         else path ++;
207         if(ext) *ext = 0;
208         myname = path;
209         filename = name;
210     }
211
212     if(!master_filename) {
213         master_filename = filename;
214         master_name = myname;
215         config.mastermovex = config.movex;
216         config.mastermovey = config.movey;
217         config.masterscalex = config.scalex;
218         config.masterscaley = config.scaley;
219         config.movex = config.movey = 0;
220         config.scalex = config.scaley = 1.0;
221     } else {             
222         msg("<verbose> slave entity %s (named \"%s\")\n", filename, myname);
223
224         slave_filename[numslaves] = filename;
225         slave_name[numslaves] = myname;
226         slave_movex[numslaves] = config.movex;
227         slave_movey[numslaves] = config.movey;
228         slave_scalex[numslaves] = config.scalex;
229         slave_scaley[numslaves] = config.scaley;
230         slave_isframe[numslaves] = config.isframe; 
231         config.isframe = 0;
232         config.movex = config.movey = 0;
233         config.scalex = config.scaley = 1.0;
234         numslaves ++;
235     }
236     return 0;
237 }
238
239 void args_callback_usage(char*name)
240 {
241     printf("Usage: %s [-rXYomlcv] [-f] masterfile [-xysf] [(name1|#id1)=]slavefile1 .. [-xysf] [(nameN|#idN)=]slavefileN\n", name);
242     printf("OR:    %s [-rXYomv] --stack[1] [-xysf] [(name1|#id1)=]slavefile1 .. [-xysf] [(nameN|#idN)=]slavefileN\n", name);
243     printf("OR:    %s [-rXYov] --cat [-xysf] [(name1|#id1)=]slavefile1 .. [-xysf] [(nameN|#idN)=]slavefileN\n", name);
244     printf("OR:    %s [-rXYomlcv] --dummy [-xys] [file]\n", name);
245     printf("\n");
246     printf("-o outputfile       --output    explicitly specify output file. (otherwise, output.swf will be used)\n");
247     printf("-t                  --stack     place each slave in a seperate frame (no master movie)\n");
248     printf("-T                  --stack1    place each slave in the first frame (no master movie)\n");
249     printf("-m                  --merge     Don't store the slaves in Sprites/MovieClips\n");
250     printf("-a                  --cat       concatenate all slave files (no master movie)\n");
251     printf("-l                  --overlay   Don't remove any master objects, only overlay new objects\n");
252     printf("-c                  --clip      Clip the slave objects by the corresponding master objects\n");
253     printf("-v                  --verbose   Use more than one -v for greater effect \n");
254     printf("-d                  --dummy     Don't require slave objects \n");
255     printf("-f                  --frame     The following identifier is a frame or framelabel, not an id or objectname\n");
256     printf("-x xpos             --movex     x Adjust position of slave by xpos twips (1/20 pixel)\n");
257     printf("-y ypos             --movey     y Adjust position of slave by ypos twips (1/20 pixel)\n");
258     printf("-s scale            --scale     Adjust size of slave by scale%\n");
259     printf("-r framerate        --rate      Set movie framerate (100 frames/sec)\n");
260     printf("-X width            --width     Force movie width to scale (default: use master width (not with -t))\n");
261     printf("-Y height           --height    Force movie height to scale (default: use master height (not with -t))\n");
262     printf("-z zlib             --zlib      Enable Flash 6 (MX) Zlib Compression\n");
263 }
264
265 static void makestackmaster(SWF*swf)
266 {
267     TAG*tag;
268     int t;
269     SRECT box;
270     int fileversion = 1;
271     int frameRate = 256;
272     RGBA rgb;
273     rgb.r=rgb.b=rgb.g=0;
274     memset(&box, 0, sizeof(box));
275
276     /* scan all slaves for bounding box */
277     for(t=numslaves-1;t>=0;t--)
278     {
279         SWF head;
280         int ret;
281         int fi=open(slave_filename[t],O_RDONLY|O_BINARY);
282         TAG*tag;
283         if(fi<0 || swf_ReadSWF(fi, &head)<0) {
284             msg("<fatal> Couldn't open/read %s.", slave_filename[t]);
285             exit(1);
286         }
287         close(fi);
288         msg("<verbose> File %s has bounding box %d:%d:%d:%d\n",
289                 slave_filename[t], 
290                 head.movieSize.xmin, head.movieSize.ymin,
291                 head.movieSize.xmax, head.movieSize.ymax);
292
293         tag = head.firstTag;
294         while(tag) {
295             if(tag->id == ST_SETBACKGROUNDCOLOR && tag->len>=3) {
296                 rgb.r = tag->data[0];
297                 rgb.g = tag->data[1];
298                 rgb.b = tag->data[2];
299             }
300             tag=tag->next;
301         }
302         frameRate = head.frameRate;
303         if(head.fileVersion > fileversion)
304             fileversion = head.fileVersion;
305         if(!t)
306             box = head.movieSize;
307         else {
308             if(head.movieSize.xmin < box.xmin)
309                 box.xmin = head.movieSize.xmin;
310             if(head.movieSize.ymin < box.ymin)
311                 box.ymin = head.movieSize.ymin;
312             if(head.movieSize.xmax > box.xmax)
313                 box.xmax = head.movieSize.xmax;
314             if(head.movieSize.ymax > box.ymax)
315                 box.ymax = head.movieSize.ymax;
316         }
317         msg("<verbose> New master bounding box is %d:%d:%d:%d\n",
318                 box.xmin, box.ymin,
319                 box.xmax, box.ymax);
320         swf_FreeTags(&head);
321     }
322
323     memset(swf, 0, sizeof(SWF));
324     swf->fileVersion = fileversion;
325     swf->movieSize = box;
326     swf->frameRate = frameRate;
327
328     swf->firstTag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
329     tag = swf->firstTag;
330     swf_SetRGB(tag, &rgb);
331     
332     for(t=0;t<numslaves;t++)
333     {
334         char buf[128];
335         sprintf(buf, "Frame%02d", t);
336         slave_name[t] = strdup(buf);
337
338         tag = swf_InsertTag(tag, ST_DEFINESPRITE);
339         swf_SetU16(tag, t+1);
340         swf_SetU16(tag, 0);
341         tag = swf_InsertTag(tag, ST_END);
342         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
343         swf_ObjectPlace(tag, t+1, 1+t,0,0, slave_name[t]);
344
345         if(!config.stack1 || t == numslaves-1) {
346             tag = swf_InsertTag(tag, ST_SHOWFRAME);
347         }
348         if(!config.stack)
349         if(t!=numslaves-1)
350         {
351             tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
352             swf_SetU16(tag, 1+t);
353         }
354     }
355     tag = swf_InsertTag(tag, ST_END);
356     msg("<verbose> temporary SWF created");
357 }
358
359 static char* slavename = 0;
360 static int slaveid = -1;
361 static int slaveframe = -1;
362 static char masterbitmap[65536];
363
364 #define FLAGS_WRITEDEFINES 1
365 #define FLAGS_WRITENONDEFINES 2
366 #define FLAGS_WRITESPRITE 4
367 #define FLAGS_WRITESLAVE 8
368
369 int get_free_id(char*bitmap)
370 {
371     int t;
372     for(t=1;t<65536;t++)
373         if(!bitmap[t]) {
374             bitmap[t] = 1;
375             return t;
376         }
377     return -1;
378 }
379
380 void jpeg_assert(SWF*master, SWF*slave)
381 {
382     /* TODO: if there's a jpegtable found, store it
383        and handle it together with the flash file
384        headers */
385
386     /* check that master and slave don't have both
387        jpegtables (which would be fatal) */
388     int pos;
389     TAG *mpos=0, *spos=0;
390     TAG *mtag,*stag;
391     pos = 0;
392     mtag = master->firstTag;
393     stag = slave->firstTag;
394     while(mtag)
395     {
396         if(mtag->id  == ST_JPEGTABLES)
397             mpos = mtag;
398         mtag = mtag->next;
399     }
400     while(stag)
401     {
402         if(stag->id == ST_JPEGTABLES)
403             spos = stag;
404         stag = stag->next;
405     }
406     if(mpos && spos)
407     {
408         if(spos->len == mpos->len &&
409         !memcmp(spos->data, mpos->data, mpos->len))
410         {
411             // ok, both have jpegtables, but they're identical.
412             // delete one and don't throw an error
413             swf_DeleteTag(spos);
414             spos = 0;
415         }
416     }
417     if(spos && mpos) {
418         msg("<error> Master and slave have incompatible JPEGTABLES.");
419     }
420 }
421
422 TAG* write_sprite_defines(TAG*tag, SWF*sprite)
423 {
424     TAG*rtag = sprite->firstTag;
425     while(rtag && rtag->id!=ST_END) {
426         if(!swf_isAllowedSpriteTag(rtag)) {
427             msg("<debug> processing sprite tag %02x", tag->id);
428             if(swf_isDefiningTag(rtag))
429             {
430                 msg("<debug> [sprite defs] write tag %02x (%d bytes in body)", 
431                         tag->id, tag->len);
432                 tag = swf_InsertTag(tag, rtag->id);
433                 swf_SetBlock(tag, rtag->data, rtag->len);
434             }
435             else if(swf_isPseudoDefiningTag(rtag))
436             {
437                 msg("<debug> [sprite defs] write tag %02x (%d bytes in body)", 
438                         tag->id, tag->len);
439                 tag = swf_InsertTag(tag, rtag->id);
440                 swf_SetBlock(tag, rtag->data, rtag->len);
441             }
442             else {
443                 switch(rtag->id)
444                 {
445                     case ST_JPEGTABLES:
446                            /* if we get here, jpeg_assert has already run,
447                               ensuring this is the only one of it's kind,
448                               so we may safely write it out */
449                            tag = swf_InsertTag(tag, rtag->id);
450                            swf_SetBlock(tag, rtag->data, rtag->len);
451                        break;
452                     case ST_EXPORTASSETS:
453                        msg("<debug> deliberately ignoring EXPORTASSETS tag");
454                        break;
455                     case ST_ENABLEDEBUGGER:
456                        msg("<debug> deliberately ignoring ENABLEDEBUGGER tag");
457                        break;
458                     case ST_SETBACKGROUNDCOLOR:
459                        msg("<debug> deliberately ignoring BACKGROUNDCOLOR tag");
460                        break;
461                     case 40:
462                     case 49:
463                     case 51:
464                        msg("<notice> found tag %d. This is a Generator template, isn't it?", tag->id);
465                        break;
466                     default:
467                        msg("<notice> funny tag: %d is neither defining nor sprite", tag->id);
468                 }
469             }
470         }
471         rtag = rtag->next;
472     }
473     return tag;
474 }
475
476 void changedepth(TAG*tag, int add)
477 {
478     /* fucking byteorders */
479     if(tag->id == ST_PLACEOBJECT)
480         PUT16(&tag->data[2],GET16(&tag->data[2])+add);
481     if(tag->id == ST_PLACEOBJECT2)
482         PUT16(&tag->data[1],GET16(&tag->data[1])+add);
483     if(tag->id == ST_REMOVEOBJECT)
484         PUT16(&tag->data[2],GET16(&tag->data[2])+add);
485     if(tag->id == ST_REMOVEOBJECT2)
486         PUT16(&tag->data[0],GET16(&tag->data[0])+add);
487 }
488
489 void matrix_adjust(MATRIX*m, int movex, int movey, float scalex, float scaley, int scalepos)
490 {
491     m->sx = (int)(m->sx*scalex);
492     m->sy = (int)(m->sy*scaley);
493     m->r1 = (int)(m->r1*scalex);
494     m->r0 = (int)(m->r0*scaley);
495     if(scalepos) {
496         m->tx *= scalex;
497         m->ty *= scaley;
498     }
499     m->tx += movex;
500     m->ty += movey;
501 }
502
503 void write_changepos(TAG*output, TAG*tag, int movex, int movey, float scalex, float scaley, int scalepos)
504 {
505     if(movex || movey || scalex != 1 || scaley != 1)
506     {
507         switch(tag->id)
508         {
509             case ST_PLACEOBJECT2: {
510                 MATRIX m;
511                 U8 flags;
512                 swf_GetMatrix(0, &m);
513                 tag->pos = 0;
514                 tag->readBit = 0;
515
516                 flags = swf_GetU8(tag);
517                 swf_SetU8(output, flags|4);
518                 swf_SetU16(output, swf_GetU16(tag)); //depth
519                 //flags&1: move
520                 if(flags&2) {
521                     swf_SetU16(output, swf_GetU16(tag)); //id
522                 }
523                 // flags & 4
524                 if(flags&4) {
525                     swf_GetMatrix(tag, &m);
526                 } else {
527                     swf_GetMatrix(0, &m);
528                 }
529                 matrix_adjust(&m, movex, movey, scalex, scaley, scalepos);
530                 swf_SetMatrix(output, &m);
531
532                 //swf_ResetReadBits(tag);
533                 swf_SetBlock(output, &tag->data[tag->pos], tag->len - tag->pos);
534                 break;
535             }
536             case ST_PLACEOBJECT: {
537                 MATRIX m;
538                 swf_SetU16(output, swf_GetU16(tag)); //id
539                 swf_SetU16(output, swf_GetU16(tag)); //depth
540                 
541                 swf_GetMatrix(tag, &m);
542                 matrix_adjust(&m, movex, movey, scalex, scaley, scalepos);
543                 swf_SetMatrix(output, &m);
544                 
545                 //swf_ResetReadBits(tag);
546                 swf_SetBlock(output, &tag->data[tag->pos], tag->len - tag->pos);
547                 break;
548             }
549             default:
550             swf_SetBlock(output, tag->data, tag->len);
551         }
552     } 
553     else 
554     {
555             swf_SetBlock(output, tag->data, tag->len);
556     }
557 }
558
559 TAG* write_sprite(TAG*tag, SWF*sprite, int spriteid, int replaceddefine)
560 {
561     TAG* definespritetag;
562     TAG* rtag;
563     int tmp;
564
565     definespritetag = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
566     swf_SetU16(tag, spriteid);
567     swf_SetU16(tag, sprite->frameCount);
568     msg ("<notice> sprite id is %d", spriteid);
569
570     tmp = sprite->frameCount;
571     msg("<debug> %d frames to go",tmp);
572
573     if(config.clip) {
574         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
575         swf_SetU8(tag, 2+64); //flags: character+clipdepth
576         swf_SetU16(tag, 0); //depth
577         swf_SetU16(tag, replaceddefine); //id
578         swf_SetU16(tag, 65535); //clipdepth
579     }
580
581     if(config.overlay && !config.isframe) {
582         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
583         swf_SetU8(tag, 2); //flags: character
584         swf_SetU16(tag, 1); //depth
585         swf_SetU16(tag, replaceddefine); //id
586     }
587
588     rtag = sprite->firstTag;
589     while(rtag && rtag->id!=ST_END)
590     {
591         if (swf_isAllowedSpriteTag(rtag)) {
592
593             msg("<debug> [sprite main] write tag %02x (%d bytes in body)", 
594                     rtag->id, rtag->len);
595             tag = swf_InsertTag(tag, rtag->id);
596             write_changepos(tag, rtag, config.movex, config.movey, config.scalex, config.scaley, 0);
597         
598             changedepth(tag, +2);
599
600             if(tag->id == ST_SHOWFRAME)
601             {
602                 tmp--;
603                 msg("<debug> %d frames to go",tmp);
604             }
605         }
606         rtag = rtag->next;
607     }
608     tag = swf_InsertTag(tag, ST_END);
609     return tag;
610 }
611
612 static char tag_ok_for_slave(int id)
613 {
614     if(id == ST_SETBACKGROUNDCOLOR)
615         return 0;
616     return 1;
617 }
618
619 TAG* write_master(TAG*tag, SWF*master, SWF*slave, int spriteid, int replaceddefine, int flags)
620 {
621     int outputslave = 0;
622     int frame = 0;
623     int sframe = 0;
624     int slavewritten = 0;
625
626     TAG* rtag = master->firstTag;
627     TAG* stag = slave->firstTag;
628
629     while(rtag && rtag->id!=ST_END)
630     {
631         if(rtag->id == ST_SHOWFRAME && outputslave)
632         {
633             while(stag && stag->id!=ST_END) {
634                 if(stag->id == ST_SHOWFRAME) {
635                     stag = stag->next;
636                     sframe++;
637                     break;
638                 }
639                 if(tag_ok_for_slave(stag->id)) {
640                     tag = swf_InsertTag(tag, stag->id);
641                     swf_SetBlock(tag, stag->data, stag->len);
642                 }
643                 stag = stag->next;
644             }
645         }
646         if(rtag->id == ST_SHOWFRAME)
647         {
648             frame ++;
649         }
650
651         if(swf_isDefiningTag(rtag) && (flags&FLAGS_WRITEDEFINES))
652         {
653             msg("<debug> [master] write tag %02x (%d bytes in body)", 
654                     rtag->id, rtag->len);
655             if(swf_GetDefineID(rtag) == spriteid && !config.isframe)
656             {
657                 if(config.overlay)
658                 {
659                     tag = swf_InsertTag(tag, rtag->id);
660                     swf_SetBlock(tag, rtag->data, rtag->len);
661                     swf_SetDefineID(tag, replaceddefine);
662                 } else {
663                     /* don't write this tag */
664                     msg("<verbose> replacing tag %d id %d with sprite", rtag->id
665                             ,spriteid);
666                 }
667
668                 if(flags&FLAGS_WRITESPRITE)
669                 {
670                     tag = write_sprite_defines(tag, slave);
671                     tag = write_sprite(tag, slave, spriteid, replaceddefine);
672                 }
673                 if(flags&FLAGS_WRITESLAVE)
674                 {
675                     outputslave = 1;
676                 }
677             } else { 
678                 tag = swf_InsertTag(tag, rtag->id);
679                 swf_SetBlock(tag, rtag->data, rtag->len);
680             }
681         }
682         if(frame == slaveframe)
683         {
684             if(flags&FLAGS_WRITESLAVE) {
685                 outputslave = 1;
686                 slavewritten = 1;
687             }
688             if((flags&FLAGS_WRITESPRITE) && !slavewritten)
689             {
690                 int id = get_free_id(masterbitmap);
691                 int depth = 0;
692                 if(config.clip) {
693                     msg("<fatal> Can't combine --clip and --frame");
694                 }
695                 
696                 tag = write_sprite_defines(tag, slave);
697                 tag = write_sprite(tag, slave, id, -1);
698
699                 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
700                     swf_SetU8(tag, 2); //flags: id
701                     swf_SetU16(tag, depth);
702                     swf_SetU16(tag, id);
703
704                 slavewritten = 1;
705             }
706         }
707         if(!swf_isDefiningTag(rtag) && (flags&FLAGS_WRITENONDEFINES))
708         {
709             int dontwrite = 0;
710             switch(rtag->id) {
711                 case ST_PLACEOBJECT:
712                 case ST_PLACEOBJECT2:
713                     if(frame == slaveframe && !config.overlay)
714                         dontwrite = 1;
715                 case ST_REMOVEOBJECT:
716                     /* place/removetags for the object we replaced
717                        should be discarded, too, as the object to insert 
718                        isn't a sprite 
719                      */
720                     if(spriteid>=0 && swf_GetPlaceID(rtag) == spriteid && 
721                             !config.isframe && config.merge)
722                         dontwrite = 1;
723                 break;
724                 case ST_REMOVEOBJECT2:
725                 break;
726             }
727             if(!dontwrite) {
728                 msg("<debug> [master] write tag %02x (%d bytes in body)", 
729                         rtag->id, rtag->len);
730                 tag = swf_InsertTag(tag, rtag->id);
731                 write_changepos(tag, rtag, config.mastermovex, config.mastermovey, config.masterscalex, config.masterscaley, 1);
732             }
733         }
734         rtag = rtag->next;
735     }
736    
737     if(outputslave) 
738     while(stag && stag->id!=ST_END)
739     {
740             if(tag_ok_for_slave(stag->id)) {
741                 tag = swf_InsertTag(tag, stag->id);
742                 swf_SetBlock(tag, stag->data, stag->len);
743             }
744             stag = stag->next;
745     }
746     if(!slavewritten && config.isframe && (flags&(FLAGS_WRITESLAVE|FLAGS_WRITESPRITE)))
747     {
748         if(slaveframe>=0)
749             msg("<warning> Frame %d doesn't exist in file. No substitution will occur",
750                     slaveframe);
751         else
752             msg("<warning> Frame \"%s\" doesn't exist in file. No substitution will occur",
753                     slavename);
754     }
755     tag = swf_InsertTag(tag, ST_END);
756     return tag;
757 }
758
759 void adjustheader(SWF*swf)
760 {
761     if(config.framerate)
762         swf->frameRate = config.framerate;
763     if(config.hassizex) {
764         swf->movieSize.xmax = 
765         swf->movieSize.xmin + config.sizex;
766     }
767     if(config.hassizey) {
768         swf->movieSize.ymax = 
769         swf->movieSize.ymin + config.sizey;
770     }
771 }
772
773 void catcombine(SWF*master, char*slave_name, SWF*slave, SWF*newswf)
774 {
775     char* depths;
776     int t;
777     TAG*tag;
778     TAG*mtag,*stag;
779     if(config.isframe) {
780         msg("<fatal> Can't combine --cat and --frame");
781         exit(1);
782     }
783    
784     tag = master->firstTag;
785     while(tag)
786     {
787         if(swf_isDefiningTag(tag)) {
788             int defineid = swf_GetDefineID(tag);
789             msg("<debug> tagid %02x defines object %d", tag->id, defineid);
790             masterbitmap[defineid] = 1;
791         }
792         tag = tag->next;
793     }
794     
795     swf_Relocate(slave, masterbitmap);
796     jpeg_assert(master, slave);
797     
798     memcpy(newswf, master, sizeof(SWF));
799     adjustheader(newswf);
800
801     tag = newswf->firstTag = swf_InsertTag(0, ST_REFLEX); // to be removed later
802
803     depths = malloc(65536);
804     if(!depths) {
805         msg("<fatal> Couldn't allocate %d bytes of memory", 65536);
806         return;
807     }
808     memset(depths, 0, 65536);
809     mtag = master->firstTag;
810     while(mtag && mtag->id!=ST_END)
811     {
812         int num=1;
813         U16 depth;
814         msg("<debug> [master] write tag %02x (%d bytes in body)", 
815                 mtag->id, mtag->len);
816         switch(mtag->id) {
817             case ST_PLACEOBJECT2:
818                 num++;
819             case ST_PLACEOBJECT: {
820                depth = swf_GetDepth(mtag);
821                depths[depth] = 1;
822             }
823             break;
824             case ST_REMOVEOBJECT: {
825                depth = swf_GetDepth(mtag);
826                depths[depth] = 0;
827             }
828             break;
829             case ST_REMOVEOBJECT2: {
830                depth = swf_GetDepth(mtag);
831                depths[depth] = 0;
832             }
833             break;
834         }
835         tag = swf_InsertTag(tag, mtag->id);
836         swf_SetBlock(tag, mtag->data, mtag->len);
837
838         mtag = mtag->next;
839     }
840
841     for(t=0;t<65536;t++) 
842     if(depths[t])
843     {
844         char data[16];
845         int len;
846         tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
847         swf_SetU16(tag, t);
848     }
849     free(depths);
850
851     stag = slave->firstTag;
852     while(stag && stag->id!=ST_END)
853     {
854         msg("<debug> [slave] write tag %02x (%d bytes in body)", 
855                 stag->id, stag->len);
856         tag = swf_InsertTag(tag, stag->id);
857         swf_SetBlock(tag, stag->data, stag->len);
858         stag = stag->next;
859     }
860     tag = swf_InsertTag(tag, ST_END);
861
862     tag = newswf->firstTag;
863     newswf->firstTag = newswf->firstTag->next; //remove temporary tag
864     swf_DeleteTag(tag);
865 }
866
867 void normalcombine(SWF*master, char*slave_name, SWF*slave, SWF*newswf)
868 {
869     int spriteid = -1;
870     int replaceddefine = -1;
871     int frame = 0;
872     char*framelabel;
873     TAG * tag = master->firstTag;
874     
875     // set the idtab
876     while(tag)
877     {
878         if(swf_isDefiningTag(tag)) {
879             int defineid = swf_GetDefineID(tag);
880             msg("<debug> tagid %02x defines object %d", tag->id, defineid);
881             masterbitmap[defineid] = 1;
882             if (!slavename && defineid==slaveid) {
883                 if(defineid>=0) {
884                   spriteid = defineid;
885                   msg("<notice> Slave file attached to object %d.", defineid);
886                 }
887             }
888         } else if(tag->id == ST_PLACEOBJECT2) {
889             char * name = swf_GetName(tag);
890             int id = swf_GetPlaceID(tag);
891
892             if(name)
893               msg("<verbose> tagid %02x places object %d named \"%s\"", tag->id, id, name);
894             else
895               msg("<verbose> tagid %02x places object %d (no name)", tag->id, id);
896
897             if (name && slavename && !strcmp(name,slavename)) {
898                 if(id>=0) {
899                   spriteid = id;
900                   msg("<notice> Slave file attached to named object %s (%d).", name, id);
901                 }
902             }
903         } else if(tag->id == ST_SHOWFRAME) {
904             if(slaveframe>=0 && frame==slaveframe) {
905                 msg("<notice> Slave file attached to frame %d.", frame);
906             }
907             frame++;
908         } else if(tag->id == ST_FRAMELABEL) {
909             char * name = tag->data;
910             if(name && slavename && config.isframe && !strcmp(name, slavename)) {
911                 slaveframe = frame;
912                 msg("<notice> Slave file attached to frame %d (%s).", frame, name);
913             }
914         }
915         tag = tag->next;
916     };
917
918     if (spriteid<0 && !config.isframe) {
919         if(slavename) {
920             if(strcmp(slavename,"!!dummy!!"))
921                 msg("<warning> Didn't find anything named %s in file. No substitutions will occur.", slavename);
922         }
923         else
924             msg("<warning> Didn't find id %d in file. No substitutions will occur.", slaveid);
925         spriteid = get_free_id(masterbitmap);
926     }
927
928     swf_Relocate (slave, masterbitmap);
929     jpeg_assert(slave, master);
930     
931     if (config.overlay)
932         replaceddefine = get_free_id(masterbitmap);
933     
934     // write file 
935
936     memcpy(newswf, master, sizeof(SWF));
937     adjustheader(newswf);
938
939     newswf->firstTag = tag = swf_InsertTag(0, ST_REFLEX); // to be removed later
940
941     if (config.antistream) {
942         if (config.merge) {
943             msg("<fatal> Can't combine --antistream and --merge");
944         }
945         tag = write_sprite_defines(tag, slave);
946         tag = write_sprite(tag, slave, spriteid, replaceddefine);
947         tag = write_master(tag, master, slave, spriteid, replaceddefine, FLAGS_WRITEDEFINES);
948         tag = write_master(tag, master, slave, spriteid, replaceddefine, FLAGS_WRITENONDEFINES);
949     } else {
950         if (config.merge)
951             tag = write_master(tag, master, slave, spriteid, replaceddefine, 
952                 FLAGS_WRITEDEFINES|FLAGS_WRITENONDEFINES|   FLAGS_WRITESLAVE    );
953         else
954             tag = write_master(tag, master, slave, spriteid, replaceddefine, 
955                 FLAGS_WRITEDEFINES|FLAGS_WRITENONDEFINES|   FLAGS_WRITESPRITE   );
956     }
957
958     tag = newswf->firstTag;
959     newswf->firstTag = newswf->firstTag->next; //remove temporary tag
960     swf_DeleteTag(tag);
961 }
962
963 void combine(SWF*master, char*slave_name, SWF*slave, SWF*newswf)
964 {
965     slavename = slave_name;
966     slaveid = -1;
967     slaveframe = -1;
968
969     swf_FoldAll(master);
970     swf_FoldAll(slave);
971
972     if(slavename[0] == '#')
973     {
974         slaveid = atoi(&slavename[1]);
975         slavename = 0;
976     }
977
978     if(config.isframe)
979     {
980         int tmp;
981         if(slavename && slavename[0]!='#' && (sscanf(slavename, "%d", &tmp) ==
982                 strlen(slavename))) {
983         /* if the name the slave should replace 
984            consists only of digits and the -f
985            option is given, it probably is not
986            a frame name but a frame number.
987          */
988             slaveid = tmp;
989             slavename = 0;
990         }
991
992         if(slaveid>=0) {
993             slaveframe = slaveid;
994             slaveid = -1;
995         } else {
996         /* if id wasn't given as either #number or number,
997            the name is a frame label. BTW: The user wouldn't necessarily have
998            needed to supply the -f option in this case */
999         }
1000     }
1001
1002     msg("<debug> move x (%d)", config.movex);
1003     msg("<debug> move y (%d)", config.movey);
1004     msg("<debug> scale x (%f)", config.scalex);
1005     msg("<debug> scale y (%f)", config.scaley);
1006     msg("<debug> master move x (%d)", config.mastermovex);
1007     msg("<debug> master move y (%d)", config.mastermovey);
1008     msg("<debug> master scale x (%f)", config.masterscalex);
1009     msg("<debug> master scale y (%f)", config.masterscaley);
1010     msg("<debug> is frame (%d)", config.isframe);
1011     
1012     memset(masterbitmap, 0, sizeof(masterbitmap));
1013
1014     if(config.cat) 
1015         return catcombine(master, slave_name, slave, newswf);
1016     else
1017         return normalcombine(master, slave_name, slave, newswf);
1018 }
1019
1020 int main(int argn, char *argv[])
1021 {
1022     int fi;
1023     SWF master;
1024     SWF slave;
1025     SWF newswf;
1026     int t;
1027
1028     config.overlay = 0; 
1029     config.antistream = 0; 
1030     config.alloctest = 0;
1031     config.cat = 0;
1032     config.merge = 0;
1033     config.clip = 0;
1034     config.loglevel = 2; 
1035     config.movex = 0;
1036     config.movey = 0;
1037     config.scalex = 1.0;
1038     config.scaley = 1.0;
1039     config.sizex = 0;
1040     config.sizey = 0;
1041     config.masterscalex = 1.0;
1042     config.masterscaley = 1.0;
1043     config.mastermovex = 0;
1044     config.mastermovey = 0;
1045     config.hassizex = 0;
1046     config.hassizey = 0;
1047     config.framerate = 0;
1048     config.stack = 0;
1049     config.stack1 = 0;
1050     config.dummy = 0;
1051     config.zlib = 0;
1052
1053     processargs(argn, argv);
1054     initLog(0,-1,0,0,-1,config.loglevel);
1055
1056     if(config.merge && config.cat) {
1057         msg("<error> Can't combine --cat and --merge");
1058         exit(1);
1059     }
1060
1061     if(config.stack) {
1062         if(config.overlay) {
1063             msg("<error> Can't combine -l and -t");
1064             exit(1);
1065         }
1066         if(config.clip) {
1067             msg("<error> Can't combine -c and -t");
1068             exit(1);
1069         }
1070         msg("<verbose> (stacking) %d files found\n", numslaves);
1071
1072         makestackmaster(&master);
1073     }
1074     else {
1075         int ret;
1076         msg("<verbose> master entity %s (named \"%s\")\n", master_filename, master_name);
1077         fi = open(master_filename, O_RDONLY|O_BINARY);
1078         if(fi<0) {
1079             msg("<fatal> Failed to open %s\n", master_filename);
1080             exit(1);
1081         }
1082         ret = swf_ReadSWF(fi, &master);
1083         if(ret<0) {
1084             msg("<fatal> Failed to read from %s\n", master_filename);
1085             exit(1);
1086         }
1087         msg("<debug> Read %d bytes from masterfile\n", ret);
1088         close(fi);
1089     }
1090
1091     for(t=0;t<numslaves;t++) {
1092             msg("<verbose> slave entity(%d) %s (%s \"%s\")\n", t+1, slave_filename[t], 
1093                     slave_isframe[t]?"frame":"object", slave_name[t]);
1094     }
1095
1096     if(config.dummy)
1097     {
1098         if(numslaves)
1099         {
1100             msg("<error> --dummy (-d) implies there are zero slave objects. You supplied %d.", numslaves);
1101             exit(1);
1102         }
1103         numslaves = 1;
1104         slave_filename[0] = "!!dummy!!";
1105         slave_name[0] = "!!dummy!!";
1106         slave_isframe[0] = 0;
1107     }
1108
1109     if (config.alloctest)
1110     {
1111         char*bitmap = malloc(sizeof(char)*65536);
1112         memset(bitmap, 0, 65536*sizeof(char));
1113         memset(bitmap, 1, 101*sizeof(char));
1114         swf_Relocate(&master, bitmap);
1115         newswf = master;
1116         free(bitmap);
1117 //      makestackmaster(&newswf);
1118     }
1119     else
1120     {
1121         if (!numslaves)
1122         {
1123             if(config.cat)
1124                 msg("<error> You must have at least two objects.");
1125             else
1126                 msg("<error> You must have at least one slave entity.");
1127             return 0;
1128         }
1129         for(t = 0; t < numslaves; t++)
1130         {
1131             config.movex = slave_movex[t];
1132             config.movey = slave_movey[t];
1133             config.scalex = slave_scalex[t];
1134             config.scaley = slave_scaley[t];
1135             config.isframe = slave_isframe[t];
1136
1137             msg("<notice> Combine [%s]%s and [%s]%s", master_name, master_filename,
1138                     slave_name[t], slave_filename[t]);
1139             if(!config.dummy)
1140             {
1141                 int ret;
1142                 fi = open(slave_filename[t], O_RDONLY|O_BINARY);
1143                 if(!fi) {
1144                     msg("<fatal> Failed to open %s\n", slave_filename[t]);
1145                     exit(1);
1146                 }
1147                 ret = swf_ReadSWF(fi, &slave);
1148                 if(ret<0) {
1149                     msg("<fatal> Failed to read from %s\n", slave_filename[t]);
1150                     exit(1);
1151                 }
1152                 msg("<debug> Read %d bytes from slavefile\n", ret);
1153                 close(fi);
1154             }
1155             else
1156             {
1157                 memset(&slave, 0, sizeof(slave));
1158                 slave.firstTag = swf_InsertTag(0, ST_END);
1159                 slave.frameRate = 0;
1160                 slave.fileVersion = 4;
1161                 slave.frameCount = 0;
1162             }
1163
1164             combine(&master, slave_name[t], &slave, &newswf);
1165             master = newswf;
1166         }
1167     }
1168
1169     fi = open(outputname, O_BINARY|O_RDWR|O_TRUNC|O_CREAT, 0777);
1170
1171     if(config.zlib)
1172         swf_WriteSWC(fi, &newswf);
1173     else {
1174         newswf.compressed = 0;
1175         swf_WriteSWF(fi, &newswf);
1176     }
1177     close(fi);
1178     return 0;
1179 }
1180