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