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