fixes.
[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         logf("<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);
282         TAG*tag;
283         if(fi<0 || swf_ReadSWF(fi, &head)<0) {
284             logf("<fatal> Couldn't open/read %s.", slave_filename[t]);
285             exit(1);
286         }
287         close(fi);
288         logf("<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         logf("<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     logf("<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         logf("<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             logf("<debug> processing sprite tag %02x", tag->id);
428             if(swf_isDefiningTag(rtag))
429             {
430                 logf("<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                 logf("<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                        logf("<debug> deliberately ignoring EXPORTASSETS tag");
454                        break;
455                     case ST_ENABLEDEBUGGER:
456                        logf("<debug> deliberately ignoring ENABLEDEBUGGER tag");
457                        break;
458                     case ST_SETBACKGROUNDCOLOR:
459                        logf("<debug> deliberately ignoring BACKGROUNDCOLOR tag");
460                        break;
461                     case 40:
462                     case 49:
463                     case 51:
464                        logf("<notice> found tag %d. This is a Generator template, isn't it?", tag->id);
465                        break;
466                     default:
467                        logf("<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)
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     m->tx += movex;
496     m->ty += movey;
497 }
498
499 void write_changepos(TAG*output, TAG*tag, int movex, int movey, float scalex, float scaley)
500 {
501     if(movex || movey || scalex != 1 || scaley != 1)
502     {
503         switch(tag->id)
504         {
505             case ST_PLACEOBJECT2: {
506                 MATRIX m;
507                 U8 flags;
508                 swf_GetMatrix(0, &m);
509                 tag->pos = 0;
510                 tag->readBit = 0;
511
512                 flags = swf_GetU8(tag);
513                 swf_SetU8(output, flags|4);
514                 swf_SetU16(output, swf_GetU16(tag)); //depth
515                 //flags&1: move
516                 if(flags&2) {
517                     swf_SetU16(output, swf_GetU16(tag)); //id
518                 }
519                 // flags & 4
520                 if(flags&4) {
521                     swf_GetMatrix(tag, &m);
522                 } else {
523                     swf_GetMatrix(0, &m);
524                 }
525                 matrix_adjust(&m, movex, movey, scalex, scaley);
526                 swf_SetMatrix(output, &m);
527
528                 //swf_ResetReadBits(tag);
529                 swf_SetBlock(output, &tag->data[tag->pos], tag->len - tag->pos);
530                 break;
531             }
532             case ST_PLACEOBJECT: {
533                 MATRIX m;
534                 swf_SetU16(output, swf_GetU16(tag)); //id
535                 swf_SetU16(output, swf_GetU16(tag)); //depth
536                 
537                 swf_GetMatrix(tag, &m);
538                 matrix_adjust(&m, movex, movey, scalex, scaley);
539                 swf_SetMatrix(output, &m);
540                 
541                 //swf_ResetReadBits(tag);
542                 swf_SetBlock(output, &tag->data[tag->pos], tag->len - tag->pos);
543                 break;
544             }
545             default:
546             swf_SetBlock(output, tag->data, tag->len);
547         }
548     } 
549     else 
550     {
551             swf_SetBlock(output, tag->data, tag->len);
552     }
553 }
554
555 TAG* write_sprite(TAG*tag, SWF*sprite, int spriteid, int replaceddefine)
556 {
557     TAG* definespritetag;
558     TAG* rtag;
559     int tmp;
560
561     definespritetag = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
562     swf_SetU16(tag, spriteid);
563     swf_SetU16(tag, sprite->frameCount);
564     logf ("<notice> sprite id is %d", spriteid);
565
566     tmp = sprite->frameCount;
567     logf("<debug> %d frames to go",tmp);
568
569     if(config.clip) {
570         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
571         swf_SetU8(tag, 2+64); //flags: character+clipdepth
572         swf_SetU16(tag, 0); //depth
573         swf_SetU16(tag, replaceddefine); //id
574         swf_SetU16(tag, 65535); //clipdepth
575     }
576
577     if(config.overlay && !config.isframe) {
578         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
579         swf_SetU8(tag, 2); //flags: character
580         swf_SetU16(tag, 1); //depth
581         swf_SetU16(tag, replaceddefine); //id
582     }
583
584     rtag = sprite->firstTag;
585     while(rtag && rtag->id!=ST_END)
586     {
587         if (swf_isAllowedSpriteTag(rtag)) {
588
589             logf("<debug> [sprite main] write tag %02x (%d bytes in body)", 
590                     rtag->id, rtag->len);
591             tag = swf_InsertTag(tag, rtag->id);
592             write_changepos(tag, rtag, config.movex, config.movey, config.scalex, config.scaley);
593         
594             changedepth(tag, +2);
595
596             if(tag->id == ST_SHOWFRAME)
597             {
598                 tmp--;
599                 logf("<debug> %d frames to go",tmp);
600             }
601         }
602         rtag = rtag->next;
603     }
604     tag = swf_InsertTag(tag, ST_END);
605     return tag;
606 }
607
608 static char tag_ok_for_slave(int id)
609 {
610     if(id == ST_SETBACKGROUNDCOLOR)
611         return 0;
612     return 1;
613 }
614
615 TAG* write_master(TAG*tag, SWF*master, SWF*slave, int spriteid, int replaceddefine, int flags)
616 {
617     int outputslave = 0;
618     int frame = 0;
619     int sframe = 0;
620     int slavewritten = 0;
621
622     TAG* rtag = master->firstTag;
623     TAG* stag = slave->firstTag;
624
625     while(rtag && rtag->id!=ST_END)
626     {
627         if(rtag->id == ST_SHOWFRAME && outputslave)
628         {
629             while(stag && stag->id!=ST_END) {
630                 if(stag->id == ST_SHOWFRAME) {
631                     stag = stag->next;
632                     sframe++;
633                     break;
634                 }
635                 if(tag_ok_for_slave(stag->id)) {
636                     tag = swf_InsertTag(tag, stag->id);
637                     swf_SetBlock(tag, stag->data, stag->len);
638                 }
639                 stag = stag->next;
640             }
641         }
642         if(rtag->id == ST_SHOWFRAME)
643         {
644             frame ++;
645         }
646
647         if(swf_isDefiningTag(rtag) && (flags&FLAGS_WRITEDEFINES))
648         {
649             logf("<debug> [master] write tag %02x (%d bytes in body)", 
650                     rtag->id, rtag->len);
651             if(swf_GetDefineID(rtag) == spriteid && !config.isframe)
652             {
653                 if(config.overlay)
654                 {
655                     tag = swf_InsertTag(tag, rtag->id);
656                     swf_SetBlock(tag, rtag->data, rtag->len);
657                     swf_SetDefineID(tag, replaceddefine);
658                 } else {
659                     /* don't write this tag */
660                     logf("<verbose> replacing tag %d id %d with sprite", rtag->id
661                             ,spriteid);
662                 }
663
664                 if(flags&FLAGS_WRITESPRITE)
665                 {
666                     tag = write_sprite_defines(tag, slave);
667                     tag = write_sprite(tag, slave, spriteid, replaceddefine);
668                 }
669                 if(flags&FLAGS_WRITESLAVE)
670                 {
671                     outputslave = 1;
672                 }
673             } else { 
674                 tag = swf_InsertTag(tag, rtag->id);
675                 swf_SetBlock(tag, rtag->data, rtag->len);
676             }
677         }
678         if(frame == slaveframe)
679         {
680             if(flags&FLAGS_WRITESLAVE) {
681                 outputslave = 1;
682                 slavewritten = 1;
683             }
684             if((flags&FLAGS_WRITESPRITE) && !slavewritten)
685             {
686                 int id = get_free_id(masterbitmap);
687                 int depth = 0;
688                 if(config.clip) {
689                     logf("<fatal> Can't combine --clip and --frame");
690                 }
691                 
692                 tag = write_sprite_defines(tag, slave);
693                 tag = write_sprite(tag, slave, id, -1);
694
695                 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
696                     swf_SetU8(tag, 2); //flags: id
697                     swf_SetU16(tag, depth);
698                     swf_SetU16(tag, id);
699
700                 slavewritten = 1;
701             }
702         }
703         if(!swf_isDefiningTag(rtag) && (flags&FLAGS_WRITENONDEFINES))
704         {
705             int dontwrite = 0;
706             switch(rtag->id) {
707                 case ST_PLACEOBJECT:
708                 case ST_PLACEOBJECT2:
709                     if(frame == slaveframe && !config.overlay)
710                         dontwrite = 1;
711                 case ST_REMOVEOBJECT:
712                     /* place/removetags for the object we replaced
713                        should be discarded, too, as the object to insert 
714                        isn't a sprite 
715                      */
716                     if(spriteid>=0 && swf_GetPlaceID(rtag) == spriteid && 
717                             !config.isframe && config.merge)
718                         dontwrite = 1;
719                 break;
720                 case ST_REMOVEOBJECT2:
721                 break;
722             }
723             if(!dontwrite) {
724                 logf("<debug> [master] write tag %02x (%d bytes in body)", 
725                         rtag->id, rtag->len);
726                 tag = swf_InsertTag(tag, rtag->id);
727                 write_changepos(tag, rtag, config.mastermovex, config.mastermovey, config.masterscalex, config.masterscaley);
728             }
729         }
730         rtag = rtag->next;
731     }
732    
733     if(outputslave) 
734     while(stag && stag->id!=ST_END)
735     {
736             if(tag_ok_for_slave(stag->id)) {
737                 tag = swf_InsertTag(tag, stag->id);
738                 swf_SetBlock(tag, stag->data, stag->len);
739             }
740             stag = stag->next;
741     }
742     if(!slavewritten && config.isframe && (flags&(FLAGS_WRITESLAVE|FLAGS_WRITESPRITE)))
743     {
744         if(slaveframe>=0)
745             logf("<warning> Frame %d doesn't exist in file. No substitution will occur",
746                     slaveframe);
747         else
748             logf("<warning> Frame \"%s\" doesn't exist in file. No substitution will occur",
749                     slavename);
750     }
751     tag = swf_InsertTag(tag, ST_END);
752     return tag;
753 }
754
755 void adjustheader(SWF*swf)
756 {
757     if(config.framerate)
758         swf->frameRate = config.framerate;
759     if(config.hassizex) {
760         swf->movieSize.xmax = 
761         swf->movieSize.xmin + config.sizex;
762     }
763     if(config.hassizey) {
764         swf->movieSize.ymax = 
765         swf->movieSize.ymin + config.sizey;
766     }
767 }
768
769 void catcombine(SWF*master, char*slave_name, SWF*slave, SWF*newswf)
770 {
771     char* depths;
772     int t;
773     TAG*tag;
774     TAG*mtag,*stag;
775     if(config.isframe) {
776         logf("<fatal> Can't combine --cat and --frame");
777         exit(1);
778     }
779    
780     tag = master->firstTag;
781     while(tag)
782     {
783         if(swf_isDefiningTag(tag)) {
784             int defineid = swf_GetDefineID(tag);
785             logf("<debug> tagid %02x defines object %d", tag->id, defineid);
786             masterbitmap[defineid] = 1;
787         }
788         tag = tag->next;
789     }
790     
791     swf_Relocate(slave, masterbitmap);
792     jpeg_assert(master, slave);
793     
794     memcpy(newswf, master, sizeof(SWF));
795     adjustheader(newswf);
796
797     tag = newswf->firstTag = swf_InsertTag(0, ST_REFLEX); // to be removed later
798
799     depths = malloc(65536);
800     if(!depths) {
801         logf("<fatal> Couldn't allocate %d bytes of memory", 65536);
802         return;
803     }
804     memset(depths, 0, 65536);
805     mtag = master->firstTag;
806     while(mtag && mtag->id!=ST_END)
807     {
808         int num=1;
809         U16 depth;
810         logf("<debug> [master] write tag %02x (%d bytes in body)", 
811                 mtag->id, mtag->len);
812         switch(mtag->id) {
813             case ST_PLACEOBJECT2:
814                 num++;
815             case ST_PLACEOBJECT: {
816                depth = swf_GetDepth(mtag);
817                depths[depth] = 1;
818             }
819             break;
820             case ST_REMOVEOBJECT: {
821                depth = swf_GetDepth(mtag);
822                depths[depth] = 0;
823             }
824             break;
825             case ST_REMOVEOBJECT2: {
826                depth = swf_GetDepth(mtag);
827                depths[depth] = 0;
828             }
829             break;
830         }
831         tag = swf_InsertTag(tag, mtag->id);
832         swf_SetBlock(tag, mtag->data, mtag->len);
833
834         mtag = mtag->next;
835     }
836
837     for(t=0;t<65536;t++) 
838     if(depths[t])
839     {
840         char data[16];
841         int len;
842         tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
843         swf_SetU16(tag, t);
844     }
845     free(depths);
846
847     stag = slave->firstTag;
848     while(stag && stag->id!=ST_END)
849     {
850         logf("<debug> [slave] write tag %02x (%d bytes in body)", 
851                 stag->id, stag->len);
852         tag = swf_InsertTag(tag, stag->id);
853         swf_SetBlock(tag, stag->data, stag->len);
854         stag = stag->next;
855     }
856     tag = swf_InsertTag(tag, ST_END);
857
858     tag = newswf->firstTag;
859     newswf->firstTag = newswf->firstTag->next; //remove temporary tag
860     swf_DeleteTag(tag);
861 }
862
863 void normalcombine(SWF*master, char*slave_name, SWF*slave, SWF*newswf)
864 {
865     int spriteid = -1;
866     int replaceddefine = -1;
867     int frame = 0;
868     char*framelabel;
869     TAG * tag = master->firstTag;
870     
871     // set the idtab
872     while(tag)
873     {
874         if(swf_isDefiningTag(tag)) {
875             int defineid = swf_GetDefineID(tag);
876             logf("<debug> tagid %02x defines object %d", tag->id, defineid);
877             masterbitmap[defineid] = 1;
878             if (!slavename && defineid==slaveid) {
879                 if(defineid>=0) {
880                   spriteid = defineid;
881                   logf("<notice> Slave file attached to object %d.", defineid);
882                 }
883             }
884         } else if(tag->id == ST_PLACEOBJECT2) {
885             char * name = swf_GetName(tag);
886             int id = swf_GetPlaceID(tag);
887
888             if(name)
889               logf("<verbose> tagid %02x places object %d named \"%s\"", tag->id, id, name);
890             else
891               logf("<verbose> tagid %02x places object %d (no name)", tag->id, id);
892
893             if (name && slavename && !strcmp(name,slavename)) {
894                 if(id>=0) {
895                   spriteid = id;
896                   logf("<notice> Slave file attached to named object %s (%d).", name, id);
897                 }
898             }
899         } else if(tag->id == ST_SHOWFRAME) {
900             if(slaveframe>=0 && frame==slaveframe) {
901                 logf("<notice> Slave file attached to frame %d.", frame);
902             }
903             frame++;
904         } else if(tag->id == ST_FRAMELABEL) {
905             char * name = tag->data;
906             if(name && slavename && config.isframe && !strcmp(name, slavename)) {
907                 slaveframe = frame;
908                 logf("<notice> Slave file attached to frame %d (%s).", frame, name);
909             }
910         }
911         tag = tag->next;
912     };
913
914     if (spriteid<0 && !config.isframe) {
915         if(slavename) {
916             if(strcmp(slavename,"!!dummy!!"))
917                 logf("<warning> Didn't find anything named %s in file. No substitutions will occur.", slavename);
918         }
919         else
920             logf("<warning> Didn't find id %d in file. No substitutions will occur.", slaveid);
921         spriteid = get_free_id(masterbitmap);
922     }
923
924     swf_Relocate (slave, masterbitmap);
925     jpeg_assert(slave, master);
926     
927     if (config.overlay)
928         replaceddefine = get_free_id(masterbitmap);
929     
930     // write file 
931
932     memcpy(newswf, master, sizeof(SWF));
933     adjustheader(newswf);
934
935     newswf->firstTag = tag = swf_InsertTag(0, ST_REFLEX); // to be removed later
936
937     if (config.antistream) {
938         if (config.merge) {
939             logf("<fatal> Can't combine --antistream and --merge");
940         }
941         tag = write_sprite_defines(tag, slave);
942         tag = write_sprite(tag, slave, spriteid, replaceddefine);
943         tag = write_master(tag, master, slave, spriteid, replaceddefine, FLAGS_WRITEDEFINES);
944         tag = write_master(tag, master, slave, spriteid, replaceddefine, FLAGS_WRITENONDEFINES);
945     } else {
946         if (config.merge)
947             tag = write_master(tag, master, slave, spriteid, replaceddefine, 
948                 FLAGS_WRITEDEFINES|FLAGS_WRITENONDEFINES|   FLAGS_WRITESLAVE    );
949         else
950             tag = write_master(tag, master, slave, spriteid, replaceddefine, 
951                 FLAGS_WRITEDEFINES|FLAGS_WRITENONDEFINES|   FLAGS_WRITESPRITE   );
952     }
953
954     tag = newswf->firstTag;
955     newswf->firstTag = newswf->firstTag->next; //remove temporary tag
956     swf_DeleteTag(tag);
957 }
958
959 void combine(SWF*master, char*slave_name, SWF*slave, SWF*newswf)
960 {
961     slavename = slave_name;
962     slaveid = -1;
963     slaveframe = -1;
964
965     swf_FoldAll(master);
966     swf_FoldAll(slave);
967
968     if(slavename[0] == '#')
969     {
970         slaveid = atoi(&slavename[1]);
971         slavename = 0;
972     }
973
974     if(config.isframe)
975     {
976         int tmp;
977         if(slavename && slavename[0]!='#' && (sscanf(slavename, "%d", &tmp) ==
978                 strlen(slavename))) {
979         /* if the name the slave should replace 
980            consists only of digits and the -f
981            option is given, it probably is not
982            a frame name but a frame number.
983          */
984             slaveid = tmp;
985             slavename = 0;
986         }
987
988         if(slaveid>=0) {
989             slaveframe = slaveid;
990             slaveid = -1;
991         } else {
992         /* if id wasn't given as either #number or number,
993            the name is a frame label. BTW: The user wouldn't necessarily have
994            needed to supply the -f option in this case */
995         }
996     }
997
998     logf("<debug> move x (%d)", config.movex);
999     logf("<debug> move y (%d)", config.movey);
1000     logf("<debug> scale x (%f)", config.scalex);
1001     logf("<debug> scale y (%f)", config.scaley);
1002     logf("<debug> master move x (%d)", config.mastermovex);
1003     logf("<debug> master move y (%d)", config.mastermovey);
1004     logf("<debug> master scale x (%f)", config.masterscalex);
1005     logf("<debug> master scale y (%f)", config.masterscaley);
1006     logf("<debug> is frame (%d)", config.isframe);
1007     
1008     memset(masterbitmap, 0, sizeof(masterbitmap));
1009
1010     if(config.cat) 
1011         return catcombine(master, slave_name, slave, newswf);
1012     else
1013         return normalcombine(master, slave_name, slave, newswf);
1014 }
1015
1016 int main(int argn, char *argv[])
1017 {
1018     int fi;
1019     SWF master;
1020     SWF slave;
1021     SWF newswf;
1022     int t;
1023
1024     config.overlay = 0; 
1025     config.antistream = 0; 
1026     config.alloctest = 0;
1027     config.cat = 0;
1028     config.merge = 0;
1029     config.clip = 0;
1030     config.loglevel = 2; 
1031     config.movex = 0;
1032     config.movey = 0;
1033     config.scalex = 1.0;
1034     config.scaley = 1.0;
1035     config.sizex = 0;
1036     config.sizey = 0;
1037     config.masterscalex = 1.0;
1038     config.masterscaley = 1.0;
1039     config.mastermovex = 0;
1040     config.mastermovey = 0;
1041     config.hassizex = 0;
1042     config.hassizey = 0;
1043     config.framerate = 0;
1044     config.stack = 0;
1045     config.stack1 = 0;
1046     config.dummy = 0;
1047     config.zlib = 0;
1048
1049     processargs(argn, argv);
1050     initLog(0,-1,0,0,-1,config.loglevel);
1051
1052     if(config.merge && config.cat) {
1053         logf("<error> Can't combine --cat and --merge");
1054         exit(1);
1055     }
1056
1057     if(config.stack) {
1058         if(config.overlay) {
1059             logf("<error> Can't combine -l and -t");
1060             exit(1);
1061         }
1062         if(config.clip) {
1063             logf("<error> Can't combine -c and -t");
1064             exit(1);
1065         }
1066         logf("<verbose> (stacking) %d files found\n", numslaves);
1067
1068         makestackmaster(&master);
1069     }
1070     else {
1071         int ret;
1072         logf("<verbose> master entity %s (named \"%s\")\n", master_filename, master_name);
1073         fi = open(master_filename, O_RDONLY);
1074         if(fi<0) {
1075             logf("<fatal> Failed to open %s\n", master_filename);
1076             exit(1);
1077         }
1078         ret = swf_ReadSWF(fi, &master);
1079         if(ret<0) {
1080             logf("<fatal> Failed to read from %s\n", master_filename);
1081             exit(1);
1082         }
1083         logf("<debug> Read %d bytes from masterfile\n", ret);
1084         close(fi);
1085     }
1086
1087     for(t=0;t<numslaves;t++) {
1088             logf("<verbose> slave entity(%d) %s (%s \"%s\")\n", t+1, slave_filename[t], 
1089                     slave_isframe[t]?"frame":"object", slave_name[t]);
1090     }
1091
1092     if(config.dummy)
1093     {
1094         if(numslaves)
1095         {
1096             logf("<error> --dummy (-d) implies there are zero slave objects. You supplied %d.", numslaves);
1097             exit(1);
1098         }
1099         numslaves = 1;
1100         slave_filename[0] = "!!dummy!!";
1101         slave_name[0] = "!!dummy!!";
1102         slave_isframe[0] = 0;
1103     }
1104
1105     if (config.alloctest)
1106     {
1107         char*bitmap = malloc(sizeof(char)*65536);
1108         memset(bitmap, 0, 65536*sizeof(char));
1109         memset(bitmap, 1, 101*sizeof(char));
1110         swf_Relocate(&master, bitmap);
1111         newswf = master;
1112         free(bitmap);
1113 //      makestackmaster(&newswf);
1114     }
1115     else
1116     {
1117         if (!numslaves)
1118         {
1119             if(config.cat)
1120                 logf("<error> You must have at least two objects.");
1121             else
1122                 logf("<error> You must have at least one slave entity.");
1123             return 0;
1124         }
1125         for(t = 0; t < numslaves; t++)
1126         {
1127             config.movex = slave_movex[t];
1128             config.movey = slave_movey[t];
1129             config.scalex = slave_scalex[t];
1130             config.scaley = slave_scaley[t];
1131             config.isframe = slave_isframe[t];
1132
1133             logf("<notice> Combine [%s]%s and [%s]%s", master_name, master_filename,
1134                     slave_name[t], slave_filename[t]);
1135             if(!config.dummy)
1136             {
1137                 int ret;
1138                 fi = open(slave_filename[t], O_RDONLY);
1139                 if(!fi) {
1140                     logf("<fatal> Failed to open %s\n", slave_filename[t]);
1141                     exit(1);
1142                 }
1143                 ret = swf_ReadSWF(fi, &slave);
1144                 if(ret<0) {
1145                     logf("<fatal> Failed to read from %s\n", slave_filename[t]);
1146                     exit(1);
1147                 }
1148                 logf("<debug> Read %d bytes from slavefile\n", ret);
1149                 close(fi);
1150             }
1151             else
1152             {
1153                 memset(&slave, 0, sizeof(slave));
1154                 slave.firstTag = swf_InsertTag(0, ST_END);
1155                 slave.frameRate = 0;
1156                 slave.fileVersion = 4;
1157                 slave.frameCount = 0;
1158             }
1159
1160             combine(&master, slave_name[t], &slave, &newswf);
1161             master = newswf;
1162         }
1163     }
1164
1165     fi = open(outputname, O_RDWR|O_TRUNC|O_CREAT, 0777);
1166
1167     if(config.zlib)
1168         swf_WriteSWC(fi, &newswf);
1169     else {
1170         newswf.compressed = 0;
1171         swf_WriteSWF(fi, &newswf);
1172     }
1173     close(fi);
1174     return 0;
1175 }
1176