Commits

Anonymous committed a006e65

parser/container/swf: handle SWF v9 doABC tag and allow v10 scripts to be parsed

  • Participants
  • Parent commits b94e407

Comments (0)

Files changed (2)

File hachoir-parser/hachoir_parser/container/action_script.py

-"""
-SWF (Macromedia/Adobe Flash) file parser.
-
-Documentation:
-
- - Alexis' SWF Reference:
-   http://www.m2osw.com/swf_alexref.html
-
-Author: Sebastien Ponce
-Creation date: 26 April 2008
-"""
-
-from hachoir_core.field import (FieldSet, ParserError,
-    Bit, Bits, UInt8, UInt32, Int16, UInt16, Float32, CString,
-    RawBytes)
-#from hachoir_core.field import Field
-from hachoir_core.field.float import FloatExponent
-from struct import unpack
-
-class FlashFloat64(FieldSet):
-    def createFields(self):
-        yield Bits(self, "mantisa_high", 20)
-        yield FloatExponent(self, "exponent", 11)
-        yield Bit(self, "negative")
-        yield Bits(self, "mantisa_low", 32)
-
-    def createValue(self):
-        # Manual computation:
-        # mantisa = mantisa_high * 2^32 + mantisa_low
-        # float = 2^exponent + (1 + mantisa / 2^52)
-        # (and float is negative if negative=True)
-        bytes = self.parent.stream.readBytes(
-            self.absolute_address, self.size//8)
-        # Mix bytes: xxxxyyyy <=> yyyyxxxx
-        bytes = bytes[4:8] + bytes[0:4]
-        return unpack('<d', bytes)[0]
-
-TYPE_INFO = {
-    0x00: (CString, "Cstring[]"),
-    0x01: (Float32, "Float[]"),
-    0x02: (None, "Null[]"),
-    0x03: (None, "Undefined[]"),
-    0x04: (UInt8, "Register[]"),
-    0x05: (UInt8, "Boolean[]"),
-    0x06: (FlashFloat64, "Double[]"),
-    0x07: (UInt32, "Integer[]"),
-    0x08: (UInt8, "Dictionnary_Lookup_Index[]"),
-    0x09: (UInt16, "Large_Dictionnary_Lookup_Index[]"),
-}
-
-def parseBranch(parent, size):
-    yield Int16(parent, "offset")
-
-def parseDeclareFunction(parent, size):
-    yield CString(parent, "name")
-    argCount = UInt16(parent, "arg_count")
-    yield argCount
-    for i in range(argCount.value):
-        yield CString(parent, "arg[]")
-    yield UInt16(parent, "function_length")
-
-def parseDeclareFunctionV7(parent, size):
-    yield CString(parent, "name")
-    argCount = UInt16(parent, "arg_count")
-    yield argCount
-    yield UInt8(parent, "reg_count")
-    yield Bits(parent, "reserved", 7)
-    yield Bit(parent, "preload_global")
-    yield Bit(parent, "preload_parent")
-    yield Bit(parent, "preload_root")
-    yield Bit(parent, "suppress_super")
-    yield Bit(parent, "preload_super")
-    yield Bit(parent, "suppress_arguments")
-    yield Bit(parent, "preload_arguments")
-    yield Bit(parent, "suppress_this")
-    yield Bit(parent, "preload_this")
-    for i in range(argCount.value):
-        yield UInt8(parent, "register[]")
-        yield CString(parent, "arg[]")
-    yield UInt16(parent, "function_length")
-
-def parseTry(parent, size):
-    yield Bits(parent, "reserved", 5)
-    catchInReg = Bit(parent, "catch_in_register")
-    yield catchInReg
-    yield Bit(parent, "finally")
-    yield Bit(parent, "catch")
-    yield UInt8(parent, "try_size")
-    yield UInt8(parent, "catch_size")
-    yield UInt8(parent, "finally_size")
-    if catchInReg.value:
-        yield CString(parent, "name")
-    else:
-        yield UInt8(parent, "register")
-
-def parsePushData(parent, size):
-    while not parent.eof:
-        codeobj = UInt8(parent, "data_type[]")
-        yield codeobj
-        code = codeobj.value
-        if code not in TYPE_INFO:
-            raise ParserError("Unknown type in Push_Data : " + hex(code))
-        parser, name = TYPE_INFO[code]
-        if parser:
-            yield parser(parent, name)
-#        else:
-#            yield Field(parent, name, 0)
-
-def parseSetTarget(parent, size):
-    yield CString(parent, "target")
-
-def parseWith(parent, size):
-    yield UInt16(parent, "size")
-
-def parseGetURL(parent, size):
-    yield CString(parent, "url")
-    yield CString(parent, "target")
-
-def parseGetURL2(parent, size):
-    yield UInt8(parent, "method")
-
-def parseGotoExpression(parent, size):
-    yield UInt8(parent, "play")
-
-def parseGotoFrame(parent, size):
-    yield UInt16(parent, "frame_no")
-
-def parseGotoLabel(parent, size):
-    yield CString(parent, "label")
-
-def parseWaitForFrame(parent, size):
-    yield UInt16(parent, "frame")
-    yield UInt8(parent, "skip")
-
-def parseWaitForFrameDyn(parent, size):
-    yield UInt8(parent, "skip")
-
-def parseDeclareDictionnary(parent, size):
-    count = UInt16(parent, "count")
-    yield count
-    for i in range(count.value):
-        yield CString(parent, "dictionnary[]")
-
-def parseStoreRegister(parent, size):
-    yield UInt8(parent, "register")
-
-def parseStrictMode(parent, size):
-    yield UInt8(parent, "strict")
-
-class Instruction(FieldSet):
-    ACTION_INFO = {
-        0x00: ("end[]", "End", None),
-        0x99: ("Branch_Always[]", "Branch Always", parseBranch),
-        0x9D: ("Branch_If_True[]", "Branch If True", parseBranch),
-        0x3D: ("Call_Function[]", "Call Function", None),
-        0x52: ("Call_Method[]", "Call Method", None),
-        0x9B: ("Declare_Function[]", "Declare Function", parseDeclareFunction),
-        0x8E: ("Declare_Function_V7[]", "Declare Function (V7)", parseDeclareFunctionV7),
-        0x3E: ("Return[]", "Return", None),
-        0x2A: ("Throw[]", "Throw", None),
-        0x8F: ("Try[]", "Try", parseTry),
-        # Stack Control
-        0x4C: ("Duplicate[]", "Duplicate", None),
-        0x96: ("Push_Data[]", "Push Data", parsePushData),
-        0x4D: ("Swap[]", "Swap", None),
-        # Action Script Context
-        0x8B: ("Set_Target[]", "Set Target", parseSetTarget),
-        0x20: ("Set_Target_dynamic[]", "Set Target (dynamic)", None),
-        0x94: ("With[]", "With", parseWith),
-        # Movie Control
-        0x9E: ("Call_Frame[]", "Call Frame", None),
-        0x83: ("Get_URL[]", "Get URL", parseGetURL),
-        0x9A: ("Get_URL2[]", "Get URL2", parseGetURL2),
-        0x9F: ("Goto_Expression[]", "Goto Expression", parseGotoExpression),
-        0x81: ("Goto_Frame[]", "Goto Frame", parseGotoFrame),
-        0x8C: ("Goto_Label[]", "Goto Label", parseGotoLabel),
-        0x04: ("Next_Frame[]", "Next Frame", None),
-        0x06: ("Play[]", "Play", None),
-        0x05: ("Previous_Frame[]", "Previous Frame", None),
-        0x07: ("Stop[]", "Stop", None),
-        0x08: ("Toggle_Quality[]", "Toggle Quality", None),
-        0x8A: ("Wait_For_Frame[]", "Wait For Frame", parseWaitForFrame),
-        0x8D: ("Wait_For_Frame_dynamic[]", "Wait For Frame (dynamic)", parseWaitForFrameDyn),
-        # Sound
-        0x09: ("Stop_Sound[]", "Stop Sound", None),
-        # Arithmetic
-        0x0A: ("Add[]", "Add", None),
-        0x47: ("Add_typed[]", "Add (typed)", None),
-        0x51: ("Decrement[]", "Decrement", None),
-        0x0D: ("Divide[]", "Divide", None),
-        0x50: ("Increment[]", "Increment", None),
-        0x18: ("Integral_Part[]", "Integral Part", None),
-        0x3F: ("Modulo[]", "Modulo", None),
-        0x0C: ("Multiply[]", "Multiply", None),
-        0x4A: ("Number[]", "Number", None),
-        0x0B: ("Subtract[]", "Subtract", None),
-        # Comparisons
-        0x0E: ("Equal[]", "Equal", None),
-        0x49: ("Equal_typed[]", "Equal (typed)", None),
-        0x66: ("Strict_Equal[]", "Strict Equal", None),
-        0x67: ("Greater_Than_typed[]", "Greater Than (typed)", None),
-        0x0F: ("Less_Than[]", "Less Than", None),
-        0x48: ("Less_Than_typed[]", "Less Than (typed)", None),
-        0x13: ("String_Equal[]", "String Equal", None),
-        0x68: ("String_Greater_Than[]", "String Greater Than", None),
-        0x29: ("String_Less_Than[]", "String Less Than", None),
-        # Logical and Bit Wise
-        0x60: ("And[]", "And", None),
-        0x10: ("Logical_And[]", "Logical And", None),
-        0x12: ("Logical_Not[]", "Logical Not", None),
-        0x11: ("Logical_Or[]", "Logical Or", None),
-        0x61: ("Or[]", "Or", None),
-        0x63: ("Shift_Left[]", "Shift Left", None),
-        0x64: ("Shift_Right[]", "Shift Right", None),
-        0x65: ("Shift_Right_Unsigned[]", "Shift Right Unsigned", None),
-        0x62: ("Xor[]", "Xor", None),
-        # Strings & Characters (See the String Object also)
-        0x33: ("Chr[]", "Chr", None),
-        0x37: ("Chr_multi-bytes[]", "Chr (multi-bytes)", None),
-        0x21: ("Concatenate_Strings[]", "Concatenate Strings", None),
-        0x32: ("Ord[]", "Ord", None),
-        0x36: ("Ord_multi-bytes[]", "Ord (multi-bytes)", None),
-        0x4B: ("String[]", "String", None),
-        0x14: ("String_Length[]", "String Length", None),
-        0x31: ("String_Length_multi-bytes[]", "String Length (multi-bytes)", None),
-        0x15: ("SubString[]", "SubString", None),
-        0x35: ("SubString_multi-bytes[]", "SubString (multi-bytes)", None),
-        # Properties
-        0x22: ("Get_Property[]", "Get Property", None),
-        0x23: ("Set_Property[]", "Set Property", None),
-        # Objects
-        0x2B: ("Cast_Object[]", "Cast Object", None),
-        0x42: ("Declare_Array[]", "Declare Array", None),
-        0x88: ("Declare_Dictionary[]", "Declare Dictionary", parseDeclareDictionnary),
-        0x43: ("Declare_Object[]", "Declare Object", None),
-        0x3A: ("Delete[]", "Delete", None),
-        0x3B: ("Delete_All[]", "Delete All", None),
-        0x24: ("Duplicate_Sprite[]", "Duplicate Sprite", None),
-        0x46: ("Enumerate[]", "Enumerate", None),
-        0x55: ("Enumerate_Object[]", "Enumerate Object", None),
-        0x69: ("Extends[]", "Extends", None),
-        0x4E: ("Get_Member[]", "Get Member", None),
-        0x45: ("Get_Target[]", "Get Target", None),
-        0x2C: ("Implements[]", "Implements", None),
-        0x54: ("Instance_Of[]", "Instance Of", None),
-        0x40: ("New[]", "New", None),
-        0x53: ("New_Method[]", "New Method", None),
-        0x25: ("Remove_Sprite[]", "Remove Sprite", None),
-        0x4F: ("Set_Member[]", "Set Member", None),
-        0x44: ("Type_Of[]", "Type Of", None),
-        # Variables
-        0x41: ("Declare_Local_Variable[]", "Declare Local Variable", None),
-        0x1C: ("Get_Variable[]", "Get Variable", None),
-        0x3C: ("Set_Local_Variable[]", "Set Local Variable", None),
-        0x1D: ("Set_Variable[]", "Set Variable", None),
-        # Miscellaneous
-        0x2D: ("FSCommand2[]", "FSCommand2", None),
-        0x34: ("Get_Timer[]", "Get Timer", None),
-        0x30: ("Random[]", "Random", None),
-        0x27: ("Start_Drag[]", "Start Drag", None),
-        0x28: ("Stop_Drag[]", "Stop Drag", None),
-        0x87: ("Store_Register[]", "Store Register", parseStoreRegister),
-        0x89: ("Strict_Mode[]", "Strict Mode", parseStrictMode),
-        0x26: ("Trace[]", "Trace", None),
-    }
-
-    def __init__(self, *args):
-        FieldSet.__init__(self, *args)
-        code = self["action_id"].value
-        if code & 128:
-            self._size = (3 + self["action_length"].value) * 8
-        else:
-            self._size = 8
-        if code in self.ACTION_INFO:
-            self._name, self._description, self.parser = self.ACTION_INFO[code]
-        else:
-            self.parser = None
-
-    def createFields(self):
-        yield Bits(self, "action_id", 8)
-        if not (self["action_id"].value & 128):
-            return
-        yield UInt16(self, "action_length")
-        size = self["action_length"].value
-        if not size:
-            return
-        if self.parser:
-            for field in self.parser(self, size):
-                yield field
-        else:
-            yield RawBytes(self, "action_data", size)
-
-    def createDescription(self):
-        return self._description
-
-    def __str__(self):
-        r = str(self._description)
-        for f in self:
-            if f.name not in ("action_id", "action_length", "count") and not f.name.startswith("data_type") :
-                r = r + "\n   " + str((self.address+f.address)/8) + " " + str(f.name) + "=" + str(f.value)
-        return r
-
-class ActionScript(FieldSet):
-    def createFields(self):
-        while not self.eof:
-            yield Instruction(self, "instr[]")
-
-    def __str__(self):
-        r = ""
-        for f in self:
-            r = r + str(f.address/8) + " " + str(f) + "\n"
-        return r
-
-def parseActionScript(parent, size):
-    yield ActionScript(parent, "action", size=size*8)
-
+"""
+SWF (Macromedia/Adobe Flash) file parser.
+
+Documentation:
+
+ - Alexis' SWF Reference:
+   http://www.m2osw.com/swf_alexref.html
+ - Tamarin ABC format:
+   http://www.m2osw.com/abc_format.html
+
+Authors: Sebastien Ponce, Robert Xiao
+Creation date: 26 April 2008
+"""
+
+from hachoir_parser import Parser
+from hachoir_core.field import (FieldSet, ParserError,
+    Bit, Bits, UInt8, UInt32, Int16, UInt16, Float32, Float64, CString, Enum,
+    Bytes, RawBytes, NullBits, String, SubFile, Field)
+from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
+from hachoir_core.field.float import FloatExponent
+from struct import unpack
+
+class FlashPackedInteger(Bits):
+    def __init__(self, parent, name, signed=False, nbits=30, description=None):
+        Bits.__init__(self, parent, name, 8, description)
+        stream = self._parent.stream
+        addr = self.absolute_address
+        size = 0
+        value = 0
+        mult = 1
+        while True:
+            byte = stream.readBits(addr+size, 8, LITTLE_ENDIAN)
+            value += mult * (byte & 0x7f)
+            size += 8
+            mult <<= 7
+            if byte < 128:
+                break
+        self._size = size
+        if signed and (1 << (nbits-1)) <= value:
+            value -= (1 << nbits)
+        self.createValue = lambda: value
+
+class FlashU30(FlashPackedInteger):
+    def __init__(self, parent, name, description=None):
+        FlashPackedInteger.__init__(self, parent, name, signed=False, nbits=30, description=description)
+
+class FlashS32(FlashPackedInteger):
+    def __init__(self, parent, name, description=None):
+        FlashPackedInteger.__init__(self, parent, name, signed=True, nbits=32, description=description)
+
+class FlashU32(FlashPackedInteger):
+    def __init__(self, parent, name, description=None):
+        FlashPackedInteger.__init__(self, parent, name, signed=False, nbits=32, description=description)
+
+class FlashFloat64(FieldSet):
+    def createFields(self):
+        yield Bits(self, "mantissa_high", 20)
+        yield FloatExponent(self, "exponent", 11)
+        yield Bit(self, "negative")
+        yield Bits(self, "mantissa_low", 32)
+
+    def createValue(self):
+        # Manual computation:
+        # mantissa = mantissa_high * 2^32 + mantissa_low
+        # float = 2^exponent + (1 + mantissa / 2^52)
+        # (and float is negative if negative=True)
+        bytes = self.parent.stream.readBytes(
+            self.absolute_address, self.size//8)
+        # Mix bytes: xxxxyyyy <=> yyyyxxxx
+        bytes = bytes[4:8] + bytes[0:4]
+        return unpack('<d', bytes)[0]
+
+TYPE_INFO = {
+    0x00: (CString, "Cstring[]"),
+    0x01: (Float32, "Float[]"),
+    0x02: (None, "Null[]"),
+    0x03: (None, "Undefined[]"),
+    0x04: (UInt8, "Register[]"),
+    0x05: (UInt8, "Boolean[]"),
+    0x06: (FlashFloat64, "Double[]"),
+    0x07: (UInt32, "Integer[]"),
+    0x08: (UInt8, "Dictionary_Lookup_Index[]"),
+    0x09: (UInt16, "Large_Dictionary_Lookup_Index[]"),
+}
+
+def parseBranch(parent, size):
+    yield Int16(parent, "offset")
+
+def parseDeclareFunction(parent, size):
+    yield CString(parent, "name")
+    argCount = UInt16(parent, "arg_count")
+    yield argCount
+    for i in range(argCount.value):
+        yield CString(parent, "arg[]")
+    yield UInt16(parent, "function_length")
+
+def parseDeclareFunctionV7(parent, size):
+    yield CString(parent, "name")
+    argCount = UInt16(parent, "arg_count")
+    yield argCount
+    yield UInt8(parent, "reg_count")
+    yield Bits(parent, "reserved", 7)
+    yield Bit(parent, "preload_global")
+    yield Bit(parent, "preload_parent")
+    yield Bit(parent, "preload_root")
+    yield Bit(parent, "suppress_super")
+    yield Bit(parent, "preload_super")
+    yield Bit(parent, "suppress_arguments")
+    yield Bit(parent, "preload_arguments")
+    yield Bit(parent, "suppress_this")
+    yield Bit(parent, "preload_this")
+    for i in range(argCount.value):
+        yield UInt8(parent, "register[]")
+        yield CString(parent, "arg[]")
+    yield UInt16(parent, "function_length")
+
+def parseTry(parent, size):
+    yield Bits(parent, "reserved", 5)
+    catchInReg = Bit(parent, "catch_in_register")
+    yield catchInReg
+    yield Bit(parent, "finally")
+    yield Bit(parent, "catch")
+    yield UInt8(parent, "try_size")
+    yield UInt8(parent, "catch_size")
+    yield UInt8(parent, "finally_size")
+    if catchInReg.value:
+        yield CString(parent, "name")
+    else:
+        yield UInt8(parent, "register")
+
+def parsePushData(parent, size):
+    while not parent.eof:
+        codeobj = UInt8(parent, "data_type[]")
+        yield codeobj
+        code = codeobj.value
+        if code not in TYPE_INFO:
+            raise ParserError("Unknown type in Push_Data : " + hex(code))
+        parser, name = TYPE_INFO[code]
+        if parser:
+            yield parser(parent, name)
+#        else:
+#            yield Field(parent, name, 0)
+
+def parseSetTarget(parent, size):
+    yield CString(parent, "target")
+
+def parseWith(parent, size):
+    yield UInt16(parent, "size")
+
+def parseGetURL(parent, size):
+    yield CString(parent, "url")
+    yield CString(parent, "target")
+
+def parseGetURL2(parent, size):
+    yield UInt8(parent, "method")
+
+def parseGotoExpression(parent, size):
+    yield UInt8(parent, "play")
+
+def parseGotoFrame(parent, size):
+    yield UInt16(parent, "frame_no")
+
+def parseGotoLabel(parent, size):
+    yield CString(parent, "label")
+
+def parseWaitForFrame(parent, size):
+    yield UInt16(parent, "frame")
+    yield UInt8(parent, "skip")
+
+def parseWaitForFrameDyn(parent, size):
+    yield UInt8(parent, "skip")
+
+def parseDeclareDictionary(parent, size):
+    count = UInt16(parent, "count")
+    yield count
+    for i in range(count.value):
+        yield CString(parent, "dictionnary[]")
+
+def parseStoreRegister(parent, size):
+    yield UInt8(parent, "register")
+
+def parseStrictMode(parent, size):
+    yield UInt8(parent, "strict")
+
+class Instruction(FieldSet):
+    ACTION_INFO = {
+        0x00: ("end[]", "End", None),
+        0x99: ("Branch_Always[]", "Branch Always", parseBranch),
+        0x9D: ("Branch_If_True[]", "Branch If True", parseBranch),
+        0x3D: ("Call_Function[]", "Call Function", None),
+        0x52: ("Call_Method[]", "Call Method", None),
+        0x9B: ("Declare_Function[]", "Declare Function", parseDeclareFunction),
+        0x8E: ("Declare_Function_V7[]", "Declare Function (V7)", parseDeclareFunctionV7),
+        0x3E: ("Return[]", "Return", None),
+        0x2A: ("Throw[]", "Throw", None),
+        0x8F: ("Try[]", "Try", parseTry),
+        # Stack Control
+        0x4C: ("Duplicate[]", "Duplicate", None),
+        0x96: ("Push_Data[]", "Push Data", parsePushData),
+        0x4D: ("Swap[]", "Swap", None),
+        # Action Script Context
+        0x8B: ("Set_Target[]", "Set Target", parseSetTarget),
+        0x20: ("Set_Target_dynamic[]", "Set Target (dynamic)", None),
+        0x94: ("With[]", "With", parseWith),
+        # Movie Control
+        0x9E: ("Call_Frame[]", "Call Frame", None),
+        0x83: ("Get_URL[]", "Get URL", parseGetURL),
+        0x9A: ("Get_URL2[]", "Get URL2", parseGetURL2),
+        0x9F: ("Goto_Expression[]", "Goto Expression", parseGotoExpression),
+        0x81: ("Goto_Frame[]", "Goto Frame", parseGotoFrame),
+        0x8C: ("Goto_Label[]", "Goto Label", parseGotoLabel),
+        0x04: ("Next_Frame[]", "Next Frame", None),
+        0x06: ("Play[]", "Play", None),
+        0x05: ("Previous_Frame[]", "Previous Frame", None),
+        0x07: ("Stop[]", "Stop", None),
+        0x08: ("Toggle_Quality[]", "Toggle Quality", None),
+        0x8A: ("Wait_For_Frame[]", "Wait For Frame", parseWaitForFrame),
+        0x8D: ("Wait_For_Frame_dynamic[]", "Wait For Frame (dynamic)", parseWaitForFrameDyn),
+        # Sound
+        0x09: ("Stop_Sound[]", "Stop Sound", None),
+        # Arithmetic
+        0x0A: ("Add[]", "Add", None),
+        0x47: ("Add_typed[]", "Add (typed)", None),
+        0x51: ("Decrement[]", "Decrement", None),
+        0x0D: ("Divide[]", "Divide", None),
+        0x50: ("Increment[]", "Increment", None),
+        0x18: ("Integral_Part[]", "Integral Part", None),
+        0x3F: ("Modulo[]", "Modulo", None),
+        0x0C: ("Multiply[]", "Multiply", None),
+        0x4A: ("Number[]", "Number", None),
+        0x0B: ("Subtract[]", "Subtract", None),
+        # Comparisons
+        0x0E: ("Equal[]", "Equal", None),
+        0x49: ("Equal_typed[]", "Equal (typed)", None),
+        0x66: ("Strict_Equal[]", "Strict Equal", None),
+        0x67: ("Greater_Than_typed[]", "Greater Than (typed)", None),
+        0x0F: ("Less_Than[]", "Less Than", None),
+        0x48: ("Less_Than_typed[]", "Less Than (typed)", None),
+        0x13: ("String_Equal[]", "String Equal", None),
+        0x68: ("String_Greater_Than[]", "String Greater Than", None),
+        0x29: ("String_Less_Than[]", "String Less Than", None),
+        # Logical and Bit Wise
+        0x60: ("And[]", "And", None),
+        0x10: ("Logical_And[]", "Logical And", None),
+        0x12: ("Logical_Not[]", "Logical Not", None),
+        0x11: ("Logical_Or[]", "Logical Or", None),
+        0x61: ("Or[]", "Or", None),
+        0x63: ("Shift_Left[]", "Shift Left", None),
+        0x64: ("Shift_Right[]", "Shift Right", None),
+        0x65: ("Shift_Right_Unsigned[]", "Shift Right Unsigned", None),
+        0x62: ("Xor[]", "Xor", None),
+        # Strings & Characters (See the String Object also)
+        0x33: ("Chr[]", "Chr", None),
+        0x37: ("Chr_multi-bytes[]", "Chr (multi-bytes)", None),
+        0x21: ("Concatenate_Strings[]", "Concatenate Strings", None),
+        0x32: ("Ord[]", "Ord", None),
+        0x36: ("Ord_multi-bytes[]", "Ord (multi-bytes)", None),
+        0x4B: ("String[]", "String", None),
+        0x14: ("String_Length[]", "String Length", None),
+        0x31: ("String_Length_multi-bytes[]", "String Length (multi-bytes)", None),
+        0x15: ("SubString[]", "SubString", None),
+        0x35: ("SubString_multi-bytes[]", "SubString (multi-bytes)", None),
+        # Properties
+        0x22: ("Get_Property[]", "Get Property", None),
+        0x23: ("Set_Property[]", "Set Property", None),
+        # Objects
+        0x2B: ("Cast_Object[]", "Cast Object", None),
+        0x42: ("Declare_Array[]", "Declare Array", None),
+        0x88: ("Declare_Dictionary[]", "Declare Dictionary", parseDeclareDictionary),
+        0x43: ("Declare_Object[]", "Declare Object", None),
+        0x3A: ("Delete[]", "Delete", None),
+        0x3B: ("Delete_All[]", "Delete All", None),
+        0x24: ("Duplicate_Sprite[]", "Duplicate Sprite", None),
+        0x46: ("Enumerate[]", "Enumerate", None),
+        0x55: ("Enumerate_Object[]", "Enumerate Object", None),
+        0x69: ("Extends[]", "Extends", None),
+        0x4E: ("Get_Member[]", "Get Member", None),
+        0x45: ("Get_Target[]", "Get Target", None),
+        0x2C: ("Implements[]", "Implements", None),
+        0x54: ("Instance_Of[]", "Instance Of", None),
+        0x40: ("New[]", "New", None),
+        0x53: ("New_Method[]", "New Method", None),
+        0x25: ("Remove_Sprite[]", "Remove Sprite", None),
+        0x4F: ("Set_Member[]", "Set Member", None),
+        0x44: ("Type_Of[]", "Type Of", None),
+        # Variables
+        0x41: ("Declare_Local_Variable[]", "Declare Local Variable", None),
+        0x1C: ("Get_Variable[]", "Get Variable", None),
+        0x3C: ("Set_Local_Variable[]", "Set Local Variable", None),
+        0x1D: ("Set_Variable[]", "Set Variable", None),
+        # Miscellaneous
+        0x2D: ("FSCommand2[]", "FSCommand2", None),
+        0x34: ("Get_Timer[]", "Get Timer", None),
+        0x30: ("Random[]", "Random", None),
+        0x27: ("Start_Drag[]", "Start Drag", None),
+        0x28: ("Stop_Drag[]", "Stop Drag", None),
+        0x87: ("Store_Register[]", "Store Register", parseStoreRegister),
+        0x89: ("Strict_Mode[]", "Strict Mode", parseStrictMode),
+        0x26: ("Trace[]", "Trace", None),
+    }
+
+    def __init__(self, *args):
+        FieldSet.__init__(self, *args)
+        code = self["action_id"].value
+        if code & 128:
+            self._size = (3 + self["action_length"].value) * 8
+        else:
+            self._size = 8
+        if code in self.ACTION_INFO:
+            self._name, self._description, self.parser = self.ACTION_INFO[code]
+        else:
+            self.parser = None
+
+    def createFields(self):
+        yield Bits(self, "action_id", 8)
+        if not (self["action_id"].value & 128):
+            return
+        yield UInt16(self, "action_length")
+        size = self["action_length"].value
+        if not size:
+            return
+        if self.parser:
+            for field in self.parser(self, size):
+                yield field
+        else:
+            yield RawBytes(self, "action_data", size)
+
+    def createDescription(self):
+        return self._description
+
+    def __str__(self):
+        r = str(self._description)
+        for f in self:
+            if f.name not in ("action_id", "action_length", "count") and not f.name.startswith("data_type") :
+                r = r + "\n   " + str((self.address+f.address)/8) + " " + str(f.name) + "=" + str(f.value)
+        return r
+
+class ActionScript(FieldSet):
+    def createFields(self):
+        while not self.eof:
+            yield Instruction(self, "instr[]")
+
+    def __str__(self):
+        r = ""
+        for f in self:
+            r = r + str(f.address/8) + " " + str(f) + "\n"
+        return r
+
+def parseActionScript(parent, size):
+    yield ActionScript(parent, "action", size=size*8)
+
+def FindABC(field):
+    while not getattr(field, "isABC", False):
+        field = field.parent
+        if field is None:
+            return None
+    return field
+
+def GetConstant(field, pool, index):
+    if index == 0:
+        return None
+    return FindABC(field)["constant_%s_pool/constant[%i]"%(pool, index)]
+
+def GetMultiname(field, index):
+    fld = GetConstant(field, "multiname", index)
+    if fld is None:
+        return "*"
+    if "name_index" not in fld:
+        return "?"
+    fld2 = GetConstant(fld, "string", fld["name_index"].value)
+    if fld2 is None:
+        return "*"
+    return fld2.value
+
+class ABCStringIndex(FlashU30):
+    def createDisplay(self):
+        fld = GetConstant(self, "string", self.value)
+        if fld is None:
+            return "*"
+        return fld.value
+
+class ABCNSIndex(FlashU30):
+    def createDisplay(self):
+        fld = GetConstant(self, "namespace", self.value)
+        if fld is None:
+            return "*"
+        return fld.display
+
+class ABCMethodIndex(FlashU30):
+    def createDisplay(self):
+        fld = FindABC(self)["method_array/method[%i]"%self.value]
+        if fld is None:
+            return "*"
+        return fld.description
+
+class ABCMultinameIndex(FlashU30):
+    def createDisplay(self):
+        return GetMultiname(self, self.value)
+
+class ABCConstantPool(FieldSet):
+    def __init__(self, parent, name, klass):
+        FieldSet.__init__(self, parent, 'constant_%s_pool'%name)
+        self.klass = klass
+    def createFields(self):
+        ctr = FlashU30(self, "count")
+        yield ctr
+        for i in xrange(ctr.value-1):
+            yield self.klass(self, "constant[%i]"%(i+1))
+
+class ABCObjectArray(FieldSet):
+    def __init__(self, parent, name, klass):
+        self.arrname = name
+        FieldSet.__init__(self, parent, name+'_array')
+        self.klass = klass
+    def createFields(self):
+        ctr = FlashU30(self, "count")
+        yield ctr
+        for i in xrange(ctr.value):
+            yield self.klass(self, self.arrname+"[]")
+
+class ABCClassArray(FieldSet):
+    def __init__(self, parent, name):
+        FieldSet.__init__(self, parent, name+'_array')
+    def createFields(self):
+        ctr = FlashU30(self, "count")
+        yield ctr
+        for i in xrange(ctr.value):
+            yield ABCInstanceInfo(self, "instance[]")
+        for i in xrange(ctr.value):
+            yield ABCClassInfo(self, "class[]")
+
+class ABCConstantString(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "length")
+        size = self["length"].value
+        if size:
+            yield String(self, "data", size, charset="UTF-8")
+
+    def createDisplay(self):
+        if "data" in self:
+            return self["data"].display
+        else:
+            return "<empty>"
+
+    def createValue(self):
+        if "data" in self:
+            return self["data"].value
+        else:
+            return ""
+
+class ABCConstantNamespace(FieldSet):
+    NAMESPACE_KIND = {8: "Namespace",
+                      5: "PrivateNamespace",
+                      22: "PackageNamespace",
+                      23: "PacakgeInternalNamespace",
+                      24: "ProtectedNamespace",
+                      25: "ExplicitNamespace",
+                      26: "MultinameL"}
+    def createFields(self):
+        yield Enum(UInt8(self, "kind"), self.NAMESPACE_KIND)
+        yield ABCStringIndex(self, "name_index")
+
+    def createDisplay(self):
+        return "%s %s"%(self["kind"].display, self["name_index"].display)
+
+    def createValue(self):
+        return self["name_index"].value
+
+class ABCConstantNamespaceSet(FieldSet):
+    def createFields(self):
+        ctr = FlashU30(self, "namespace_count")
+        yield ctr
+        for i in xrange(ctr.value):
+            yield ABCNSIndex(self, "namespace_index[]")
+
+    def createDescription(self):
+        ret = [fld.display for fld in self.array("namespace_index")]
+        return ', '.join(ret)
+
+class ABCConstantMultiname(FieldSet):
+    MULTINAME_KIND = {7: "Qname",
+                      13: "QnameA",
+                      9: "Multiname",
+                      14: "MultinameA",
+                      15: "RTQname",
+                      16: "RTQnameA",
+                      27: "MultinameL",
+                      17: "RTQnameL",
+                      18: "RTQnameLA"}
+    def createFields(self):
+        yield Enum(UInt8(self, "kind"), self.MULTINAME_KIND)
+        kind = self["kind"].value
+        if kind in (7,13): # Qname
+            yield FlashU30(self, "namespace_index")
+            yield ABCStringIndex(self, "name_index")
+        elif kind in (9,14): # Multiname
+            yield ABCStringIndex(self, "name_index")
+            yield FlashU30(self, "namespace_set_index")
+        elif kind in (15,16): # RTQname
+            yield ABCStringIndex(self, "name_index")
+        elif kind == 27: # MultinameL
+            yield FlashU30(self, "namespace_set_index")
+        elif kind in (17,18): # RTQnameL
+            pass
+
+    def createDisplay(self):
+        kind = self["kind"].display
+        if "name_index" in self:
+            return kind + " " + self["name_index"].display
+        return kind
+
+    def createValue(self):
+        return self["kind"].value
+
+class ABCTrait(FieldSet):
+    TRAIT_KIND = {0: "slot",
+                  1: "method",
+                  2: "getter",
+                  3: "setter",
+                  4: "class",
+                  5: "function",
+                  6: "const",}
+    def createFields(self):
+        yield ABCMultinameIndex(self, "name_index")
+        yield Enum(Bits(self, "kind", 4), self.TRAIT_KIND)
+        yield Enum(Bit(self, "is_final"), {True:'final',False:'virtual'})
+        yield Enum(Bit(self, "is_override"), {True:'override',False:'new'})
+        yield Bit(self, "has_metadata")
+        yield Bits(self, "unused", 1)
+        kind = self["kind"].value
+        if kind in (0,6): # slot, const
+            yield FlashU30(self, "slot_id")
+            yield ABCMultinameIndex(self, "type_index")
+            ### TODO reference appropriate constant pool using value_kind
+            yield FlashU30(self, "value_index")
+            if self['value_index'].value != 0:
+                yield UInt8(self, "value_kind")
+        elif kind in (1,2,3): # method, getter, setter
+            yield FlashU30(self, "disp_id")
+            yield ABCMethodIndex(self, "method_info")
+        elif kind == 4: # class
+            yield FlashU30(self, "disp_id")
+            yield FlashU30(self, "class_info")
+        elif kind == 5: # function
+            yield FlashU30(self, "disp_id")
+            yield ABCMethodIndex(self, "method_info")
+        if self['has_metadata'].value:
+            yield ABCObjectArray(self, "metadata", FlashU30)
+
+class ABCValueKind(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "value_index")
+        yield UInt8(self, "value_kind")
+
+class ABCMethodInfo(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "param_count")
+        yield ABCMultinameIndex(self, "ret_type")
+        for i in xrange(self["param_count"].value):
+            yield ABCMultinameIndex(self, "param_type[]")
+        yield ABCStringIndex(self, "name_index")
+        yield Bit(self, "need_arguments")
+        yield Bit(self, "need_activation")
+        yield Bit(self, "need_rest")
+        yield Bit(self, "has_optional")
+        yield Bit(self, "ignore_rest")
+        yield Bit(self, "explicit")
+        yield Bit(self, "setsdxns")
+        yield Bit(self, "has_paramnames")
+        if self["has_optional"].value:
+            yield ABCObjectArray(self, "optional", ABCValueKind)
+        if self["has_paramnames"].value:
+            for i in xrange(self["param_count"].value):
+                yield FlashU30(self, "param_name[]")
+
+    def createDescription(self):
+        ret = GetMultiname(self, self["ret_type"].value)
+        ret += " " + self["name_index"].display
+        ret += "(" + ", ".join(GetMultiname(self, fld.value) for fld in self.array("param_type")) + ")"
+        return ret
+
+class ABCMetadataInfo(FieldSet):
+    def createFields(self):
+        yield ABCStringIndex(self, "name_index")
+        yield FlashU30(self, "values_count")
+        count = self["values_count"].value
+        for i in xrange(count):
+            yield FlashU30(self, "key[]")
+        for i in xrange(count):
+            yield FlashU30(self, "value[]")
+
+class ABCInstanceInfo(FieldSet):
+    def createFields(self):
+        yield ABCMultinameIndex(self, "name_index")
+        yield ABCMultinameIndex(self, "super_index")
+        yield Bit(self, "is_sealed")
+        yield Bit(self, "is_final")
+        yield Bit(self, "is_interface")
+        yield Bit(self, "is_protected")
+        yield Bits(self, "unused", 4)
+        if self['is_protected'].value:
+            yield ABCNSIndex(self, "protectedNS")
+        yield FlashU30(self, "interfaces_count")
+        for i in xrange(self["interfaces_count"].value):
+            yield ABCMultinameIndex(self, "interface[]")
+        yield ABCMethodIndex(self, "iinit_index")
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+class ABCClassInfo(FieldSet):
+    def createFields(self):
+        yield ABCMethodIndex(self, "cinit_index")
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+class ABCScriptInfo(FieldSet):
+    def createFields(self):
+        yield ABCMethodIndex(self, "init_index")
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+class ABCException(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "start")
+        yield FlashU30(self, "end")
+        yield FlashU30(self, "target")
+        yield FlashU30(self, "type_index")
+        yield FlashU30(self, "name_index")
+
+class ABCMethodBody(FieldSet):
+    def createFields(self):
+        yield ABCMethodIndex(self, "method_info")
+        yield FlashU30(self, "max_stack")
+        yield FlashU30(self, "max_regs")
+        yield FlashU30(self, "scope_depth")
+        yield FlashU30(self, "max_scope")
+        yield FlashU30(self, "code_length")
+        yield RawBytes(self, "code", self['code_length'].value)
+        yield ABCObjectArray(self, "exception", ABCException)
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+def parseABC(parent, size):
+    code = parent["code"].value
+    if code == parent.TAG_DO_ABC_DEFINE:
+        yield UInt32(parent, "action_flags")
+        yield CString(parent, "action_name")
+    yield UInt16(parent, "minor_version")
+    yield UInt16(parent, "major_version")
+    parent.isABC = True
+
+    yield ABCConstantPool(parent, "int", FlashS32)
+    yield ABCConstantPool(parent, "uint", FlashU32)
+    yield ABCConstantPool(parent, "double", Float64)
+    yield ABCConstantPool(parent, "string", ABCConstantString)
+    yield ABCConstantPool(parent, "namespace", ABCConstantNamespace)
+    yield ABCConstantPool(parent, "namespace_set", ABCConstantNamespaceSet)
+    yield ABCConstantPool(parent, "multiname", ABCConstantMultiname)
+
+    yield ABCObjectArray(parent, "method", ABCMethodInfo)
+    yield ABCObjectArray(parent, "metadata", ABCMetadataInfo)
+    yield ABCClassArray(parent, "class")
+    yield ABCObjectArray(parent, "script", ABCScriptInfo)
+    yield ABCObjectArray(parent, "body", ABCMethodBody)
+

File hachoir-parser/hachoir_parser/container/swf.py

-"""
-SWF (Macromedia/Adobe Flash) file parser.
-
-Documentation:
-
- - Alexis' SWF Reference:
-   http://www.m2osw.com/swf_alexref.html
- - http://www.half-serious.com/swf/format/
- - http://www.anotherbigidea.com/javaswf/
- - http://www.gnu.org/software/gnash/
-
-Author: Victor Stinner
-Creation date: 29 october 2006
-"""
-
-from hachoir_parser import Parser
-from hachoir_core.field import (FieldSet, ParserError,
-    Bit, Bits, UInt8, UInt32, UInt16, CString, Enum,
-    Bytes, RawBytes, NullBits, String, SubFile)
-from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
-from hachoir_core.text_handler import textHandler, filesizeHandler
-from hachoir_core.tools import paddingSize, humanFrequency
-from hachoir_parser.image.common import RGB
-from hachoir_parser.image.jpeg import JpegChunk, JpegFile
-from hachoir_core.stream import StringInputStream, ConcatStream
-from hachoir_parser.common.deflate import Deflate, has_deflate
-from hachoir_parser.container.action_script import parseActionScript
-import math
-
-# Maximum file size (50 MB)
-MAX_FILE_SIZE = 50 * 1024 * 1024
-
-TWIPS = 20
-
-class RECT(FieldSet):
-    endian = BIG_ENDIAN
-    def createFields(self):
-        yield Bits(self, "nbits", 5)
-        nbits = self["nbits"].value
-        if not nbits:
-            raise ParserError("SWF parser: Invalid RECT field size (0)")
-        yield Bits(self, "xmin", nbits, "X minimum in twips")
-        yield Bits(self, "xmax", nbits, "X maximum in twips")
-        yield Bits(self, "ymin", nbits, "Y minimum in twips")
-        yield Bits(self, "ymax", nbits, "Y maximum in twips")
-        size = paddingSize(self.current_size, 8)
-        if size:
-            yield NullBits(self, "padding", size)
-
-    def getWidth(self):
-        return math.ceil(float(self["xmax"].value) / TWIPS)
-    def getHeight(self):
-        return math.ceil(float(self["ymax"].value) / TWIPS)
-
-    def createDescription(self):
-        return "Rectangle: %ux%u" % (self.getWidth(), self.getHeight())
-
-class FixedFloat16(FieldSet):
-    def createFields(self):
-        yield UInt8(self, "float_part")
-        yield UInt8(self, "int_part")
-
-    def createValue(self):
-        return self["int_part"].value +  float(self["float_part"].value) / 256
-
-def parseBackgroundColor(parent, size):
-    yield RGB(parent, "color")
-
-def bit2hertz(field):
-    return humanFrequency(5512.5 * (2 ** field.value))
-
-SOUND_CODEC_MP3 = 2
-SOUND_CODEC = {
-    0: "RAW",
-    1: "ADPCM",
-    SOUND_CODEC_MP3: "MP3",
-    3: "Uncompressed",
-    6: "Nellymoser",
-}
-
-class SoundEnvelope(FieldSet):
-    def createFields(self):
-        yield UInt8(self, "count")
-        for index in xrange(self["count"].value):
-            yield UInt32(self, "mark44[]")
-            yield UInt16(self, "level0[]")
-            yield UInt16(self, "level1[]")
-
-def parseSoundBlock(parent, size):
-    # TODO: Be able to get codec... Need to know last sound "def_sound[]" field
-#    if not (...)sound_header:
-#        raise ParserError("Sound block without header")
-    if True: #sound_header == SOUND_CODEC_MP3:
-        yield UInt16(parent, "samples")
-        yield UInt16(parent, "left")
-    size = (parent.size - parent.current_size) // 8
-    if size:
-        yield RawBytes(parent, "music_data", size)
-
-def parseStartSound(parent, size):
-    yield UInt16(parent, "sound_id")
-    yield Bit(parent, "has_in_point")
-    yield Bit(parent, "has_out_point")
-    yield Bit(parent, "has_loops")
-    yield Bit(parent, "has_envelope")
-    yield Bit(parent, "no_multiple")
-    yield Bit(parent, "stop_playback")
-    yield NullBits(parent, "reserved", 2)
-
-    if parent["has_in_point"].value:
-        yield UInt32(parent, "in_point")
-    if parent["has_out_point"].value:
-        yield UInt32(parent, "out_point")
-    if parent["has_loops"].value:
-        yield UInt16(parent, "loop_count")
-    if parent["has_envelope"].value:
-        yield SoundEnvelope(parent, "envelope")
-
-def parseDefineSound(parent, size):
-    yield UInt16(parent, "sound_id")
-
-    yield Bit(parent, "is_stereo")
-    yield Bit(parent, "is_16bit")
-    yield textHandler(Bits(parent, "rate", 2), bit2hertz)
-    yield Enum(Bits(parent, "codec", 4), SOUND_CODEC)
-
-    yield UInt32(parent, "sample_count")
-
-    if parent["codec"].value == SOUND_CODEC_MP3:
-        yield UInt16(parent, "len")
-
-    size = (parent.size - parent.current_size) // 8
-    if size:
-        yield RawBytes(parent, "music_data", size)
-
-def parseSoundHeader(parent, size):
-    yield Bit(parent, "playback_is_stereo")
-    yield Bit(parent, "playback_is_16bit")
-    yield textHandler(Bits(parent, "playback_rate", 2), bit2hertz)
-    yield NullBits(parent, "reserved", 4)
-
-    yield Bit(parent, "sound_is_stereo")
-    yield Bit(parent, "sound_is_16bit")
-    yield textHandler(Bits(parent, "sound_rate", 2), bit2hertz)
-    yield Enum(Bits(parent, "codec", 4), SOUND_CODEC)
-
-    yield UInt16(parent, "sample_count")
-
-    if parent["codec"].value == 2:
-        yield UInt16(parent, "latency_seek")
-
-class JpegHeader(FieldSet):
-    endian = BIG_ENDIAN
-    def createFields(self):
-        count = 1
-        while True:
-            chunk = JpegChunk(self, "jpeg_chunk[]")
-            yield chunk
-            if 1 < count and chunk["type"].value in (JpegChunk.TAG_SOI, JpegChunk.TAG_EOI):
-                break
-            count += 1
-
-def parseJpeg(parent, size):
-    yield UInt16(parent, "char_id", "Character identifier")
-    size -= 2
-
-    code = parent["code"].value
-    if code != Tag.TAG_BITS:
-        if code == Tag.TAG_BITS_JPEG3:
-            yield UInt32(parent, "alpha_offset", "Character identifier")
-            size -= 4
-
-        addr = parent.absolute_address + parent.current_size + 16
-        if parent.stream.readBytes(addr, 2) in ("\xff\xdb", "\xff\xd8"):
-            header = JpegHeader(parent, "jpeg_header")
-            yield header
-            hdr_size = header.size // 8
-            size -= hdr_size
-        else:
-            hdr_size = 0
-
-        if code == Tag.TAG_BITS_JPEG3:
-            img_size = parent["alpha_offset"].value - hdr_size
-        else:
-            img_size = size
-    else:
-        img_size = size
-    yield SubFile(parent, "image", img_size, "JPEG picture", parser=JpegFile)
-    if code == Tag.TAG_BITS_JPEG3:
-        size = (parent.size - parent.current_size) // 8
-        yield RawBytes(parent, "alpha", size, "Image data")
-
-def parseVideoFrame(parent, size):
-    yield UInt16(parent, "stream_id")
-    yield UInt16(parent, "frame_num")
-    if 4 < size:
-        yield RawBytes(parent, "video_data", size-4)
-
-class Export(FieldSet):
-    def createFields(self):
-        yield UInt16(self, "object_id")
-        yield CString(self, "name")
-
-def parseExport(parent, size):
-    yield UInt16(parent, "count")
-    for index in xrange(parent["count"].value):
-        yield Export(parent, "export[]")
-
-class Tag(FieldSet):
-    TAG_BITS = 6
-    TAG_BITS_JPEG2 = 32
-    TAG_BITS_JPEG3 = 35
-    TAG_INFO = {
-        # SWF version 1.0
-         0: ("end[]", "End", None),
-         1: ("show_frame[]", "Show frame", None),
-         2: ("def_shape[]", "Define shape", None),
-         3: ("free_char[]", "Free character", None),
-         4: ("place_obj[]", "Place object", None),
-         5: ("remove_obj[]", "Remove object", None),
-         6: ("def_bits[]", "Define bits", parseJpeg),
-         7: ("def_but[]", "Define button", None),
-         8: ("jpg_table", "JPEG tables", None),
-         9: ("bkgd_color[]", "Set background color", parseBackgroundColor),
-        10: ("def_font[]", "Define font", None),
-        11: ("def_text[]", "Define text", None),
-        12: ("action[]", "Action script", parseActionScript),
-        13: ("def_font_info[]", "Define font info", None),
-
-        # SWF version 2.0
-        14: ("def_sound[]", "Define sound", parseDefineSound),
-        15: ("start_sound[]", "Start sound", parseStartSound),
-        16: ("stop_sound[]", "Stop sound", None),
-        17: ("def_but_sound[]", "Define button sound", None),
-        18: ("sound_hdr", "Sound stream header", parseSoundHeader),
-        19: ("sound_blk[]", "Sound stream block", parseSoundBlock),
-        20: ("def_bits_lossless[]", "Define bits lossless", None),
-        21: ("def_bits_jpeg2[]", "Define bits JPEG 2", parseJpeg),
-        22: ("def_shape2[]", "Define shape 2", None),
-        23: ("def_but_cxform[]", "Define button CXFORM", None),
-        24: ("protect", "File is protected", None),
-
-        # SWF version 3.0
-        25: ("path_are_ps[]", "Paths are Postscript", None),
-        26: ("place_obj2[]", "Place object 2", None),
-        28: ("remove_obj2[]", "Remove object 2", None),
-        29: ("sync_frame[]", "Synchronize frame", None),
-        31: ("free_all[]", "Free all", None),
-        32: ("def_shape3[]", "Define shape 3", None),
-        33: ("def_text2[]", "Define text 2", None),
-        34: ("def_but2[]", "Define button2", None),
-        35: ("def_bits_jpeg3[]", "Define bits JPEG 3", parseJpeg),
-        36: ("def_bits_lossless2[]", "Define bits lossless 2", None),
-        39: ("def_sprite[]", "Define sprite", None),
-        40: ("name_character[]", "Name character", None),
-        41: ("serial_number", "Serial number", None),
-        42: ("generator_text[]", "Generator text", None),
-        43: ("frame_label[]", "Frame label", None),
-        45: ("sound_hdr2[]", "Sound stream header2", parseSoundHeader),
-        46: ("def_morph_shape[]", "Define morph shape", None),
-        47: ("gen_frame[]", "Generate frame", None),
-        48: ("def_font2[]", "Define font 2", None),
-        49: ("tpl_command[]", "Template command", None),
-
-        # SWF version 4.0
-        37: ("def_text_field[]", "Define text field", None),
-        38: ("def_quicktime_movie[]", "Define QuickTime movie", None),
-
-        # SWF version 5.0
-        50: ("def_cmd_obj[]", "Define command object", None),
-        51: ("flash_generator", "Flash generator", None),
-        52: ("gen_ext_font[]", "Gen external font", None),
-        56: ("export[]", "Export", parseExport),
-        57: ("import[]", "Import", None),
-        58: ("ebnable_debug", "Enable debug", None),
-
-        # SWF version 6.0
-        59: ("do_init_action[]", "Do init action", None),
-        60: ("video_str[]", "Video stream", None),
-        61: ("video_frame[]", "Video frame", parseVideoFrame),
-        62: ("def_font_info2[]", "Define font info 2", None),
-        63: ("mx4[]", "MX4", None),
-        64: ("enable_debug2", "Enable debugger 2", None),
-
-        # SWF version 7.0
-        65: ("script_limits[]", "Script limits", None),
-        66: ("tab_index[]", "Set tab index", None),
-
-        # SWF version 8.0
-        69: ("file_attr[]", "File attributes", None),
-        70: ("place_obj3[]", "Place object 3", None),
-        71: ("import2[]", "Import a definition list from another movie", None),
-        73: ("def_font_align[]", "Define font alignment zones", None),
-        74: ("csm_txt_set[]", "CSM text settings", None),
-        75: ("def_font3[]", "Define font text 3", None),
-        77: ("metadata[]", "XML code describing the movie", None),
-        78: ("def_scale_grid[]", "Define scaling factors", None),
-        83: ("def_shape4[]", "Define shape 4", None),
-        84: ("def_morph2[]", "Define a morphing shape 2", None),
-    }
-
-    def __init__(self, *args):
-        FieldSet.__init__(self, *args)
-        size = self["length"].value
-        if self[0].name == "length_ext":
-            self._size = (6+size) * 8
-        else:
-            self._size = (2+size) * 8
-        code = self["code"].value
-        if code in self.TAG_INFO:
-            self._name, self._description, self.parser = self.TAG_INFO[code]
-        else:
-            self.parser = None
-
-    def createFields(self):
-        if self.stream.readBits(self.absolute_address, 6, self.endian) == 63:
-            yield Bits(self, "length_ext", 6)
-            yield Bits(self, "code", 10)
-            yield filesizeHandler(UInt32(self, "length"))
-        else:
-            yield filesizeHandler(Bits(self, "length", 6))
-            yield Bits(self, "code", 10)
-        size = self["length"].value
-        if 0 < size:
-            if self.parser:
-                for field in self.parser(self, size):
-                    yield field
-            else:
-                yield RawBytes(self, "data", size)
-
-    def createDescription(self):
-        return "Tag: %s (%s)" % (self["code"].display, self["length"].display)
-
-class SwfFile(Parser):
-    VALID_VERSIONS = set(xrange(1, 9+1))
-    PARSER_TAGS = {
-        "id": "swf",
-        "category": "container",
-        "file_ext": ["swf"],
-        "mime": (u"application/x-shockwave-flash",),
-        "min_size": 64,
-        "description": u"Macromedia Flash data"
-    }
-    PARSER_TAGS["magic"] = []
-    for version in VALID_VERSIONS:
-        PARSER_TAGS["magic"].append(("FWS%c" % version, 0))
-        PARSER_TAGS["magic"].append(("CWS%c" % version, 0))
-    endian = LITTLE_ENDIAN
-    SWF_SCALE_FACTOR = 1.0 / 20
-
-    def validate(self):
-        if self.stream.readBytes(0, 3) not in ("FWS", "CWS"):
-            return "Wrong file signature"
-        if self["version"].value not in self.VALID_VERSIONS:
-            return "Unknown version"
-        if MAX_FILE_SIZE < self["filesize"].value:
-            return "File too big (%u)" % self["filesize"].value
-        if self["signature"].value == "FWS":
-            if self["rect/padding"].value != 0:
-                return "Unknown rectangle padding value"
-        return True
-
-    def createFields(self):
-        yield String(self, "signature", 3, "SWF format signature", charset="ASCII")
-        yield UInt8(self, "version")
-        yield filesizeHandler(UInt32(self, "filesize"))
-        if self["signature"].value != "CWS":
-            yield RECT(self, "rect")
-            yield FixedFloat16(self, "frame_rate")
-            yield UInt16(self, "frame_count")
-
-            while not self.eof:
-                yield Tag(self, "tag[]")
-        else:
-            size = (self.size - self.current_size) // 8
-            if has_deflate:
-                data = Deflate(Bytes(self, "compressed_data", size), False)
-                def createInputStream(cis, source=None, **args):
-                    stream = cis(source=source)
-                    header = StringInputStream("FWS" + self.stream.readBytes(3*8, 5))
-                    args.setdefault("tags",[]).append(("class", SwfFile))
-                    return ConcatStream((header, stream), source=stream.source, **args)
-                data.setSubIStream(createInputStream)
-                yield data
-            else:
-                yield Bytes(self, "compressed_data", size)
-
-    def createDescription(self):
-        desc = ["version %u" % self["version"].value]
-        if self["signature"].value == "CWS":
-            desc.append("compressed")
-        return u"Macromedia Flash data: %s" % (", ".join(desc))
-
-    def createContentSize(self):
-        if self["signature"].value == "FWS":
-            return self["filesize"].value * 8
-        else:
-            # TODO: Size of compressed Flash?
-            return None
-
+"""
+SWF (Macromedia/Adobe Flash) file parser.
+
+Documentation:
+
+ - Alexis' SWF Reference:
+   http://www.m2osw.com/swf_alexref.html
+ - http://www.half-serious.com/swf/format/
+ - http://www.anotherbigidea.com/javaswf/
+ - http://www.gnu.org/software/gnash/
+
+Author: Victor Stinner
+Creation date: 29 october 2006
+"""
+
+from hachoir_parser import Parser
+from hachoir_core.field import (FieldSet, ParserError,
+    Bit, Bits, UInt8, UInt16, Int32, UInt32, Int64, CString, Enum,
+    Bytes, RawBytes, NullBits, String, SubFile)
+from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
+from hachoir_core.text_handler import textHandler, filesizeHandler
+from hachoir_core.tools import paddingSize, humanFrequency
+from hachoir_parser.image.common import RGB
+from hachoir_parser.image.jpeg import JpegChunk, JpegFile
+from hachoir_core.stream import StringInputStream, ConcatStream
+from hachoir_parser.common.deflate import Deflate, has_deflate
+from hachoir_parser.container.action_script import parseActionScript, parseABC
+import math
+
+# Maximum file size (50 MB)
+MAX_FILE_SIZE = 50 * 1024 * 1024
+
+TWIPS = 20
+
+class RECT(FieldSet):
+    endian = BIG_ENDIAN
+    def createFields(self):
+        yield Bits(self, "nbits", 5)
+        nbits = self["nbits"].value
+        if not nbits:
+            raise ParserError("SWF parser: Invalid RECT field size (0)")
+        yield Bits(self, "xmin", nbits, "X minimum in twips")
+        yield Bits(self, "xmax", nbits, "X maximum in twips")
+        yield Bits(self, "ymin", nbits, "Y minimum in twips")
+        yield Bits(self, "ymax", nbits, "Y maximum in twips")
+        size = paddingSize(self.current_size, 8)
+        if size:
+            yield NullBits(self, "padding", size)
+
+    def getWidth(self):
+        return math.ceil(float(self["xmax"].value) / TWIPS)
+    def getHeight(self):
+        return math.ceil(float(self["ymax"].value) / TWIPS)
+
+    def createDescription(self):
+        return "Rectangle: %ux%u" % (self.getWidth(), self.getHeight())
+
+class FixedFloat16(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "float_part")
+        yield UInt8(self, "int_part")
+
+    def createValue(self):
+        return self["int_part"].value +  float(self["float_part"].value) / 256
+
+def parseBackgroundColor(parent, size):
+    yield RGB(parent, "color")
+
+def bit2hertz(field):
+    return humanFrequency(5512.5 * (2 ** field.value))
+
+SOUND_CODEC_MP3 = 2
+SOUND_CODEC = {
+    0: "RAW",
+    1: "ADPCM",
+    SOUND_CODEC_MP3: "MP3",
+    3: "Uncompressed",
+    6: "Nellymoser",
+}
+
+class SoundEnvelope(FieldSet):
+    def createFields(self):
+        yield UInt8(self, "count")
+        for index in xrange(self["count"].value):
+            yield UInt32(self, "mark44[]")
+            yield UInt16(self, "level0[]")
+            yield UInt16(self, "level1[]")
+
+def parseSoundBlock(parent, size):
+    # TODO: Be able to get codec... Need to know last sound "def_sound[]" field
+#    if not (...)sound_header:
+#        raise ParserError("Sound block without header")
+    if True: #sound_header == SOUND_CODEC_MP3:
+        yield UInt16(parent, "samples")
+        yield UInt16(parent, "left")
+    size = (parent.size - parent.current_size) // 8
+    if size:
+        yield RawBytes(parent, "music_data", size)
+
+def parseStartSound(parent, size):
+    yield UInt16(parent, "sound_id")
+    yield Bit(parent, "has_in_point")
+    yield Bit(parent, "has_out_point")
+    yield Bit(parent, "has_loops")
+    yield Bit(parent, "has_envelope")
+    yield Bit(parent, "no_multiple")
+    yield Bit(parent, "stop_playback")
+    yield NullBits(parent, "reserved", 2)
+
+    if parent["has_in_point"].value:
+        yield UInt32(parent, "in_point")
+    if parent["has_out_point"].value:
+        yield UInt32(parent, "out_point")
+    if parent["has_loops"].value:
+        yield UInt16(parent, "loop_count")
+    if parent["has_envelope"].value:
+        yield SoundEnvelope(parent, "envelope")
+
+def parseDefineSound(parent, size):
+    yield UInt16(parent, "sound_id")
+
+    yield Bit(parent, "is_stereo")
+    yield Bit(parent, "is_16bit")
+    yield textHandler(Bits(parent, "rate", 2), bit2hertz)
+    yield Enum(Bits(parent, "codec", 4), SOUND_CODEC)
+
+    yield UInt32(parent, "sample_count")
+
+    if parent["codec"].value == SOUND_CODEC_MP3:
+        yield UInt16(parent, "len")
+
+    size = (parent.size - parent.current_size) // 8
+    if size:
+        yield RawBytes(parent, "music_data", size)
+
+def parseSoundHeader(parent, size):
+    yield Bit(parent, "playback_is_stereo")
+    yield Bit(parent, "playback_is_16bit")
+    yield textHandler(Bits(parent, "playback_rate", 2), bit2hertz)
+    yield NullBits(parent, "reserved", 4)
+
+    yield Bit(parent, "sound_is_stereo")
+    yield Bit(parent, "sound_is_16bit")
+    yield textHandler(Bits(parent, "sound_rate", 2), bit2hertz)
+    yield Enum(Bits(parent, "codec", 4), SOUND_CODEC)
+
+    yield UInt16(parent, "sample_count")
+
+    if parent["codec"].value == 2:
+        yield UInt16(parent, "latency_seek")
+
+class JpegHeader(FieldSet):
+    endian = BIG_ENDIAN
+    def createFields(self):
+        count = 1
+        while True:
+            chunk = JpegChunk(self, "jpeg_chunk[]")
+            yield chunk
+            if 1 < count and chunk["type"].value in (JpegChunk.TAG_SOI, JpegChunk.TAG_EOI):
+                break
+            count += 1
+
+def parseJpeg(parent, size):
+    yield UInt16(parent, "char_id", "Character identifier")
+    size -= 2
+
+    code = parent["code"].value
+    if code != Tag.TAG_BITS:
+        if code == Tag.TAG_BITS_JPEG3:
+            yield UInt32(parent, "alpha_offset", "Character identifier")
+            size -= 4
+
+        addr = parent.absolute_address + parent.current_size + 16
+        if parent.stream.readBytes(addr, 2) in ("\xff\xdb", "\xff\xd8"):
+            header = JpegHeader(parent, "jpeg_header")
+            yield header
+            hdr_size = header.size // 8
+            size -= hdr_size
+        else:
+            hdr_size = 0
+
+        if code == Tag.TAG_BITS_JPEG3:
+            img_size = parent["alpha_offset"].value - hdr_size
+        else:
+            img_size = size
+    else:
+        img_size = size
+    yield SubFile(parent, "image", img_size, "JPEG picture", parser=JpegFile)
+    if code == Tag.TAG_BITS_JPEG3:
+        size = (parent.size - parent.current_size) // 8
+        yield RawBytes(parent, "alpha", size, "Image data")
+
+def parseVideoFrame(parent, size):
+    yield UInt16(parent, "stream_id")
+    yield UInt16(parent, "frame_num")
+    if 4 < size:
+        yield RawBytes(parent, "video_data", size-4)
+
+class Export(FieldSet):
+    def createFields(self):
+        yield UInt16(self, "object_id")
+        yield CString(self, "name")
+
+def parseExport(parent, size):
+    yield UInt16(parent, "count")
+    for index in xrange(parent["count"].value):
+        yield Export(parent, "export[]")
+
+def parseProductInfo(parent, size):
+    yield Int32(parent, "product_id")
+    yield Int32(parent, "edition")
+    yield UInt8(parent, "major_version")
+    yield UInt8(parent, "minor_version")
+    yield Int64(parent, "build_number")
+    yield Int64(parent, "compilation_date")
+
+def parseScriptLimits(parent, size):
+    yield UInt16(parent, "max_recursion_limit")
+    yield UInt16(parent, "timeout_seconds", "Seconds of processing until the SWF is considered 'stuck'")
+
+def parseSymbolClass(parent, size):
+    yield UInt16(parent, "count")
+    for index in xrange(parent["count"].value):
+        yield UInt16(parent, "symbol_id[]")
+        yield CString(parent, "symbol_name[]")
+
+def parseBinaryData(parent, size):
+    yield UInt16(parent, "data_id")
+    yield UInt32(parent, "reserved")
+    if size > 6:
+        yield RawBytes(parent, "data", size-6)
+
+class Tag(FieldSet):
+    TAG_BITS = 6
+    TAG_BITS_JPEG2 = 32
+    TAG_BITS_JPEG3 = 35
+    TAG_DO_ABC_DEFINE = 82
+    TAG_INFO = {
+        # SWF version 1.0
+         0: ("end[]", "End", None),
+         1: ("show_frame[]", "Show frame", None),
+         2: ("def_shape[]", "Define shape", None),
+         3: ("free_char[]", "Free character", None),
+         4: ("place_obj[]", "Place object", None),
+         5: ("remove_obj[]", "Remove object", None),
+         6: ("def_bits[]", "Define bits", parseJpeg),
+         7: ("def_but[]", "Define button", None),
+         8: ("jpg_table", "JPEG tables", None),
+         9: ("bkgd_color[]", "Set background color", parseBackgroundColor),
+        10: ("def_font[]", "Define font", None),
+        11: ("def_text[]", "Define text", None),
+        12: ("action[]", "Action script", parseActionScript),
+        13: ("def_font_info[]", "Define font info", None),
+
+        # SWF version 2.0
+        14: ("def_sound[]", "Define sound", parseDefineSound),
+        15: ("start_sound[]", "Start sound", parseStartSound),
+        16: ("stop_sound[]", "Stop sound", None),
+        17: ("def_but_sound[]", "Define button sound", None),
+        18: ("sound_hdr", "Sound stream header", parseSoundHeader),
+        19: ("sound_blk[]", "Sound stream block", parseSoundBlock),
+        20: ("def_bits_lossless[]", "Define bits lossless", None),
+        21: ("def_bits_jpeg2[]", "Define bits JPEG 2", parseJpeg),
+        22: ("def_shape2[]", "Define shape 2", None),
+        23: ("def_but_cxform[]", "Define button CXFORM", None),
+        24: ("protect", "File is protected", None),
+
+        # SWF version 3.0
+        25: ("path_are_ps[]", "Paths are Postscript", None),
+        26: ("place_obj2[]", "Place object 2", None),
+        28: ("remove_obj2[]", "Remove object 2", None),
+        29: ("sync_frame[]", "Synchronize frame", None),
+        31: ("free_all[]", "Free all", None),
+        32: ("def_shape3[]", "Define shape 3", None),
+        33: ("def_text2[]", "Define text 2", None),
+        34: ("def_but2[]", "Define button2", None),
+        35: ("def_bits_jpeg3[]", "Define bits JPEG 3", parseJpeg),
+        36: ("def_bits_lossless2[]", "Define bits lossless 2", None),
+        39: ("def_sprite[]", "Define sprite", None),
+        40: ("name_character[]", "Name character", None),
+        41: ("product_info", "Generator product info", parseProductInfo),
+        42: ("generator_text[]", "Generator text", None),
+        43: ("frame_label[]", "Frame label", None),
+        45: ("sound_hdr2[]", "Sound stream header2", parseSoundHeader),
+        46: ("def_morph_shape[]", "Define morph shape", None),
+        47: ("gen_frame[]", "Generate frame", None),
+        48: ("def_font2[]", "Define font 2", None),
+        49: ("tpl_command[]", "Template command", None),
+
+        # SWF version 4.0
+        37: ("def_text_field[]", "Define text field", None),
+        38: ("def_quicktime_movie[]", "Define QuickTime movie", None),
+
+        # SWF version 5.0
+        50: ("def_cmd_obj[]", "Define command object", None),
+        51: ("flash_generator", "Flash generator", None),
+        52: ("gen_ext_font[]", "Gen external font", None),
+        56: ("export[]", "Export", parseExport),
+        57: ("import[]", "Import", None),
+        58: ("ebnable_debug", "Enable debug", None),
+
+        # SWF version 6.0
+        59: ("do_init_action[]", "Do init action", None),
+        60: ("video_str[]", "Video stream", None),
+        61: ("video_frame[]", "Video frame", parseVideoFrame),
+        62: ("def_font_info2[]", "Define font info 2", None),
+        63: ("mx4[]", "MX4", None),
+        64: ("enable_debug2", "Enable debugger 2", None),
+
+        # SWF version 7.0
+        65: ("script_limits[]", "Script limits", parseScriptLimits),
+        66: ("tab_index[]", "Set tab index", None),
+
+        # SWF version 8.0
+        69: ("file_attr[]", "File attributes", None),
+        70: ("place_obj3[]", "Place object 3", None),
+        71: ("import2[]", "Import a definition list from another movie", None),
+        73: ("def_font_align[]", "Define font alignment zones", None),
+        74: ("csm_txt_set[]", "CSM text settings", None),
+        75: ("def_font3[]", "Define font text 3", None),
+        77: ("metadata[]", "XML code describing the movie", None),
+        78: ("def_scale_grid[]", "Define scaling factors", None),
+        83: ("def_shape4[]", "Define shape 4", None),
+        84: ("def_morph2[]", "Define a morphing shape 2", None),
+
+        # SWF version 9.0
+        72: ("do_abc[]", "SWF 9 ActionScript container; actions only", parseABC),
+        76: ("symbol_class[]", "Instantiate objects from a set of classes", parseSymbolClass),
+        82: ("do_abc_define[]", "SWF 9 ActionScript container; identifier, name, actions", parseABC),
+        86: ("def_scene_frame[]", "Define raw data for scenes and frames", None),
+        87: ("def_binary_data[]", "Defines a buffer of any size with any binary user data", parseBinaryData),
+        88: ("def_font_name[]", "Define the legal font name and copyright", None),
+    }
+
+    def __init__(self, *args):
+        FieldSet.__init__(self, *args)
+        size = self["length"].value
+        if self[0].name == "length_ext":
+            self._size = (6+size) * 8
+        else:
+            self._size = (2+size) * 8
+        code = self["code"].value
+        if code in self.TAG_INFO:
+            self._name, self._description, self.parser = self.TAG_INFO[code]
+        else:
+            self.parser = None
+
+    def createFields(self):
+        if self.stream.readBits(self.absolute_address, 6, self.endian) == 63:
+            yield Bits(self, "length_ext", 6)
+            yield Bits(self, "code", 10)
+            yield filesizeHandler(UInt32(self, "length"))
+        else:
+            yield filesizeHandler(Bits(self, "length", 6))
+            yield Bits(self, "code", 10)
+        size = self["length"].value
+        if 0 < size:
+            if self.parser:
+                for field in self.parser(self, size):
+                    yield field
+            else:
+                yield RawBytes(self, "data", size)
+
+    def createDescription(self):
+        return "Tag: %s (%s)" % (self["code"].display, self["length"].display)
+
+class SwfFile(Parser):
+    VALID_VERSIONS = set(xrange(1, 10+1))
+    PARSER_TAGS = {
+        "id": "swf",
+        "category": "container",
+        "file_ext": ["swf"],
+        "mime": (u"application/x-shockwave-flash",),
+        "min_size": 64,
+        "description": u"Macromedia Flash data"
+    }
+    PARSER_TAGS["magic"] = []
+    for version in VALID_VERSIONS:
+        PARSER_TAGS["magic"].append(("FWS%c" % version, 0))
+        PARSER_TAGS["magic"].append(("CWS%c" % version, 0))
+    endian = LITTLE_ENDIAN
+    SWF_SCALE_FACTOR = 1.0 / 20
+
+    def validate(self):
+        if self.stream.readBytes(0, 3) not in ("FWS", "CWS"):
+            return "Wrong file signature"
+        if self["version"].value not in self.VALID_VERSIONS:
+            return "Unknown version"
+        if MAX_FILE_SIZE < self["filesize"].value:
+            return "File too big (%u)" % self["filesize"].value
+        if self["signature"].value == "FWS":
+            if self["rect/padding"].value != 0:
+                return "Unknown rectangle padding value"
+        return True
+
+    def createFields(self):
+        yield String(self, "signature", 3, "SWF format signature", charset="ASCII")
+        yield UInt8(self, "version")
+        yield filesizeHandler(UInt32(self, "filesize"))
+        if self["signature"].value != "CWS":
+            yield RECT(self, "rect")
+            yield FixedFloat16(self, "frame_rate")
+            yield UInt16(self, "frame_count")
+
+            while not self.eof:
+                yield Tag(self, "tag[]")
+        else:
+            size = (self.size - self.current_size) // 8
+            if has_deflate:
+                data = Deflate(Bytes(self, "compressed_data", size), False)
+                def createInputStream(cis, source=None, **args):
+                    stream = cis(source=source)
+                    header = StringInputStream("FWS" + self.stream.readBytes(3*8, 5))
+                    args.setdefault("tags",[]).append(("class", SwfFile))
+                    return ConcatStream((header, stream), source=stream.source, **args)
+                data.setSubIStream(createInputStream)
+                yield data
+            else:
+                yield Bytes(self, "compressed_data", size)
+
+    def createDescription(self):
+        desc = ["version %u" % self["version"].value]
+        if self["signature"].value == "CWS":
+            desc.append("compressed")
+        return u"Macromedia Flash data: %s" % (", ".join(desc))
+
+    def createContentSize(self):
+        if self["signature"].value == "FWS":
+            return self["filesize"].value * 8
+        else:
+            # TODO: Size of compressed Flash?
+            return None
+