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