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