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 Chunk(FieldSet): CHUNK_INFO = { 0x0001: ("string_table", "String Table", StringChunk, None), 0x0003: ("xml_file", "XML File", Top, None), 0x0100: ("namespace_start[]", "Start Namespace", NamespaceTag, NamespaceStartValue), 0x0101: ("namespace_end[]", "End Namespace", NamespaceTag, NamespaceEndValue), 0x0102: ("tag_start[]", "Start Tag", TagStart, TagStartValue), 0x0103: ("tag_end[]", "End Tag", TagEnd, TagEndValue), 0x0104: ("text[]", "Text", TextChunk, None), 0x0180: ("resource_ids", "Resource IDs", ResourceIDs, None), } CHUNK_DESC = createDict(CHUNK_INFO, 1) def __init__(self, parent, name, description=None): FieldSet.__init__(self, parent, name, description) self._size = self['chunk_size'].value * 8 type = self['type'].value self.parse_func = None if type in self.CHUNK_INFO: self._name, self._description, self.parse_func, value_func = self.CHUNK_INFO[ type] if value_func: self.createValue = lambda: value_func(self) def createFields(self): yield Enum(UInt16(self, "type"), self.CHUNK_DESC) yield UInt16(self, "header_size") yield UInt32(self, "chunk_size") if self.parse_func: for field in self.parse_func(self): yield field
class XMLAttribute(FieldSet): TYPE_INFO = { 0: ('Null', IntTextHandler(lambda field: '')), 1: ('Reference', IntTextHandler(lambda field: '@%08x' % field.value)), 2: ('Attribute', IntTextHandler(lambda field: '?%08x' % field.value)), 3: ('String', IntTextHandler(stringIndex)), 4: ('Float', Float32), 5: ('Dimension', XMLDimensionFloat), 6: ('Fraction', XMLFractionFloat), 16: ('Int_Dec', Int32), 17: ('Int_Hex', IntTextHandler(hexadecimal)), 18: ('Int_Boolean', IntTextHandler(booleanText)), 28: ('Int_Color_Argb8', IntTextHandler(lambda field: '#%08x' % field.value)), 29: ('Int_Color_Rgb8', IntTextHandler(lambda field: '#%08x' % field.value)), 30: ('Int_Color_Argb4', IntTextHandler(lambda field: '#%08x' % field.value)), 31: ('Int_Color_Rgb4', IntTextHandler(lambda field: '#%08x' % field.value)), } TYPE_NAME = createDict(TYPE_INFO, 0) TYPE_FUNC = createDict(TYPE_INFO, 1) static_size = 5 * 32 def createFields(self): yield textHandler(Int32(self, "ns"), stringIndex) yield textHandler(Int32(self, "name"), stringIndex) yield textHandler(Int32(self, "value_string"), stringIndex) yield UInt16(self, "unk[]") yield UInt8(self, "unk[]") yield Enum(UInt8(self, "value_type"), self.TYPE_NAME) func = self.TYPE_FUNC.get(self['value_type'].value, None) if not func: func = UInt32 yield func(self, "value_data") def createValue(self): return (self['name'].display, self['value_data'].value) def createDisplay(self): return '%s="%s"' % (self['name'].display, self['value_data'].display)
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 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 MetadataBlock(FieldSet): "Metadata block field: http://flac.sourceforge.net/format.html#metadata_block" BLOCK_TYPES = { 0: ("stream_info", u"Stream info", StreamInfo), 1: ("padding[]", u"Padding", None), 2: ("application[]", u"Application", None), 3: ("seek_table", u"Seek table", SeekTable), 4: ("comment", u"Vorbis comment", VorbisComment), 5: ("cue_sheet[]", u"Cue sheet", None), 6: ("picture[]", u"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 TcpdumpFile(Parser): PARSER_TAGS = { "id": "tcpdump", "category": "misc", "min_size": 24 * 8, "description": "Tcpdump file (network)", "magic": (("\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 != "\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)
from hachoir_py2.parser import Parser from hachoir_py2.field import (FieldSet, UInt8, UInt24, UInt32, NullBits, NullBytes, Bit, Bits, String, RawBytes, Enum) from hachoir_py2.core.endian import BIG_ENDIAN from hachoir_py2.parser.audio.mpeg_audio import Frame from hachoir_py2.parser.video.amf import AMFObject from hachoir_py2.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: u"Uncompressed", 1: u"ADPCM", 2: u"MP3", 5: u"Nellymoser 8kHz mono", 6: u"Nellymoser", } VIDEO_CODEC_NAME = { 2: u"Sorensen H.263", 3: u"Screen video", 4: u"On2 VP6",
class AuFile(Parser): PARSER_TAGS = { "id": "sun_next_snd", "category": "audio", "file_ext": ("au", "snd"), "mime": (u"audio/basic", ), "min_size": 24 * 8, "magic": ((".snd", 0), ), "description": "Sun/NeXT audio" } endian = BIG_ENDIAN CODEC_INFO = { 1: (8, u"8-bit ISDN u-law"), 2: (8, u"8-bit linear PCM"), 3: (16, u"16-bit linear PCM"), 4: (24, u"24-bit linear PCM"), 5: (32, u"32-bit linear PCM"), 6: (32, u"32-bit IEEE floating point"), 7: (64, u"64-bit IEEE floating point"), 8: (None, u"Fragmented sample data"), 9: (None, u"DSP program"), 10: (8, u"8-bit fixed point"), 11: (16, u"16-bit fixed point"), 12: (24, u"24-bit fixed point"), 13: (32, u"32-bit fixed point"), 18: (16, u"16-bit linear with emphasis"), 19: (16, u"16-bit linear compressed"), 20: (16, u"16-bit linear with emphasis and compression"), 21: (None, u"Music kit DSP commands"), 23: (None, u"4-bit ISDN u-law compressed (CCITT G.721 ADPCM)"), 24: (None, u"ITU-T G.722 ADPCM"), 25: (None, u"ITU-T G.723 3-bit ADPCM"), 26: (None, u"ITU-T G.723 5-bit ADPCM"), 27: (8, u"8-bit ISDN A-law"), } # Create bit rate and codec name dictionnaries BITS_PER_SAMPLE = createDict(CODEC_INFO, 0) CODEC_NAME = createDict(CODEC_INFO, 1) VALID_NB_CHANNEL = set((1, 2)) # FIXME: 4, 5, 7, 8 channels are supported? def validate(self): if self.stream.readBytes(0, 4) != ".snd": return "Wrong file signature" if self["channels"].value not in self.VALID_NB_CHANNEL: return "Invalid number of channel" return True def getBitsPerSample(self): """ Get bit rate (number of bit per sample per channel), may returns None if you unable to compute it. """ return self.BITS_PER_SAMPLE.get(self["codec"].value) def createFields(self): yield String(self, "signature", 4, 'Format signature (".snd")', charset="ASCII") yield UInt32(self, "data_ofs", "Data offset") yield filesizeHandler(UInt32(self, "data_size", "Data size")) yield Enum(UInt32(self, "codec", "Audio codec"), self.CODEC_NAME) yield displayHandler( UInt32(self, "sample_rate", "Number of samples/second"), humanFrequency) yield UInt32(self, "channels", "Number of interleaved channels") size = self["data_ofs"].value - self.current_size // 8 if 0 < size: yield String(self, "info", size, "Information", strip=" \0", charset="ISO-8859-1") size = min(self["data_size"].value, (self.size - self.current_size) // 8) yield RawBytes(self, "audio_data", size, "Audio data") def createContentSize(self): return (self["data_ofs"].value + self["data_size"].value) * 8
0x0538: ("POLYPOLYGON", u"Draw multiple polygons", None), 0x0548: ("EXTFLOODFILL", u"Extend flood fill", None), 0x061C: ("ROUNDRECT", u"Draw a rounded rectangle", None), 0x061D: ("PATBLT", u"Pattern blitting", None), 0x0626: ("ESCAPE", u"Escape", None), 0x06FF: ("CREATEREGION", u"Create region", None), 0x0817: ("ARC", u"Draw an arc", None), 0x081A: ("PIE", u"Draw a pie", None), 0x0830: ("CHORD", u"Draw a chord", None), 0x0940: ("DIBBITBLT", u"DIB bit blitting", None), 0x0a32: ("EXTTEXTOUT", u"Draw text (extra)", None), 0x0b41: ("DIBSTRETCHBLT", u"DIB stretch blitting", None), 0x0d33: ("SETDIBTODEV", u"Set DIB to device", None), 0x0f43: ("STRETCHDIB", u"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",
class Command(FieldSet): COMMAND = {} for channel in xrange(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: for field in parser(self, size): yield field 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] for field in parser(self): yield field def createDescription(self): if "meta_command" in self: return self["meta_command"].display else: return self.COMMAND_DESC[self.command]
class Object(FieldSet): TYPE_INFO = { 0: ("end[]", None, "End (reserved for BER, None)", None), # TODO: Write parser 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 8: ("external[]", None, "External, instance of", None), # TODO: Write parser # External? 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), 13: ("rel_obj_id[]", None, "Relative object identifier", None), # TODO: Write parser 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: for field in self._handler(self, size): yield field 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) != "\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)