class BasicIFDEntry(FieldSet): TYPE_BYTE = 0 TYPE_UNDEFINED = 7 TYPE_RATIONAL = 5 TYPE_SIGNED_RATIONAL = 10 TYPE_INFO = { 1: (UInt8, "BYTE (8 bits)"), 2: (ASCIIString, "ASCII (8 bits)"), 3: (UInt16, "SHORT (16 bits)"), 4: (UInt32, "LONG (32 bits)"), 5: (RationalUInt32, "RATIONAL (2x LONG, 64 bits)"), 6: (Int8, "SBYTE (8 bits)"), 7: (Bytes, "UNDEFINED (8 bits)"), 8: (Int16, "SSHORT (16 bits)"), 9: (Int32, "SLONG (32 bits)"), 10: (RationalInt32, "SRATIONAL (2x SLONG, 64 bits)"), 11: (Float32, "FLOAT (32 bits)"), 12: (Float64, "DOUBLE (64 bits)"), } ENTRY_FORMAT = createDict(TYPE_INFO, 0) TYPE_NAME = createDict(TYPE_INFO, 1) TAG_INFO = {} def createFields(self): yield IFDTag(self, "tag", "Tag") yield Enum(UInt16(self, "type", "Type"), self.TYPE_NAME) self.value_cls = self.ENTRY_FORMAT.get(self['type'].value, Bytes) if issubclass(self.value_cls, Bytes): self.value_size = 8 else: self.value_size = self.value_cls.static_size yield UInt32(self, "count", "Count") count = self['count'].value totalsize = self.value_size * count if count == 0: yield NullBytes(self, "padding", 4) elif totalsize <= 32: name = "value" if issubclass(self.value_cls, Bytes): yield self.value_cls(self, name, count) elif count == 1: yield self.value_cls(self, name) else: yield ValueArray(self, name, self.value_cls, count) if totalsize < 32: yield NullBits(self, "padding", 32 - totalsize) else: yield UInt32(self, "offset", "Value offset") def createValue(self): if "value" in self: return self['value'].value return None def createDescription(self): return "Entry: " + self["tag"].getTag()[1]
class IndexOffset(FieldSet): TYPE_DESC = createDict(RESOURCE_TYPE, 1) def __init__(self, parent, name, res_type=None): FieldSet.__init__(self, parent, name) self.res_type = res_type def createFields(self): if self.res_type is None: # immediate subdirectory of the root yield Enum(UInt32(self, "type"), self.TYPE_DESC) else: # sub-subdirectory, "type" field is just an ID yield textHandler(UInt32(self, "type"), lambda field: "ID %d" % field.value) yield Bits(self, "offset", 31) yield Bit(self, "is_subdir") def getResType(self): return self["type"].value def createDescription(self): if self["is_subdir"].value: return "Sub-directory: %s at %s" % (self["type"].display, self["offset"].value) else: return "Index: %s at %s" % (self["type"].display, self["offset"].value)
class Layer2(Layer): PROTO_INFO = { 0x0800: ("ipv4", IPv4, "IPv4"), 0x0806: ("arp", ARP, "ARP"), 0x86dd: ("ipv6", IPv6, "IPv6"), } PROTO_DESC = createDict(PROTO_INFO, 2) def parseNext(self, parent): try: name, parser, desc = self.PROTO_INFO[self["protocol"].value] return parser(parent, name) except KeyError: return None
class MetadataBlock(FieldSet): "Metadata block field: http://flac.sourceforge.net/format.html#metadata_block" BLOCK_TYPES = { 0: ("stream_info", "Stream info", StreamInfo), 1: ("padding[]", "Padding", None), 2: ("application[]", "Application", None), 3: ("seek_table", "Seek table", SeekTable), 4: ("comment", "Vorbis comment", VorbisComment), 5: ("cue_sheet[]", "Cue sheet", None), 6: ("picture[]", "Picture", None), } BLOCK_TYPE_DESC = createDict(BLOCK_TYPES, 1) def __init__(self, *args, **kw): FieldSet.__init__(self, *args, **kw) self._size = 32 + self["metadata_length"].value * 8 try: key = self["block_type"].value self._name, self._description, self.handler = self.BLOCK_TYPES[key] except KeyError: self.handler = None def createFields(self): yield Bit(self, "last_metadata_block", "True if this is the last metadata block") yield Enum(Bits(self, "block_type", 7, "Metadata block header type"), self.BLOCK_TYPE_DESC) yield UInt24( self, "metadata_length", "Length of following metadata in bytes (doesn't include this header)" ) block_type = self["block_type"].value size = self["metadata_length"].value if not size: return try: handler = self.BLOCK_TYPES[block_type][2] except KeyError: handler = None if handler: yield handler(self, "content", size=size * 8) elif self["block_type"].value == 1: yield NullBytes(self, "padding", size) else: yield RawBytes(self, "rawdata", size)
class IP(Layer): PROTOCOL_INFO = { 1: ("icmp", ICMP, "ICMP"), 6: ("tcp", TCP, "TCP"), 17: ("udp", UDP, "UDP"), 58: ("icmpv6", ICMPv6, "ICMPv6"), 60: ("ipv6_opts", None, "IPv6 destination option"), } PROTOCOL_NAME = createDict(PROTOCOL_INFO, 2) def parseNext(self, parent): proto = self["protocol"].value if proto not in self.PROTOCOL_INFO: return None name, parser, desc = self.PROTOCOL_INFO[proto] if not parser: return None return parser(parent, name)
class TcpdumpFile(Parser): PARSER_TAGS = { "id": "tcpdump", "category": "misc", "min_size": 24 * 8, "description": "Tcpdump file (network)", "magic": ((b"\xd4\xc3\xb2\xa1", 0),), } endian = LITTLE_ENDIAN LINK_TYPE = { 1: ("ethernet", Ethernet), 113: ("unicast", Unicast), } LINK_TYPE_DESC = createDict(LINK_TYPE, 0) def validate(self): if self["id"].value != b"\xd4\xc3\xb2\xa1": return "Wrong file signature" if self["link_type"].value not in self.LINK_TYPE: return "Unknown link type" return True def createFields(self): yield Bytes(self, "id", 4, "Tcpdump identifier") yield UInt16(self, "maj_ver", "Major version") yield UInt16(self, "min_ver", "Minor version") yield Int32(self, "this_zone", "GMT to local time zone correction") yield Int32(self, "sigfigs", "accuracy of timestamps") yield UInt32(self, "snap_len", "max length saved portion of each pkt") yield Enum(UInt32(self, "link_type", "data link type"), self.LINK_TYPE_DESC) link = self["link_type"].value if link not in self.LINK_TYPE: raise ParserError("Unknown link type: %s" % link) name, parser = self.LINK_TYPE[link] while self.current_size < self.size: yield Packet(self, "packet[]", parser, name)
class PropertyContent(FieldSet): class NullHandler(FieldSet): def createFields(self): yield UInt32(self, "unknown[]") yield PascalString32(self, "data") def createValue(self): return self["data"].value class BlobHandler(FieldSet): def createFields(self): self.osconfig = self.parent.osconfig yield UInt32(self, "size") yield UInt32(self, "count") for i in range(self["count"].value): yield PropertyContent(self, "item[]") n = paddingSize(self.current_size, 32) if n: yield PaddingBits(self, "padding[]", n) class WidePascalString32(FieldSet): ''' uses number of characters instead of number of bytes ''' def __init__(self, parent, name, charset='ASCII'): FieldSet.__init__(self, parent, name) self.charset = charset def createFields(self): yield UInt32(self, "length", "Length of this string") yield String(self, "data", self["length"].value * 2, charset=self.charset) def createValue(self): return self["data"].value def createDisplay(self): return 'u' + self["data"].display TYPE_LPSTR = 30 TYPE_INFO = { 0: ("EMPTY", None), 1: ("NULL", NullHandler), 2: ("UInt16", UInt16), 3: ("UInt32", UInt32), 4: ("Float32", Float32), 5: ("Float64", Float64), 6: ("CY", None), 7: ("DATE", None), 8: ("BSTR", None), 9: ("DISPATCH", None), 10: ("ERROR", None), 11: ("BOOL", Bool), 12: ("VARIANT", None), 13: ("UNKNOWN", None), 14: ("DECIMAL", None), 16: ("I1", None), 17: ("UI1", None), 18: ("UI2", None), 19: ("UI4", None), 20: ("I8", None), 21: ("UI8", None), 22: ("INT", None), 23: ("UINT", None), 24: ("VOID", None), 25: ("HRESULT", None), 26: ("PTR", None), 27: ("SAFEARRAY", None), 28: ("CARRAY", None), 29: ("USERDEFINED", None), 30: ("LPSTR", PascalString32), 31: ("LPWSTR", WidePascalString32), 64: ("FILETIME", TimestampWin64), 65: ("BLOB", BlobHandler), 66: ("STREAM", None), 67: ("STORAGE", None), 68: ("STREAMED_OBJECT", None), 69: ("STORED_OBJECT", None), 70: ("BLOB_OBJECT", None), 71: ("THUMBNAIL", Thumbnail), 72: ("CLSID", None), 0x1000: ("Vector", None), } TYPE_NAME = createDict(TYPE_INFO, 0) def createFields(self): self.osconfig = self.parent.osconfig if True: yield Enum(Bits(self, "type", 12), self.TYPE_NAME) yield Bit(self, "is_vector") yield NullBits(self, "padding", 32 - 12 - 1) else: yield Enum(Bits(self, "type", 32), self.TYPE_NAME) tag = self["type"].value kw = {} try: handler = self.TYPE_INFO[tag][1] if handler in (self.WidePascalString32, PascalString32): cur = self while not hasattr(cur, 'osconfig'): cur = cur.parent if cur is None: raise LookupError('Cannot find osconfig') osconfig = cur.osconfig if tag == self.TYPE_LPSTR: kw["charset"] = osconfig.charset else: kw["charset"] = osconfig.utf16 elif handler == TimestampWin64: if self.description == "TotalEditingTime": handler = TimedeltaWin64 except LookupError: handler = None if not handler: self.warning("OLE2: Unable to parse property of type %s" % self["type"].display) # raise ParserError( elif self["is_vector"].value: yield UInt32(self, "count") for index in range(self["count"].value): yield handler(self, "item[]", **kw) else: yield handler(self, "value", **kw) self.createValue = lambda: self["value"].value
class Command(FieldSet): COMMAND = {} for channel in range(16): COMMAND[0x80 + channel] = ("Note off (channel %u)" % channel, parseNote) COMMAND[0x90 + channel] = ("Note on (channel %u)" % channel, parseNote) COMMAND[0xA0 + channel] = ("Key after-touch (channel %u)" % channel, parseNote) COMMAND[0xB0 + channel] = ("Control change (channel %u)" % channel, parseControl) COMMAND[0xC0 + channel] = ("Program (patch) change (channel %u)" % channel, parsePatch) COMMAND[0xD0 + channel] = ("Channel after-touch (channel %u)" % channel, parseChannel) COMMAND[0xE0 + channel] = ("Pitch wheel change (channel %u)" % channel, parsePitch) COMMAND_DESC = createDict(COMMAND, 0) COMMAND_PARSER = createDict(COMMAND, 1) META_COMMAND_TEXT = 1 META_COMMAND_NAME = 3 META_COMMAND = { 0x00: ("Sets the track's sequence number", None), 0x01: ("Text event", parseText), 0x02: ("Copyright info", parseText), 0x03: ("Sequence or Track name", parseText), 0x04: ("Track instrument name", parseText), 0x05: ("Lyric", parseText), 0x06: ("Marker", parseText), 0x07: ("Cue point", parseText), 0x20: ("MIDI Channel Prefix", parseChannel), 0x2F: ("End of the track", None), 0x51: ("Set tempo", parseTempo), 0x54: ("SMPTE offset", parseSMPTEOffset), 0x58: ("Time Signature", parseTimeSignature), 0x59: ("Key signature", None), 0x7F: ("Sequencer specific information", None), } META_COMMAND_DESC = createDict(META_COMMAND, 0) META_COMMAND_PARSER = createDict(META_COMMAND, 1) def __init__(self, *args, **kwargs): if 'prev_command' in kwargs: self.prev_command = kwargs['prev_command'] del kwargs['prev_command'] else: self.prev_command = None self.command = None FieldSet.__init__(self, *args, **kwargs) def createFields(self): yield Integer(self, "time", "Delta time in ticks") next = self.stream.readBits(self.absolute_address + self.current_size, 8, self.root.endian) if next & 0x80 == 0: # "Running Status" command if self.prev_command is None: raise ParserError( "Running Status command not preceded by another command.") self.command = self.prev_command.command else: yield Enum(textHandler(UInt8(self, "command"), hexadecimal), self.COMMAND_DESC) self.command = self["command"].value if self.command == 0xFF: yield Enum(textHandler(UInt8(self, "meta_command"), hexadecimal), self.META_COMMAND_DESC) yield UInt8(self, "data_len") size = self["data_len"].value if size: command = self["meta_command"].value if command in self.META_COMMAND_PARSER: parser = self.META_COMMAND_PARSER[command] else: parser = None if parser is not None: yield from parser(self, size) else: yield RawBytes(self, "data", size) else: if self.command not in self.COMMAND_PARSER: raise ParserError("Unknown command: %s" % self["command"].display) parser = self.COMMAND_PARSER[self.command] yield from parser(self) def createDescription(self): if "meta_command" in self: return self["meta_command"].display else: return self.COMMAND_DESC[self.command]
class Attribute(FieldSet): # --- Common code --- def __init__(self, *args): FieldSet.__init__(self, *args) self._size = self["size"].value * 8 type = self["type"].value if type in self.ATTR_INFO: self._name = self.ATTR_INFO[type][0] self._parser = self.ATTR_INFO[type][2] def createFields(self): yield Enum(textHandler(UInt32(self, "type"), hexadecimal), self.ATTR_NAME) yield UInt32(self, "size") yield UInt8(self, "non_resident", "Non-resident flag") yield UInt8(self, "name_length", "Name length in bytes") yield UInt16(self, "name_offset", "Name offset") yield UInt16(self, "flags") yield textHandler(UInt16(self, "attribute_id"), hexadecimal) if self['non_resident'].value: yield UInt64(self, "runlist_start", "Starting Virtual Cluster Number of the runlist") yield UInt64(self, "runlist_stop", "Ending Virtual Cluster Number of the runlist") yield UInt16(self, "runlist_offset", "Offset to the runlist") yield UInt16(self, "compression_unit", "Compression unit size") yield UInt32(self, "unused[]") yield UInt64(self, "size_allocated", "Allocated size of the attribute content") yield UInt64(self, "size_actual", "Actual size of the attribute content") yield UInt64(self, "size_initialized", "Initialized size of the attribute content") if self['name_length'].value: padding = self.seekByte(self['name_offset'].value) if padding: yield padding yield String(self, "name", self['name_length'].value * 2, charset="UTF-16-LE") padding = self.seekByte(self['runlist_offset'].value) if padding: yield padding yield RunList(self, "runlist") else: yield UInt32(self, "length_attr", "Length of the Attribute") yield UInt16(self, "offset_attr", "Offset of the Attribute") yield UInt8(self, "indexed_flag") if self['name_length'].value: padding = self.seekByte(self['name_offset'].value) if padding: yield padding yield String(self, "name", self['name_length'].value * 2, charset="UTF-16-LE") padding = self.seekByte(self['offset_attr'].value) if padding: yield padding if self._parser: yield from self._parser(self) else: size = self["length_attr"].value if size: yield RawBytes(self, "data", size) size = (self.size - self.current_size) // 8 if size: yield PaddingBytes(self, "end_padding", size) def createDescription(self): return "Attribute %s" % self["type"].display FILENAME_NAMESPACE = { 0: "POSIX", 1: "Win32", 2: "DOS", 3: "Win32 & DOS", } # --- Parser specific to a type --- def parseStandardInfo(self): yield TimestampWin64(self, "ctime", "File Creation") yield TimestampWin64(self, "atime", "File Altered") yield TimestampWin64(self, "mtime", "MFT Changed") yield TimestampWin64(self, "rtime", "File Read") yield MSDOSFileAttr32(self, "file_attr", "DOS File Permissions") yield UInt32(self, "max_version", "Maximum Number of Versions") yield UInt32(self, "version", "Version Number") yield UInt32(self, "class_id") yield UInt32(self, "owner_id") yield UInt32(self, "security_id") yield filesizeHandler(UInt64(self, "quota_charged", "Quota Charged")) yield UInt64(self, "usn", "Update Sequence Number (USN)") def parseFilename(self): yield UInt64(self, "ref", "File reference to the parent directory") yield TimestampWin64(self, "ctime", "File Creation") yield TimestampWin64(self, "atime", "File Altered") yield TimestampWin64(self, "mtime", "MFT Changed") yield TimestampWin64(self, "rtime", "File Read") yield filesizeHandler( UInt64(self, "alloc_size", "Allocated size of the file")) yield filesizeHandler( UInt64(self, "real_size", "Real size of the file")) yield UInt32(self, "file_flags") yield UInt32(self, "file_flags2", "Used by EAs and Reparse") yield UInt8(self, "filename_length", "Filename length in characters") yield Enum(UInt8(self, "filename_namespace"), self.FILENAME_NAMESPACE) size = self["filename_length"].value * 2 if size: yield String(self, "filename", size, charset="UTF-16-LE") def parseData(self): size = (self.size - self.current_size) // 8 if size: yield Bytes(self, "data", size) def parseBitmap(self): size = (self.size - self.current_size) for index in range(size): yield Bit(self, "bit[]") # --- Type information --- ATTR_INFO = { 0x10: ('standard_info', 'STANDARD_INFORMATION ', parseStandardInfo), 0x20: ('attr_list', 'ATTRIBUTE_LIST ', None), 0x30: ('filename[]', 'FILE_NAME ', parseFilename), 0x40: ('vol_ver', 'VOLUME_VERSION', None), 0x50: ('security', 'SECURITY_DESCRIPTOR ', None), 0x60: ('vol_name', 'VOLUME_NAME ', None), 0x70: ('vol_info', 'VOLUME_INFORMATION ', None), 0x80: ('data', 'DATA ', parseData), 0x90: ('index_root', 'INDEX_ROOT ', None), 0xA0: ('index_alloc', 'INDEX_ALLOCATION ', None), 0xB0: ('bitmap', 'BITMAP ', parseBitmap), 0xC0: ('reparse', 'REPARSE_POINT ', None), 0xD0: ('ea_info', 'EA_INFORMATION ', None), 0xE0: ('ea', 'EA ', None), 0xF0: ('prop_set', 'PROPERTY_SET', None), 0x100: ('log_util', 'LOGGED_UTILITY_STREAM', None), } ATTR_NAME = createDict(ATTR_INFO, 1)
0x0538: ("POLYPOLYGON", "Draw multiple polygons", None), 0x0548: ("EXTFLOODFILL", "Extend flood fill", None), 0x061C: ("ROUNDRECT", "Draw a rounded rectangle", None), 0x061D: ("PATBLT", "Pattern blitting", None), 0x0626: ("ESCAPE", "Escape", None), 0x06FF: ("CREATEREGION", "Create region", None), 0x0817: ("ARC", "Draw an arc", None), 0x081A: ("PIE", "Draw a pie", None), 0x0830: ("CHORD", "Draw a chord", None), 0x0940: ("DIBBITBLT", "DIB bit blitting", None), 0x0a32: ("EXTTEXTOUT", "Draw text (extra)", None), 0x0b41: ("DIBSTRETCHBLT", "DIB stretch blitting", None), 0x0d33: ("SETDIBTODEV", "Set DIB to device", None), 0x0f43: ("STRETCHDIB", "Stretch DIB", None), } META_NAME = createDict(META, 0) META_DESC = createDict(META, 1) # ---------------------------------------------------------------------------- # EMF constants # EMF mapping modes EMF_MAPPING_MODE = { 1: "TEXT", 2: "LOMETRIC", 3: "HIMETRIC", 4: "LOENGLISH", 5: "HIENGLISH", 6: "TWIPS", 7: "ISOTROPIC", 8: "ANISOTROPIC",
from hachoir_py3.parser import Parser from hachoir_py3.field import (FieldSet, UInt8, UInt24, UInt32, NullBits, NullBytes, Bit, Bits, String, RawBytes, Enum) from hachoir_py3.core.endian import BIG_ENDIAN from hachoir_py3.parser.audio.mpeg_audio import Frame from hachoir_py3.parser.video.amf import AMFObject from hachoir_py3.core.tools import createDict SAMPLING_RATE = { 0: (5512, "5.5 kHz"), 1: (11025, "11 kHz"), 2: (22050, "22.1 kHz"), 3: (44100, "44.1 kHz"), } SAMPLING_RATE_VALUE = createDict(SAMPLING_RATE, 0) SAMPLING_RATE_TEXT = createDict(SAMPLING_RATE, 1) AUDIO_CODEC_MP3 = 2 AUDIO_CODEC_NAME = { 0: "Uncompressed", 1: "ADPCM", 2: "MP3", 5: "Nellymoser 8kHz mono", 6: "Nellymoser", } VIDEO_CODEC_NAME = { 2: "Sorensen H.263", 3: "Screen video", 4: "On2 VP6",
class Object(FieldSet): TYPE_INFO = { # TODO: Write parser 0: ("end[]", None, "End (reserved for BER, None)", None), 1: ("boolean[]", readBoolean, "Boolean", None), 2: ("integer[]", readInteger, "Integer", None), 3: ("bit_str[]", readBitString, "Bit string", None), 4: ("octet_str[]", readOctetString, "Octet string", None), 5: ("null[]", None, "NULL (empty, None)", None), 6: ("obj_id[]", readObjectID, "Object identifier", formatObjectID), 7: ("obj_desc[]", None, "Object descriptor", None), # TODO: Write parser # TODO: Write parser # External? 8: ("external[]", None, "External, instance of", None), 9: ("real[]", readASCIIString, "Real number", None), # TODO: Write parser 10: ("enum[]", readInteger, "Enumerated", None), 11: ("embedded[]", None, "Embedded PDV", None), # TODO: Write parser 12: ("utf8_str[]", readUTF8String, "Printable string", None), # TODO: Write parser 13: ("rel_obj_id[]", None, "Relative object identifier", None), 14: ("time[]", None, "Time", None), # TODO: Write parser # 15: invalid??? sequence of??? 16: ("seq[]", readSequence, "Sequence", None), 17: ("set[]", readSet, "Set", None), 18: ("num_str[]", readASCIIString, "Numeric string", None), 19: ("print_str[]", readASCIIString, "Printable string", formatValue), 20: ("teletex_str[]", readASCIIString, "Teletex (T61, None) string", None), 21: ("videotex_str[]", readASCIIString, "Videotex string", None), 22: ("ia5_str[]", readASCIIString, "IA5 string", formatValue), 23: ("utc_time[]", readASCIIString, "UTC time", formatUTCTime), 24: ("general_time[]", readASCIIString, "Generalized time", None), 25: ("graphic_str[]", readASCIIString, "Graphic string", None), 26: ("visible_str[]", readASCIIString, "Visible (ISO64, None) string", None), 27: ("general_str[]", readASCIIString, "General string", None), 28: ("universal_str[]", readASCIIString, "Universal string", None), 29: ("unrestricted_str[]", readASCIIString, "Unrestricted string", None), 30: ("bmp_str[]", readBMPString, "BMP string", None), # 31: multiple octet tag number, TODO: not supported # Extended tag values: # 31: Date # 32: Time of day # 33: Date-time # 34: Duration } TYPE_DESC = createDict(TYPE_INFO, 2) CLASS_DESC = {0: "universal", 1: "application", 2: "context", 3: "private"} FORM_DESC = {False: "primitive", True: "constructed"} def __init__(self, *args, **kw): FieldSet.__init__(self, *args, **kw) key = self["type"].value & 31 if self['class'].value == 0: # universal object if key in self.TYPE_INFO: self._name, self._handler, self._description, create_desc = self.TYPE_INFO[ key] if create_desc: self.createDescription = lambda: "%s: %s" % ( self.TYPE_INFO[key][2], create_desc(self)) self._description = None elif key == 31: raise ParserError( "ASN.1 Object: tag bigger than 30 are not supported") else: self._handler = None elif self['form'].value: # constructed: treat as sequence self._name = 'seq[]' self._handler = readSequence self._description = 'constructed object type %i' % key else: # primitive, context/private self._name = 'raw[]' self._handler = readASCIIString self._description = '%s object type %i' % (self['class'].display, key) field = self["size"] self._size = field.address + field.size + field.value * 8 def createFields(self): yield Enum(Bits(self, "class", 2), self.CLASS_DESC) yield Enum(Bit(self, "form"), self.FORM_DESC) if self['class'].value == 0: yield Enum(Bits(self, "type", 5), self.TYPE_DESC) else: yield Bits(self, "type", 5) yield ASNInteger(self, "size", "Size in bytes") size = self["size"].value if size: if self._handler: yield from self._handler(self, size) else: yield RawBytes(self, "raw", size)
class Photoshop8BIM(FieldSet): TAG_INFO = { 0x03ed: ("res_info", ResolutionInfo, "Resolution information"), 0x03f3: ("print_flag", PrintFlags, "Print flags: labels, crop marks, colour bars, etc."), 0x03f5: ("col_half_info", None, "Colour half-toning information"), 0x03f8: ("color_trans_func", None, "Colour transfer function"), 0x0404: ("iptc", IPTC, "IPTC/NAA"), 0x0406: ("jpeg_qual", None, "JPEG quality"), 0x0408: ("grid_guide", GridGuides, "Grid guides informations"), 0x0409: ("thumb_res", Thumbnail, "Thumbnail resource (PS 4.0)"), 0x0410: ("watermark", UInt8, "Watermark"), 0x040a: ("copyright_flag", UInt8, "Copyright flag"), 0x040b: ("url", None, "URL"), 0x040c: ("thumb_res2", Thumbnail, "Thumbnail resource (PS 5.0)"), 0x040d: ("glob_angle", UInt32, "Global lighting angle for effects"), 0x0411: ("icc_tagged", None, "ICC untagged (1 means intentionally untagged)"), 0x0414: ("base_layer_id", UInt32, "Base value for new layers ID's"), 0x0416: ("indexed_colors", UInt16, "Number of colors in table that are actually defined"), 0x0417: ("transparency_index", UInt16, "Index of transparent color"), 0x0419: ("glob_altitude", UInt32, "Global altitude"), 0x041a: ("slices", None, "Slices"), 0x041e: ("url_list", None, "Unicode URLs"), 0x0421: ("version", Version, "Version information"), 0x0425: ("caption_digest", None, "16-byte MD5 caption digest"), 0x0426: ("printscale", PrintScale, "Printer scaling"), 0x2710: ("print_flag2", PrintFlags2, "Print flags (2)"), } TAG_NAME = createDict(TAG_INFO, 0) CONTENT_HANDLER = createDict(TAG_INFO, 1) TAG_DESC = createDict(TAG_INFO, 2) def __init__(self, *args, **kw): FieldSet.__init__(self, *args, **kw) try: self._name, self.handler, self._description = self.TAG_INFO[ self["tag"].value] except KeyError: self.handler = None size = self["size"] self._size = size.address + size.size + alignValue(size.value, 2) * 8 def createFields(self): yield String(self, "signature", 4, "8BIM signature", charset="ASCII") if self["signature"].value != "8BIM": raise ParserError( "Stream doesn't look like 8BIM item (wrong signature)!") yield textHandler(UInt16(self, "tag"), hexadecimal) if self.stream.readBytes(self.absolute_address + self.current_size, 4) != b"\0\0\0\0": yield PascalString8(self, "name") size = 2 + (self["name"].size // 8) % 2 yield NullBytes(self, "name_padding", size) else: yield String(self, "name", 4, strip="\0") yield UInt16(self, "size") size = alignValue(self["size"].value, 2) if not size: return if self.handler: if issubclass(self.handler, FieldSet): yield self.handler(self, "content", size=size * 8) else: yield self.handler(self, "content") else: yield RawBytes(self, "content", size)