def __init__(self, data=None):
        """Represents depot manifest

        :param data: manifest data
        :type  data: bytes
        """
        self.metadata = ContentManifestMetadata()
        self.payload = ContentManifestPayload()
        self.signature = ContentManifestSignature()

        if data:
            self.deserialize(data)
    def deserialize(self, data):
        """Deserialize a manifest (compressed or uncompressed)

        :param data: manifest data
        :type  data: bytes
        """
        try:
            with ZipFile(BytesIO(data)) as zf:
                data = zf.read(zf.filelist[0])
        except BadZipFile:
            pass

        data = StructReader(data)

        magic, length = data.unpack('<II')

        if magic != DepotManifest.PROTOBUF_PAYLOAD_MAGIC:
            raise Exception("Expecting protobuf payload")

        self.payload = ContentManifestPayload()
        self.payload.ParseFromString(data.read(length))

        magic, length = data.unpack('<II')

        if magic != DepotManifest.PROTOBUF_METADATA_MAGIC:
            raise Exception("Expecting protobuf metadata")

        self.metadata = ContentManifestMetadata()
        self.metadata.ParseFromString(data.read(length))

        magic, length = data.unpack('<II')

        if magic != DepotManifest.PROTOBUF_SIGNATURE_MAGIC:
            raise Exception("Expecting protobuf signature")

        self.signature = ContentManifestSignature()
        self.signature.ParseFromString(data.read(length))

        magic, = data.unpack('<I')

        if magic != DepotManifest.PROTOBUF_ENDOFMANIFEST_MAGIC:
            raise Exception("Expecting end of manifest")
class DepotManifest(object):
    DepotFileClass = DepotFile
    PROTOBUF_PAYLOAD_MAGIC = 0x71F617D0
    PROTOBUF_METADATA_MAGIC = 0x1F4812BE
    PROTOBUF_SIGNATURE_MAGIC = 0x1B81B817
    PROTOBUF_ENDOFMANIFEST_MAGIC = 0x32C415AB

    def __init__(self, data=None):
        """Represents depot manifest

        :param data: manifest data
        :type  data: bytes
        """
        self.metadata = ContentManifestMetadata()
        self.payload = ContentManifestPayload()
        self.signature = ContentManifestSignature()

        if data:
            self.deserialize(data)

    def __repr__(self):
        params = ', '.join([
            "depot_id=" + str(self.depot_id),
            "gid=" + str(self.gid),
            "creation_time=" + repr(
                datetime.utcfromtimestamp(
                    self.metadata.creation_time).isoformat().replace('T',
                                                                     ' ')),
        ])

        if self.metadata.filenames_encrypted:
            params += ', filenames_encrypted=True'

        return "<%s(%s)>" % (
            self.__class__.__name__,
            params,
        )

    @property
    def depot_id(self):
        """:type: int"""
        return self.metadata.depot_id

    @property
    def gid(self):
        """:type: int"""
        return self.metadata.gid_manifest

    @property
    def creation_time(self):
        """:type: int"""
        return self.metadata.creation_time

    @property
    def size_original(self):
        """:type: int"""
        return self.metadata.cb_disk_original

    @property
    def size_compressed(self):
        """:type: int"""
        return self.metadata.cb_disk_compressed

    @property
    def filenames_encrypted(self):
        """:type: bool"""
        return self.metadata.filenames_encrypted

    def decrypt_filenames(self, depot_key):
        """Decrypt all filenames in the manifest

        :param depot_key: depot key
        :type  depot_key: bytes
        :raises: :class:`RuntimeError`
        """
        if not self.metadata.filenames_encrypted:
            return

        for mapping in self.payload.mappings:
            filename = b64decode(mapping.filename)

            try:
                filename = symmetric_decrypt(filename, depot_key)
            except Exception:
                RuntimeError("Unable to decrypt filename for depot manifest")

            mapping.filename = filename

        self.metadata.filenames_encrypted = False

    def deserialize(self, data):
        """Deserialize a manifest (compressed or uncompressed)

        :param data: manifest data
        :type  data: bytes
        """
        try:
            with ZipFile(BytesIO(data)) as zf:
                data = zf.read(zf.filelist[0])
        except BadZipFile:
            pass

        data = StructReader(data)

        magic, length = data.unpack('<II')

        if magic != DepotManifest.PROTOBUF_PAYLOAD_MAGIC:
            raise Exception("Expecting protobuf payload")

        self.payload = ContentManifestPayload()
        self.payload.ParseFromString(data.read(length))

        magic, length = data.unpack('<II')

        if magic != DepotManifest.PROTOBUF_METADATA_MAGIC:
            raise Exception("Expecting protobuf metadata")

        self.metadata = ContentManifestMetadata()
        self.metadata.ParseFromString(data.read(length))

        magic, length = data.unpack('<II')

        if magic != DepotManifest.PROTOBUF_SIGNATURE_MAGIC:
            raise Exception("Expecting protobuf signature")

        self.signature = ContentManifestSignature()
        self.signature.ParseFromString(data.read(length))

        magic, = data.unpack('<I')

        if magic != DepotManifest.PROTOBUF_ENDOFMANIFEST_MAGIC:
            raise Exception("Expecting end of manifest")

    def serialize(self, compress=True):
        """Serialize manifest

        :param compress: wether the output should be Zip compressed
        :type  compress: bytes
        """
        data = BytesIO()

        part = self.payload.SerializeToString()
        data.write(pack('<II', DepotManifest.PROTOBUF_PAYLOAD_MAGIC,
                        len(part)))
        data.write(part)

        part = self.metadata.SerializeToString()
        data.write(
            pack('<II', DepotManifest.PROTOBUF_METADATA_MAGIC, len(part)))
        data.write(part)

        part = self.signature.SerializeToString()
        data.write(
            pack('<II', DepotManifest.PROTOBUF_SIGNATURE_MAGIC, len(part)))
        data.write(part)

        data.write(pack('<I', DepotManifest.PROTOBUF_ENDOFMANIFEST_MAGIC))

        if compress:
            zbuff = BytesIO()
            with ZipFile(zbuff, 'w', ZIP_DEFLATED) as zf:
                zf.writestr('z', data.getvalue())

            return zbuff.getvalue()
        else:
            return data.getvalue()

    def __iter__(self):
        if not self.filenames_encrypted:
            for mapping in self.payload.mappings:
                yield self.DepotFileClass(self, mapping)

    def iter_files(self, pattern=None):
        """
        :param pattern: unix shell wildcard pattern, see :func:`.fnmatch`
        :type  pattern: str
        """
        if not self.filenames_encrypted:
            for mapping in self.payload.mappings:
                if (pattern is not None and not fnmatch(
                        mapping.filename.rstrip('\x00 \n\t'), pattern)):
                    continue
                yield self.DepotFileClass(self, mapping)

    def __len__(self):
        return len(self.payload.mappings)