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