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