applied swfextract binary resource patch from Paul Fitzpatrick
[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 program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
21
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <fcntl.h>
25 #include "../lib/rfxswf.h"
26 #include "../lib/args.h"
27 #include "../lib/log.h"
28 #ifdef HAVE_ZLIB_H
29 #ifdef HAVE_LIBZ
30 #include "zlib.h"
31 #define _ZLIB_INCLUDED_
32 #endif
33 #endif
34
35 char * filename = 0;
36 char * destfilename = "output.swf";
37 int verbose = 3;
38
39 char* extractids = 0;
40 char* extractframes = 0;
41 char* extractjpegids = 0;
42 char* extractfontids = 0;
43 char* extractpngids = 0;
44 char* extractsoundids = 0;
45 char* extractbinaryids = 0;
46 char extractmp3 = 0;
47
48 char* extractname = 0;
49
50 char hollow = 0;
51 char originalplaceobjects = 0;
52 char movetozero = 0;
53
54 int numextracts = 0;
55
56 struct options_t options[] =
57 {
58  {"o","output"},
59  {"w","hollow"},
60  {"v","verbose"},
61  {"i","id"},
62  {"j","jpegs"},
63  {"p","pngs"},
64  {"P","placeobject"},
65  {"0","movetozero"},
66  {"m","mp3"},
67  {"s","sound"},
68  {"n","name"},
69  {"f","frame"},
70  {"F","font"},
71  {"V","version"},
72  {"b","binary"},
73  {0,0}
74 };
75
76
77 int args_callback_option(char*name,char*val)
78 {
79     if(!strcmp(name, "V")) {
80         printf("swfextract - part of %s %s\n", PACKAGE, VERSION);
81         exit(0);
82     } 
83     else if(!strcmp(name, "o")) {
84         destfilename = val;
85         return 1;
86     } 
87     else if(!strcmp(name, "i")) {
88         extractids = val;
89         numextracts++;
90         if(extractname) {
91             fprintf(stderr, "You can only supply either name or id\n");
92             exit(1);
93         }
94         return 1;
95     } 
96     else if(!strcmp(name, "n")) {
97         extractname = val;
98         numextracts++;
99         if(extractids) {
100             fprintf(stderr, "You can only supply either name or id\n");
101             exit(1);
102         }
103         return 1;
104     } 
105     else if(!strcmp(name, "v")) {
106         verbose ++;
107         return 0;
108     } 
109     else if(!strcmp(name, "m")) {
110         extractmp3 = 1;
111         numextracts++;
112         return 0;
113     }
114     else if(!strcmp(name, "j")) {
115         if(extractjpegids) {
116             fprintf(stderr, "Only one --jpegs argument is allowed. (Try to use a range, e.g. -j 1,2,3)\n");
117             exit(1);
118         }
119         /* TODO: count number of IDs in val range */
120         numextracts++;
121         extractjpegids = val;
122         return 1;
123     } 
124     else if(!strcmp(name, "F")) {
125         if(extractfontids) {
126             fprintf(stderr, "Only one --font argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n");
127             exit(1);
128         }
129         numextracts++;
130         extractfontids = val;
131         return 1;
132     } 
133     else if(!strcmp(name, "s")) {
134         if(extractsoundids) {
135             fprintf(stderr, "Only one --sound argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n");
136             exit(1);
137         }
138         numextracts++;
139         extractsoundids = val;
140         return 1;
141     } 
142     else if(!strcmp(name, "b")) {
143         if(extractbinaryids) {
144             fprintf(stderr, "Only one --binary argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n");
145             exit(1);
146         }
147         numextracts++;
148         extractbinaryids = val;
149         return 1;
150     } 
151 #ifdef _ZLIB_INCLUDED_
152     else if(!strcmp(name, "p")) {
153         if(extractpngids) {
154             fprintf(stderr, "Only one --png argument is allowed. (Try to use a range, e.g. -p 1,2,3)\n");
155             exit(1);
156         }
157         numextracts++;
158         extractpngids = val;
159         return 1;
160     } 
161 #endif
162     else if(!strcmp(name, "f")) {
163         numextracts++;
164         extractframes = val;
165         return 1;
166     }
167     else if(!strcmp(name, "P")) {
168         originalplaceobjects = 1;
169         return 0;
170     }
171     else if(!strcmp(name, "0")) {
172         movetozero = 1;
173         return 0;
174     }
175     else if(!strcmp(name, "w")) {
176         hollow = 1;
177         return 0;
178     }
179     else {
180         printf("Unknown option: -%s\n", name);
181         exit(1);
182     }
183
184     return 0;
185 }
186 int args_callback_longoption(char*name,char*val)
187 {
188     return args_long2shortoption(options, name, val);
189 }
190 void args_callback_usage(char*name)
191 {    
192     printf("Usage: %s [-v] [-n name] [-ijf ids] file.swf\n", name);
193     printf("\t-v , --verbose\t\t\t Be more verbose\n");
194     printf("\t-o , --output filename\t\t set output filename\n");
195     printf("\t-V , --version\t\t\t Print program version and exit\n\n");
196     printf("SWF Subelement extraction:\n");
197     printf("\t-n , --name name\t\t instance name of the object (SWF Define) to extract\n");
198     printf("\t-i , --id ID\t\t\t ID of the object, shape or movieclip to extract\n");
199     printf("\t-f , --frame frames\t\t frame numbers to extract\n");
200     printf("\t-w , --hollow\t\t\t hollow mode: don't remove empty frames\n"); 
201     printf("\t             \t\t\t (use with -f)\n");
202     printf("\t-P , --placeobject\t\t\t Insert original placeobject into output file\n"); 
203     printf("\t             \t\t\t (use with -i)\n");
204     printf("SWF Font/Text extraction:\n");
205     printf("\t-F , --font ID\t\t\t Extract font(s)\n");
206     printf("Picture extraction:\n");
207     printf("\t-j , --jpeg ID\t\t\t Extract JPEG picture(s)\n");
208 #ifdef _ZLIB_INCLUDED_
209     printf("\t-p , --pngs ID\t\t\t Extract PNG picture(s)\n");
210 #endif
211     printf("\n");
212     printf("Sound extraction:\n");
213     printf("\t-m , --mp3\t\t\t Extract main mp3 stream\n");
214     printf("\t-s , --sound ID\t\t\t Extract Sound(s)\n");
215 }
216 int args_callback_command(char*name,char*val)
217 {
218     if(filename) {
219         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
220                  filename, name);
221     }
222     filename = name;
223     return 0;
224 }
225
226 U8 mainr,maing,mainb;
227 /* 1 = used, not expanded,
228    3 = used, expanded
229    5 = wanted, not expanded
230    7 = wanted, expanded
231  */
232 char used[65536];
233 TAG*tags[65536];
234 int changed;
235 char * tagused;
236 int extractname_id = -1;
237
238 void idcallback(void*data)
239 {
240     if(!(used[GET16(data)]&1)) {
241         changed = 1;
242         used[GET16(data)] |= 1;
243     }
244 }
245
246 void enumerateIDs(TAG*tag, void(*callback)(void*))
247 {
248 /*    U8*data;
249     int len = tag->len;
250     if(tag->len>=64) {
251         len += 6;
252         data = (U8*)malloc(len);
253         PUT16(data, (tag->id<<6)+63);
254         *(U8*)&data[2] = tag->len;
255         *(U8*)&data[3] = tag->len>>8;
256         *(U8*)&data[4] = tag->len>>16;
257         *(U8*)&data[5] = tag->len>>24;
258         memcpy(&data[6], tag->data, tag->len);
259     } else {
260         len += 2;
261         data = (U8*)malloc(len);
262         PUT16(data, (tag->id<<6)+tag->len);
263         memcpy(&data[2], tag->data, tag->len);
264     }
265     map_ids_mem(data, len, callback);
266  */
267     int num = swf_GetNumUsedIDs(tag);
268     int *ptr = malloc(sizeof(int)*num);
269     int t;
270     swf_GetUsedIDs(tag, ptr);
271     for(t=0;t<num;t++)
272         callback(&tag->data[ptr[t]]);
273 }
274
275 void moveToZero(TAG*tag)
276 {
277     if(!swf_isPlaceTag(tag))
278         return;
279     SWFPLACEOBJECT obj;
280     swf_GetPlaceObject(tag, &obj);
281     obj.matrix.tx = 0;
282     obj.matrix.ty = 0;
283     swf_ResetTag(tag, tag->id);
284     swf_SetPlaceObject(tag, &obj);
285 }
286
287 void extractTag(SWF*swf, char*filename)
288 {
289     SWF newswf;
290     TAG*desttag;
291     TAG*srctag;
292     RGBA rgb;
293     SRECT objectbbox;
294     char sprite;
295     int f;
296     int t;
297     int tagnum;
298     int copy = 0;
299     memset(&newswf,0x00,sizeof(SWF));        // set global movie parameters
300
301     newswf.fileVersion    = swf->fileVersion;
302     newswf.frameRate      = swf->frameRate;
303     newswf.movieSize      = swf->movieSize;
304     if(movetozero && originalplaceobjects) {
305         newswf.movieSize.xmax = swf->movieSize.xmax - swf->movieSize.xmin;
306         newswf.movieSize.ymax = swf->movieSize.ymax - swf->movieSize.ymin;
307         newswf.movieSize.xmin = 0;
308         newswf.movieSize.ymin = 0;
309     }
310                 
311     newswf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
312     desttag = newswf.firstTag;
313     rgb.r = mainr;
314     rgb.g = maing;
315     rgb.b = mainb;
316     swf_SetRGB(desttag,&rgb);
317
318     swf_GetRect(0, &objectbbox);
319
320     do {
321        changed = 0;
322        for(t=0;t<65536;t++) {
323            if(used[t] && !(used[t]&2)) {
324              if(tags[t]==0) {
325                  msg("<warning> ID %d is referenced, but never defined.", t);
326              } else if(tags[t]->id==ST_DEFINESPRITE) {
327                  TAG*tag = tags[t];
328                  while(tag->id != ST_END)
329                  {
330                      enumerateIDs(tag, idcallback);
331                      tag = tag->next;
332                  }
333              }
334              else 
335                enumerateIDs(tags[t], idcallback);
336              used[t] |= 2;
337            }
338        }
339     }
340     while(changed);
341
342     srctag = swf->firstTag;
343     tagnum = 0;
344     sprite = 0;
345     while(srctag && (srctag->id || sprite)) {
346         int reset = 0;
347         if(!sprite) {
348             copy = 0;
349         }
350         if(srctag->id == ST_END) {
351             sprite = 0;
352         }
353         if(srctag->id == ST_DEFINESPRITE)
354             sprite = 1;
355         if(srctag->id == ST_JPEGTABLES)
356             copy = 1;
357         if(swf_isDefiningTag(srctag)) {
358             int id = swf_GetDefineID(srctag);
359             if(used[id])  {
360                 SRECT b;
361                 copy = 1;
362                 b = swf_GetDefineBBox(srctag);
363                 swf_ExpandRect2(&objectbbox, &b);
364             }
365         } else 
366         if ((((swf_isPlaceTag(srctag) && originalplaceobjects)
367               || srctag->id == ST_STARTSOUND) && (used[swf_GetPlaceID(srctag)]&4) ) ||
368               (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)]) ||
369               (tagused[tagnum])) 
370         {
371                 if(copy == 0)
372                     reset = 1;
373                 copy = 1;
374         } 
375         if(srctag->id == ST_REMOVEOBJECT) {
376             if(!used[swf_GetPlaceID(srctag)])
377                 copy = 0;
378         }
379
380         if(copy) {
381             TAG*ttag = (TAG*)malloc(sizeof(TAG));
382             desttag = swf_InsertTag(desttag, srctag->id);
383             desttag->len = desttag->memsize = srctag->len;
384             desttag->data = malloc(srctag->len);
385             memcpy(desttag->data, srctag->data, srctag->len);
386             if(movetozero && swf_isPlaceTag(desttag)) {
387                 moveToZero(desttag);
388             }
389             if(reset)
390                 copy = 0;
391         }
392         
393         srctag = srctag->next;
394         tagnum ++;
395     }
396     if(!extractframes && !hollow) {
397         if(!originalplaceobjects && (extractids||extractname_id>=0)) {
398             int number = 0;
399             int id = 0;
400             int t;
401             TAG* objtag = 0;
402             SRECT bbox;
403             memset(&bbox, 0, sizeof(SRECT));
404             if(extractids) {
405                 for(t=0;t<65536;t++) {
406                     if(is_in_range(t, extractids)) {
407                         id = t;
408                         number++;
409                     }
410                 }
411             }
412             if(number>=2) {
413                 printf("warning! You should use the -P when extracting multiple objects\n");
414             }
415
416             if(number == 1) {
417                 /* if there is only one object, we will scale it.
418                    So let's figure out its bounding box */
419                 TAG*tag = swf->firstTag;
420                 while(tag) {
421                     if(swf_isDefiningTag(tag) && tag->id != ST_DEFINESPRITE) {
422                         if(swf_GetDefineID(tag) == id)
423                             bbox = swf_GetDefineBBox(tag);
424                         objtag = tag;
425                     }
426                     tag = tag->next;
427                 }
428                 newswf.movieSize.xmin = 0;
429                 newswf.movieSize.ymin = 0;
430                 newswf.movieSize.xmax = 512*20;
431                 newswf.movieSize.ymax = 512*20;
432             } else {
433                 if((objectbbox.xmin|objectbbox.ymin|objectbbox.xmax|objectbbox.ymax)!=0)
434                     newswf.movieSize = objectbbox;
435             }
436
437             if(extractname_id>=0) {
438                 desttag = swf_InsertTag(desttag, ST_PLACEOBJECT2);
439                 swf_ObjectPlace(desttag, extractname_id, extractname_id, 0,0,extractname);
440             } else {
441                 for(t=0;t<65536;t++) {
442                     if(is_in_range(t, extractids)) {
443                         MATRIX m;
444                         desttag = swf_InsertTag(desttag, ST_PLACEOBJECT2);
445                         swf_GetMatrix(0, &m);
446                         if(objtag) {
447                             int width = bbox.xmax - bbox.xmin;
448                             int height = bbox.ymax - bbox.ymin;
449                             int max = width>height?width:height;
450                             m.tx = -bbox.xmin;
451                             m.ty = -bbox.ymin;
452                             if(max) {
453                                 m.sx = (512*20*65536)/max;
454                                 m.sy = (512*20*65536)/max;
455                             }
456                             //newswf.movieSize = swf_TurnRect(newswf.movieSize, &m);
457                         }
458                         swf_ObjectPlace(desttag, t, t, &m,0,0);
459                     }
460                 }
461             }
462         }
463         desttag = swf_InsertTag(desttag,ST_SHOWFRAME);
464     }
465     desttag = swf_InsertTag(desttag,ST_END);
466     
467     f = open(filename, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0644);
468     if FAILED(swf_WriteSWF(f,&newswf)) fprintf(stderr,"WriteSWF() failed.\n");
469     close(f);
470
471     swf_FreeTags(&newswf);                       // cleanup
472 }
473
474 int isOfType(int t, TAG*tag)
475 {
476     int show = 0;
477     if(t == 0 && (tag->id == ST_DEFINESHAPE ||
478         tag->id == ST_DEFINESHAPE2 ||
479         tag->id == ST_DEFINESHAPE3)) {
480         show = 1;
481     }
482     if(t==1 && tag->id == ST_DEFINESPRITE) {
483         show = 1;
484     }
485     if(t == 2 && (tag->id == ST_DEFINEBITS ||
486         tag->id == ST_DEFINEBITSJPEG2 ||
487         tag->id == ST_DEFINEBITSJPEG3)) {
488         show = 1;
489     }
490     if(t == 3 && (tag->id == ST_DEFINEBITSLOSSLESS ||
491         tag->id == ST_DEFINEBITSLOSSLESS2)) {
492         show = 1;
493     }
494     if(t == 4 && (tag->id == ST_DEFINESOUND)) {
495         show = 1;
496     }
497     if(t == 5 && (tag->id == ST_DEFINEFONT || tag->id == ST_DEFINEFONT2 || tag->id == ST_DEFINEFONT3)) {
498         show = 1;
499     }
500     if (t== 6 && (tag->id == ST_DEFINEBINARY)) {
501         show = 1;
502     }
503     return show;
504 }
505
506 void listObjects(SWF*swf)
507 {
508     TAG*tag;
509     char first;
510     int t;
511     int frame = 0;
512     char*names[] = {"Shape", "MovieClip", "JPEG", "PNG", "Sound", "Font", "Binary"};
513     char*options[] = {"-i", "-i", "-j", "-p", "-s", "-F","-b"};
514     int mp3=0;
515     printf("Objects in file %s:\n",filename);
516     swf_FoldAll(swf);
517     for(t=0;t<sizeof(names)/sizeof(names[0]);t++) {
518         int nr=0;
519         int lastid = -2, lastprint=-1;
520         int follow=0;
521         tag = swf->firstTag;
522         first = 1;
523         while(tag) {
524             if(tag->id == ST_SOUNDSTREAMHEAD || tag->id == ST_SOUNDSTREAMHEAD2)
525                 mp3 = 1;
526             if(isOfType(t,tag))
527                 nr++;
528             tag = tag->next;
529         }
530         if(!nr)
531             continue;
532         
533         printf(" [%s] %d %s%s: ID(s) ", options[t], nr, names[t], nr>1?"s":"");
534
535         tag = swf->firstTag;
536         while(tag) {
537             char text[80];
538             char show = isOfType(t,tag);
539             int id;
540             if(!show) {
541                 tag = tag->next;
542                 continue;
543             }
544             id = swf_GetDefineID(tag);
545
546             if(id == lastid+1) {
547                 follow=1;
548             } else {
549                 if(first || !follow) {
550                     if(!first)
551                         printf(", ");
552                     printf("%d", id);
553                 } else {
554                     if(lastprint + 1 == lastid) 
555                         printf(", %d, %d", lastid, id);
556                     else
557                         printf("-%d, %d", lastid, id);
558                 }
559                 lastprint = id;
560                 first = 0;
561                 follow = 0;
562             }
563             lastid = id;
564             tag=tag->next;
565         }
566         if(follow) {
567             if(lastprint + 1 == lastid)
568                 printf(", %d", lastid);
569             else
570                 printf("-%d", lastid);
571         }
572         printf("\n");
573     }
574
575     if(frame)
576         printf(" [-f] %d Frames: ID(s) 0-%d\n", frame, frame);
577     else
578         printf(" [-f] 1 Frame: ID(s) 0\n");
579
580     if(mp3)
581         printf(" [-m] 1 MP3 Soundstream\n");
582 }
583
584 void handlefont(SWF*swf, TAG*tag)
585 {
586     SWFFONT* f=0;
587     U16 id;
588     char name[80];
589     char*filename = name;
590     int t;
591
592     id = swf_GetDefineID(tag);
593     sprintf(name, "font%d.swf", id);
594     if(numextracts==1) {
595         filename = destfilename;
596     }
597
598     swf_FontExtract(swf, id, &f);
599     if(!f) {
600         printf("Couldn't extract font %d\n", id);
601         return;
602     }
603
604     swf_WriteFont(f, filename);
605     swf_FontFree(f);
606 }
607
608 static char has_jpegtables=0;
609 static U8*jpegtables = 0;
610 static int jpegtablessize = 0;
611
612 void handlejpegtables(TAG*tag)
613 {
614     if(tag->id == ST_JPEGTABLES) {
615         jpegtables = tag->data;
616         jpegtablessize = tag->len;
617         has_jpegtables = 1;
618     }
619 }
620
621 FILE* save_fopen(char* name, char* mode)
622 {
623     FILE*fi = fopen(name, mode);
624     if(!fi) {
625         fprintf(stderr, "Error: Couldn't open %s\n", name);
626         exit(1);
627     }
628     return fi;
629 }
630
631 int findjpegboundary(U8*data, int len)
632 {
633     int t;
634     int pos=-1;
635     for(t=0;t<len-4;t++) {
636         if(data[t  ]==0xff &&
637            data[t+1]==0xd9 &&
638            data[t+2]==0xff &&
639            data[t+3]==0xd8) {
640             pos = t;
641         }
642     }
643     return pos;
644 }
645
646 /* extract jpeg data out of a tag */
647 void handlejpeg(TAG*tag)
648 {
649     char name[80];
650     char*filename = name;
651     FILE*fi;
652     
653     sprintf(name, "pic%d.jpg", GET16(tag->data));
654     if(numextracts==1) {
655         filename = destfilename;
656         if(!strcmp(filename,"output.swf"))
657             filename = "output.jpg";
658     }
659     /* swf jpeg images have two streams, which both start with ff d8 and
660        end with ff d9. The following code handles sorting the middle
661        <ff d9 ff d8> bytes out, so that one stream remains */
662     if(tag->id == ST_DEFINEBITSJPEG && tag->len>2 && has_jpegtables) {
663         fi = save_fopen(filename, "wb");
664         if(jpegtablessize>=2) {
665             fwrite(jpegtables, 1, jpegtablessize-2, fi); //don't write end tag (ff,d8)
666             fwrite(&tag->data[2+2], tag->len-2-2, 1, fi); //don't write start tag (ff,d9)
667         } else {
668             fwrite(tag->data+2, tag->len-2, 1, fi);
669         }
670         fclose(fi);
671     }
672     else if(tag->id == ST_DEFINEBITSJPEG2 && tag->len>2) {
673         int end = tag->len;
674         int pos = findjpegboundary(&tag->data[2], tag->len-2);
675         if(pos>=0) {
676             pos+=2;
677             fi = save_fopen(filename, "wb");
678             fwrite(&tag->data[2], pos-2, 1, fi);
679             fwrite(&tag->data[pos+4], end-(pos+4), 1, fi);
680             fclose(fi);
681         } else {
682             fi = save_fopen(filename, "wb");
683             fwrite(&tag->data[2], end-2, 1, fi);
684             fclose(fi);
685         }
686     }
687     else if(tag->id == ST_DEFINEBITSJPEG3 && tag->len>6) {
688         U32 end = GET32(&tag->data[2])+6;
689         int pos = findjpegboundary(&tag->data[6], tag->len-6);
690         if(pos<0) {
691             fi = save_fopen(filename, "wb");
692             fwrite(&tag->data[6], end-6, 1, fi);
693             fclose(fi);
694         } else {
695             pos+=6;
696             fi = save_fopen(filename, "wb");
697             fwrite(&tag->data[6], pos-6, 1, fi);
698             fwrite(&tag->data[pos+4], end-(pos+4), 1, fi);
699             fclose(fi);
700         }
701     }
702     else {
703         int id = GET16(tag->data);
704         fprintf(stderr, "Object %d is not a JPEG picture!\n",id, jpegtables);
705         exit(1);
706     }
707 }
708
709 #ifdef _ZLIB_INCLUDED_
710 static U32 mycrc32;
711
712 static U32*crc32_table = 0;
713 static void make_crc32_table(void)
714 {
715   int t;
716   if(crc32_table) 
717       return;
718   crc32_table = (U32*)malloc(1024);
719
720   for (t = 0; t < 256; t++) {
721     U32 c = t;
722     int s;
723     for (s = 0; s < 8; s++) {
724       c = (0xedb88320L*(c&1)) ^ (c >> 1);
725     }
726     crc32_table[t] = c;
727   }
728 }
729 static inline void png_write_byte(FILE*fi, U8 byte)
730 {
731     fwrite(&byte,1,1,fi);
732     mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
733 }
734 static void png_start_chunk(FILE*fi, char*type, int len)
735 {
736     U8 mytype[4]={0,0,0,0};
737     U32 mylen = REVERSESWAP32(len);
738     memcpy(mytype,type,strlen(type));
739     fwrite(&mylen, 4, 1, fi);
740     mycrc32=0xffffffff;
741     png_write_byte(fi,mytype[0]);
742     png_write_byte(fi,mytype[1]);
743     png_write_byte(fi,mytype[2]);
744     png_write_byte(fi,mytype[3]);
745 }
746 static void png_write_bytes(FILE*fi, U8*bytes, int len)
747 {
748     int t;
749     for(t=0;t<len;t++)
750         png_write_byte(fi,bytes[t]);
751 }
752 static void png_write_dword(FILE*fi, U32 dword)
753 {
754     png_write_byte(fi,dword>>24);
755     png_write_byte(fi,dword>>16);
756     png_write_byte(fi,dword>>8);
757     png_write_byte(fi,dword);
758 }
759 static void png_end_chunk(FILE*fi)
760 {
761     U32 tmp = REVERSESWAP32((mycrc32^0xffffffff));
762     fwrite(&tmp,4,1,fi);
763 }
764
765
766 /* extract a lossless image (png) out of a tag 
767    This routine was originally meant to be a one-pager. I just
768    didn't know png is _that_ much fun. :) -mk
769  */
770 void handlelossless(TAG*tag)
771 {
772     char name[80];
773     char*filename = name;
774     FILE*fi;
775     int width, height;
776     int crc;
777     int id;
778     int t;
779     U8 bpp = 1;
780     U8 format;
781     U8 tmp;
782     Bytef* data=0;
783     U8* data2=0;
784     U8* data3=0;
785     uLongf datalen;
786     uLongf datalen2;
787     U32 datalen3;
788     U8 head[] = {137,80,78,71,13,10,26,10};
789     int cols;
790     char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
791     RGBA* palette;
792     int pos;
793     int error;
794     U32 tmp32;
795
796     make_crc32_table();
797
798     if(tag->id != ST_DEFINEBITSLOSSLESS &&
799        tag->id != ST_DEFINEBITSLOSSLESS2) {
800         int id = GET16(tag->data);
801         fprintf(stderr, "Object %d is not a PNG picture!\n",id);
802         exit(1);
803     }
804
805     id =swf_GetU16(tag);
806     format = swf_GetU8(tag);
807     if(format == 3) bpp = 8;
808     if(format == 4) bpp = 16;
809     if(format == 5) bpp = 32;
810     if(format!=3 && format!=5) {
811         if(format==4)
812         fprintf(stderr, "Can't handle 16-bit palette images yet (image %d)\n",id);
813         else 
814         fprintf(stderr, "Unknown image type %d in image %d\n", format, id);
815         return;
816     }
817     width = swf_GetU16(tag);
818     height = swf_GetU16(tag);
819     if(format == 3) cols = swf_GetU8(tag) + 1;
820 // this is what format means according to the flash specification. (which is
821 // clearly wrong)
822 //    if(format == 4) cols = swf_GetU16(tag) + 1;
823 //    if(format == 5) cols = swf_GetU32(tag) + 1;
824     else cols = 0;
825
826     msg("<verbose> Width %d", width);
827     msg("<verbose> Height %d", height);
828     msg("<verbose> Format %d", format);
829     msg("<verbose> Cols %d", cols);
830     msg("<verbose> Bpp %d", bpp);
831
832     datalen = (width*height*bpp/8+cols*8);
833     do {
834         if(data)
835             free(data);
836         datalen+=4096;
837         data = malloc(datalen);
838         error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
839     } while(error == Z_BUF_ERROR);
840     if(error != Z_OK) {
841         fprintf(stderr, "Zlib error %d (image %d)\n", error, id);
842         return;
843     }
844     msg("<verbose> Uncompressed image is %d bytes (%d colormap)", datalen, (3+alpha)*cols);
845     pos = 0;
846     datalen2 = datalen+16;
847     data2 = malloc(datalen2);
848     palette = (RGBA*)malloc(cols*sizeof(RGBA));
849
850     for(t=0;t<cols;t++) {
851         palette[t].r = data[pos++];
852         palette[t].g = data[pos++];
853         palette[t].b = data[pos++];
854         if(alpha) {
855             palette[t].a = data[pos++];
856         }
857     }
858
859     sprintf(name, "pic%d.png", id);
860     if(numextracts==1) {
861         filename = destfilename;
862         if(!strcmp(filename,"output.swf"))
863             filename = "output.png";
864     }
865     fi = save_fopen(filename, "wb");
866     fwrite(head,sizeof(head),1,fi);     
867
868     png_start_chunk(fi, "IHDR", 13);
869      png_write_dword(fi,width);
870      png_write_dword(fi,height);
871      png_write_byte(fi,8);
872      if(format == 3)
873      png_write_byte(fi,3); //indexed
874      else if(format == 5 && alpha==0)
875      png_write_byte(fi,2); //rgb
876      else if(format == 5 && alpha==1)
877      png_write_byte(fi,6); //rgba
878      else return;
879
880      png_write_byte(fi,0); //compression mode
881      png_write_byte(fi,0); //filter mode
882      png_write_byte(fi,0); //interlace mode
883     png_end_chunk(fi);
884    
885     if(format == 3) {
886         png_start_chunk(fi, "PLTE", 768);
887          
888          for(t=0;t<256;t++) {
889              png_write_byte(fi,palette[t].r);
890              png_write_byte(fi,palette[t].g);
891              png_write_byte(fi,palette[t].b);
892          }
893         png_end_chunk(fi);
894
895         if(alpha) {
896             /* write alpha palette */
897             png_start_chunk(fi, "tRNS", 256);
898             for(t=0;t<256;t++) {
899                 png_write_byte(fi,palette[t].a);
900             }
901             png_end_chunk(fi);
902         }
903     }
904     {
905         int pos2 = 0;
906         int x,y;
907         int srcwidth = width * (bpp/8);
908         datalen3 = (width*4+5)*height;
909         data3 = (U8*)malloc(datalen3);
910         for(y=0;y<height;y++)
911         {
912            data3[pos2++]=0; //filter type
913            if(bpp==32) {
914             if(!alpha) {
915                 // 32 bit to 24 bit "conversion"
916                 for(x=0;x<width;x++) {
917                     data3[pos2++]=data[pos+1];
918                     data3[pos2++]=data[pos+2];
919                     data3[pos2++]=data[pos+3];
920                     pos+=4; //ignore padding byte
921                 }
922             } else {
923                 for(x=0;x<width;x++) {
924                     data3[pos2++]=data[pos+1];
925                     data3[pos2++]=data[pos+2];
926                     data3[pos2++]=data[pos+3];
927                     data3[pos2++]=data[pos+0]; //alpha
928                     pos+=4;
929                 }
930             }
931            }
932            else {
933                 for(x=0;x<srcwidth;x++)
934                     data3[pos2++]=data[pos++];
935            }
936            
937            pos+=((srcwidth+3)&~3)-srcwidth; //align
938         }
939         datalen3=pos2;
940     }
941
942     if(compress (data2, &datalen2, data3, datalen3) != Z_OK) {
943         fprintf(stderr, "zlib error in pic %d\n", id);
944         return;
945     }
946     msg("<verbose> Compressed data is %d bytes", datalen2);
947     png_start_chunk(fi, "IDAT", datalen2);
948     png_write_bytes(fi,data2,datalen2);
949     png_end_chunk(fi);
950     png_start_chunk(fi, "IEND", 0);
951     png_end_chunk(fi);
952
953     free(data);
954     free(data2);
955     free(data3);
956 }
957 #endif
958
959 static FILE*mp3file=0;
960 void handlesoundstream(TAG*tag)
961 {
962     char*filename = "output.mp3";
963     if(numextracts==1) {
964         filename = destfilename;
965         if(!strcmp(filename,"output.swf"))
966             filename = "output.mp3";
967     }
968     switch(tag->id) {
969         case ST_SOUNDSTREAMHEAD:
970             if((tag->data[1]&0x30) == 0x20) { //mp3 compression
971                 mp3file = fopen(filename, "wb");
972                 msg("<notice> Writing mp3 data to %s",filename);
973             }
974             else
975                 msg("<error> Soundstream is not mp3");
976         break;
977         case ST_SOUNDSTREAMHEAD2:
978             if((tag->data[1]&0x30) == 0x20) {//mp3 compression
979                 mp3file = fopen(filename, "wb");
980                 msg("<notice> Writing mp3 data to %s",filename);
981             }
982             else
983                 msg("<error> Soundstream is not mp3 (2)");
984         break;
985         case ST_SOUNDSTREAMBLOCK:
986             if(mp3file)
987                 fwrite(&tag->data[4],tag->len-4,1,mp3file);
988         break;
989     }
990 }
991
992 void handledefinesound(TAG*tag)
993 {
994     U8 flags;
995     U32 samples;
996     char buf[128];
997     char*filename = buf;
998     FILE*fi;
999     char*extension = 0;
1000     int format;
1001     U16 id;
1002     int rate,bits,stereo;
1003     char*rates[] = {"5500","11025","22050","44100"};
1004     id = swf_GetU16(tag); //id
1005     
1006     flags = swf_GetU8(tag);
1007     format = flags>>4;
1008     rate = (flags>>2)&3;
1009     bits = flags&2?16:8;
1010     stereo = flags&1;
1011     
1012     samples = swf_GetU32(tag);
1013
1014     extension = "raw";
1015
1016     if(format == 2) { // mp3
1017         swf_GetU16(tag); //numsamples_seek
1018         extension = "mp3";
1019     } else if(format == 0) { // raw
1020         printf("Sound is RAW, format: %s samples/sec, %d bit, %s\n", rates[rate], bits, stereo?"stereo":"mono");
1021         // TODO: convert to WAV
1022         extension = "raw";
1023     } else if(format == 1) { // adpcm
1024         printf("Sound is ADPCM, format: %s samples/sec, %d bit, %s\n", rates[rate], bits, stereo?"stereo":"mono");
1025         extension = "adpcm";
1026     }
1027     sprintf(buf, "sound%d.%s", id, extension);
1028     if(numextracts==1) {
1029         filename = destfilename;
1030         if(!strcmp(filename,"output.swf")) {
1031             sprintf(buf, "output.%s", extension);
1032             filename = buf;
1033         }
1034     }
1035     fi = save_fopen(filename, "wb");
1036     fwrite(&tag->data[tag->pos], tag->len - tag->pos, 1, fi);
1037     fclose(fi);
1038 }
1039
1040 void handlebinary(TAG*tag) {
1041     FILE *fout = NULL;
1042     char buf[100];
1043     char *filename = buf;
1044     int len = tag->memsize;
1045     int dx = 6; // offset to binary data
1046     if (tag->id!=ST_DEFINEBINARY) {
1047         fprintf(stderr, "Object %d is not a binary entity!\n",
1048                         GET16(tag->data));
1049         return;
1050     }
1051     sprintf(buf, "binary%d.bin", GET16(tag->data));
1052     if(numextracts==1) {
1053         filename = destfilename;
1054         if(!strcmp(filename,"output.swf")) {
1055             sprintf(buf, "output.bin");
1056             filename = buf;
1057         }
1058     }
1059     fout = fopen(filename, "wb");
1060     fwrite(tag->data+dx,len-dx,1,fout);
1061     fclose(fout);
1062 }
1063
1064 int main (int argc,char ** argv)
1065
1066     TAG*tag;
1067     SWF swf;
1068     int f;
1069     int found = 0;
1070     int frame = 0;
1071     int tagnum = 0;
1072     char depths[65536];
1073     char listavailable = 0;
1074     processargs(argc, argv);
1075
1076     if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids
1077         && !extractmp3 && !extractsoundids && !extractfontids && !extractbinaryids)
1078         listavailable = 1;
1079
1080     if(!originalplaceobjects && movetozero) {
1081         fprintf(stderr, "Error: -0 (--movetozero) can only be used in conjunction with -P (--placeobject)\n");
1082         return 0;
1083     }
1084
1085     if(!filename)
1086     {
1087         fprintf(stderr, "You must supply a filename.\n");
1088         return 1;
1089     }
1090     initLog(0,-1,0,0,-1, verbose);
1091
1092     f = open(filename,O_RDONLY|O_BINARY);
1093
1094     if (f<0)
1095     { 
1096         perror("Couldn't open file: ");
1097         exit(1);
1098     }
1099     if (swf_ReadSWF(f,&swf) < 0)
1100     { 
1101         fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
1102         close(f);
1103         exit(1);
1104     }
1105     close(f);
1106
1107     if(listavailable) {
1108         listObjects(&swf);
1109         swf_FreeTags(&swf);
1110         return 0;
1111     }
1112
1113     tag = swf.firstTag;
1114     tagnum = 0;
1115     while(tag) {
1116         tagnum ++;
1117         tag = tag->next;
1118     }
1119
1120     tagused = (char*)malloc(tagnum);
1121     memset(tagused, 0, tagnum);
1122     memset(used, 0, 65536);
1123     memset(depths, 0, 65536);
1124
1125     tag = swf.firstTag;
1126     tagnum = 0;
1127     while(tag) {
1128         if(swf_isAllowedSpriteTag(tag)) {
1129             int write = 0;
1130             if(extractframes && is_in_range(frame, extractframes)) {
1131                 write = 1;
1132                 if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) {
1133                     depths[swf_GetDepth(tag)] = 1;
1134                 }
1135                 if(tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) {
1136                     int depth = swf_GetDepth(tag);
1137                     if(!depths[depth]) 
1138                         write = 0;
1139                     depths[swf_GetDepth(tag)] = 0;
1140                 }
1141             } else {
1142                 if((tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) && 
1143                     (depths[swf_GetDepth(tag)]) && hollow) {
1144                     write = 1;
1145                     depths[swf_GetDepth(tag)] = 0;
1146                 }
1147             }
1148             if(write) {
1149                 enumerateIDs(tag, idcallback);
1150                 found = 1;
1151                 tagused[tagnum] = 1;
1152             }
1153         }
1154
1155         if(tag->id == ST_SOUNDSTREAMHEAD ||
1156            tag->id == ST_SOUNDSTREAMHEAD2 ||
1157            tag->id == ST_SOUNDSTREAMBLOCK) {
1158             if(extractmp3)
1159                 handlesoundstream(tag);
1160         }
1161
1162         if(tag->id == ST_JPEGTABLES) {
1163             handlejpegtables(tag);
1164         }
1165
1166         if(swf_isDefiningTag(tag)) {
1167             int id = swf_GetDefineID(tag);
1168             tags[id] = tag;
1169             if(extractids && is_in_range(id, extractids)) {
1170                 used[id] = 5;
1171                 found = 1;
1172             }
1173             if(extractfontids && is_in_range(id, extractfontids)) {
1174                 handlefont(&swf, tag);
1175             }
1176             if(extractjpegids && is_in_range(id, extractjpegids)) {
1177                 handlejpeg(tag);
1178             }
1179             if(extractsoundids && is_in_range(id, extractsoundids)) {
1180                 handledefinesound(tag);
1181             }
1182             if(extractbinaryids && is_in_range(id, extractbinaryids)) {
1183                 handlebinary(tag);
1184             }
1185 #ifdef _ZLIB_INCLUDED_
1186             if(extractpngids && is_in_range(id, extractpngids)) {
1187                 handlelossless(tag);
1188             }
1189 #endif
1190         }
1191         else if (tag->id == ST_SETBACKGROUNDCOLOR) {
1192             mainr = tag->data[0];
1193             maing = tag->data[1];
1194             mainb = tag->data[2];
1195         }
1196         else if(swf_isPlaceTag(tag) && tag->id != ST_PLACEOBJECT ) {
1197             char*name = swf_GetName(tag);
1198             if(name && extractname && !strcmp(name, extractname)) {
1199                 int id = swf_GetPlaceID(tag); 
1200                 used[id] = 5;
1201                 found = 1;
1202                 if(originalplaceobjects) {
1203                     tagused[tagnum] = 1;
1204                 }
1205                 depths[swf_GetDepth(tag)] = 1;
1206                 extractname_id = id;
1207             }
1208         }
1209         else if(tag->id == ST_SHOWFRAME) {
1210             frame ++;
1211             if(hollow) {
1212                 tagused[tagnum] = 1;
1213                 found = 1;
1214             }
1215         }
1216         
1217         if(tag->id == ST_DEFINESPRITE) {
1218             while(tag->id != ST_END) { 
1219                 tag = tag->next;
1220                 tagnum ++;
1221             }
1222         }
1223         tag = tag->next;
1224         tagnum ++;
1225     }
1226     if (found)
1227         extractTag(&swf, destfilename);
1228
1229     if(mp3file) {
1230         fclose(mp3file);
1231     } else {
1232         if(extractmp3) {
1233             msg("<error> Didn't find a soundstream in file");
1234         }
1235     }
1236
1237     swf_FreeTags(&swf);
1238     return 0;
1239 }
1240