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