def __fill_missing(self, fileobj): """Raises IOError and apev2.error""" fileobj.seek(self.metadata + 8) data = fileobj.read(16) if len(data) != 16: raise error self.version = data[:4] self.size = cdata.uint32_le(data[4:8]) self.items = cdata.uint32_le(data[8:12]) self.flags = cdata.uint32_le(data[12:]) if self.header is not None: self.data = self.header + 32 # If we're reading the header, the size is the header # offset + the size, which includes the footer. self.end = self.data + self.size fileobj.seek(self.end - 32, 0) if fileobj.read(8) == b"APETAGEX": self.footer = self.end - 32 elif self.footer is not None: self.end = self.footer + 32 self.data = self.end - self.size if self.flags & HAS_HEADER: self.header = self.data - 32 else: self.header = self.data else: raise APENoHeaderError("No APE tag found") # exclude the footer from size if self.footer is not None: self.size -= 32
def __parse_tag(self, tag, count): """Raises IOError and APEBadItemError""" fileobj = cBytesIO(tag) for i in xrange(count): tag_data = fileobj.read(8) # someone writes wrong item counts if not tag_data: break if len(tag_data) != 8: raise error size = cdata.uint32_le(tag_data[:4]) flags = cdata.uint32_le(tag_data[4:8]) # Bits 1 and 2 bits are flags, 0-3 # Bit 0 is read/write flag, ignored kind = (flags & 6) >> 1 if kind == 3: raise APEBadItemError("value type must be 0, 1, or 2") key = value = fileobj.read(1) if not key: raise APEBadItemError while key[-1:] != b'\x00' and value: value = fileobj.read(1) if not value: raise APEBadItemError key += value if key[-1:] == b"\x00": key = key[:-1] if PY3: try: key = key.decode("ascii") except UnicodeError as err: reraise(APEBadItemError, err, sys.exc_info()[2]) value = fileobj.read(size) if len(value) != size: raise APEBadItemError value = _get_value_type(kind)._new(value) self[key] = value