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