2 Shows the structure of a swf file
4 Part of the swftools package.
6 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
8 This file is distributed under the GPL, see file COPYING for details */
10 #include "../config.h"
12 #ifdef HAVE_SYS_STAT_H
18 #ifdef HAVE_SYS_TYPES_H
19 #include <sys/types.h>
28 #include "../lib/rfxswf.h"
29 #include "../lib/args.h"
31 static char * filename = 0;
33 /* idtab stores the ids which are defined in the file. This allows us
34 to detect errors in the file. (i.e. ids which are defined more than
36 static char idtab[65536];
37 static char * indent = " ";
39 static int placements = 0;
40 static int action = 0;
43 static int showtext = 0;
47 struct options_t options[] =
65 int args_callback_option(char*name,char*val)
67 if(!strcmp(name, "V")) {
68 printf("swfdump - part of %s %s\n", PACKAGE, VERSION);
71 else if(name[0]=='a') {
75 else if(name[0]=='p') {
79 else if(name[0]=='t') {
83 else if(name[0]=='e') {
87 else if(name[0]=='X') {
91 else if(name[0]=='Y') {
95 else if(name[0]=='r') {
99 else if(name[0]=='f') {
103 else if(name[0]=='d') {
107 else if(name[0]=='u') {
111 else if(name[0]=='D') {
112 action = placements = showtext = 1;
116 printf("Unknown option: -%s\n", name);
122 int args_callback_longoption(char*name,char*val)
124 return args_long2shortoption(options, name, val);
126 void args_callback_usage(char*name)
128 printf("Usage: %s [-at] file.swf\n", name);
129 printf("\t-h , --help\t\t Print help and exit\n");
130 printf("\t-D , --full\t\t Show everything. The same as -atMp\n");
131 printf("\t-e , --html\t\t Create html output embedding the file (simple, but useful)\n");
132 printf("\t-X , --width\t\t Prints out a string of the form \"-X width\"\n");
133 printf("\t-Y , --height\t\t Prints out a string of the form \"-Y height\"\n");
134 printf("\t-r , --rate\t\t Prints out a string of the form \"-r rate\"\n");
135 printf("\t-f , --frames\t\t Prints out a string of the form \"-f framenum\"\n");
136 printf("\t-a , --action\t\t Disassemble action tags\n");
137 printf("\t-p , --placements\t Show extra placement information\n");
138 printf("\t-t , --text\t\t Show text data\n");
139 printf("\t-d , --hex\t\t Print hex output of tag data, too\n");
140 printf("\t-u , --used\t\t Show referred IDs for each Tag\n");
141 printf("\t-V , --version\t\t Print program version and exit\n");
143 int args_callback_command(char*name,char*val)
146 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
154 char* testfunc(char*str)
156 printf("%s: %s\n", what, str);
160 void dumpButton2Actions(TAG*tag, char*prefix)
166 oldTagPos = swf_GetTagPos(tag);
168 // scan DefineButton2 Record
170 swf_GetU16(tag); // Character ID
171 swf_GetU8(tag); // Flags;
173 offsetpos = swf_GetTagPos(tag); // first offset
176 while (swf_GetU8(tag)) // state -> parse ButtonRecord
177 { swf_GetU16(tag); // id
178 swf_GetU16(tag); // layer
179 swf_GetMatrix(tag,NULL); // matrix
180 swf_GetCXForm(tag,NULL,1); // cxform
187 if(tag->pos >= tag->len)
190 offsetpos = swf_GetU16(tag);
191 condition = swf_GetU16(tag); // condition
193 actions = swf_ActionGet(tag);
194 printf("%s condition %04x\n", prefix, condition);
195 swf_DumpActions(actions, prefix);
198 swf_SetTagPos(tag,oldTagPos);
202 void dumpButtonActions(TAG*tag, char*prefix)
205 swf_GetU16(tag); // id
206 while (swf_GetU8(tag)) // state -> parse ButtonRecord
207 { swf_GetU16(tag); // id
208 swf_GetU16(tag); // layer
209 swf_GetMatrix(tag,NULL); // matrix
211 actions = swf_ActionGet(tag);
212 swf_DumpActions(actions, prefix);
215 #define ET_HASTEXT 32768
216 #define ET_WORDWRAP 16384
217 #define ET_MULTILINE 8192
218 #define ET_PASSWORD 4096
219 #define ET_READONLY 2048
220 #define ET_HASTEXTCOLOR 1024
221 #define ET_HASMAXLENGTH 512
222 #define ET_HASFONT 256
225 #define ET_HASLAYOUT 32
226 #define ET_NOSELECT 16
230 #define ET_USEOUTLINES 1
236 void textcallback(int*glyphs, int nr, int fontid)
239 printf(" <%2d glyphs in font %2d> ",nr, fontid);
240 for(t=0;t<fontnum;t++)
242 if(fonts[t]->id == fontid) {
252 if(glyphs[t] >= fonts[font]->numchars /*glyph is in range*/
253 || !fonts[font]->glyph2ascii /* font has ascii<->glyph mapping */
256 a = fonts[font]->glyph2ascii[glyphs[t]];
263 printf("\\x%x", (int)a);
268 void handleText(TAG*tag)
271 swf_FontExtract_DefineTextCallback(-1,0,tag,4, textcallback);
274 void handleDefineSound(TAG*tag)
276 U16 id = swf_GetU16(tag);
277 U8 flags = swf_GetU8(tag);
278 int compression = (flags>>4)&3;
279 int rate = (flags>>2)&3;
280 int bits = flags&2?16:8;
281 int stereo = flags&1;
283 if(compression == 0) printf("Raw ");
284 else if(compression == 1) printf("ADPCM ");
285 else if(compression == 2) printf("MP3 ");
287 if(rate == 0) printf("5.5Khz ");
288 if(rate == 1) printf("11Khz ");
289 if(rate == 2) printf("22Khz ");
290 if(rate == 3) printf("44Khz ");
291 printf("%dBit ", bits);
292 if(stereo) printf("stereo");
297 void handleDefineBits(TAG*tag)
303 id = swf_GetU16(tag);
304 mode = swf_GetU8(tag);
305 width = swf_GetU16(tag);
306 height = swf_GetU16(tag);
307 printf(" image %dx%d",width,height);
308 if(mode == 3) printf(" (8 bpp)");
309 else if(mode == 4) printf(" (16 bpp)");
310 else if(mode == 5) printf(" (32 bpp)");
311 else printf(" (? bpp)");
314 void handleEditText(TAG*tag)
319 id = swf_GetU16(tag);
321 //swf_ResetReadBits(tag);
326 flags = swf_GetBits(tag,16);
327 if(flags & ET_HASFONT) {
328 swf_GetU16(tag); //font
329 swf_GetU16(tag); //fontheight
331 if(flags & ET_HASTEXTCOLOR) {
332 swf_GetU8(tag); //rgba
337 if(flags & ET_HASMAXLENGTH) {
338 swf_GetU16(tag); //maxlength
340 if(flags & ET_HASLAYOUT) {
341 swf_GetU8(tag); //align
342 swf_GetU16(tag); //left margin
343 swf_GetU16(tag); //right margin
344 swf_GetU16(tag); //indent
345 swf_GetU16(tag); //leading
347 printf(" variable \"%s\"", &tag->data[tag->pos]);
349 if(flags & (ET_X1 | ET_X2 | ET_X3 | ET_X0))
351 printf(" undefined flags: %d%d%d%d",
358 while(tag->data[tag->pos++]);
359 if(flags & ET_HASTEXT)
360 // printf(" text \"%s\"\n", &tag->data[tag->pos])
363 void printhandlerflags(U16 handlerflags)
365 if(handlerflags&1) printf("[on load]");
366 if(handlerflags&2) printf("[enter frame]");
367 if(handlerflags&4) printf("[unload]");
368 if(handlerflags&8) printf("[mouse move]");
369 if(handlerflags&16) printf("[mouse down]");
370 if(handlerflags&32) printf("[mouse up]");
371 if(handlerflags&64) printf("[key down]");
372 if(handlerflags&128) printf("[key up]");
373 if(handlerflags&256) printf("[data]");
374 if(handlerflags&0xfe00) printf("[???]");
376 void handleVideoStream(TAG*tag, char*prefix)
378 U16 id = swf_GetU16(tag);
379 U16 frames = swf_GetU16(tag);
380 U16 width = swf_GetU16(tag);
381 U16 height = swf_GetU16(tag);
382 U8 flags = swf_GetU8(tag); //5-2(videopacket 01=off 10=on)-1(smoothing 1=on)
383 U8 codec = swf_GetU8(tag);
384 printf(" (%d frames, %dx%d", frames, width, height);
388 printf(" sorenson h.263)");
390 printf(" codec 0x%02x)", codec);
392 void handleVideoFrame(TAG*tag, char*prefix)
394 U32 code, version, reference, sizeflags;
397 U16 id = swf_GetU16(tag);
398 U16 frame = swf_GetU16(tag);
399 U8 deblock,flags, tmp, bit;
401 char*types[] = {"I-frame", "P-frame", "disposable P-frame", "<reserved>"};
402 printf(" (frame %d) ", frame);
404 /* video packet follows */
405 code = swf_GetBits(tag, 17);
406 version = swf_GetBits(tag, 5);
407 reference = swf_GetBits(tag, 8);
409 sizeflags = swf_GetBits(tag, 3);
412 case 0: width = swf_GetBits(tag, 8); height = swf_GetBits(tag, 8); break;
413 case 1: width = swf_GetBits(tag, 16); height = swf_GetBits(tag, 16); break;
414 case 2: width = 352; height = 288; break;
415 case 3: width = 176; height = 144; break;
416 case 4: width = 128; height = 96; break;
417 case 5: width = 320; height = 240; break;
418 case 6: width = 160; height = 120; break;
419 case 7: width = -1; height = -1;/*reserved*/ break;
421 printf("%dx%d ", width, height);
422 type = swf_GetBits(tag, 2);
423 printf("%s", types[type]);
425 deblock = swf_GetBits(tag, 1);
427 printf(" deblock ", deblock);
428 quantizer = swf_GetBits(tag, 5);
429 printf(" quant: %d ", quantizer);
432 void handlePlaceObject2(TAG*tag, char*prefix)
434 U8 flags = swf_GetU8(tag);
438 int ppos[3] = {0,0,0};
439 swf_GetU16(tag); //depth
441 if(flags&2) swf_GetU16(tag); //id
443 swf_GetMatrix(tag,&m);
445 ppos[0] += sprintf(pstr[0], "| Matrix ");
446 ppos[1] += sprintf(pstr[1], "| %5.3f %5.3f %6.2f ", m.sx/65536.0, m.r1/65536.0, m.tx/20.0);
447 ppos[2] += sprintf(pstr[2], "| %5.3f %5.3f %6.2f ", m.r0/65536.0, m.sy/65536.0, m.ty/20.0);
451 swf_GetCXForm(tag, &cx, 1);
453 ppos[0] += sprintf(pstr[0]+ppos[0], "| CXForm r g b a ");
454 ppos[1] += sprintf(pstr[1]+ppos[1], "| mul %4.1f %4.1f %4.1f %4.1f ", cx.r0/256.0, cx.g0/256.0, cx.b0/256.0, cx.a0/256.0);
455 ppos[2] += sprintf(pstr[2]+ppos[2], "| add %4d %4d %4d %4d ", cx.r1, cx.g1, cx.b1, cx.a1);
459 U16 ratio = swf_GetU16(tag); //ratio
461 ppos[0] += sprintf(pstr[0]+ppos[0], "| Ratio ");
462 ppos[1] += sprintf(pstr[1]+ppos[1], "| %-5d ", ratio);
463 ppos[2] += sprintf(pstr[2]+ppos[2], "| ");
467 U16 clip = swf_GetU16(tag); //clip
469 ppos[0] += sprintf(pstr[0]+ppos[0], "| Clip ");
470 ppos[1] += sprintf(pstr[1]+ppos[1], "| %-5d ", clip);
471 ppos[2] += sprintf(pstr[2]+ppos[2], "| ");
474 if(flags&32) { while(swf_GetU8(tag)); }
475 if(placements && ppos[0]) {
477 printf("%s %s\n", prefix, pstr[0]);
478 printf("%s %s\n", prefix, pstr[1]);
479 printf("%s %s", prefix, pstr[2]);
488 unknown = swf_GetU16(tag);
489 globalflags = swf_GetU16(tag);
491 printf("Unknown parameter field not zero: %04x\n", unknown);
494 printf("global flags: %04x\n", globalflags);
495 handlerflags = swf_GetU16(tag);
497 handlerflags = swf_GetU32(tag);
500 while(handlerflags) {
505 globalflags &= ~handlerflags;
506 printf("%s flags %08x ",prefix, handlerflags);
507 printhandlerflags(handlerflags);
508 length = swf_GetU32(tag);
509 printf(", %d bytes actioncode\n",length);
510 a = swf_ActionGet(tag);
511 swf_DumpActions(a,prefix);
514 handlerflags = is32?swf_GetU32(tag):swf_GetU16(tag);
516 if(globalflags) // should go to sterr.
517 printf("ERROR: unsatisfied handlerflags: %02x\n", globalflags);
519 printf(" has action code\n");
524 void handlePlaceObject(TAG*tag, char*prefix)
529 void fontcallback1(U16 id,U8 * name)
533 void fontcallback2(U16 id,U8 * name)
535 swf_FontExtract(&swf,id,&fonts[fontnum]);
539 void hexdumpTag(TAG*tag, char* prefix)
542 printf(" %s-=> ",prefix);
543 for(t=0;t<tag->len;t++) {
544 printf("%02x ", tag->data[t]);
545 if((t && ((t&15)==15)) || (t==tag->len-1))
550 printf("\n %s-=> ",prefix);
555 void handleExportAssets(TAG*tag, char* prefix)
561 num = swf_GetU16(tag);
564 id = swf_GetU16(tag);
565 name = swf_GetString(tag);
566 printf("%sexports %04d as \"%s\"\n", prefix, id, name);
570 void dumperror(const char* format, ...)
575 va_start(arglist, format);
576 vsprintf(buf, format, arglist);
580 printf("==== Error: %s ====\n", buf);
583 static char strbuf[800];
586 char* timestring(double f)
588 int hours = (int)(f/3600);
589 int minutes = (int)((f-hours*3600)/60);
590 int seconds = (int)((f-hours*3600-minutes*60));
591 int useconds = (int)((f-(int)f)*1000+0.5);
594 sprintf(&strbuf[bufpos], "%02d:%02d:%02d,%03d",hours,minutes,seconds,useconds);
595 return &strbuf[bufpos];
598 int main (int argc,char ** argv)
606 char issprite = 0; // are we inside a sprite definition?
609 char* spriteframelabel = 0;
610 char* framelabel = 0;
614 memset(idtab,0,65536);
616 processargs(argc, argv);
620 fprintf(stderr, "You must supply a filename.\n");
624 f = open(filename,O_RDONLY|O_BINARY);
628 perror("Couldn't open file: ");
631 if FAILED(swf_ReadSWF(f,&swf))
633 fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
640 if(statbuf.st_size != swf.fileSize && !swf.compressed)
641 dumperror("Real Filesize (%d) doesn't match header Filesize (%d)",
642 statbuf.st_size, swf.fileSize);
643 filesize = statbuf.st_size;
648 xsize = (swf.movieSize.xmax-swf.movieSize.xmin)/20;
649 ysize = (swf.movieSize.ymax-swf.movieSize.ymin)/20;
653 printf("-X %d", xsize);
659 printf("-Y %d", ysize);
665 printf("-r %d", swf.frameRate*100/256);
671 printf("-f %d", swf.frameCount);
678 char*fileversions[] = {"","1,0,0,0", "2,0,0,0","3,0,0,0","4,0,0,0",
679 "5,0,0,0","6,0,23,0","7,0,0,0","8,0,0,0"};
680 if(swf.fileVersion>8) {
681 fprintf(stderr, "Fileversion>8\n");
684 printf("<OBJECT CLASSID=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\"\n"
686 //" BGCOLOR=#ffffffff\n"?
688 //http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,23,0?
689 " CODEBASE=\"http://active.macromedia.com/flash5/cabs/swflash.cab#version=%s\">\n"
690 " <PARAM NAME=\"MOVIE\" VALUE=\"%s\">\n"
691 " <PARAM NAME=\"PLAY\" VALUE=\"true\">\n"
692 " <PARAM NAME=\"LOOP\" VALUE=\"true\">\n"
693 " <PARAM NAME=\"QUALITY\" VALUE=\"high\">\n"
694 " <EMBED SRC=\"%s\" WIDTH=\"%d\" HEIGHT=\"%d\"\n" //bgcolor=#ffffff?
695 " PLAY=\"true\" ALIGN=\"\" LOOP=\"true\" QUALITY=\"high\"\n"
696 " TYPE=\"application/x-shockwave-flash\"\n"
697 " PLUGINSPAGE=\"http://www.macromedia.com/go/getflashplayer\">\n"
699 "</OBJECT>\n", xsize, ysize, fileversions[swf.fileVersion],
700 filename, filename, xsize, ysize);
703 printf("[HEADER] File version: %d\n", swf.fileVersion);
705 printf("[HEADER] File is zlib compressed.");
706 if(filesize && swf.fileSize)
707 printf(" Ratio: %02d%%\n", filesize*100/(swf.fileSize));
711 printf("[HEADER] File size: %ld%s\n", swf.fileSize, swf.compressed?" (Depacked)":"");
712 printf("[HEADER] Frame rate: %f\n",swf.frameRate/256.0);
713 printf("[HEADER] Frame count: %d\n",swf.frameCount);
714 printf("[HEADER] Movie width: %.2f",(swf.movieSize.xmax-swf.movieSize.xmin)/20.0);
715 if(swf.movieSize.xmin)
716 printf(" (left offset: %.2f)\n", swf.movieSize.xmin/20.0);
719 printf("[HEADER] Movie height: %.2f",(swf.movieSize.ymax-swf.movieSize.ymin)/20.0);
720 if(swf.movieSize.ymin)
721 printf(" (top offset: %.2f)\n", swf.movieSize.ymin/20.0);
729 swf_FontEnumerate(&swf,&fontcallback1);
730 fonts = (SWFFONT**)malloc(fontnum*sizeof(SWFFONT*));
732 swf_FontEnumerate(&swf,&fontcallback2);
736 char*name = swf_TagGetName(tag);
739 dumperror("Unknown tag:0x%03x", tag->id);
743 printf("[%03x] %9ld %s%s", tag->id, tag->len, prefix, swf_TagGetName(tag));
745 if(tag->id == ST_FREECHARACTER) {
746 U16 id = swf_GetU16(tag);
750 if(swf_isDefiningTag(tag)) {
751 U16 id = swf_GetDefineID(tag);
752 printf(" defines id %04d", id);
754 dumperror("Id %04d is defined more than once.", id);
757 else if(swf_isPseudoDefiningTag(tag)) {
758 U16 id = swf_GetDefineID(tag);
759 printf(" adds information to id %04d", id);
761 dumperror("Id %04d is not yet defined.\n", id);
763 else if(tag->id == ST_PLACEOBJECT) {
764 printf(" places id %04d at depth %04x", swf_GetPlaceID(tag), swf_GetDepth(tag));
766 printf(" name \"%s\"",swf_GetName(tag));
767 handlePlaceObject(tag, myprefix);
769 else if(tag->id == ST_PLACEOBJECT2) {
776 printf(" id %04d",swf_GetPlaceID(tag));
780 printf(" at depth %04d", swf_GetDepth(tag));
782 printf(" name \"%s\"",swf_GetName(tag));
784 else if(tag->id == ST_REMOVEOBJECT) {
785 printf(" removes id %04d from depth %04d", swf_GetPlaceID(tag), swf_GetDepth(tag));
787 else if(tag->id == ST_REMOVEOBJECT2) {
788 printf(" removes object from depth %04d", swf_GetDepth(tag));
790 else if(tag->id == ST_FREECHARACTER) {
791 printf(" frees object %04d", swf_GetPlaceID(tag));
793 else if(tag->id == ST_STARTSOUND) {
796 id = swf_GetU16(tag);
797 flags = swf_GetU8(tag);
799 printf(" stops sound with id %04d", id);
801 printf(" starts sound with id %04d", id);
803 printf(" (if not already playing)");
809 printf(" looping %d times", swf_GetU16(tag));
812 else if(tag->id == ST_FRAMELABEL) {
813 int l = strlen(tag->data);
814 printf(" \"%s\"", tag->data);
816 printf(" has %d extra bytes", tag->len-1-l);
817 if(tag ->len-1-l == 1 && tag->data[tag->len-1] == 1)
820 if((framelabel && !issprite) ||
821 (spriteframelabel && issprite)) {
822 dumperror("Frame %d has more than one label",
823 issprite?spriteframe:mainframe);
825 if(issprite) spriteframelabel = tag->data;
826 else framelabel = tag->data;
828 else if(tag->id == ST_SHOWFRAME) {
829 char*label = issprite?spriteframelabel:framelabel;
830 int frame = issprite?spriteframe:mainframe;
833 while(tag->next && tag->next->id == ST_SHOWFRAME && tag->next->len == 0) {
835 if(issprite) spriteframe++;
841 printf(" %d (%s)", frame, timestring(frame*(256.0/(swf.frameRate+0.1))));
843 printf(" %d-%d (%s-%s)", frame, nframe,
844 timestring(frame*(256.0/(swf.frameRate+0.1))),
845 timestring(nframe*(256.0/(swf.frameRate+0.1)))
848 printf(" (label \"%s\")", label);
849 if(issprite) {spriteframe++; spriteframelabel = 0;}
850 if(!issprite) {mainframe++; framelabel = 0;}
853 if(tag->id == ST_SETBACKGROUNDCOLOR) {
854 U8 r = swf_GetU8(tag);
855 U8 g = swf_GetU8(tag);
856 U8 b = swf_GetU8(tag);
857 printf(" (%02x/%02x/%02x)\n",r,g,b);
859 else if(tag->id == ST_DEFINEBITSLOSSLESS ||
860 tag->id == ST_DEFINEBITSLOSSLESS2) {
861 handleDefineBits(tag);
864 else if(tag->id == ST_DEFINESOUND) {
865 handleDefineSound(tag);
868 else if(tag->id == ST_VIDEOFRAME) {
869 handleVideoFrame(tag, myprefix);
872 else if(tag->id == ST_DEFINEVIDEOSTREAM) {
873 handleVideoStream(tag, myprefix);
876 else if(tag->id == ST_DEFINEEDITTEXT) {
880 else if(tag->id == ST_DEFINEMOVIE) {
881 U16 id = swf_GetU16(tag);
882 char*s = swf_GetString(tag);
883 printf(" URL: %s\n", s);
885 else if(tag->id == ST_DEFINETEXT || tag->id == ST_DEFINETEXT2) {
891 else if(tag->id == ST_PLACEOBJECT2) {
893 else if(tag->id == ST_NAMECHARACTER) {
895 printf(" \"%s\"\n", swf_GetString(tag));
901 sprintf(myprefix, " %s", prefix);
903 if(tag->id == ST_DEFINESPRITE) {
904 sprintf(prefix, " ");
906 dumperror("Sprite definition inside a sprite definition");
910 spriteframelabel = 0;
912 else if(tag->id == ST_END) {
915 spriteframelabel = 0;
917 dumperror("End Tag not empty");
919 else if(tag->id == ST_EXPORTASSETS) {
920 handleExportAssets(tag, myprefix);
922 else if(tag->id == ST_DOACTION && action) {
924 actions = swf_ActionGet(tag);
925 swf_DumpActions(actions, myprefix);
927 else if(tag->id == ST_DEFINEBUTTON && action) {
928 dumpButtonActions(tag, myprefix);
930 else if(tag->id == ST_DEFINEBUTTON2 && action) {
931 dumpButton2Actions(tag, myprefix);
933 else if(tag->id == ST_PLACEOBJECT2) {
934 handlePlaceObject2(tag, myprefix);
937 if(tag->len && used) {
938 int num = swf_GetNumUsedIDs(tag);
942 used = (int*)malloc(sizeof(int)*num);
943 swf_GetUsedIDs(tag, used);
944 printf("%s%suses IDs: ", indent, prefix);
946 swf_SetTagPos(tag, used[t]);
947 printf("%d%s", swf_GetU16(tag), t<num-1?", ":"");
953 if(tag->len && hex) {
954 hexdumpTag(tag, prefix);