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