PyXR

c:\python24\lib \ chunk.py



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
SourceForge.net Logo