class CARRenditionRaw(Model): fields = [ ('magic', Parse.fixed("<4s")), ('reserved', Parse.fixed("<I")), ('length', Parse.fixed("<I")), ('binary', Parse.dynamic("length")), ]
class CARRenditionInfo(Model): fields = [ ('magic', Parse.fixed("<I")), ('length', Parse.fixed("<I")), ('content', Parse.dynamic("length")), ] @cached_property def parsed(self): content = StringIO.StringIO(self.content) make_map = { 1001: Parse.array_dynamic(model=CARRenditionSlice), 1003: Parse.array_dynamic(model=CARRenditionMetric), 1004: Parse.model(CARRenditionComposition), 1005: Parse.model(CARRenditionUTI), 1006: Parse.model(CARRenditionBitmapInfo), 1007: Parse.model(CARRenditionBytesPerRow), 1010: Parse.model(CARRenditionReference), } content_make = make_map.get(self.magic) return content_make(self, content) if content_make else None def __str__(self): return "<CARRenditionInfo magic=%s, length=%s, parsed=%s>" % \ (self.magic, self.length, self.parsed)
class CARRenditionMetadata(Model): fields = [ ('modification_date', Parse.fixed("<I")), ('layout_raw', Parse.fixed("<H")), ('reserved', Parse.fixed("<H")), ('name', Parse.terminated("\x00", fixed=128)), ]
class CARRenditionSlice(Model): fields = [ ('x', Parse.fixed("<I")), ('y', Parse.fixed("<I")), ('width', Parse.fixed("<I")), ('height', Parse.fixed("<I")), ]
class CARKeyFormat(Model): fields = [ ('magic', Parse.fixed(">4s")), ('reserved', Parse.fixed("<I")), ('num_identifiers', Parse.fixed("<I")), ('identifiers', Parse.array(model=CARKeyFormatIdentifier, count="num_identifiers")), ]
class BOMPath(Model): fields = [ ('is_leaf', Parse.fixed(">H")), ('count', Parse.fixed(">H")), ('forward', Parse.fixed(">I")), ('backwards', Parse.fixed(">I")), ('indexes', Parse.array(model=BOMPathIndex, count="count")), ]
class CARHeader(Model): def _uuid(self, stream): content, = struct.unpack("<16s", stream.read(16)) return content.encode("hex") fields = [ ('magic', Parse.fixed(">4s")), ('ui_version', Parse.fixed("<I")), ('storage_version', Parse.fixed("<I")), ('storage_timestamp', Parse.fixed("<I")), ('rendition_count', Parse.fixed("<I")), ('file_creator', Parse.terminated("\n", fixed=128)), ('other_creator', Parse.terminated("\x00", fixed=256)), ('uuid', _uuid), ('associated_checksum', Parse.fixed("<I")), ('schema_version', Parse.fixed("<I")), ('color_space_id', Parse.fixed("<I")), ('key_semantics', Parse.fixed("<I")), ] def dump(self): print "Magic: %s" % self.magic print "UI version: %x" % self.ui_version print "Storage version: %x" % self.storage_version print "Storage Timestamp: %d" % self.storage_timestamp print "Rendition Count: %x" % self.rendition_count print "Creator: %s" % self.file_creator print "Other Creator: %s" % self.other_creator print "UUID: %s" % self.uuid print "Associated Checksum: %x" % self.associated_checksum print "Schema Version: %d" % self.schema_version print "Color space ID: %d" % self.color_space_id print "Key Semantics: %d" % self.key_semantics
class CARFacet(Model): custom = ['name'] fields = [ ('x', Parse.fixed("<H")), ('y', Parse.fixed("<H")), ('attributes_count', Parse.fixed("<H")), ('attributes_raw', Parse.array(model=CARFacetAttribute, count="attributes_count")), ] def dump(self): print "Facet: %s" % self.name for attribute, value in self.attributes.iteritems(): print "[%.2d] %s = %s" % \ (attribute.identifier_raw, attribute.identifier, value)
class CARRenditionReference(Model): fields = [ ('magic', Parse.fixed("<4s")), ('padding', Parse.fixed("<I")), ('x', Parse.fixed("<I")), ('y', Parse.fixed("<I")), ('width', Parse.fixed("<I")), ('height', Parse.fixed("<I")), ('layout', Parse.fixed("<H")), ('key_length', Parse.fixed("<H")), ]
class CARKeyFormatIdentifier(Model): fields = [ ('identifier_raw', Parse.fixed("<I")), ] def __eq__(self, other): return self.identifier_raw == other.identifier_raw @property def identifier(self): return CAR_ATTRIBUTE_BY_ID.get(self.identifier_raw, "unknown")
class BOMHeader(Model): fields = [ ('magic', Parse.fixed(">8s")), ('version', Parse.fixed(">I")), ('block_count', Parse.fixed(">I")), ('index_offset', Parse.fixed(">I")), ('index_size', Parse.fixed(">I")), ('table_offset', Parse.fixed(">I")), ('table_size', Parse.fixed(">I")), ]
class BOMTree(Model): def iterate(self, stream_index): if self.magic != "tree" or self.version != 1: raise BOMInvalidTreeType("Invalid tree type %s" % self) path = BOMPath.make(stream_index(self.child).stream) if not path.is_leaf: index = path.indexes[0] path = BOMPath.make(stream_index(index.value_index).stream) while path: for index in path.indexes: stream, block = stream_index(index.key_index) key = stream.read(block.size) stream, block = stream_index(index.value_index) value = stream.read(block.size) yield key, value if not path.forward: break path = BOMPath.make(stream_index(path.forward).stream) custom = ["name"] fields = [ ('magic', Parse.fixed(">4s")), ('version', Parse.fixed(">I")), ('child', Parse.fixed(">I")), ('block_size', Parse.fixed(">I")), ('path_count', Parse.fixed(">I")), ('unknown', Parse.fixed(">b")), ]
class CARFacetAttribute(Model): fields = [ ('identifier', Parse.fixed("<H")), ('value', Parse.fixed("<H")), ]
class CARRenditionBytesPerRow(Model): fields = [ ('bytes_per_row', Parse.fixed("<I")), ]
class CARRenditionBitmapInfo(Model): fields = [ ('exif_orientation', Parse.fixed("<I")), ]
class CARRenditionUTI(Model): fields = [ ('length', Parse.fixed("<I")), ('uti', Parse.fixed("<1s")), ]
class BOMPathIndex(Model): fields = [ ('value_index', Parse.fixed(">I")), ('key_index', Parse.fixed(">I")), ]
class BOMExtendedMetadata(Model): fields = [ ('magic', Parse.fixed(">4s")), ('contents', Parse.terminated("\x00", fixed=768)), ('creator', Parse.terminated("\n", fixed=256)), ]
class CARRenditionFlag(Model): fields = [ ('flags', Parse.fixed("<B")), ('reserved', Parse.fixed("<3s")), ]
class CARRendition(Model): RESIZE_MODE_FIXED = "Fixed Size" RESIZE_MODE_TILE = "Tile" RESIZE_MODE_SCALE = "Scale" RESIZE_MODE_HUNIFORM_VSCALE = "Horizontal Uniform; Vertical Scale" RESIZE_MODE_HSCALE_VUNIFORM = "Horizontal Scale; Vertical Uniform" @classmethod def make_from_buffer(cls, buffer, **kwargs): stream = StringIO.StringIO(buffer) return cls.make(stream, **kwargs) fields = [ ('magic', Parse.fixed("<4s")), ('version', Parse.fixed("<I")), ('flags', Parse.model(CARRenditionFlag)), ('width', Parse.fixed("<I")), ('height', Parse.fixed("<I")), ('scale_factor', Parse.fixed("<I")), ('pixel_format', Parse.fixed("<4s")), ('color_space_id', Parse.fixed("<B")), ('reserved', Parse.fixed("<3s")), ('modification_date', Parse.fixed("<I")), ('layout_raw', Parse.fixed("<H")), ('reserved', Parse.fixed("<H")), ('name', Parse.terminated("\x00", fixed=128)), ('info_len', Parse.fixed("<I")), ('bitmap_count', Parse.fixed("<I")), ('reserved', Parse.fixed("<I")), ('payload_size', Parse.fixed("<I")), ('info', Parse.array_fixed(model=CARRenditionInfo, size='info_len')), ('content', Parse.dynamic("payload_size")), ] @cached_property def raw(self): if len(self.content): return CARRenditionRaw.make_from_buffer(self.content) @cached_property def layout(self): return CAR_RENDITION_LAYOUT_BY_ID.get(self.layout_raw, "unknown") @cached_property def is_resizable(self): return 25 >= self.layout_raw >= 20 and len(self.slices) > 1 @cached_property def slices(self): return filter(lambda x: x.parsed is CARRenditionSlice, self.info) @cached_property def resize_mode(self): mode_map = { "one_part_tile": CARRendition.RESIZE_MODE_TILE, "three_part_horizontal_tile": CARRendition.RESIZE_MODE_TILE, "three_part_vertical_tile": CARRendition.RESIZE_MODE_TILE, "nine_part_tile": CARRendition.RESIZE_MODE_TILE, "one_part_scale": CARRendition.RESIZE_MODE_SCALE, "three_part_horizontal_scale": CARRendition.RESIZE_MODE_SCALE, "three_part_vertical_scale": CARRendition.RESIZE_MODE_SCALE, "nine_part_scale": CARRendition.RESIZE_MODE_SCALE, "nine_part_horizontal_uniform_vertical_scale": CARRendition.RESIZE_MODE_HUNIFORM_VSCALE, "nine_part_horizontal_uniform_vertical_scale": CARRendition.RESIZE_MODE_HSCALE_VUNIFORM, } return mode_map.get(self.layout, CARRendition.RESIZE_MODE_FIXED) def dump(self): print "Rendition: %s" % self.name print "Width: %d" % self.width print "Height: %d" % self.height print "Scale: %f" % (self.scale_factor / 100.0) print "Layout: %s" % self.layout_raw print "Resizable: %d" % self.is_resizable print "Payload size: %d" % self.payload_size if self.is_resizable: for i, slice in enumerate(self.slices): print "Slice %d: (%u, %u) %u x %u" % \ (i, slice.x, slice.y, slice.width, slice.height) print "Resize mode: %s" % self.resize_mode print "Attributes:" for attribute, value in self.attributes.iteritems(): print "[%.2d] %s = %s" % \ (attribute.identifier_raw, attribute.identifier, value) print ""
class CARRenditionMetric(Model): fields = [ ('width', Parse.fixed("<I")), ('height', Parse.fixed("<I")), ]
class CARRenditionComposition(Model): fields = [ ('blend_mode', Parse.fixed("<I")), ('opacity', Parse.fixed("<f")), ]
class BOMBlock(Model): fields = [ ('index', Parse.fixed(">I")), ('size', Parse.fixed(">I")), ]