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