* Added support for extracting frames and multiple ids.
[swftools.git] / src / swfextract.c
1 /* swfextract.c
2    Allows to extract parts of the swf into a new file.
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 <unistd.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include "../lib/rfxswf.h"
14 #include "../lib/args.h"
15 #include "reloc.h"
16
17 char * filename = 0;
18 char * destfilename = "output.swf";
19 int verbose = 2;
20
21 char* extractids = 0;
22 char* extractframes = 0;
23
24 char* extractname = 0;
25
26 char hollow = 0;
27
28 struct options_t options[] =
29 {
30  {"o","output"},
31  {"w","hollow"},
32  {"v","verbose"},
33  {"i","id"},
34  {"n","name"},
35  {"f","frame"},
36  {"V","version"},
37  {0,0}
38 };
39
40 int args_callback_option(char*name,char*val)
41 {
42     if(!strcmp(name, "V")) {
43         printf("swfextract - part of %s %s\n", PACKAGE, VERSION);
44         exit(0);
45     } 
46     else if(!strcmp(name, "o")) {
47         destfilename = val;
48         return 1;
49     } 
50     else if(!strcmp(name, "i")) {
51         extractids = val;
52         if(extractname) {
53             fprintf(stderr, "You can only supply either name or id\n");
54             exit(1);
55         }
56         return 1;
57     } 
58     else if(!strcmp(name, "n")) {
59         extractname = val;
60         if(extractids) {
61             fprintf(stderr, "You can only supply either name or id\n");
62             exit(1);
63         }
64         return 1;
65     } 
66     else if(!strcmp(name, "v")) {
67         verbose ++;
68         return 0;
69     } 
70     else if(!strcmp(name, "f")) {
71         extractframes = val;
72         return 1;
73     }
74     else if(!strcmp(name, "w")) {
75         hollow = 1;
76         return 0;
77     }
78     else {
79         printf("Unknown option: -%s\n", name);
80         return 0;
81     }
82
83     return 0;
84 }
85 int args_callback_longoption(char*name,char*val)
86 {
87     return args_long2shortoption(options, name, val);
88 }
89 void args_callback_usage(char*name)
90 {    
91     printf("Usage: %s [-v] [-i id] file.swf\n", name);
92     printf("\t-v , --verbose\t\t\t Be more verbose\n");
93     printf("\t-o , --output filename\t\t set output filename\n");
94     printf("\t-i , --id ID\t\t\t ID of the object to extract\n");
95     printf("\t-n , --name name\t\t instance name of the object to extract\n");
96     printf("\t-f , --frame frame\t\t frame number to extract\n");
97     printf("\t-w , --hollow\t\t\t hollow mode: don't remove empty frames\n");
98     printf("\t-V , --version\t\t\t Print program version and exit\n");
99 }
100 int args_callback_command(char*name,char*val)
101 {
102     if(filename) {
103         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
104                  filename, name);
105     }
106     filename = name;
107     return 0;
108 }
109
110 U8 mainr,maing,mainb;
111 /* 1 = used, not expanded,
112    3 = used, expanded
113    5 = wanted, not expanded
114    7 = wanted, expanded
115  */
116 char used[65536];
117 TAG*tags[65536];
118 int changed;
119 char * tagused;
120
121 void idcallback(void*data)
122 {
123     if(!(used[*(U16*)data]&1)) {
124         changed = 1;
125         used[*(U16*)data] |= 1;
126     }
127 }
128
129 void enumerateIDs(TAG*tag, void(*callback)(void*))
130 {
131     U8*data;
132     int len = tag->len;
133     if(tag->len>=64) {
134         len += 6;
135         data = (U8*)malloc(len);
136         *(U16*)data = (tag->id<<6)+63;
137         *(U32*)&data[2] = tag->len;
138         memcpy(&data[6], tag->data, tag->len);
139     } else {
140         len += 2;
141         data = (U8*)malloc(len);
142         *(U16*)data = (tag->id<<6)+tag->len;
143         memcpy(&data[2], tag->data, tag->len);
144     }
145     map_ids_mem(data, len, callback);
146 }
147
148 void extractTag(SWF*swf, char*filename)
149 {
150     SWF newswf;
151     TAG*desttag;
152     TAG*srctag;
153     RGBA rgb;
154     char sprite;
155     int f;
156     int t;
157     int tagnum;
158     int copy = 0;
159     memset(&newswf,0x00,sizeof(SWF));        // set global movie parameters
160
161     newswf.fileVersion    = swf->fileVersion;
162     newswf.frameRate      = swf->frameRate;
163     newswf.movieSize      = swf->movieSize;
164                 
165     newswf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
166     desttag = newswf.firstTag;
167     rgb.r = mainr;
168     rgb.g = maing;
169     rgb.b = mainb;
170     swf_SetRGB(desttag,&rgb);
171
172     do {
173        changed = 0;
174        for(t=0;t<65536;t++) {
175            if(used[t] && !(used[t]&2)) {
176              if(tags[t]->id==ST_DEFINESPRITE) {
177                  TAG*tag = tags[t];
178                  while(tag->id != ST_END)
179                  {
180                      enumerateIDs(tag, idcallback);
181                      tag = tag->next;
182                  }
183              }
184              else 
185                enumerateIDs(tags[t], idcallback);
186              used[t] |= 2;
187            }
188        }
189     }
190     while(changed);
191
192     srctag = swf->firstTag;
193     tagnum = 0;
194     sprite = 0;
195     while(srctag && (srctag->id || sprite)) {
196         int reset = 0;
197         if(!sprite) {
198             copy = 0;
199         }
200         if(srctag->id == ST_END) {
201             sprite = 0;
202         }
203         if(srctag->id == ST_DEFINESPRITE)
204             sprite = 1;
205         if(swf_isDefiningTag(srctag)) {
206             int id = swf_GetDefineID(srctag);
207             if(used[id]) 
208                 copy = 1;
209         } else 
210         if (((srctag->id == ST_PLACEOBJECT ||
211               srctag->id == ST_PLACEOBJECT2 ||
212               srctag->id == ST_STARTSOUND) && (used[swf_GetPlaceID(srctag)]&4) ) ||
213               (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)]) ||
214               (tagused[tagnum])) 
215         {
216                 if(copy == 0)
217                     reset = 1;
218                 copy = 1;
219         } 
220         if(srctag->id == ST_REMOVEOBJECT) {
221             if(!used[swf_GetPlaceID(srctag)])
222                 copy = 0;
223         }
224
225         if(copy) {
226             TAG*ttag = (TAG*)malloc(sizeof(TAG));
227             desttag = swf_InsertTag(desttag, srctag->id);
228             desttag->len = desttag->memsize = srctag->len;
229             desttag->data = malloc(srctag->len);
230             memcpy(desttag->data, srctag->data, srctag->len);
231             if(reset)
232                 copy = 0;
233         }
234         
235         srctag = srctag->next;
236         tagnum ++;
237     }
238     if(!extractframes && !hollow)
239         desttag = swf_InsertTag(desttag,ST_SHOWFRAME);
240
241     desttag = swf_InsertTag(desttag,ST_END);
242     
243     f = open(filename, O_TRUNC|O_WRONLY|O_CREAT, 0644);
244     if FAILED(swf_WriteSWF(f,&newswf)) fprintf(stderr,"WriteSWF() failed.\n");
245     close(f);
246
247     swf_FreeTags(&newswf);                       // cleanup
248 }
249
250 void listObjects(SWF*swf)
251 {
252     TAG*tag;
253     char first;
254     int t;
255     int frame = 0;
256     char*names[] = {"Shapes","MovieClips","Bitmaps","Sounds","Frames"};
257     printf("Objects in file %s:\n",filename);
258     for(t=0;t<5;t++) {
259         tag = swf->firstTag;
260         first = 1;
261         while(tag) {
262             char show = 0;
263             char text[80];
264             if(t == 0 &&
265                (tag->id == ST_DEFINESHAPE ||
266                 tag->id == ST_DEFINESHAPE2 ||
267                 tag->id == ST_DEFINESHAPE3)) {
268                 show = 1;
269                 sprintf(text,"%d", swf_GetDefineID(tag));
270             }
271
272             if(tag->id == ST_DEFINESPRITE) {
273                 if (t == 1)  {
274                     show = 1;
275                     sprintf(text,"%d", swf_GetDefineID(tag));
276                 }
277
278                 while(tag->id != ST_END)
279                     tag = tag->next;
280             }
281
282             if(t == 2 && (tag->id == ST_DEFINEBITSLOSSLESS ||
283                 tag->id == ST_DEFINEBITSJPEG2 ||
284                 tag->id == ST_DEFINEBITSLOSSLESS2 ||
285                 tag->id == ST_DEFINEBITSJPEG3)) {
286                 show = 1;
287                 sprintf(text,"%d", swf_GetDefineID(tag));
288             }
289
290             if(t == 3 && (tag->id == ST_DEFINESOUND)) {
291                 show = 1;
292                 sprintf(text,"%d", swf_GetDefineID(tag));
293             }
294             
295             if(t == 4 && (tag->id == ST_SHOWFRAME)) {
296                 show = 1;
297                 sprintf(text,"%d", frame);
298                 frame ++;
299             }
300
301             if(show) {
302                 if(!first)
303                     printf(", ");
304                 else
305                     printf("%s: ", names[t]);
306                 printf("%s", text);
307                 first = 0;
308             }
309             tag=tag->next;
310         }
311         if(!first)
312             printf("\n");
313     }
314 }
315
316 int main (int argc,char ** argv)
317
318     TAG*tag;
319     SWF swf;
320     int f;
321     int found = 0;
322     int frame = 0;
323     int tagnum = 0;
324     char depths[65536];
325     char listavailable = 0;
326     processargs(argc, argv);
327
328     if(!extractframes && !extractids && ! extractname)
329         listavailable = 1;
330
331     if(!filename)
332     {
333         fprintf(stderr, "You must supply a filename.\n");
334         return 1;
335     }
336     initLog(0,-1,0,0,-1, verbose);
337
338     f = open(filename,O_RDONLY);
339
340     if (f<0)
341     { 
342         perror("Couldn't open file: ");
343         exit(1);
344     }
345     if (swf_ReadSWF(f,&swf) < 0)
346     { 
347         fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
348         close(f);
349         exit(1);
350     }
351     close(f);
352
353     if(listavailable) {
354         listObjects(&swf);
355         swf_FreeTags(&swf);
356         return 0;
357     }
358
359     tag = swf.firstTag;
360     tagnum = 0;
361     while(tag) {
362         tagnum ++;
363         tag = tag->next;
364     }
365
366     tagused = (char*)malloc(tagnum);
367     memset(tagused, 0, tagnum);
368     memset(used, 0, 65536);
369     memset(depths, 0, 65536);
370
371     tag = swf.firstTag;
372     tagnum = 0;
373     while(tag) {
374         if(swf_isAllowedSpriteTag(tag)) {
375             int write = 0;
376             if(extractframes && is_in_range(frame, extractframes)) {
377                 write = 1;
378                 if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) {
379                     depths[swf_GetDepth(tag)] = 1;
380                 }
381                 if(tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) {
382                     int depth = swf_GetDepth(tag);
383                     if(!depths[depth]) 
384                         write = 0;
385                     depths[swf_GetDepth(tag)] = 0;
386                 }
387             } else {
388                 if((tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) && 
389                     (depths[swf_GetDepth(tag)]) && hollow) {
390                     write = 1;
391                     depths[swf_GetDepth(tag)] = 0;
392                 }
393             }
394             if(write) {
395                 enumerateIDs(tag, idcallback);
396                 found = 1;
397                 tagused[tagnum] = 1;
398             }
399         }
400
401         if(swf_isDefiningTag(tag)) {
402             int id = swf_GetDefineID(tag);
403             tags[id] = tag;
404             if(extractids && is_in_range(id, extractids)) {
405                 used[id] = 5;
406                 found = 1;
407             }
408         }
409         else if (tag->id == ST_SETBACKGROUNDCOLOR) {
410             mainr = tag->data[0];
411             maing = tag->data[1];
412             mainb = tag->data[2];
413         }
414         else if(tag->id == ST_PLACEOBJECT2) {
415             char*name = swf_GetName(tag);
416             if(name && extractname && !strcmp(name, extractname)) {
417                 int id = swf_GetPlaceID(tag); 
418                 used[id] = 5;
419                 found = 1;
420                 tagused[tagnum] = 1;
421                 depths[swf_GetDepth(tag)] = 1;
422             }
423         }
424         else if(tag->id == ST_SHOWFRAME) {
425             frame ++;
426             if(hollow) {
427                 tagused[tagnum] = 1;
428                 found = 1;
429             }
430         }
431         
432         if(tag->id == ST_DEFINESPRITE) {
433             while(tag->id != ST_END) { 
434                 tag = tag->next;
435                 tagnum ++;
436             }
437         }
438         tag = tag->next;
439         tagnum ++;
440     }
441     if (found)
442         extractTag(&swf, destfilename);
443
444     swf_FreeTags(&swf);
445     return 0;
446 }
447