0001 """Simple class to read IFF chunks. 0002 0003 An IFF chunk (used in formats such as AIFF, TIFF, RMFF (RealMedia File 0004 Format)) has the following structure: 0005 0006 +----------------+ 0007 | ID (4 bytes) | 0008 +----------------+ 0009 | size (4 bytes) | 0010 +----------------+ 0011 | data | 0012 | ... | 0013 +----------------+ 0014 0015 The ID is a 4-byte string which identifies the type of chunk. 0016 0017 The size field (a 32-bit value, encoded using big-endian byte order) 0018 gives the size of the whole chunk, including the 8-byte header. 0019 0020 Usually an IFF-type file consists of one or more chunks. The proposed 0021 usage of the Chunk class defined here is to instantiate an instance at 0022 the start of each chunk and read from the instance until it reaches 0023 the end, after which a new instance can be instantiated. At the end 0024 of the file, creating a new instance will fail with a EOFError 0025 exception. 0026 0027 Usage: 0028 while True: 0029 try: 0030 chunk = Chunk(file) 0031 except EOFError: 0032 break 0033 chunktype = chunk.getname() 0034 while True: 0035 data = chunk.read(nbytes) 0036 if not data: 0037 pass 0038 # do something with data 0039 0040 The interface is file-like. The implemented methods are: 0041 read, close, seek, tell, isatty. 0042 Extra methods are: skip() (called by close, skips to the end of the chunk), 0043 getname() (returns the name (ID) of the chunk) 0044 0045 The __init__ method has one required argument, a file-like object 0046 (including a chunk instance), and one optional argument, a flag which 0047 specifies whether or not chunks are aligned on 2-byte boundaries. The 0048 default is 1, i.e. aligned. 0049 """ 0050 0051 class Chunk: 0052 def __init__(self, file, align=True, bigendian=True, inclheader=False): 0053 import struct 0054 self.closed = False 0055 self.align = align # whether to align to word (2-byte) boundaries 0056 if bigendian: 0057 strflag = '>' 0058 else: 0059 strflag = '<' 0060 self.file = file 0061 self.chunkname = file.read(4) 0062 if len(self.chunkname) < 4: 0063 raise EOFError 0064 try: 0065 self.chunksize = struct.unpack(strflag+'l', file.read(4))[0] 0066 except struct.error: 0067 raise EOFError 0068 if inclheader: 0069 self.chunksize = self.chunksize - 8 # subtract header 0070 self.size_read = 0 0071 try: 0072 self.offset = self.file.tell() 0073 except (AttributeError, IOError): 0074 self.seekable = False 0075 else: 0076 self.seekable = True 0077 0078 def getname(self): 0079 """Return the name (ID) of the current chunk.""" 0080 return self.chunkname 0081 0082 def getsize(self): 0083 """Return the size of the current chunk.""" 0084 return self.chunksize 0085 0086 def close(self): 0087 if not self.closed: 0088 self.skip() 0089 self.closed = True 0090 0091 def isatty(self): 0092 if self.closed: 0093 raise ValueError, "I/O operation on closed file" 0094 return False 0095 0096 def seek(self, pos, whence=0): 0097 """Seek to specified position into the chunk. 0098 Default position is 0 (start of chunk). 0099 If the file is not seekable, this will result in an error. 0100 """ 0101 0102 if self.closed: 0103 raise ValueError, "I/O operation on closed file" 0104 if not self.seekable: 0105 raise IOError, "cannot seek" 0106 if whence == 1: 0107 pos = pos + self.size_read 0108 elif whence == 2: 0109 pos = pos + self.chunksize 0110 if pos < 0 or pos > self.chunksize: 0111 raise RuntimeError 0112 self.file.seek(self.offset + pos, 0) 0113 self.size_read = pos 0114 0115 def tell(self): 0116 if self.closed: 0117 raise ValueError, "I/O operation on closed file" 0118 return self.size_read 0119 0120 def read(self, size=-1): 0121 """Read at most size bytes from the chunk. 0122 If size is omitted or negative, read until the end 0123 of the chunk. 0124 """ 0125 0126 if self.closed: 0127 raise ValueError, "I/O operation on closed file" 0128 if self.size_read >= self.chunksize: 0129 return '' 0130 if size < 0: 0131 size = self.chunksize - self.size_read 0132 if size > self.chunksize - self.size_read: 0133 size = self.chunksize - self.size_read 0134 data = self.file.read(size) 0135 self.size_read = self.size_read + len(data) 0136 if self.size_read == self.chunksize and \ 0137 self.align and \ 0138 (self.chunksize & 1): 0139 dummy = self.file.read(1) 0140 self.size_read = self.size_read + len(dummy) 0141 return data 0142 0143 def skip(self): 0144 """Skip the rest of the chunk. 0145 If you are not interested in the contents of the chunk, 0146 this method should be called so that the file points to 0147 the start of the next chunk. 0148 """ 0149 0150 if self.closed: 0151 raise ValueError, "I/O operation on closed file" 0152 if self.seekable: 0153 try: 0154 n = self.chunksize - self.size_read 0155 # maybe fix alignment 0156 if self.align and (self.chunksize & 1): 0157 n = n + 1 0158 self.file.seek(n, 1) 0159 self.size_read = self.size_read + n 0160 return 0161 except IOError: 0162 pass 0163 while self.size_read < self.chunksize: 0164 n = min(8192, self.chunksize - self.size_read) 0165 dummy = self.read(n) 0166 if not dummy: 0167 raise EOFError 0168
Generated by PyXR 0.9.4