1
"""Simple class to read IFF chunks.
3
An IFF chunk (used in formats such as AIFF, TIFF, RMFF (RealMedia File
4
Format)) has the following structure:
15
The ID is a 4-byte string which identifies the type of chunk.
17
The size field (a 32-bit value, encoded using big-endian byte order)
18
gives the size of the whole chunk, including the 8-byte header.
20
Usually an IFF-type file consists of one or more chunks. The proposed
21
usage of the Chunk class defined here is to instantiate an instance at
22
the start of each chunk and read from the instance until it reaches
23
the end, after which a new instance can be instantiated. At the end
24
of the file, creating a new instance will fail with a EOFError
33
chunktype = chunk.getname()
35
data = chunk.read(nbytes)
38
# do something with data
40
The interface is file-like. The implemented methods are:
41
read, close, seek, tell, isatty.
42
Extra methods are: skip() (called by close, skips to the end of the chunk),
43
getname() (returns the name (ID) of the chunk)
45
The __init__ method has one required argument, a file-like object
46
(including a chunk instance), and one optional argument, a flag which
47
specifies whether or not chunks are aligned on 2-byte boundaries. The
48
default is 1, i.e. aligned.
52
def __init__(self, file, align=True, bigendian=True, inclheader=False):
55
self.align = align # whether to align to word (2-byte) boundaries
61
self.chunkname = file.read(4)
62
if len(self.chunkname) < 4:
65
self.chunksize = struct.unpack(strflag+'L', file.read(4))[0]
69
self.chunksize = self.chunksize - 8 # subtract header
72
self.offset = self.file.tell()
73
except (AttributeError, IOError):
79
"""Return the name (ID) of the current chunk."""
83
"""Return the size of the current chunk."""
93
raise ValueError, "I/O operation on closed file"
96
def seek(self, pos, whence=0):
97
"""Seek to specified position into the chunk.
98
Default position is 0 (start of chunk).
99
If the file is not seekable, this will result in an error.
103
raise ValueError, "I/O operation on closed file"
104
if not self.seekable:
105
raise IOError, "cannot seek"
107
pos = pos + self.size_read
109
pos = pos + self.chunksize
110
if pos < 0 or pos > self.chunksize:
112
self.file.seek(self.offset + pos, 0)
117
raise ValueError, "I/O operation on closed file"
118
return self.size_read
120
def read(self, size=-1):
121
"""Read at most size bytes from the chunk.
122
If size is omitted or negative, read until the end
127
raise ValueError, "I/O operation on closed file"
128
if self.size_read >= self.chunksize:
131
size = self.chunksize - self.size_read
132
if size > self.chunksize - self.size_read:
133
size = self.chunksize - self.size_read
134
data = self.file.read(size)
135
self.size_read = self.size_read + len(data)
136
if self.size_read == self.chunksize and \
138
(self.chunksize & 1):
139
dummy = self.file.read(1)
140
self.size_read = self.size_read + len(dummy)
144
"""Skip the rest of the chunk.
145
If you are not interested in the contents of the chunk,
146
this method should be called so that the file points to
147
the start of the next chunk.
151
raise ValueError, "I/O operation on closed file"
154
n = self.chunksize - self.size_read
155
# maybe fix alignment
156
if self.align and (self.chunksize & 1):
159
self.size_read = self.size_read + n
163
while self.size_read < self.chunksize:
164
n = min(8192, self.chunksize - self.size_read)