import os import struct from io import IOBase list_headers = (b'RIFF', b'LIST') class UnexpectedEOF(Exception): pass class RiffIndexChunk(object): def __init__(self, fh, header, length, position): self.file = fh self.header = header self.length = int(length) self.position = position def __str__(self): return str(self.bytes()) def bytes(self) -> bytes: return self.header + struct.pack(' self.length: end = self.length data = self.file.read(end-start) self.file.seek(current) return data else: return '' def __getitem__(self, index): if isinstance(index, slice): return self.__getslice__(index.start, index.stop) return self[index:index+1] def _data(self): """Read data from the file.""" current_position = self.file.tell() self.file.seek(self.position) data = self.file.read(self.length) self.file.seek(current_position) if self.length % 2: data += b'\x00' # Padding byte return data data = property(_data) def as_data(self): """Return a RiffDataChunk read from the file.""" raise NotImplementedError() class RiffIndexList(RiffIndexChunk): def __init__(self, header, list_type, *args, **kwargs): self.header = header self.type = list_type self.file = kwargs.get('file', None) self.position = kwargs.get('position', 0) self.chunks = kwargs.get('chunks', []) def __getitem__(self, index): return self.chunks[index] def __setitem__(self, index, value): return self.chunks.__setitem__(index, value) def __delitem__(self, index): return self.chunks.__delitem__(index) def __iter__(self): return iter(self.chunks) def __len__(self): """Return total data length of the list and its headers.""" return self.chunk_length() + len(self.type) + len(self.header) + 4 def chunk_length(self): length = 0 for chunk in self.chunks: chunk_len = len(chunk) length += chunk_len + 8 # Header and length bytes length += chunk_len % 2 # Pad byte return length def __str__(self): return str(self.bytes()) def bytes(self) -> bytes: """Returns a byte representation of the chunk.""" length_bytes = struct.pack(' bytes: """Returns a byte array representation of the chunk.""" return self.header + struct.pack(' None: def print_chunks(chunks): for chunk in chunks: fh.write(chunk.bytes()) if chunk.header in (b'RIFF', b'LIST'): print_chunks(chunk.chunks) print_chunks(self.chunks) def get_size(self): current = self.file.tell() self.file.seek(0, 2) size = self.file.tell() self.file.seek(current) return size def readlen(self, length): buf = self.file.read(length) if len(buf) == length: return buf else: raise UnexpectedEOF( 'End of file reached after {0} bytes.'.format(len(buf))) def scan_file(self): header = self.readlen(4) if header == b'RIFF': length, list_type = struct.unpack('