1 /* -*- indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2004-2006
21 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
42 import flash
.utils
.ByteArray
46 include "abc-constants.as"
49 "Displays the contents of abc or swf files",
53 " abcdump [options] file ...",
55 "Each file can be an ABC or SWF/SWC format file",
59 " -a Extract the ABC blocks from the SWF/SWC, but do not",
60 " otherwise display their contents. The file names are",
61 " of the form file<n>.abc where \"file\" is the name",
62 " of the input file minus the .swf/.swc extension;",
63 " and <n> is omitted if it is 0.",
65 " -i Print information about the ABC, but do not dump the byte code.",
67 " -abs Print the bytecode, but no information about the ABC",
68 " -api Print the public API exposed by this abc/swf",
69 " -mdversions Use in conjunction with -api when the abc/swf uses old-style versioning",
70 " -pools Print out the contents of the constant pools",
71 " --decompress-only Write out a decompressed version of the swc and exit"
77 var opSizes
:Array = new Array(256)
78 var opCounts
:Array = new Array(256)
80 function dumpPrint
(s
) {
85 function infoPrint
(s
) {
87 print
((doExtractAbs
? "// " : "") + s
)
90 function toStringNull
(x
) {
91 return (x
== null) ? "<null>" : x
.toString
()
94 // keep track of old-style versioning metadata in a global stack
95 var currentVersionMetadata
:Array = new Array()
96 var apiVersionNames
:Array = ["9", "air1", "10", "air1.5", "air1.5.1",
97 "10.0.32", "air1.5.2", "10.1", "air2",
100 function getVersionMetadata
():Array {
101 if (currentVersionMetadata
.length
> 0)
102 return currentVersionMetadata
[currentVersionMetadata
.length
- 1]
107 function pushVersionMetadata
(md
:Array):Boolean {
112 for each(var m
in md
) {
113 if(m
.name
== "Version")
114 for each(var t
:Tuple
in m
.tuples
)
117 for each(var t
:Tuple
in m
.tuples
)
118 vers
.push
(currentVersionMetadata
[int(t
.value
) - 660])
121 if(vers
.length
> 0) {
122 currentVersionMetadata
.push
(vers
)
128 function popVersionMetadata
(cond
:Boolean):void {
130 currentVersionMetadata
.pop
()
136 public var uri
:String
137 public var apiVersions
:Array = new Array()
139 public function ABCNamespace
(ns
:String, k
:int = 0x08 /* CONSTANT_Namespace */) {
141 uri
= ns
== null ? null : stripVersioningChars
(ns
)
144 public function clone
():ABCNamespace
{
145 var ns
:ABCNamespace
= new ABCNamespace
(uri
, kind
)
146 ns
.apiVersions
= apiVersions
.concat
()
150 function stripVersioningChars
(s
:String) {
151 var c
:int = s
.charCodeAt
(s
.length
-1)
154 apiVersions
.push
(apiVersionNames
[c
- 0xE294])
155 return stripVersioningChars
(s
.slice
(0,s
.length
-1))
160 public function toString
(useMD
:Boolean = false):String {
161 var vers
:Array = useMD
? getVersionMetadata
() : apiVersions
162 return (uri
== null ? "" : uri
) + (vers
.length
> 0 && doDumpAPI
? ("[api: " + vers
.join
(',') + "]") : "") // + ("(" + constantKinds[kind] + ")")
165 public function isHidden
():Boolean {
166 return (kind
== CONSTANT_PrivateNs
|| kind
== CONSTANT_PackageInternalNs
)
172 public var ns
:ABCNamespace
173 public var localname
:String
175 public function QualifiedName
(n
:ABCNamespace
, ln
:String) {
180 public function toString
(useMD
:Boolean = false):String {
181 var nsstr
= (ns
== null ? "" : ns
.toString
(useMD
))
182 return (nsstr
== "" ? "" : nsstr
+ "::") + localname
185 public function isHidden
():Boolean {
194 function Multiname
(nsset
:Array, name
:String)
200 public function toString
(useMD
:Boolean = false)
202 if (nsset
.length
== 1)
203 return (new QualifiedName
(nsset
[0], name
).toString
(useMD
))
205 return '{' + joinNsset
(nsset
, useMD
) + '}::' + toStringNull
(name
)
208 function joinNsset
(nsset
:Array, useMD
:Boolean):String {
210 for each (var ns
:ABCNamespace
in nsset
) {
211 s
+= (ns
.toString
(useMD
) + ", ")
216 public function isHidden
():Boolean {
217 for each(var ns
in nsset
)
223 public static function createMultiname
(nsset
:Array, name
:String, flatten
:Boolean) {
225 var cur
:ABCNamespace
= nsset
[0].clone
()
226 for(var i
:int=1; i
<nsset
.length
; i
++) {
227 var ns
:ABCNamespace
= nsset
[i
]
228 if (cur
.uri
== ns
.uri
) {
229 for each(var v
:String in ns
.apiVersions
)
230 if(cur
.apiVersions
.indexOf
(v
) == -1)
231 cur
.apiVersions
.push
(v
)
239 return new QualifiedName
(cur
, name
)
241 return new Multiname
(nsset
, name
)
249 function TypeName
(name
, types
)
255 public function toString
()
257 var s
: String = name
.toString
();
259 for( var i
= 0; i
< types
.length
; ++i
)
260 s
+= types
[i
] != null ? types
[i
].toString
() : "*" + " ";
268 public var key
:String
269 public var value
:String
271 function Tuple
(k
:String, v
:String) {
276 public function toString
() {
277 return key
+ "=" + value
283 public var name
:String
284 public var tuples
:Vector
.<Tuple
> = new Vector
.<Tuple
>()
286 public function toString
():String
289 var s
:String = last
= '['+name
+'('
290 for each (var t
:Tuple
in tuples
)
291 s
= (last
= s
+ t
.key
+ "=" + '"' + t
.value
+ '"') + ','
295 public function addPair
(k
:String, v
:String) {
296 tuples
.push
(new Tuple
(k
, v
))
308 dynamic class LabelInfo
311 function labelFor
(target
:int):String
315 return this[target
] = "L" + (++count
)
328 class MethodInfo
extends MemberInfo
343 var exceptions
// ExceptionInfo[]
344 var activation
:Traits
346 public function toString
():String
351 public function format
():String
353 var name
= this.name
? (this.name
is String ? this.name
: this.name
.toString
(useMetadataVersions
)) : "function"
355 return name
+ "(" + paramTypes
+ "):" + returnType
+ (doDumpAPI
? "" : "\t/* disp_id=" + id
+ " method_id=" + method_id
+ " */")
358 function dump
(abc
:Abc
, indent
:String, attr
:String="")
360 if(doDumpAPI
&& name
&& (name
is String || name
.isHidden
()))
366 if (metadata
&& !doDumpAPI
) {
367 for each (var md
in metadata
)
371 var mdpushed
:Boolean = pushVersionMetadata
(metadata
)
374 if (flags
& NATIVE
&& !doDumpAPI
)
376 s
+= traitKinds
[kind
] + " "
378 dumpPrint
(indent
+attr
+s
+format
())
379 if (code
&& !doDumpAPI
)
381 dumpPrint
(indent
+"{")
382 var oldindent
= indent
384 if (flags
& NEED_ACTIVATION
) {
385 dumpPrint
(indent
+"activation {")
386 activation
.dump
(abc
, indent
+TAB
, "")
387 dumpPrint
(indent
+"}")
389 dumpPrint
(indent
+"// local_count="+local_count
+
390 " max_scope=" + max_scope
+
391 " max_stack=" + max_stack
+
392 " framesize=" + (local_count
+ max_scope
+ max_stack
) +
393 " code_len=" + code
.length
+
394 " code_offset=" + code_offset
)
396 var labels
:LabelInfo
= new LabelInfo
()
397 while (code
.bytesAvailable
> 0)
399 var start
:int = code
.position
400 var s
= indent
+ start
401 while (s
.length
< 12) s
+= ' ';
402 var opcode
= code
.readUnsignedByte
()
404 if (opcode
== OP_label
|| ((code
.position
-1) in labels
)) {
406 dumpPrint
(indent
+ labels
.labelFor
(code
.position
-1) + ": ")
410 s
+= opNames
[opcode
].length
< 8 ? "\t\t" : "\t"
416 s
+= '"' + abc
.strings
[readU32
()].replace
(/\n/g
,"\\n").replace
(/\t/g
,"\\t") + '"'
418 case OP_pushnamespace
:
419 s
+= abc
.namespaces
[readU32
()]
422 var i
:int = abc
.ints
[readU32
()]
423 s
+= i
+ "\t// 0x" + i
.toString
(16)
426 var u
:uint = abc
.uints
[readU32
()]
427 s
+= u
+ "\t// 0x" + u
.toString
(16)
430 s
+= abc
.doubles
[readU32
()]
435 case OP_initproperty
:
438 case OP_findpropstrict
:
439 case OP_findproperty
:
441 case OP_deleteproperty
:
445 case OP_getdescendants
:
446 s
+= abc
.names
[readU32
()]
448 case OP_constructprop
:
449 case OP_callproperty
:
452 case OP_callsupervoid
:
453 case OP_callpropvoid
:
454 s
+= abc
.names
[readU32
()]
455 s
+= " (" + readU32
() + ")"
457 case OP_newfunction
: {
458 var method_id
= readU32
()
459 s
+= abc
.methods
[method_id
]
463 s
+= abc
.methods
[readU32
()]
464 s
+= " (" + readU32
() + ")"
467 s
+= abc
.instances
[readU32
()]
469 case OP_lookupswitch
:
470 var pos
= code
.position
-1;
471 var target
= pos
+ readS24
()
472 var maxindex
= readU32
()
473 s
+= "default:" + labels
.labelFor
(target
) // target + "("+(target-pos)+")"
474 s
+= " maxcase:" + maxindex
475 for (var i
:int=0; i
<= maxindex
; i
++) {
476 target
= pos
+ readS24
();
477 s
+= " " + labels
.labelFor
(target
) // target + "("+(target-pos)+")"
481 case OP_iftrue
: case OP_iffalse
:
482 case OP_ifeq
: case OP_ifne
:
483 case OP_ifge
: case OP_ifnge
:
484 case OP_ifgt
: case OP_ifngt
:
485 case OP_ifle
: case OP_ifnle
:
486 case OP_iflt
: case OP_ifnlt
:
487 case OP_ifstricteq
: case OP_ifstrictne
:
488 var offset
= readS24
()
489 var target
= code
.position
+offset
490 //s += target + " ("+offset+")"
491 s
+= labels
.labelFor
(target
)
492 if (!((code
.position
) in labels
))
504 case OP_getglobalslot
:
506 case OP_setglobalslot
:
510 case OP_getouterscope
:
514 s
+= code
.readUnsignedByte
()
516 s
+= " " + code
.readUnsignedByte
()
520 s
+= "{" + readU32
() + "}"
523 s
+= "[" + readU32
() + "]"
527 case OP_constructsuper
:
529 s
+= "(" + readU32
() + ")"
532 case OP_getscopeobject
:
536 s
+= readU32
() + " " + readU32
()
538 /*if (opNames[opcode] == ("0x"+opcode.toString(16).toUpperCase()))
539 s += " UNKNOWN OPCODE"*/
542 var size
:int = code
.position
- start
544 opSizes
[opcode
] = int(opSizes
[opcode
]) + size
545 opCounts
[opcode
] = int(opCounts
[opcode
]) + 1
549 for each (var ex
in exceptions
)
550 dumpPrint
(indent
+ "// handler [" + ex
.from
+ ", " + ex
.to
+ "] -> " + ex
.target
+
551 (ex
.name
? (" " + ex
.name
+ ":" + ex
.type
) : (" :" + ex
.type
)));
553 dumpPrint
(oldindent
+"}\n")
556 popVersionMetadata
(mdpushed
)
559 function readU32
():int
561 var result
:int = code
.readUnsignedByte
();
562 if (!(result
& 0x00000080))
564 result
= result
& 0x0000007f | code
.readUnsignedByte
()<<7;
565 if (!(result
& 0x00004000))
567 result
= result
& 0x00003fff | code
.readUnsignedByte
()<<14;
568 if (!(result
& 0x00200000))
570 result
= result
& 0x001fffff | code
.readUnsignedByte
()<<21;
571 if (!(result
& 0x10000000))
573 return result
& 0x0fffffff | code
.readUnsignedByte
()<<28;
576 function readS24
():int
578 var b
:int = code
.readUnsignedByte
()
579 b
|= code
.readUnsignedByte
()<<8
580 b
|= code
.readByte
()<<16
585 class SlotInfo
extends MemberInfo
589 public function format
():String
591 return traitKinds
[kind
] + " " + name
.toString
(useMetadataVersions
) + ":" + type
+
592 (value
!== undefined ? (" = " + (value
is String ? ('"'+value
+'"') : value
)) : "") +
593 (doDumpAPI
? "" : "\t/* slot_id " + id
+ " */")
595 function dump
(abc
:Abc
, indent
:String, attr
:String="")
597 if(doDumpAPI
&& name
.isHidden
())
600 var mdpushed
:Boolean = pushVersionMetadata
(metadata
)
602 if (kind
== TRAIT_Const
|| kind
== TRAIT_Slot
)
604 if (metadata
&& !doDumpAPI
) {
605 for each (var md
in metadata
)
608 dumpPrint
(indent
+attr
+format
())
609 popVersionMetadata
(mdpushed
)
614 var ct
:Traits
= value
615 var it
:Traits
= ct
.itraits
617 if (metadata
&& !doDumpAPI
) {
618 for each (var md
in metadata
)
622 if (it
.flags
& CLASS_FLAG_interface
)
626 if (!(it
.flags
& CLASS_FLAG_sealed
))
627 def
= "dynamic " + def
;
628 if (it
.flags
& CLASS_FLAG_final
)
629 def
= "final " + def
;
632 dumpPrint
(indent
+attr
+def
+" "+name
.toString
(useMetadataVersions
)+" extends "+it
.base
)
633 var oldindent
= indent
635 if (it
.interfaces
.length
> 0)
636 dumpPrint
(indent
+"implements "+it
.interfaces
)
639 var prefix
:String = indent
+attr
+def
+" "+name
.toString
(useMetadataVersions
)+" "
640 it
.init
.dump
(abc
,prefix
)
641 it
.dump
(abc
,indent
,prefix
)
642 ct
.dump
(abc
,indent
,prefix
+ "static ")
643 ct
.init
.dump
(abc
,indent
,prefix
+ "static ")
645 dumpPrint
(oldindent
+"{")
646 it
.init
.dump
(abc
,indent
)
648 ct
.dump
(abc
,indent
,"static ")
649 ct
.init
.dump
(abc
,indent
,"static ")
650 dumpPrint
(oldindent
+"}\n")
653 popVersionMetadata
(mdpushed
)
664 var protectedNs
:ABCNamespace
665 const interfaces
:Array = []
666 const names
:Object = {}
667 const slots
:Array = []
668 const methods
:Array = []
669 const members
:Array = []
671 public function toString
():String
676 public function dump
(abc
:Abc
, indent
:String, attr
:String="")
678 for each (var m
in members
)
679 m
.dump
(abc
, indent
, attr
)
685 private var data
:ByteArray
698 var defaults
:Array = new Array(constantKinds
.length
)
706 var publicNs
= new ABCNamespace
("")
707 var anyNs
= new ABCNamespace
("*")
711 function Abc
(data
:ByteArray)
715 magic
= data
.readInt
()
717 infoPrint
("magic " + magic
.toString
(16))
719 if (magic
!= (46<<16|14) && magic
!= (46<<16|15) && magic
!= (46<<16|16))
720 throw new Error("not an abc file. magic=" + magic
.toString
(16))
724 defaults
[CONSTANT_Utf8
] = strings
725 defaults
[CONSTANT_Int
] = ints
726 defaults
[CONSTANT_UInt
] = uints
727 defaults
[CONSTANT_Double
] = doubles
728 defaults
[CONSTANT_Int
] = ints
729 defaults
[CONSTANT_False
] = { 10:false }
730 defaults
[CONSTANT_True
] = { 11:true }
731 defaults
[CONSTANT_Namespace
] = namespaces
732 defaults
[CONSTANT_PrivateNs
] = namespaces
733 defaults
[CONSTANT_PackageNs
] = namespaces
734 defaults
[CONSTANT_PackageInternalNs
] = namespaces
735 defaults
[CONSTANT_ProtectedNs
] = namespaces
736 defaults
[CONSTANT_StaticProtectedNs
] = namespaces
737 defaults
[CONSTANT_StaticProtectedNs2
] = namespaces
738 defaults
[CONSTANT_Null
] = { 12: null }
747 if (doExtractAbc
==true)
748 File.writeByteArray
(nextAbcFname
(), data
);
751 function readU32
():int
753 var result
:int = data
.readUnsignedByte
();
754 if (!(result
& 0x00000080))
756 result
= result
& 0x0000007f | data
.readUnsignedByte
()<<7;
757 if (!(result
& 0x00004000))
759 result
= result
& 0x00003fff | data
.readUnsignedByte
()<<14;
760 if (!(result
& 0x00200000))
762 result
= result
& 0x001fffff | data
.readUnsignedByte
()<<21;
763 if (!(result
& 0x10000000))
765 return result
& 0x0fffffff | data
.readUnsignedByte
()<<28;
768 function dumpPool
(name
:String, pool
:Array)
773 for(var i
:int = 0; i
<pool
.length
; i
++)
774 infoPrint
(name
+ "[" + i
+ "] = " + pool
[i
])
777 function padString
(s
:String, l
:uint)
780 return padString
(s
+ " ", l
)
784 function parseCpool
()
790 var start
:int = data
.position
795 for (i
=1; i
< n
; i
++)
797 dumpPool
("int", ints
)
802 for (i
=1; i
< n
; i
++)
803 uints
[i
] = uint(readU32
())
804 dumpPool
("uint", uints
)
809 for (i
=1; i
< n
; i
++)
810 doubles
[i
] = data
.readDouble
()
811 dumpPool
("double", doubles
)
813 infoPrint
("Cpool numbers size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+" %")
814 start
= data
.position
819 for (i
=1; i
< n
; i
++)
820 strings
[i
] = data
.readUTFBytes
(readU32
())
821 dumpPool
("string", strings
)
823 infoPrint
("Cpool strings count "+ n
+" size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+" %")
824 start
= data
.position
828 namespaces
= [publicNs
]
830 for (i
=1; i
< n
; i
++) {
831 switch (nskind
= data
.readByte
())
833 case CONSTANT_Namespace
:
834 case CONSTANT_PackageNs
:
835 case CONSTANT_PackageInternalNs
:
836 case CONSTANT_ProtectedNs
:
837 case CONSTANT_StaticProtectedNs
:
838 case CONSTANT_StaticProtectedNs2
:
840 namespaces
[i
] = new ABCNamespace
(strings
[readU32
()], nskind
)
843 case CONSTANT_PrivateNs
:
845 namespaces
[i
] = new ABCNamespace
("private", nskind
)
849 infoPrint
("namespace[" + i
+ "] = " + padString
(constantKinds
[nskind
], 20) + ": " + namespaces
[i
].uri
)
852 infoPrint
("Cpool namespaces count "+ n
+" size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+" %")
853 start
= data
.position
858 for (i
=1; i
< n
; i
++)
860 var count
:int = readU32
()
861 var nsset
= nssets
[i
] = []
863 for (j
=0; j
< count
; j
++) {
866 nsset
[j
] = namespaces
[nsid
]
869 infoPrint
("nsset[" + i
+ "] = {" + nsids
.join
(", ") + "}")
872 infoPrint
("Cpool nssets count "+ n
+" size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+" %")
873 start
= data
.position
878 namespaces
[0] = anyNs
879 strings
[0] = "*" // any name
880 for (i
=1; i
< n
; i
++) {
881 var nametype
= data
.readByte
()
885 case CONSTANT_QnameA
:
886 names
[i
] = new QualifiedName
(namespaces
[readU32
()], strings
[readU32
()])
889 case CONSTANT_RTQname
:
890 case CONSTANT_RTQnameA
:
891 names
[i
] = new QualifiedName
(null, strings
[readU32
()])
894 case CONSTANT_RTQnameL
:
895 case CONSTANT_RTQnameLA
:
900 case CONSTANT_NameLA
:
901 names
[i
] = new QualifiedName
(publicNs
, null)
904 case CONSTANT_Multiname
:
905 case CONSTANT_MultinameA
:
906 var name
= strings
[readU32
()]
907 names
[i
] = Multiname
.createMultiname
(nssets
[readU32
()], name
, doDumpAPI
)
910 case CONSTANT_MultinameL
:
911 case CONSTANT_MultinameLA
:
912 names
[i
] = Multiname
.createMultiname
(nssets
[readU32
()], null, doDumpAPI
)
915 case CONSTANT_TypeName
:
916 var name
= names
[readU32
()];
917 var count
= readU32
();
919 for( var t
=0; t
< count
; ++t
)
920 types
.push
(names
[readU32
()]);
921 names
[i
] = new TypeName
(name
, types
);
925 throw new Error("invalid kind " + data
[data
.position
-1])
928 infoPrint
("name[" + i
+ "] = " + padString
(constantKinds
[nametype
], 12) + ": " + names
[i
])
931 infoPrint
("Cpool names count "+ n
+" size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+" %")
932 start
= data
.position
934 namespaces
[0] = publicNs
938 function parseMethodInfos
()
940 var start
:int = data
.position
941 names
[0] = new QualifiedName
(publicNs
, "*")
942 var method_count
:int = readU32
()
944 for (var i
:int=0; i
< method_count
; i
++)
946 var m
= methods
[i
] = new MethodInfo
()
948 var param_count
:int = readU32
()
949 m
.returnType
= names
[readU32
()]
951 for (var j
:int=0; j
< param_count
; j
++)
952 m
.paramTypes
[j
] = names
[readU32
()]
953 m
.debugName
= strings
[readU32
()]
954 m
.flags
= data
.readByte
()
955 if (m
.flags
& HAS_OPTIONAL
)
958 var optional_count
:int = readU32
();
959 m
.optionalValues
= []
960 for( var k
:int = param_count
-optional_count
; k
< param_count
; ++k
)
962 var index
= readU32
() // optional value index
963 var kind
:int = data
.readByte
() // kind byte for each default value
966 // kind is ignored, default value is based on type
967 m
.optionalValues
[k
] = undefined
972 print
("ERROR kind="+kind
+" method_id " + i
)
974 m
.optionalValues
[k
] = defaults
[kind
][index
]
978 if (m
.flags
& HAS_ParamNames
)
981 for( var k
:int = 0; k
< param_count
; ++k
)
987 infoPrint
("MethodInfo count " +method_count
+ " size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+" %")
990 function parseMetadataInfos
()
992 var start
:int = data
.position
993 var count
:int = readU32
()
995 for (var i
:int=0; i
< count
; i
++)
998 var m
= metadata
[i
] = new MetaData
()
999 m
.name
= strings
[readU32
()];
1000 var values_count
:int = readU32
();
1001 var names
:Array = []
1003 var values
:Array = []
1005 for(var q
:int = 0; q
< values_count
; ++q
)
1006 keys
[q
] = strings
[readU32
()]
1007 for(var q
:int = 0; q
< values_count
; ++q
)
1008 values
[q
] = strings
[readU32
()]
1010 for(var q
:int = 0; q
< values_count
; ++q
)
1011 m
.addPair
(keys
[q
], values
[q
])
1014 infoPrint
("metadata[" + i
+ "] = {" + m
.tuples
.join
(", ") + "}")
1016 infoPrint
("MetadataInfo count " +values_count
+ " size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+" %")
1019 function parseInstanceInfos
()
1021 var start
:int = data
.position
1022 var count
:int = readU32
()
1024 for (var i
:int=0; i
< count
; i
++)
1026 var t
= instances
[i
] = new Traits
()
1027 t
.name
= names
[readU32
()]
1028 t
.base
= names
[readU32
()]
1029 t
.flags
= data
.readByte
()
1031 t
.protectedNs
= namespaces
[readU32
()]
1032 var interface_count
= readU32
()
1033 for (var j
:int=0; j
< interface_count
; j
++)
1034 t
.interfaces
[j
] = names
[readU32
()]
1035 var m
= t
.init
= methods
[readU32
()]
1037 m
.kind
= TRAIT_Method
1041 infoPrint
("InstanceInfo count " + count
+ " size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+" %")
1044 function parseTraits
(t
:Traits
)
1046 var namecount
= readU32
()
1047 for (var i
:int=0; i
< namecount
; i
++)
1049 var name
= names
[readU32
()]
1050 var tag
= data
.readByte
()
1051 var kind
= tag
& 0xf
1057 var slot
= member
= new SlotInfo
()
1059 t
.slots
[slot
.id
] = slot
1060 if (kind
==TRAIT_Slot
|| kind
==TRAIT_Const
)
1062 slot
.type
= names
[readU32
()]
1065 slot
.value
= defaults
[data
.readByte
()][index
]
1067 else // (kind == TRAIT_Class)
1069 slot
.value
= classes
[readU32
()]
1075 var disp_id
= readU32
()
1076 var method
= member
= methods
[readU32
()]
1077 t
.methods
[disp_id
] = method
1079 //print("\t",traitKinds[kind],name,disp_id,method,"// disp_id", disp_id)
1083 print
("error trait kind "+kind
)
1086 t
.names
[String(name
)] = t
.members
[i
] = member
1088 if ( (tag
>> 4) & ATTR_metadata
) {
1089 member
.metadata
= []
1090 for(var j
:int=0, mdCount
:int=readU32
(); j
< mdCount
; ++j
)
1091 member
.metadata
[j
] = metadata
[readU32
()]
1096 function parseClassInfos
()
1098 var start
:int = data
.position
1099 var count
:int = instances
.length
1101 for (var i
:int=0; i
< count
; i
++)
1103 var t
:Traits
= classes
[i
] = new Traits
()
1104 t
.init
= methods
[readU32
()]
1106 t
.itraits
= instances
[i
]
1107 t
.name
= t
.itraits
.name
+ "$"
1108 t
.init
.name
= t
.itraits
.name
+ "$cinit"
1109 t
.init
.kind
= TRAIT_Method
1112 infoPrint
("ClassInfo count " + count
+ " size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+"%")
1115 function parseScriptInfos
()
1117 var start
:int = data
.position
1118 var count
:int = readU32
()
1120 for (var i
:int=0; i
< count
; i
++)
1122 var t
= new Traits
()
1124 t
.name
= "script" + i
1125 t
.base
= names
[0] // Object
1126 t
.init
= methods
[readU32
()]
1127 t
.init
.name
= t
.name
+ "$init"
1128 t
.init
.kind
= TRAIT_Method
1131 infoPrint
("ScriptInfo size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+" %")
1134 function parseMethodBodies
()
1136 var start
:int = data
.position
1137 var count
:int = readU32
()
1138 for (var i
:int=0; i
< count
; i
++)
1140 var m
= methods
[readU32
()]
1141 m
.max_stack
= readU32
()
1142 m
.local_count
= readU32
()
1143 var initScopeDepth
= readU32
()
1144 var maxScopeDepth
= readU32
()
1145 m
.max_scope
= maxScopeDepth
- initScopeDepth
1146 var code_length
= readU32
()
1147 m
.code
= new ByteArray()
1148 m
.code
.endian
= "littleEndian"
1149 if (code_length
> 0) {
1150 m
.code_offset
= data
.position
;
1151 data
.readBytes
(m
.code
, 0, code_length
)
1153 var ex_count
= readU32
()
1156 for (var j
:int = 0; j
< ex_count
; j
++)
1158 var ex
= new ExceptionInfo
()
1159 m
.exceptions
.push
(ex
)
1162 ex
.target
= readU32
()
1163 ex
.type
= names
[readU32
()]
1164 //print("magic " + magic.toString(16))
1165 //if (magic >= (46<<16|16))
1166 ex
.name
= names
[readU32
()];
1167 //infoPrint("exception method_id=" + i + " [" + ex.from + ", " + ex.to + "] " + ex.type + " -> " + ex.target)
1170 parseTraits
(m
.activation
= new Traits
)
1172 infoPrint
("MethodBodies count " + count
+ " size "+(data
.position
-start
)+" "+int(100*(data
.position
-start
)/data
.length
)+" %")
1175 function dump
(indent
:String="")
1177 for each (var t
in scripts
)
1179 infoPrint
(indent
+t
.name
)
1181 t
.init
.dump
(this,indent
)
1184 for each (var m
in methods
)
1190 infoPrint
(align
(14,"OPCODE",' ',"left")+"\tCOUNT\t SIZE\t% OF "+totalSize
)
1195 var maxsize
:int = 0;
1196 for (var i
:int=0; i
< 256; i
++)
1198 if (opSizes
[i
] > maxsize
&& !done
[i
])
1201 maxsize
= opSizes
[i
];
1207 infoPrint
(opNames
[max
]+"\t"+align
(6,int(opCounts
[max
]))+"\t"+align
(6,int(opSizes
[max
]))+"\t"+align
(2,int(100*opSizes
[max
]/totalSize
))+"%")
1211 // right/left align 's' to 'len' characters using 'pad' as padding
1212 function align
(len
, s
, pad
=' ', ment
='right')
1214 return (ment
== 'right')
1215 ? new Array(Math.max
(0,len
-String(s
).length
)).join
(pad
)+String(s
)
1216 : new String(s
)+Array(Math.max
(0,len
-String(s
).length
)).join
(pad
)
1223 var xMin
:int, xMax
:int
1224 var yMin
:int, yMax
:int
1225 public function toString
()
1227 return "[Rect "+xMin
+" "+yMin
+" "+xMax
+" "+yMax
+"]"
1231 const stagDoABC
:int = 72; // embedded .abc (AVM+) bytecode
1232 const stagSymbolClass
:int = 76;
1233 const stagMetadata
:int = 77;
1234 const stagDoABC2
:int = 82; // revised ABC version with a name
1236 var tagNames
:Array = [
1239 "DefineShape", // 02
1240 "FreeCharacter", // 03
1241 "PlaceObject", // 04
1242 "RemoveObject", // 05
1244 "DefineButton", // 07
1246 "SetBackgroundColor", // 09
1251 "DefineFontInfo", // 13
1253 "DefineSound", // 14
1257 "DefineButtonSound", // 17
1259 "SoundStreamHead", // 18
1260 "SoundStreamBlock", // 19
1262 "DefineBitsLossless", // 20
1263 "DefineBitsJPEG2", // 21
1265 "DefineShape2", // 22
1266 "DefineButtonCxform", // 23
1270 "PathsArePostScript", // 25
1272 "PlaceObject2", // 26
1273 "27 (invalid)", // 27
1274 "RemoveObject2", // 28
1277 "30 (invalid)", // 30
1280 "DefineShape3", // 32
1281 "DefineText2", // 33
1282 "DefineButton2", // 34
1283 "DefineBitsJPEG3", // 35
1284 "DefineBitsLossless2", // 36
1285 "DefineEditText", // 37
1287 "DefineVideo", // 38
1289 "DefineSprite", // 39
1290 "NameCharacter", // 40
1291 "ProductInfo", // 41
1292 "DefineTextFormat", // 42
1294 "DefineBehavior", // 44
1295 "SoundStreamHead2", // 45
1296 "DefineMorphShape", // 46
1298 "DefineFont2", // 48
1300 "DefineCommandObj", // 50
1301 "CharacterSet", // 51
1304 "DefineFunction", // 53
1305 "PlaceFunction", // 54
1307 "GenTagObject", // 55
1309 "ExportAssets", // 56
1310 "ImportAssets", // 57
1312 "EnableDebugger", // 58
1314 "DoInitAction", // 59
1315 "DefineVideoStream", // 60
1318 "DefineFontInfo2", // 62
1320 "EnableDebugger2", // 64
1321 "ScriptLimits", // 65
1323 "SetTabIndex", // 66
1325 "DefineShape4", // 67
1326 "DefineMorphShape2", // 68
1328 "FileAttributes", // 69
1330 "PlaceObject3", // 70
1331 "ImportAssets2", // 71
1334 "DefineFontAlignZones", // 73
1335 "CSMSettings", // 74
1336 "DefineFont3", // 75
1337 "SymbolClass", // 76
1339 "DefineScalingGrid", // 78
1340 "DefineDeviceVideo", // 79
1341 "80 (invalid)", // 80
1342 "81 (invalid)", // 81
1344 "DefineShape4", // 83
1345 "DefineMorphShape2", // 84
1346 "PlaceImagePrivate", // 85
1347 "DefineSceneAndFrameLabelData", // 86
1348 "DefineBinaryData", // 87
1349 "DefineFontName", // 88
1351 "DefineBitsJPEG64", // 90
1352 "DefineFont4", // 91
1358 private var bitPos
:int
1359 private var bitBuf
:int
1361 private var data
:ByteArray
1363 function Swf
(data
:ByteArray)
1366 infoPrint
("size "+decodeRect
())
1367 infoPrint
("frame rate "+(data
.readUnsignedByte
()<<8|data
.readUnsignedByte
()))
1368 infoPrint
("frame count "+data
.readUnsignedShort
())
1372 static function emitSwf
(fn
:String,data
:ByteArray,version
:uint)
1374 var ba
:ByteArray = new ByteArray
1375 ba
.endian
= "littleEndian"
1376 ba
.writeByte
(0x46); ba
.writeByte
(0x57); ba
.writeByte
(0x53); // FWS
1377 ba
.writeByte
(version
>>24)
1378 ba
.writeUnsignedInt
(data
.length
+8)
1379 ba
.writeBytes
(data
,0,data
.length
)
1380 File.writeByteArray
(fn
+".swf",ba
)
1381 infoPrint
("wrote "+ba
.length
+" bytes to file "+fn
+".swf")
1384 private function decodeTags
()
1386 var type
:int, h
:int, length
:int
1389 while (data
.position
< data
.length
)
1391 type
= (h
= data
.readUnsignedShort
()) >> 6;
1393 if (((length
= h
& 0x3F) == 0x3F))
1394 length
= data
.readInt
();
1396 var tagN
= tagNames
[type
]
1397 if (type
>= tagNames
.length
)
1398 tagN
= type
+" (unknown)"
1400 infoPrint
(tagN
+" "+length
+"b "+int(100*length
/data
.length
)+"%")
1405 var pos1
:int = data
.position
1407 infoPrint
("\nabc name "+readString
())
1408 length
-= (data
.position
-pos1
)
1411 var data2
= new ByteArray
1412 data2
.endian
= "littleEndian"
1413 data
.readBytes
(data2
,0,length
)
1414 new Abc
(data2
).dump
(" ")
1418 infoPrint
(new XML(readString
()));
1421 data
.position
+= length
1426 private function readString
():String
1431 while (c
=data
.readUnsignedByte
())
1432 s
+= String.fromCharCode
(c
)
1437 private function syncBits
()
1442 private function decodeRect
():Rect
1446 var rect
:Rect
= new Rect
();
1448 var nBits
:int = readUBits
(5)
1449 rect
.xMin
= readSBits
(nBits
);
1450 rect
.xMax
= readSBits
(nBits
);
1451 rect
.yMin
= readSBits
(nBits
);
1452 rect
.yMax
= readSBits
(nBits
);
1457 function readSBits
(numBits
:int):int
1460 throw new Error("Number of bits > 32");
1462 var num
:int = readUBits
(numBits
);
1463 var shift
:int = 32-numBits
;
1465 num
= (num
<< shift
) >> shift
;
1469 function readUBits
(numBits
:int):uint
1474 var bitsLeft
:int = numBits
;
1477 if (bitPos
== 0) //no value in the buffer - read a byte
1479 bitBuf
= data
.readUnsignedByte
()
1485 var shift
:int = bitsLeft
- bitPos
;
1488 // Consume the entire buffer
1489 result
|= bitBuf
<< shift
;
1492 // Get the next byte from the input stream
1493 bitBuf
= data
.readUnsignedByte
();
1498 // Consume a portion of the buffer
1499 result
|= bitBuf
>> -shift
;
1501 bitBuf
&= 0xff >> (8 - bitPos
); // mask off the consumed bits
1503 // if (print) System.out.println(" read"+numBits+" " + result);
1508 // unreachable, but fixes a spurious compiler warning
1519 function processArg
(arg
:String)
1522 doExtractAbc
= true;
1523 doExtractInfo
= false
1524 doExtractAbs
= false
1525 } else if (arg
== '-i') {
1526 // suppress abs output
1527 doExtractAbs
= false;
1528 } else if (arg
== '-abs') {
1529 // suppress info output
1530 doExtractInfo
= false
1531 } else if (arg
== '-api') {
1533 doExtractInfo
= false
1534 } else if (arg
== '-mdversions') {
1535 useMetadataVersions
= true
1536 } else if (arg
== '-pools') {
1538 } else if (arg
== '--decompress-only') {
1539 doDecompressOnly
= true
1541 print
('Unknown option '+arg
)
1546 function nextAbcFname
():String
1548 var s
= currentFname
1549 if (currentFcount
>0)
1550 s
= s
.concat
(currentFcount
);
1556 var doExtractAbc
= false
1557 var doExtractInfo
= true
1558 var doExtractAbs
= true
1559 var doDumpAPI
= false
1560 var doDumpPools
= false
1561 var doDecompressOnly
= false
1562 var useMetadataVersions
= false
1563 var currentFname
= ''
1564 var currentFcount
= 0
1565 for each (var file
in System.argv
)
1567 if (file
.indexOf
('-')==0)
1574 if ((x
= file
.lastIndexOf
(".swf")) != -1 || (x
= file
.lastIndexOf
(".swc")) != -1)
1575 currentFname
= file
.substring
(0,x
);
1577 currentFname
= file
;
1578 var data
:ByteArray = File.readByteArray
(file
)
1579 data
.endian
= "littleEndian"
1580 var version
:uint = data
.readUnsignedInt
()
1581 switch (version
&0xffffff) {
1585 var abc
:Abc
= new Abc
(data
)
1588 case 67|87<<8|83<<16: // SWC
1589 var udata
:ByteArray = new ByteArray
1590 udata
.endian
= "littleEndian"
1592 data
.readBytes
(udata
,0,data
.length
-data
.position
)
1593 var csize
:int = udata
.length
1595 infoPrint
("decompressed swf "+csize
+" -> "+udata
.length
)
1596 if (doDecompressOnly
) {
1597 Swf
.emitSwf
(file
,udata
,version
)
1601 /*var swf:Swf =*/ new Swf
(udata
)
1603 case 70|87<<8|83<<16: // SWF
1604 if (doDecompressOnly
)
1606 data
.position
= 8 // skip header and length
1607 /*var swf:Swf =*/ new Swf
(data
)
1610 print
('unknown format 0x'+version
.toString
(16))
1615 if (System.argv
.length
< 1)