131a9a0dccaf8116a95e090451e7e400593cd474
[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 "../lib/log.h"
16 #include "reloc.h"
17 #ifdef HAVE_ZLIB_H
18 #ifdef HAVE_LIBZ
19 #include "zlib.h"
20 #define _ZLIB_INCLUDED_
21 #endif
22 #endif
23
24 char * filename = 0;
25 char * destfilename = "output.swf";
26 int verbose = 3;
27
28 char* extractids = 0;
29 char* extractframes = 0;
30 char* extractjpegids = 0;
31 char* extractpngids = 0;
32 char extractmp3 = 0;
33
34 char* extractname = 0;
35
36 char hollow = 0;
37
38 int numextracts = 0;
39
40 struct options_t options[] =
41 {
42  {"o","output"},
43  {"w","hollow"},
44  {"v","verbose"},
45  {"i","id"},
46  {"j","jpegs"},
47  {"p","pngs"},
48  {"m","mp3"},
49  {"n","name"},
50  {"f","frame"},
51  {"V","version"},
52  {0,0}
53 };
54
55
56 int args_callback_option(char*name,char*val)
57 {
58     if(!strcmp(name, "V")) {
59         printf("swfextract - part of %s %s\n", PACKAGE, VERSION);
60         exit(0);
61     } 
62     else if(!strcmp(name, "o")) {
63         destfilename = val;
64         return 1;
65     } 
66     else if(!strcmp(name, "i")) {
67         extractids = val;
68         numextracts++;
69         if(extractname) {
70             fprintf(stderr, "You can only supply either name or id\n");
71             exit(1);
72         }
73         return 1;
74     } 
75     else if(!strcmp(name, "n")) {
76         extractname = val;
77         numextracts++;
78         if(extractids) {
79             fprintf(stderr, "You can only supply either name or id\n");
80             exit(1);
81         }
82         return 1;
83     } 
84     else if(!strcmp(name, "v")) {
85         verbose ++;
86         return 0;
87     } 
88     else if(!strcmp(name, "m")) {
89         extractmp3 = 1;
90         numextracts++;
91         return 0;
92     }
93     else if(!strcmp(name, "j")) {
94         if(extractjpegids) {
95             fprintf(stderr, "Only one --jpegs argument is allowed. (Try to use a range, e.g. -j 1,2,3)\n");
96             exit(1);
97         }
98         numextracts++;
99         extractjpegids = val;
100         return 1;
101     } 
102 #ifdef _ZLIB_INCLUDED_
103     else if(!strcmp(name, "p")) {
104         if(extractpngids) {
105             fprintf(stderr, "Only one --pngs argument is allowed. (Try to use a range, e.g. -p 1,2,3)\n");
106             exit(1);
107         }
108         numextracts++;
109         extractpngids = val;
110         return 1;
111     } 
112 #endif
113     else if(!strcmp(name, "f")) {
114         numextracts++;
115         extractframes = val;
116         return 1;
117     }
118     else if(!strcmp(name, "w")) {
119         hollow = 1;
120         return 0;
121     }
122     else {
123         printf("Unknown option: -%s\n", name);
124         return 0;
125     }
126
127     return 0;
128 }
129 int args_callback_longoption(char*name,char*val)
130 {
131     return args_long2shortoption(options, name, val);
132 }
133 void args_callback_usage(char*name)
134 {    
135     printf("Usage: %s [-v] [-n name] [-ijf ids] file.swf\n", name);
136     printf("\t-v , --verbose\t\t\t Be more verbose\n");
137     printf("\t-o , --output filename\t\t set output filename\n");
138     printf("\t-n , --name name\t\t instance name of the object to extract\n");
139     printf("\t-i , --id IDs\t\t\t ID of the object to extract\n");
140     printf("\t-j , --jpeg IDs\t\t\t IDs of the JPEG pictures to extract\n");
141 #ifdef _ZLIB_INCLUDED_
142     printf("\t-p , --pngs IDs\t\t\t IDs of the PNG pictures to extract\n");
143 #endif
144     printf("\t-m , --mp3\t\t\t Extract main mp3 stream\n");
145     printf("\t-f , --frame frames\t\t frame numbers to extract\n");
146     printf("\t-w , --hollow\t\t\t hollow mode: don't remove empty frames (use with -f)\n");
147     printf("\t-V , --version\t\t\t Print program version and exit\n");
148 }
149 int args_callback_command(char*name,char*val)
150 {
151     if(filename) {
152         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
153                  filename, name);
154     }
155     filename = name;
156     return 0;
157 }
158
159 U8 mainr,maing,mainb;
160 /* 1 = used, not expanded,
161    3 = used, expanded
162    5 = wanted, not expanded
163    7 = wanted, expanded
164  */
165 char used[65536];
166 TAG*tags[65536];
167 int changed;
168 char * tagused;
169
170 void idcallback(void*data)
171 {
172     if(!(used[GET16(data)]&1)) {
173         changed = 1;
174         used[GET16(data)] |= 1;
175     }
176 }
177
178 void enumerateIDs(TAG*tag, void(*callback)(void*))
179 {
180     U8*data;
181     int len = tag->len;
182     if(tag->len>=64) {
183         len += 6;
184         data = (U8*)malloc(len);
185         PUT16(data, (tag->id<<6)+63);
186         *(U8*)&data[2] = tag->len;
187         *(U8*)&data[3] = tag->len>>8;
188         *(U8*)&data[4] = tag->len>>16;
189         *(U8*)&data[5] = tag->len>>24;
190         memcpy(&data[6], tag->data, tag->len);
191     } else {
192         len += 2;
193         data = (U8*)malloc(len);
194         PUT16(data, (tag->id<<6)+tag->len);
195         memcpy(&data[2], tag->data, tag->len);
196     }
197     map_ids_mem(data, len, callback);
198 }
199
200 void extractTag(SWF*swf, char*filename)
201 {
202     SWF newswf;
203     TAG*desttag;
204     TAG*srctag;
205     RGBA rgb;
206     char sprite;
207     int f;
208     int t;
209     int tagnum;
210     int copy = 0;
211     memset(&newswf,0x00,sizeof(SWF));        // set global movie parameters
212
213     newswf.fileVersion    = swf->fileVersion;
214     newswf.frameRate      = swf->frameRate;
215     newswf.movieSize      = swf->movieSize;
216                 
217     newswf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
218     desttag = newswf.firstTag;
219     rgb.r = mainr;
220     rgb.g = maing;
221     rgb.b = mainb;
222     swf_SetRGB(desttag,&rgb);
223
224     do {
225        changed = 0;
226        for(t=0;t<65536;t++) {
227            if(used[t] && !(used[t]&2)) {
228              if(tags[t]->id==ST_DEFINESPRITE) {
229                  TAG*tag = tags[t];
230                  while(tag->id != ST_END)
231                  {
232                      enumerateIDs(tag, idcallback);
233                      tag = tag->next;
234                  }
235              }
236              else 
237                enumerateIDs(tags[t], idcallback);
238              used[t] |= 2;
239            }
240        }
241     }
242     while(changed);
243
244     srctag = swf->firstTag;
245     tagnum = 0;
246     sprite = 0;
247     while(srctag && (srctag->id || sprite)) {
248         int reset = 0;
249         if(!sprite) {
250             copy = 0;
251         }
252         if(srctag->id == ST_END) {
253             sprite = 0;
254         }
255         if(srctag->id == ST_DEFINESPRITE)
256             sprite = 1;
257         if(swf_isDefiningTag(srctag)) {
258             int id = swf_GetDefineID(srctag);
259             if(used[id]) 
260                 copy = 1;
261         } else 
262         if (((srctag->id == ST_PLACEOBJECT ||
263               srctag->id == ST_PLACEOBJECT2 ||
264               srctag->id == ST_STARTSOUND) && (used[swf_GetPlaceID(srctag)]&4) ) ||
265               (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)]) ||
266               (tagused[tagnum])) 
267         {
268                 if(copy == 0)
269                     reset = 1;
270                 copy = 1;
271         } 
272         if(srctag->id == ST_REMOVEOBJECT) {
273             if(!used[swf_GetPlaceID(srctag)])
274                 copy = 0;
275         }
276
277         if(copy) {
278             TAG*ttag = (TAG*)malloc(sizeof(TAG));
279             desttag = swf_InsertTag(desttag, srctag->id);
280             desttag->len = desttag->memsize = srctag->len;
281             desttag->data = malloc(srctag->len);
282             memcpy(desttag->data, srctag->data, srctag->len);
283             if(reset)
284                 copy = 0;
285         }
286         
287         srctag = srctag->next;
288         tagnum ++;
289     }
290     if(!extractframes && !hollow)
291         desttag = swf_InsertTag(desttag,ST_SHOWFRAME);
292
293     desttag = swf_InsertTag(desttag,ST_END);
294     
295     f = open(filename, O_TRUNC|O_WRONLY|O_CREAT, 0644);
296     if FAILED(swf_WriteSWF(f,&newswf)) fprintf(stderr,"WriteSWF() failed.\n");
297     close(f);
298
299     swf_FreeTags(&newswf);                       // cleanup
300 }
301
302 void listObjects(SWF*swf)
303 {
304     TAG*tag;
305     char first;
306     int t;
307     int frame = 0;
308     char*names[] = {"Shapes","MovieClips","JPEGs","PNGs","Sounds","Frames"};
309     printf("Objects in file %s:\n",filename);
310     for(t=0;t<6;t++) {
311         tag = swf->firstTag;
312         first = 1;
313         while(tag) {
314             char show = 0;
315             char text[80];
316             if(t == 0 &&
317                (tag->id == ST_DEFINESHAPE ||
318                 tag->id == ST_DEFINESHAPE2 ||
319                 tag->id == ST_DEFINESHAPE3)) {
320                 show = 1;
321                 sprintf(text,"%d", swf_GetDefineID(tag));
322             }
323
324             if(tag->id == ST_DEFINESPRITE) {
325                 if (t == 1)  {
326                     show = 1;
327                     sprintf(text,"%d", swf_GetDefineID(tag));
328                 }
329
330                 while(tag->id != ST_END)
331                     tag = tag->next;
332             }
333
334             if(t == 2 && (tag->id == ST_DEFINEBITS ||
335                 tag->id == ST_DEFINEBITSJPEG2 ||
336                 tag->id == ST_DEFINEBITSJPEG3)) {
337                 show = 1;
338                 sprintf(text,"%d", swf_GetDefineID(tag));
339             }
340
341             if(t == 3 && (tag->id == ST_DEFINEBITSLOSSLESS ||
342                 tag->id == ST_DEFINEBITSLOSSLESS2)) {
343                 show = 1;
344                 sprintf(text,"%d", swf_GetDefineID(tag));
345             }
346
347
348             if(t == 4 && (tag->id == ST_DEFINESOUND)) {
349                 show = 1;
350                 sprintf(text,"%d", swf_GetDefineID(tag));
351             }
352             
353             if(t == 5 && (tag->id == ST_SHOWFRAME)) {
354                 show = 1;
355                 sprintf(text,"%d", frame);
356                 frame ++;
357             }
358
359             if(show) {
360                 if(!first)
361                     printf(", ");
362                 else
363                     printf("%s: ", names[t]);
364                 printf("%s", text);
365                 first = 0;
366             }
367             tag=tag->next;
368         }
369         if(!first)
370             printf("\n");
371     }
372 }
373
374 U8*jpegtables = 0;
375 int jpegtablessize;
376
377 void handlejpegtables(TAG*tag)
378 {
379     if(tag->id == ST_JPEGTABLES) {
380         jpegtables = tag->data;
381         jpegtablessize = tag->len;
382     }
383 }
384
385 FILE* save_fopen(char* name, char* mode)
386 {
387     FILE*fi = fopen(name, mode);
388     if(!fi) {
389         fprintf(stderr, "Error: Couldn't open %s\n", name);
390         exit(1);
391     }
392     return fi;
393 }
394
395 int findjpegboundary(U8*data, int len)
396 {
397     int t;
398     int pos=-1;
399     for(t=0;t<len;t++) {
400         if(data[t  ]==0xff &&
401            data[t+1]==0xd9 &&
402            data[t+2]==0xff &&
403            data[t+3]==0xd8) {
404             pos = t;
405         }
406     }
407     return pos;
408 }
409
410 /* extract jpeg data out of a tag */
411 void handlejpeg(TAG*tag)
412 {
413     char name[80];
414     FILE*fi;
415     sprintf(name, "pic%d.jpeg", GET16(tag->data));
416     /* swf jpeg images have two streams, which both start with ff d8 and
417        end with ff d9. The following code handles sorting the middle
418        <ff d9 ff d8> bytes out, so that one stream remains */
419     if(tag->id == ST_DEFINEBITS && tag->len>2 && jpegtables) {
420         fi = save_fopen(name, "wb");
421         fwrite(jpegtables, 1, jpegtablessize-2, fi); //don't write end tag (ff,d8)
422         fwrite(&tag->data[2+2], tag->len-2-2, 1, fi); //don't write start tag (ff,d9)
423         fclose(fi);
424     }
425     if(tag->id == ST_DEFINEBITSJPEG2 && tag->len>2) {
426         int end = tag->len;
427         int pos = findjpegboundary(&tag->data[2], tag->len-2);
428         if(pos<0)
429             return;
430         pos+=2;
431         fi = save_fopen(name, "wb");
432         fwrite(&tag->data[2], pos-2, 1, fi);
433         fwrite(&tag->data[pos+4], end-(pos+4), 1, fi);
434         fclose(fi);
435     }
436     if(tag->id == ST_DEFINEBITSJPEG3 && tag->len>6) {
437         U32 end = GET32(&tag->data[2])+6;
438         int pos = findjpegboundary(&tag->data[6], tag->len-6);
439         if(pos<0)
440             return;
441         pos+=6;
442         fi = save_fopen(name, "wb");
443         fwrite(&tag->data[6], pos-6, 1, fi);
444         fwrite(&tag->data[pos+4], end-(pos+4), 1, fi);
445         fclose(fi);
446     }
447 }
448
449 #ifdef _ZLIB_INCLUDED_
450 static U32 mycrc32;
451
452 static U32*crc32_table = 0;
453 static void make_crc32_table(void)
454 {
455   int t;
456   if(crc32_table) 
457       return;
458   crc32_table = (U32*)malloc(1024);
459
460   for (t = 0; t < 256; t++) {
461     U32 c = t;
462     int s;
463     for (s = 0; s < 8; s++) {
464       c = (0xedb88320L*(c&1)) ^ (c >> 1);
465     }
466     crc32_table[t] = c;
467   }
468 }
469 static void png_write_byte(FILE*fi, U8 byte)
470 {
471     fwrite(&byte,1,1,fi);
472     mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
473 }
474 static void png_start_chunk(FILE*fi, char*type, int len)
475 {
476     U8 mytype[4]={0,0,0,0};
477     U32 mylen = REVERSESWAP32(len);
478     memcpy(mytype,type,strlen(type));
479     fwrite(&mylen, 4, 1, fi);
480     mycrc32=0xffffffff;
481     png_write_byte(fi,mytype[0]);
482     png_write_byte(fi,mytype[1]);
483     png_write_byte(fi,mytype[2]);
484     png_write_byte(fi,mytype[3]);
485 }
486 static void png_write_bytes(FILE*fi, U8*bytes, int len)
487 {
488     int t;
489     for(t=0;t<len;t++)
490         png_write_byte(fi,bytes[t]);
491 }
492 static void png_write_dword(FILE*fi, U32 dword)
493 {
494     png_write_byte(fi,dword>>24);
495     png_write_byte(fi,dword>>16);
496     png_write_byte(fi,dword>>8);
497     png_write_byte(fi,dword);
498 }
499 static void png_end_chunk(FILE*fi)
500 {
501     U32 tmp = REVERSESWAP32((mycrc32^0xffffffff));
502     fwrite(&tmp,4,1,fi);
503 }
504
505
506 /* extract a lossless image (png) out of a tag 
507    This routine was originally meant to be a one-pager. I just
508    didn't know png is _that_ much fun. :) -mk
509  */
510 void handlelossless(TAG*tag)
511 {
512     char name[80];
513     FILE*fi;
514     int width, height;
515     int crc;
516     int id;
517     int t;
518     U8 bpp = 1;
519     U8 format;
520     U8 tmp;
521     U8* data=0;
522     U8* data2=0;
523     U8* data3=0;
524     U32 datalen;
525     U32 datalen2;
526     U32 datalen3;
527     U8 head[] = {137,80,78,71,13,10,26,10};
528     int cols;
529     char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
530     RGBA* palette;
531     int pos;
532     int error;
533     U32 tmp32;
534
535     make_crc32_table();
536
537     if(tag->id != ST_DEFINEBITSLOSSLESS &&
538        tag->id != ST_DEFINEBITSLOSSLESS2)
539         return;
540
541     id =swf_GetU16(tag);
542     format = swf_GetU8(tag);
543     if(format == 3) bpp = 8;
544     if(format == 4) bpp = 16;
545     if(format == 5) bpp = 32;
546     if(format!=3 && format!=5) {
547         if(format==4)
548         fprintf(stderr, "Can't handle 16-bit palette images yet (image %d)\n",id);
549         else 
550         fprintf(stderr, "Unknown image type %d in image %d\n", format, id);
551         return;
552     }
553     width = swf_GetU16(tag);
554     height = swf_GetU16(tag);
555     if(format == 3) cols = swf_GetU8(tag) + 1;
556 // this is what format means according to the flash specification. (which is
557 // clearly wrong)
558 //    if(format == 4) cols = swf_GetU16(tag) + 1;
559 //    if(format == 5) cols = swf_GetU32(tag) + 1;
560     else cols = 0;
561
562     logf("<verbose> Width %d", width);
563     logf("<verbose> Height %d", height);
564     logf("<verbose> Format %d", format);
565     logf("<verbose> Cols %d", cols);
566     logf("<verbose> Bpp %d", bpp);
567
568     datalen = (width*height*bpp/8+cols*8);
569     do {
570         if(data)
571             free(data);
572         datalen+=4096;
573         data = malloc(datalen);
574         error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
575     } while(error == Z_BUF_ERROR);
576     if(error != Z_OK) {
577         fprintf(stderr, "Zlib error %d (image %d)\n", error, id);
578         return;
579     }
580     logf("<verbose> Uncompressed image is %d bytes (%d colormap)", datalen, (3+alpha)*cols);
581     pos = 0;
582     datalen2 = datalen;
583     data2 = malloc(datalen2);
584     palette = (RGBA*)malloc(cols*sizeof(RGBA));
585
586     for(t=0;t<cols;t++) {
587         palette[t].r = data[pos++];
588         palette[t].g = data[pos++];
589         palette[t].b = data[pos++];
590         if(alpha) {
591             palette[t].a = data[pos++];
592         }
593     }
594
595     sprintf(name, "pic%d.png", id);
596     fi = save_fopen(name, "wb");
597     fwrite(head,sizeof(head),1,fi);     
598
599     png_start_chunk(fi, "IHDR", 13);
600      png_write_dword(fi,width);
601      png_write_dword(fi,height);
602      png_write_byte(fi,8);
603      if(format == 3)
604      png_write_byte(fi,3); //indexed
605      else if(format == 5)
606      png_write_byte(fi,2); //rgb
607      else return;
608
609      png_write_byte(fi,0); //compression mode
610      png_write_byte(fi,0); //filter mode
611      png_write_byte(fi,0); //interlace mode
612     png_end_chunk(fi);
613    
614     if(format == 3) {
615         png_start_chunk(fi, "PLTE", 768);
616          for(t=0;t<256;t++) {
617              png_write_byte(fi,palette[t].r);
618              png_write_byte(fi,palette[t].g);
619              png_write_byte(fi,palette[t].b);
620          }
621         png_end_chunk(fi);
622     }
623     {
624         int pos2 = 0;
625         int x,y;
626         int srcwidth = width * (bpp/8);
627         datalen3 = width*height*4;
628         data3 = (U8*)malloc(datalen3);
629         for(y=0;y<height;y++)
630         {
631            data3[pos2++]=0; //filter type
632            if(bpp==32) {
633             // 32 bit to 24 bit "conversion"
634             for(x=0;x<width;x++) {
635                 data3[pos2++]=data[pos+1];
636                 data3[pos2++]=data[pos+2];
637                 data3[pos2++]=data[pos+3];
638                 pos+=4; //ignore padding byte
639             }
640            }
641            else {
642                 for(x=0;x<srcwidth;x++)
643                     data3[pos2++]=data[pos++];
644            }
645            
646            pos+=((srcwidth+3)&~3)-srcwidth; //align
647         }
648         datalen3=pos2;
649     }
650
651     if(compress (data2, &datalen2, data3, datalen3) != Z_OK) {
652         fprintf(stderr, "zlib error in pic %d\n", id);
653         return;
654     }
655     logf("<verbose> Compressed data is %d bytes", datalen2);
656     png_start_chunk(fi, "IDAT", datalen2);
657     png_write_bytes(fi,data2,datalen2);
658     png_end_chunk(fi);
659     png_start_chunk(fi, "IEND", 0);
660     png_end_chunk(fi);
661
662     free(data);
663     free(data2);
664     free(data3);
665 }
666 #endif
667
668 FILE*mp3file;
669 void handlesoundstream(TAG*tag)
670 {
671     char*filename = "output.mp3";
672     if(numextracts==1) {
673         filename = destfilename;
674         if(!strcmp(filename,"output.swf"))
675             filename = "output.mp3";
676     }
677     switch(tag->id) {
678         case ST_SOUNDSTREAMHEAD:
679             if((tag->data[1]&0x30) == 0x20) { //mp3 compression
680                 mp3file = fopen(filename, "wb");
681                 logf("<notice> Writing mp3 data to %s",filename);
682             }
683             else
684                 logf("<error> Soundstream is not mp3");
685         break;
686         case ST_SOUNDSTREAMHEAD2:
687             if((tag->data[1]&0x30) == 0x20) {//mp3 compression
688                 mp3file = fopen("mainstream.mp3", "wb");
689                 logf("<notice> Writing mp3 data to %s",filename);
690             }
691             else
692                 logf("<error> Soundstream is not mp3 (2)");
693         break;
694         case ST_SOUNDSTREAMBLOCK:
695             if(mp3file)
696                 fwrite(&tag->data[4],tag->len-4,1,mp3file);
697         break;
698     }
699 }
700
701 int main (int argc,char ** argv)
702
703     TAG*tag;
704     SWF swf;
705     int f;
706     int found = 0;
707     int frame = 0;
708     int tagnum = 0;
709     char depths[65536];
710     char listavailable = 0;
711     processargs(argc, argv);
712
713     if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids
714         && !extractmp3)
715         listavailable = 1;
716
717     if(!filename)
718     {
719         fprintf(stderr, "You must supply a filename.\n");
720         return 1;
721     }
722     initLog(0,-1,0,0,-1, verbose);
723
724     f = open(filename,O_RDONLY);
725
726     if (f<0)
727     { 
728         perror("Couldn't open file: ");
729         exit(1);
730     }
731     if (swf_ReadSWF(f,&swf) < 0)
732     { 
733         fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
734         close(f);
735         exit(1);
736     }
737     close(f);
738
739     if(listavailable) {
740         listObjects(&swf);
741         swf_FreeTags(&swf);
742         return 0;
743     }
744
745     tag = swf.firstTag;
746     tagnum = 0;
747     while(tag) {
748         tagnum ++;
749         tag = tag->next;
750     }
751
752     tagused = (char*)malloc(tagnum);
753     memset(tagused, 0, tagnum);
754     memset(used, 0, 65536);
755     memset(depths, 0, 65536);
756
757     tag = swf.firstTag;
758     tagnum = 0;
759     while(tag) {
760         if(swf_isAllowedSpriteTag(tag)) {
761             int write = 0;
762             if(extractframes && is_in_range(frame, extractframes)) {
763                 write = 1;
764                 if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) {
765                     depths[swf_GetDepth(tag)] = 1;
766                 }
767                 if(tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) {
768                     int depth = swf_GetDepth(tag);
769                     if(!depths[depth]) 
770                         write = 0;
771                     depths[swf_GetDepth(tag)] = 0;
772                 }
773             } else {
774                 if((tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) && 
775                     (depths[swf_GetDepth(tag)]) && hollow) {
776                     write = 1;
777                     depths[swf_GetDepth(tag)] = 0;
778                 }
779             }
780             if(write) {
781                 enumerateIDs(tag, idcallback);
782                 found = 1;
783                 tagused[tagnum] = 1;
784             }
785         }
786
787         if(tag->id == ST_SOUNDSTREAMHEAD ||
788            tag->id == ST_SOUNDSTREAMHEAD2 ||
789            tag->id == ST_SOUNDSTREAMBLOCK) {
790             handlesoundstream(tag);
791         }
792
793         if(tag->id == ST_JPEGTABLES)
794             handlejpegtables(tag);
795
796         if(swf_isDefiningTag(tag)) {
797             int id = swf_GetDefineID(tag);
798             tags[id] = tag;
799             if(extractids && is_in_range(id, extractids)) {
800                 used[id] = 5;
801                 found = 1;
802             }
803             if(extractjpegids && is_in_range(id, extractjpegids)) {
804                 handlejpeg(tag);
805             }
806 #ifdef _ZLIB_INCLUDED_
807             if(extractpngids && is_in_range(id, extractpngids)) {
808                 handlelossless(tag);
809             }
810 #endif
811         }
812         else if (tag->id == ST_SETBACKGROUNDCOLOR) {
813             mainr = tag->data[0];
814             maing = tag->data[1];
815             mainb = tag->data[2];
816         }
817         else if(tag->id == ST_PLACEOBJECT2) {
818             char*name = swf_GetName(tag);
819             if(name && extractname && !strcmp(name, extractname)) {
820                 int id = swf_GetPlaceID(tag); 
821                 used[id] = 5;
822                 found = 1;
823                 tagused[tagnum] = 1;
824                 depths[swf_GetDepth(tag)] = 1;
825             }
826         }
827         else if(tag->id == ST_SHOWFRAME) {
828             frame ++;
829             if(hollow) {
830                 tagused[tagnum] = 1;
831                 found = 1;
832             }
833         }
834         
835         if(tag->id == ST_DEFINESPRITE) {
836             while(tag->id != ST_END) { 
837                 tag = tag->next;
838                 tagnum ++;
839             }
840         }
841         tag = tag->next;
842         tagnum ++;
843     }
844     if (found)
845         extractTag(&swf, destfilename);
846
847     if(mp3file)
848         fclose(mp3file);
849
850     swf_FreeTags(&swf);
851     return 0;
852 }
853