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[] =
66 int args_callback_option(char*name,char*val)
68 if(!strcmp(name, "V")) {
69 printf("swfdump - part of %s %s\n", PACKAGE, VERSION);
72 else if(name[0]=='a') {
76 else if(name[0]=='p') {
80 else if(name[0]=='t') {
84 else if(name[0]=='e') {
88 else if(name[0]=='X') {
92 else if(name[0]=='Y') {
96 else if(name[0]=='r') {
100 else if(name[0]=='f') {
104 else if(name[0]=='d') {
108 else if(name[0]=='u') {
112 else if(name[0]=='D') {
113 action = placements = showtext = 1;
117 printf("Unknown option: -%s\n", name);
123 int args_callback_longoption(char*name,char*val)
125 return args_long2shortoption(options, name, val);
127 void args_callback_usage(char*name)
129 printf("Usage: %s [-at] file.swf\n", name);
130 printf("\t-h , --help\t\t Print help and exit\n");
131 printf("\t-D , --full\t\t Show everything. The same as -atMp\n");
132 printf("\t-e , --html\t\t Create html output embedding the file (simple, but useful)\n");
133 printf("\t-X , --width\t\t Prints out a string of the form \"-X width\"\n");
134 printf("\t-Y , --height\t\t Prints out a string of the form \"-Y height\"\n");
135 printf("\t-r , --rate\t\t Prints out a string of the form \"-r rate\"\n");
136 printf("\t-f , --frames\t\t Prints out a string of the form \"-f framenum\"\n");
137 printf("\t-a , --action\t\t Disassemble action tags\n");
138 printf("\t-p , --placements\t Show extra placement information\n");
139 printf("\t-t , --text\t\t Show text data\n");
140 printf("\t-d , --hex\t\t Print hex output of tag data, too\n");
141 printf("\t-u , --used\t\t Show referred IDs for each Tag\n");
142 printf("\t-V , --version\t\t Print program version and exit\n");
144 int args_callback_command(char*name,char*val)
147 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
155 char* testfunc(char*str)
157 printf("%s: %s\n", what, str);
161 void dumpButton2Actions(TAG*tag, char*prefix)
167 oldTagPos = swf_GetTagPos(tag);
169 // scan DefineButton2 Record
171 swf_GetU16(tag); // Character ID
172 swf_GetU8(tag); // Flags;
174 offsetpos = swf_GetTagPos(tag); // first offset
177 while (swf_GetU8(tag)) // state -> parse ButtonRecord
178 { swf_GetU16(tag); // id
179 swf_GetU16(tag); // layer
180 swf_GetMatrix(tag,NULL); // matrix
181 swf_GetCXForm(tag,NULL,1); // cxform
188 if(tag->pos >= tag->len)
191 offsetpos = swf_GetU16(tag);
192 condition = swf_GetU16(tag); // condition
194 actions = swf_ActionGet(tag);
195 printf("%s condition %04x\n", prefix, condition);
196 swf_DumpActions(actions, prefix);
199 swf_SetTagPos(tag,oldTagPos);
203 void dumpButtonActions(TAG*tag, char*prefix)
206 swf_GetU16(tag); // id
207 while (swf_GetU8(tag)) // state -> parse ButtonRecord
208 { swf_GetU16(tag); // id
209 swf_GetU16(tag); // layer
210 swf_GetMatrix(tag,NULL); // matrix
212 actions = swf_ActionGet(tag);
213 swf_DumpActions(actions, prefix);
216 #define ET_HASTEXT 32768
217 #define ET_WORDWRAP 16384
218 #define ET_MULTILINE 8192
219 #define ET_PASSWORD 4096
220 #define ET_READONLY 2048
221 #define ET_HASTEXTCOLOR 1024
222 #define ET_HASMAXLENGTH 512
223 #define ET_HASFONT 256
226 #define ET_HASLAYOUT 32
227 #define ET_NOSELECT 16
231 #define ET_USEOUTLINES 1
237 void textcallback(int*glyphs, int nr, int fontid)
240 printf(" <%2d glyphs in font %2d> ",nr, fontid);
241 for(t=0;t<fontnum;t++)
243 if(fonts[t]->id == fontid) {
253 if(glyphs[t] >= fonts[font]->numchars /*glyph is in range*/
254 || !fonts[font]->glyph2ascii /* font has ascii<->glyph mapping */
257 a = fonts[font]->glyph2ascii[glyphs[t]];
264 printf("\\x%x", (int)a);
269 void handleText(TAG*tag)
272 swf_FontExtract_DefineTextCallback(-1,0,tag,4, textcallback);
275 void handleDefineSound(TAG*tag)
277 U16 id = swf_GetU16(tag);
278 U8 flags = swf_GetU8(tag);
279 int compression = (flags>>4)&3;
280 int rate = (flags>>2)&3;
281 int bits = flags&2?16:8;
282 int stereo = flags&1;
284 if(compression == 0) printf("Raw ");
285 else if(compression == 1) printf("ADPCM ");
286 else if(compression == 2) printf("MP3 ");
288 if(rate == 0) printf("5.5Khz ");
289 if(rate == 1) printf("11Khz ");
290 if(rate == 2) printf("22Khz ");
291 if(rate == 3) printf("44Khz ");
292 printf("%dBit ", bits);
293 if(stereo) printf("stereo");
298 void handleDefineBits(TAG*tag)
304 id = swf_GetU16(tag);
305 mode = swf_GetU8(tag);
306 width = swf_GetU16(tag);
307 height = swf_GetU16(tag);
308 printf(" image %dx%d",width,height);
309 if(mode == 3) printf(" (8 bpp)");
310 else if(mode == 4) printf(" (16 bpp)");
311 else if(mode == 5) printf(" (32 bpp)");
312 else printf(" (? bpp)");
315 void handleEditText(TAG*tag)
320 id = swf_GetU16(tag);
322 //swf_ResetReadBits(tag);
327 flags = swf_GetBits(tag,16);
328 if(flags & ET_HASFONT) {
329 swf_GetU16(tag); //font
330 swf_GetU16(tag); //fontheight
332 if(flags & ET_HASTEXTCOLOR) {
333 swf_GetU8(tag); //rgba
338 if(flags & ET_HASMAXLENGTH) {
339 swf_GetU16(tag); //maxlength
341 if(flags & ET_HASLAYOUT) {
342 swf_GetU8(tag); //align
343 swf_GetU16(tag); //left margin
344 swf_GetU16(tag); //right margin
345 swf_GetU16(tag); //indent
346 swf_GetU16(tag); //leading
348 printf(" variable \"%s\"", &tag->data[tag->pos]);
350 if(flags & (ET_X1 | ET_X2 | ET_X3 | ET_X0))
352 printf(" undefined flags: %d%d%d%d",
359 while(tag->data[tag->pos++]);
360 if(flags & ET_HASTEXT)
361 // printf(" text \"%s\"\n", &tag->data[tag->pos])
364 void printhandlerflags(U16 handlerflags)
366 if(handlerflags&1) printf("[on load]");
367 if(handlerflags&2) printf("[enter frame]");
368 if(handlerflags&4) printf("[unload]");
369 if(handlerflags&8) printf("[mouse move]");
370 if(handlerflags&16) printf("[mouse down]");
371 if(handlerflags&32) printf("[mouse up]");
372 if(handlerflags&64) printf("[key down]");
373 if(handlerflags&128) printf("[key up]");
374 if(handlerflags&256) printf("[data]");
375 if(handlerflags&0xfe00) printf("[???]");
377 void handleVideoStream(TAG*tag, char*prefix)
379 U16 id = swf_GetU16(tag);
380 U16 frames = swf_GetU16(tag);
381 U16 width = swf_GetU16(tag);
382 U16 height = swf_GetU16(tag);
383 U8 flags = swf_GetU8(tag); //5-2(videopacket 01=off 10=on)-1(smoothing 1=on)
384 U8 codec = swf_GetU8(tag);
385 printf(" (%d frames, %dx%d", frames, width, height);
389 printf(" sorenson h.263)");
391 printf(" codec 0x%02x)", codec);
393 void handleVideoFrame(TAG*tag, char*prefix)
395 U32 code, version, reference, sizeflags;
398 U16 id = swf_GetU16(tag);
399 U16 frame = swf_GetU16(tag);
400 U8 deblock,flags, tmp, bit;
402 char*types[] = {"I-frame", "P-frame", "disposable P-frame", "<reserved>"};
403 printf(" (frame %d) ", frame);
405 /* video packet follows */
406 code = swf_GetBits(tag, 17);
407 version = swf_GetBits(tag, 5);
408 reference = swf_GetBits(tag, 8);
410 sizeflags = swf_GetBits(tag, 3);
413 case 0: width = swf_GetBits(tag, 8); height = swf_GetBits(tag, 8); break;
414 case 1: width = swf_GetBits(tag, 16); height = swf_GetBits(tag, 16); break;
415 case 2: width = 352; height = 288; break;
416 case 3: width = 176; height = 144; break;
417 case 4: width = 128; height = 96; break;
418 case 5: width = 320; height = 240; break;
419 case 6: width = 160; height = 120; break;
420 case 7: width = -1; height = -1;/*reserved*/ break;
422 printf("%dx%d ", width, height);
423 type = swf_GetBits(tag, 2);
424 printf("%s", types[type]);
426 deblock = swf_GetBits(tag, 1);
428 printf(" deblock ", deblock);
429 quantizer = swf_GetBits(tag, 5);
430 printf(" quant: %d ", quantizer);
433 void handlePlaceObject2(TAG*tag, char*prefix)
435 U8 flags = swf_GetU8(tag);
439 int ppos[3] = {0,0,0};
440 swf_GetU16(tag); //depth
442 if(flags&2) swf_GetU16(tag); //id
444 swf_GetMatrix(tag,&m);
446 ppos[0] += sprintf(pstr[0], "| Matrix ");
447 ppos[1] += sprintf(pstr[1], "| %5.3f %5.3f %6.2f ", m.sx/65536.0, m.r1/65536.0, m.tx/20.0);
448 ppos[2] += sprintf(pstr[2], "| %5.3f %5.3f %6.2f ", m.r0/65536.0, m.sy/65536.0, m.ty/20.0);
452 swf_GetCXForm(tag, &cx, 1);
454 ppos[0] += sprintf(pstr[0]+ppos[0], "| CXForm r g b a ");
455 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);
456 ppos[2] += sprintf(pstr[2]+ppos[2], "| add %4d %4d %4d %4d ", cx.r1, cx.g1, cx.b1, cx.a1);
460 U16 ratio = swf_GetU16(tag); //ratio
462 ppos[0] += sprintf(pstr[0]+ppos[0], "| Ratio ");
463 ppos[1] += sprintf(pstr[1]+ppos[1], "| %-5d ", ratio);
464 ppos[2] += sprintf(pstr[2]+ppos[2], "| ");
468 U16 clip = swf_GetU16(tag); //clip
470 ppos[0] += sprintf(pstr[0]+ppos[0], "| Clip ");
471 ppos[1] += sprintf(pstr[1]+ppos[1], "| %-5d ", clip);
472 ppos[2] += sprintf(pstr[2]+ppos[2], "| ");
475 if(flags&32) { while(swf_GetU8(tag)); }
476 if(placements && ppos[0]) {
478 printf("%s %s\n", prefix, pstr[0]);
479 printf("%s %s\n", prefix, pstr[1]);
480 printf("%s %s", prefix, pstr[2]);
489 unknown = swf_GetU16(tag);
490 globalflags = swf_GetU16(tag);
492 printf("Unknown parameter field not zero: %04x\n", unknown);
495 printf("global flags: %04x\n", globalflags);
496 handlerflags = swf_GetU16(tag);
498 handlerflags = swf_GetU32(tag);
501 while(handlerflags) {
506 globalflags &= ~handlerflags;
507 printf("%s flags %08x ",prefix, handlerflags);
508 printhandlerflags(handlerflags);
509 length = swf_GetU32(tag);
510 printf(", %d bytes actioncode\n",length);
511 a = swf_ActionGet(tag);
512 swf_DumpActions(a,prefix);
515 handlerflags = is32?swf_GetU32(tag):swf_GetU16(tag);
517 if(globalflags) // should go to sterr.
518 printf("ERROR: unsatisfied handlerflags: %02x\n", globalflags);
520 printf(" has action code\n");
525 void handlePlaceObject(TAG*tag, char*prefix)
530 void fontcallback1(U16 id,U8 * name)
534 void fontcallback2(U16 id,U8 * name)
536 swf_FontExtract(&swf,id,&fonts[fontnum]);
540 void hexdumpTag(TAG*tag, char* prefix)
543 printf(" %s-=> ",prefix);
544 for(t=0;t<tag->len;t++) {
545 printf("%02x ", tag->data[t]);
546 if((t && ((t&15)==15)) || (t==tag->len-1))
551 printf("\n %s-=> ",prefix);
556 void handleExportAssets(TAG*tag, char* prefix)
562 num = swf_GetU16(tag);
565 id = swf_GetU16(tag);
566 name = swf_GetString(tag);
567 printf("%sexports %04d as \"%s\"\n", prefix, id, name);
571 void dumperror(const char* format, ...)
576 va_start(arglist, format);
577 vsprintf(buf, format, arglist);
581 printf("==== Error: %s ====\n", buf);
584 static char strbuf[800];
587 char* timestring(double f)
589 int hours = (int)(f/3600);
590 int minutes = (int)((f-hours*3600)/60);
591 int seconds = (int)((f-hours*3600-minutes*60));
592 int useconds = (int)((f-(int)f)*1000+0.5);
595 sprintf(&strbuf[bufpos], "%02d:%02d:%02d,%03d",hours,minutes,seconds,useconds);
596 return &strbuf[bufpos];
599 int main (int argc,char ** argv)
607 char issprite = 0; // are we inside a sprite definition?
610 char* spriteframelabel = 0;
611 char* framelabel = 0;
615 memset(idtab,0,65536);
617 processargs(argc, argv);
621 fprintf(stderr, "You must supply a filename.\n");
625 f = open(filename,O_RDONLY);
629 perror("Couldn't open file: ");
632 if FAILED(swf_ReadSWF(f,&swf))
634 fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
641 if(statbuf.st_size != swf.fileSize && !swf.compressed)
642 dumperror("Real Filesize (%d) doesn't match header Filesize (%d)",
643 statbuf.st_size, swf.fileSize);
644 filesize = statbuf.st_size;
649 xsize = (swf.movieSize.xmax-swf.movieSize.xmin)/20;
650 ysize = (swf.movieSize.ymax-swf.movieSize.ymin)/20;
654 printf("-X %d", xsize);
660 printf("-Y %d", ysize);
666 printf("-r %d", swf.frameRate*100/256);
672 printf("-f %d", swf.frameCount);
679 char*fileversions[] = {"","1,0,0,0", "2,0,0,0","3,0,0,0","4,0,0,0",
680 "5,0,0,0","6,0,23,0","7,0,0,0","8,0,0,0"};
681 if(swf.fileVersion>8) {
682 fprintf(stderr, "Fileversion>8\n");
685 printf("<OBJECT CLASSID=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\"\n"
687 //" BGCOLOR=#ffffffff\n"?
689 //http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,23,0?
690 " CODEBASE=\"http://active.macromedia.com/flash5/cabs/swflash.cab#version=%s\">\n"
691 " <PARAM NAME=\"MOVIE\" VALUE=\"%s\">\n"
692 " <PARAM NAME=\"PLAY\" VALUE=\"true\">\n"
693 " <PARAM NAME=\"LOOP\" VALUE=\"true\">\n"
694 " <PARAM NAME=\"QUALITY\" VALUE=\"high\">\n"
695 " <EMBED SRC=\"%s\" WIDTH=\"%d\" HEIGHT=\"%d\"\n" //bgcolor=#ffffff?
696 " PLAY=\"true\" ALIGN=\"\" LOOP=\"true\" QUALITY=\"high\"\n"
697 " TYPE=\"application/x-shockwave-flash\"\n"
698 " PLUGINSPAGE=\"http://www.macromedia.com/go/getflashplayer\">\n"
700 "</OBJECT>\n", xsize, ysize, fileversions[swf.fileVersion],
701 filename, filename, xsize, ysize);
704 printf("[HEADER] File version: %d\n", swf.fileVersion);
706 printf("[HEADER] File is zlib compressed.");
707 if(filesize && swf.fileSize)
708 printf(" Ratio: %02d%%\n", filesize*100/(swf.fileSize));
712 printf("[HEADER] File size: %ld%s\n", swf.fileSize, swf.compressed?" (Depacked)":"");
713 printf("[HEADER] Frame rate: %f\n",swf.frameRate/256.0);
714 printf("[HEADER] Frame count: %d\n",swf.frameCount);
715 printf("[HEADER] Movie width: %.2f",(swf.movieSize.xmax-swf.movieSize.xmin)/20.0);
716 if(swf.movieSize.xmin)
717 printf(" (left offset: %.2f)\n", swf.movieSize.xmin/20.0);
720 printf("[HEADER] Movie height: %.2f",(swf.movieSize.ymax-swf.movieSize.ymin)/20.0);
721 if(swf.movieSize.ymin)
722 printf(" (top offset: %.2f)\n", swf.movieSize.ymin/20.0);
730 swf_FontEnumerate(&swf,&fontcallback1);
731 fonts = (SWFFONT**)malloc(fontnum*sizeof(SWFFONT*));
733 swf_FontEnumerate(&swf,&fontcallback2);
737 char*name = swf_TagGetName(tag);
740 dumperror("Unknown tag:0x%03x", tag->id);
744 printf("[%03x] %9ld %s%s", tag->id, tag->len, prefix, swf_TagGetName(tag));
746 if(tag->id == ST_FREECHARACTER) {
747 U16 id = swf_GetU16(tag);
751 if(swf_isDefiningTag(tag)) {
752 U16 id = swf_GetDefineID(tag);
753 printf(" defines id %04d", id);
755 dumperror("Id %04d is defined more than once.", id);
758 else if(swf_isPseudoDefiningTag(tag)) {
759 U16 id = swf_GetDefineID(tag);
760 printf(" adds information to id %04d", id);
762 dumperror("Id %04d is not yet defined.\n", id);
764 else if(tag->id == ST_PLACEOBJECT) {
765 printf(" places id %04d at depth %04x", swf_GetPlaceID(tag), swf_GetDepth(tag));
767 printf(" name \"%s\"",swf_GetName(tag));
768 handlePlaceObject(tag, myprefix);
770 else if(tag->id == ST_PLACEOBJECT2) {
777 printf(" id %04d",swf_GetPlaceID(tag));
781 printf(" at depth %04d", swf_GetDepth(tag));
783 printf(" name \"%s\"",swf_GetName(tag));
785 else if(tag->id == ST_REMOVEOBJECT) {
786 printf(" removes id %04d from depth %04d", swf_GetPlaceID(tag), swf_GetDepth(tag));
788 else if(tag->id == ST_REMOVEOBJECT2) {
789 printf(" removes object from depth %04d", swf_GetDepth(tag));
791 else if(tag->id == ST_FREECHARACTER) {
792 printf(" frees object %04d", swf_GetPlaceID(tag));
794 else if(tag->id == ST_STARTSOUND) {
797 id = swf_GetU16(tag);
798 flags = swf_GetU8(tag);
800 printf(" stops sound with id %04d", id);
802 printf(" starts sound with id %04d", id);
804 printf(" (if not already playing)");
810 printf(" looping %d times", swf_GetU16(tag));
813 else if(tag->id == ST_FRAMELABEL) {
814 int l = strlen(tag->data);
815 printf(" \"%s\"", tag->data);
817 printf(" has %d extra bytes", tag->len-1-l);
818 if(tag ->len-1-l == 1 && tag->data[tag->len-1] == 1)
821 if((framelabel && !issprite) ||
822 (spriteframelabel && issprite)) {
823 dumperror("Frame %d has more than one label",
824 issprite?spriteframe:mainframe);
826 if(issprite) spriteframelabel = tag->data;
827 else framelabel = tag->data;
829 else if(tag->id == ST_SHOWFRAME) {
830 char*label = issprite?spriteframelabel:framelabel;
831 int frame = issprite?spriteframe:mainframe;
834 while(tag->next && tag->next->id == ST_SHOWFRAME && tag->next->len == 0) {
836 if(issprite) spriteframe++;
842 printf(" %d (%s)", frame, timestring(frame*(256.0/(swf.frameRate+0.1))));
844 printf(" %d-%d (%s-%s)", frame, nframe,
845 timestring(frame*(256.0/(swf.frameRate+0.1))),
846 timestring(nframe*(256.0/(swf.frameRate+0.1)))
849 printf(" (label \"%s\")", label);
850 if(issprite) {spriteframe++; spriteframelabel = 0;}
851 if(!issprite) {mainframe++; framelabel = 0;}
854 if(tag->id == ST_SETBACKGROUNDCOLOR) {
855 U8 r = swf_GetU8(tag);
856 U8 g = swf_GetU8(tag);
857 U8 b = swf_GetU8(tag);
858 printf(" (%02x/%02x/%02x)\n",r,g,b);
860 else if(tag->id == ST_DEFINEBITSLOSSLESS ||
861 tag->id == ST_DEFINEBITSLOSSLESS2) {
862 handleDefineBits(tag);
865 else if(tag->id == ST_DEFINESOUND) {
866 handleDefineSound(tag);
869 else if(tag->id == ST_VIDEOFRAME) {
870 handleVideoFrame(tag, myprefix);
873 else if(tag->id == ST_DEFINEVIDEOSTREAM) {
874 handleVideoStream(tag, myprefix);
877 else if(tag->id == ST_DEFINEEDITTEXT) {
881 else if(tag->id == ST_DEFINETEXT || tag->id == ST_DEFINETEXT2) {
887 else if(tag->id == ST_PLACEOBJECT2) {
889 else if(tag->id == ST_NAMECHARACTER) {
891 printf(" \"%s\"\n", swf_GetString(tag));
897 sprintf(myprefix, " %s", prefix);
899 if(tag->id == ST_DEFINESPRITE) {
900 sprintf(prefix, " ");
902 dumperror("Sprite definition inside a sprite definition");
906 spriteframelabel = 0;
908 else if(tag->id == ST_END) {
911 spriteframelabel = 0;
913 dumperror("End Tag not empty");
915 else if(tag->id == ST_EXPORTASSETS) {
916 handleExportAssets(tag, myprefix);
918 else if(tag->id == ST_DOACTION && action) {
920 actions = swf_ActionGet(tag);
921 swf_DumpActions(actions, myprefix);
923 else if(tag->id == ST_DEFINEBUTTON && action) {
924 dumpButtonActions(tag, myprefix);
926 else if(tag->id == ST_DEFINEBUTTON2 && action) {
927 dumpButton2Actions(tag, myprefix);
929 else if(tag->id == ST_PLACEOBJECT2) {
930 handlePlaceObject2(tag, myprefix);
933 if(tag->len && used) {
934 int num = swf_GetNumUsedIDs(tag);
938 used = (int*)malloc(sizeof(int)*num);
939 swf_GetUsedIDs(tag, used);
940 printf("%s%suses IDs: ", indent, prefix);
942 swf_SetTagPos(tag, used[t]);
943 printf("%d%s", swf_GetU16(tag), t<num-1?", ":"");
949 if(tag->len && hex) {
950 hexdumpTag(tag, prefix);