* jpegtables handling
[swftools.git] / src / swfcombine.c
1 /* swfcombine.c
2    main routine for swfcombine(1), a tool for merging .swf-files.
3
4    Part of the swftools package.
5    
6    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org> 
7
8    This file is distributed under the GPL, see file COPYING for details */
9
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include "../lib/rfxswf.h"
14 #include "../lib/args.h"
15 #include "combine.h"
16 #include "settings.h"
17 #include "types.h"
18 #include "flash.h"
19 #include "../config.h"
20
21 char * master_filename = 0;
22 char * master_name = 0;
23 char * slave_filename[128];
24 char * slave_name[128];
25 int slave_movex[128];
26 int slave_movey[128];
27 float slave_scalex[128];
28 float slave_scaley[128];
29 char slave_isframe[128];
30 int numslaves = 0;
31
32 char * outputname = "output.swf";
33
34 int args_callback_option(char*name,char*val) {
35     if(!strcmp(name,"c"))
36     {
37         config.clip = 1;
38         return 0;
39     }
40     else if(!strcmp(name,"l"))
41     {
42         config.overlay = 1;
43         return 0;
44     }
45     else if (!strcmp(name, "o"))
46     {
47         outputname = val;
48         return 1;
49     }
50     else if (!strcmp(name, "v"))
51     {
52         config.loglevel ++;
53         return 0;
54     }
55     else if (!strcmp(name, "a"))
56     {
57         config.cat = 1;
58         return 0;
59     }
60     else if (!strcmp(name, "A"))
61     {
62         config.alloctest = 1;
63         return 0;
64     }
65     else if (!strcmp(name, "x"))
66     {
67         config.movex = atoi(val);
68         return 1;
69     }
70     else if (!strcmp(name, "y"))
71     {
72         config.movey = atoi(val);
73         return 1;
74     }
75     else if (!strcmp(name, "m"))
76     {
77         config.merge = 1;
78         return 0;
79     }
80     else if (!strcmp(name, "f"))
81     {
82         config.isframe = 1;
83         return 0;
84     }
85     else if (!strcmp(name, "d"))
86     {
87         config.dummy = 1;
88         return 0;
89     }
90     else if (!strcmp(name, "r"))
91     {
92         config.framerate = atoi(val)*256/100;
93         return 1;
94     }
95     else if (!strcmp(name, "X"))
96     {
97         config.sizex = atoi(val)*20;
98         config.hassizex = 1;
99         return 1;
100     }
101     else if (!strcmp(name, "Y"))
102     {
103         config.sizey = atoi(val)*20;
104         config.hassizey = 1;
105         return 1;
106     }
107     else if (!strcmp(name, "s"))
108     {
109         config.scalex = config.scaley = atoi(val)/100.0;
110         return 1;
111     }
112     else if (!strcmp(name, "t") || !strcmp(name, "T"))
113     {
114         if(master_filename) {
115             fprintf(stderr, "error with arguments. Try --help.\n");
116             exit(1);
117         }
118         config.stack = 1;
119         if(!strcmp(name,"T"))
120             config.stack1 = 1;
121         master_filename = "__none__";
122         return 0;
123     }
124     else if (!strcmp(name, "V"))
125     {   
126         printf("swfcombine - part of %s %s\n", PACKAGE, VERSION);
127         exit(0);
128     }
129     else 
130     {
131         fprintf(stderr, "Unknown option: -%s\n", name);
132         exit(1);
133     }
134 }
135
136 struct options_t options[] =
137 {{"o","output"},
138  {"s","scale"},
139  {"d","dummy"},
140  {"x","xpos"},
141  {"y","ypos"},
142  {"X","width"},
143  {"Y","height"},
144  {"r","rate"},
145  {"f","frame"},
146  {"l","overlay"},
147  {"m","merge"},
148  {"t","stack"},
149  {"T","stack1"},
150  {"v","verbose"},
151  {"V","version"},
152  {"c","clip"},
153  {"a","cat"},
154  {0,0}
155 };
156
157 int args_callback_longoption(char*name,char*val) {
158     return args_long2shortoption(options, name, val);
159 }
160
161 int args_callback_command(char*name, char*val) {
162     char*myname = strdup(name);
163     char*filename;
164     filename = strchr(myname, '=');
165     if(filename) {
166         *filename = 0;
167         filename++;
168     } else {
169         // argument has no explicit name field. guess one from the file name
170         char*path = strrchr(myname, '/');
171         char*ext = strrchr(myname, '.');
172         if(!path) path = myname;
173         else path ++;
174         if(ext) *ext = 0;
175         myname = path;
176         filename = name;
177     }
178
179     if(!master_filename) {
180         master_filename = filename;
181         master_name = myname;
182     } else {             
183         logf("<verbose> slave entity %s (named \"%s\")\n", filename, myname);
184
185         slave_filename[numslaves] = filename;
186         slave_name[numslaves] = myname;
187         slave_movex[numslaves] = config.movex;
188         slave_movey[numslaves] = config.movey;
189         slave_scalex[numslaves] = config.scalex;
190         slave_scaley[numslaves] = config.scaley;
191         slave_isframe[numslaves] = config.isframe; 
192         config.isframe = 0;
193         config.movex = config.movey = 0;
194         config.scalex = config.scaley = 1.0;
195         numslaves ++;
196     }
197     return 0;
198 }
199
200 void args_callback_usage(char*name)
201 {
202     printf("Usage: %s [-rXYomlcv] [-f] masterfile] [-xysf] [(name1|#id1)=]slavefile1 .. [-xysf] [(nameN|#idN)=]slavefileN\n", name);
203     printf("OR:    %s [-rXYomv] --stack[1] [-xysf] [(name1|#id1)=]slavefile1 .. [-xysf] [(nameN|#idN)=]slavefileN\n", name);
204     printf("OR:    %s [-rXYov] --cat [-xysf] [(name1|#id1)=]slavefile1 .. [-xysf] [(nameN|#idN)=]slavefileN\n", name);
205     printf("OR:    %s [-rXYomlcv] --dummy [-xys] [file]\n", name);
206     printf("\n");
207     printf("-o outputfile       --output    explicitly specify output file. (otherwise, output.swf will be used\n");
208     printf("-t                  --stack     place each slave in a seperate frame (no master movie\n");
209     printf("-T                  --stack1    place each slave in the first frame (no master movie\n");
210     printf("-m                  --merge     Don't store the slaves in Sprites/MovieClips\n");
211     printf("-a                  --cat       concatenate all slave files (no master movie\n");
212     printf("-l                  --overlay   Don't remove any master objects, only overlay new objects\n");
213     printf("-c                  --clip      Clip the slave objects by the corresponding master objects\n");
214     printf("-v                  --verbose   Use more than one -v for greater effect \n");
215     printf("-d                  --dummy     Don't require slave objects \n");
216     printf("-f                  --frame     The following identifier is a frame or framelabel, not an id or objectname\n");
217     printf("-x xpos             --movex     x Adjust position of slave by xpos twips (1/20 pixel\n");
218     printf("-y ypos             --movey     y Adjust position of slave by ypos twips (1/20 pixel\n");
219     printf("-s scale            --scale     Adjust size of slave by scale%\n");
220     printf("-r framerate        --rate      Set movie framerate (100 frames/sec\n");
221     printf("-X width            --width     Force movie width to scale (default: use master width (not with -t\n");
222     printf("-Y height           --height    Force movie height to scale (default: use master height (not with -t\n");
223 }
224
225 /* read a whole file in memory */
226 char* fi_slurp(FILE*fi, unsigned int * setlength)
227 {
228     char * mem;
229     long long int length; //;)  
230     long long int pos = 0;
231     fseek(fi,0,SEEK_END);
232     length = ftell(fi);
233     fseek(fi,0,SEEK_SET);
234     if(!length)
235         return 0;
236     mem = malloc(length);
237     if(!mem)
238         return 0;
239     while(!feof(fi))
240     {
241         pos += fread(&mem[pos], 1, 65536, fi);
242     }
243     if (setlength) 
244         *setlength = length;
245     return mem;
246 }
247
248 void fi_dump(FILE*fi, void*_mem, int length)
249 {
250     char*mem = (char*)_mem;
251     int pos = 0;
252     while(pos < length)
253     {
254         int size = 65536;
255         if (size > (length - pos))
256                 size = (length - pos);
257         pos += fwrite(&mem[pos], 1, size, fi);
258     }
259 }
260
261 /* todo: use rfxswf */
262 void makestackmaster(u8**masterdata, int*masterlength)
263 {
264     u8 head[] = {'F','W','S'};
265     u8 *pos;
266     u32 * fixpos;
267     int t;
268     struct RECT box;
269     int strlength = 0;
270     int fileversion = 1;
271
272     /* scan all slaves for bounding box */
273     for(t=0;t<numslaves;t++)
274     {
275         FILE*fi=fopen(slave_filename[t],"rb");
276         u8 data[256];
277         int ret;
278         struct flash_header head;
279         struct reader_t r;
280         strlength += strlen(slave_name[t]) + 9;
281         if(!fi) {
282             logf("<fatal> Couldn't open %s.", slave_filename[t]);
283             exit(1);
284         }
285         ret = fread(data,1,256,fi);
286         if(ret < 13) {
287             logf("<fatal> File %s is to small (%d bytes)", slave_filename[t], ret);
288             exit(1);
289         }
290         swf_init(&r, data,256);
291         head = swf_read_header(&r);
292         logf("<verbose> File %s has bounding box %d:%d:%d:%d\n",
293                 slave_filename[t], 
294                 head.boundingBox.x1, head.boundingBox.y1,
295                 head.boundingBox.x2, head.boundingBox.y2);
296         if(head.version > fileversion)
297             fileversion = head.version;
298         if(!t)
299             box = head.boundingBox;
300         else {
301             if(head.boundingBox.x1 < box.x1)
302                 box.x1 = head.boundingBox.x1;
303             if(head.boundingBox.y1 < box.y1)
304                 box.y1 = head.boundingBox.y1;
305             if(head.boundingBox.x2 > box.x2)
306                 box.x2 = head.boundingBox.x2;
307             if(head.boundingBox.y2 > box.y2)
308                 box.y2 = head.boundingBox.y2;
309         }
310         logf("<verbose> New master bounding box is %d:%d:%d:%d\n",
311                 box.x1, box.y1,
312                 box.x2, box.y2);
313         fclose(fi);
314     }
315
316     /* we don't have a master, so we create one ourselves. */
317     *masterlength = (numslaves + 1) * 32 + strlength;
318     *masterdata = (u8*)malloc(*masterlength);
319     pos = *masterdata;
320     memcpy(pos, head, sizeof(head));
321     pos += sizeof(head);
322     *pos++ = fileversion;
323     fixpos = (u32*)pos;
324     *(u32*)pos = SWAP32(0x12345678); // to be overwritten
325     pos += 4;
326     writeRECT(&pos, &box);
327     *(u16*)pos = SWAP16(0x2000); // framerate
328     pos += 2;
329     *(u16*)pos = SWAP16(numslaves);
330     pos += 2;
331     for(t=0;t<numslaves;t++)
332     {
333         char buf[128];
334         int namelen;
335
336         if(1) {
337             sprintf(buf, "Frame%02d", t);
338             slave_name[t] = strdup(buf);
339         } 
340         namelen = strlen(slave_name[t]);
341
342         *(u16*)&pos[0] = SWAP16((u16)(TAGID_DEFINESPRITE<<6) + 6);
343         *(u16*)&pos[2] = SWAP16(t+1); //ID
344         *(u16*)&pos[4] = 0; // Frames
345         *(u16*)&pos[6] = 0; // TAG1
346         *(u16*)&pos[8] = SWAP16((u16)(TAGID_PLACEOBJECT2<<6) + 6 + namelen);
347         *(u16*)&pos[10]= SWAP16(34); //flags: id+name
348         *(u16*)&pos[11]= SWAP16(1+t); // depth
349         *(u16*)&pos[13]= SWAP16(t+1); // id
350         sprintf(&pos[15],slave_name[t]);
351         pos += 15 + namelen + 1;
352         if(!config.stack1 || t == numslaves-1) {
353             *(u16*)&pos[0]= SWAP16((u16)(TAGID_SHOWFRAME<<6) + 0);
354             pos += 2;
355         }
356         if(!config.stack)
357         if(t!=numslaves-1)
358         {
359             *(u16*)&pos[0]= SWAP16((u16)(TAGID_REMOVEOBJECT2<<6) + 2);
360             *(u16*)&pos[2]= SWAP16(1+t); // depth;
361             pos += 4;
362         }
363     }
364     *(u16*)pos = SWAP16(TAGID_END<<6 + 0);
365     *masterlength = pos - *masterdata;
366     *fixpos = SWAP32(*masterlength);
367 }
368
369 struct config_t config;
370 int main(int argn, char *argv[])
371 {
372     FILE*fi;
373     u8*masterdata;
374     unsigned int masterlength;
375     u8*slavedata;
376     unsigned int slavelength;
377     u8*newdata;
378     unsigned int newlength;
379     int t;
380
381     config.overlay = 0; 
382     config.antistream = 0; 
383     config.alloctest = 0;
384     config.cat = 0;
385     config.merge = 0;
386     config.clip = 0;
387     config.loglevel = 2; 
388     config.movex = 0;
389     config.movey = 0;
390     config.scalex = 1.0;
391     config.scaley = 1.0;
392     config.sizex = 0;
393     config.sizey = 0;
394     config.hassizex = 0;
395     config.hassizey = 0;
396     config.framerate = 0;
397     config.stack = 0;
398     config.stack1 = 0;
399     config.dummy = 0;
400
401     processargs(argn, argv);
402     initLog(0,-1,0,0,-1,config.loglevel);
403
404     if(config.merge && config.cat) {
405         logf("<error> Can't combine --cat and --merge");
406         exit(1);
407     }
408
409     if(config.stack) {
410
411         if(config.overlay) {
412             logf("<error> Can't combine -l and -t");
413             exit(1);
414         }
415         if(config.clip) {
416             logf("<error> Can't combine -c and -t");
417             exit(1);
418         }
419         logf("<verbose> (stacking) %d files found\n", numslaves);
420
421         makestackmaster(&masterdata,&masterlength);
422
423         logf("<verbose> Generated %d bytes of master data", masterlength);
424     }
425     else {
426         logf("<verbose> master entity %s (named \"%s\")\n", master_filename, master_name);
427         fi = fopen(master_filename, "rb");
428         if(!fi) {
429             fprintf(stderr, "Failed to open %s\n", master_filename);
430             return 1;
431         }
432         masterdata = fi_slurp(fi, &masterlength);
433         if(!masterdata) {
434             fprintf(stderr, "Failed to read from %s\n", master_filename);
435             return 1;
436         }
437         logf("<debug> Read %d bytes from masterfile\n", masterlength);
438         fclose(fi);
439     }
440
441     for(t=0;t<numslaves;t++) {
442             logf("<verbose> slave entity(%d) %s (%s \"%s\")\n", t+1, slave_filename[t], 
443                     slave_isframe[t]?"frame":"object", slave_name[t]);
444     }
445
446     if(config.dummy)
447     {
448         if(numslaves)
449         {
450             logf("<error> --dummy (-d) implies there are zero slave objects. You supplied %d.", numslaves);
451             exit(1);
452         }
453         numslaves = 1;
454         slave_filename[0] = "!!dummy!!";
455         slave_name[0] = "!!dummy!!";
456         slave_isframe[0] = 0;
457     }
458
459     if (config.alloctest)
460     {
461         int*bitmap = malloc(sizeof(int)*65536);
462         memset(bitmap, -1, 65536*sizeof(int));
463         memset(bitmap, 1, 101*sizeof(int));
464         swf_relocate(masterdata, masterlength, bitmap);
465         newdata = masterdata;
466         newlength = masterlength;
467         free(bitmap);
468     }
469     else
470     {
471         if (!numslaves)
472         {
473             logf("<error> You must have at least one slave entity.");
474             return 0;
475         }
476         for(t = 0; t < numslaves; t++)
477         {
478             config.movex = slave_movex[t];
479             config.movey = slave_movey[t];
480             config.scalex = slave_scalex[t];
481             config.scaley = slave_scaley[t];
482             config.isframe = slave_isframe[t];
483
484             logf("<notice> Combine [%s]%s and [%s]%s", master_name, master_filename,
485                     slave_name[t], slave_filename[t]);
486             if(!config.dummy)
487             {
488                 fi = fopen(slave_filename[t], "rb");
489                 if(!fi) {
490                     fprintf(stderr, "Failed to open %s\n", slave_filename[t]);
491                     return 1;
492                 }
493                 slavedata = fi_slurp(fi, &slavelength);
494                 if(!slavedata) {
495                     fprintf(stderr, "Failed to read from %s\n", slave_filename[t]);
496                     return 1;
497                 }
498                 logf("<debug> Read %d bytes from slavefile\n", slavelength);
499                 fclose(fi);
500             }
501             else
502             {
503                 slavedata = (u8*)malloc(16);
504                 slavedata[0] = 'F';
505                 slavedata[1] = 'W';
506                 slavedata[2] = 'S';
507                 slavedata[3] = 4; //version
508                 *(u32*)&slavedata[4] = SWAP32(14); // length
509                 slavedata[8] = 0; // boundingbox
510                 *(u16*)&slavedata[9] = SWAP16(0); // rate
511                 *(u16*)&slavedata[11] = SWAP16(0); // count
512                 *(u16*)&slavedata[13] = SWAP16(0); // end tag
513                 slavelength = 17;
514             }
515
516             newdata = combine(masterdata, masterlength, slave_name[t], slavedata, slavelength, &newlength);
517             if(!newdata) { 
518                 logf("<fatal> Aborting.");
519                 return 1;
520             }
521
522             free(masterdata);
523             masterdata = newdata;
524             masterlength = newlength;
525         }
526     }
527
528     logf("<debug> New File is %d bytes \n", newlength);
529     if(newdata && newlength) {
530         FILE*fi = fopen(outputname, "wb");
531         fi_dump(fi, newdata, newlength);
532         fclose(fi);
533     }
534     return 0;
535 }
536