Annotation of /trunk/ModularRex/RexParts/RexPython/Resources/Lib/chunk.py
Parent Directory
|
Revision Log
Revision 52 - (view) (download) (as text)
| 1 : | mikkopa | 52 | """Simple class to read IFF chunks. |
| 2 : | |||
| 3 : | An IFF chunk (used in formats such as AIFF, TIFF, RMFF (RealMedia File | ||
| 4 : | Format)) has the following structure: | ||
| 5 : | |||
| 6 : | +----------------+ | ||
| 7 : | | ID (4 bytes) | | ||
| 8 : | +----------------+ | ||
| 9 : | | size (4 bytes) | | ||
| 10 : | +----------------+ | ||
| 11 : | | data | | ||
| 12 : | | ... | | ||
| 13 : | +----------------+ | ||
| 14 : | |||
| 15 : | The ID is a 4-byte string which identifies the type of chunk. | ||
| 16 : | |||
| 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. | ||
| 19 : | |||
| 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 | ||
| 25 : | exception. | ||
| 26 : | |||
| 27 : | Usage: | ||
| 28 : | while True: | ||
| 29 : | try: | ||
| 30 : | chunk = Chunk(file) | ||
| 31 : | except EOFError: | ||
| 32 : | break | ||
| 33 : | chunktype = chunk.getname() | ||
| 34 : | while True: | ||
| 35 : | data = chunk.read(nbytes) | ||
| 36 : | if not data: | ||
| 37 : | pass | ||
| 38 : | # do something with data | ||
| 39 : | |||
| 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) | ||
| 44 : | |||
| 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. | ||
| 49 : | """ | ||
| 50 : | |||
| 51 : | class Chunk: | ||
| 52 : | def __init__(self, file, align=True, bigendian=True, inclheader=False): | ||
| 53 : | import struct | ||
| 54 : | self.closed = False | ||
| 55 : | self.align = align # whether to align to word (2-byte) boundaries | ||
| 56 : | if bigendian: | ||
| 57 : | strflag = '>' | ||
| 58 : | else: | ||
| 59 : | strflag = '<' | ||
| 60 : | self.file = file | ||
| 61 : | self.chunkname = file.read(4) | ||
| 62 : | if len(self.chunkname) < 4: | ||
| 63 : | raise EOFError | ||
| 64 : | try: | ||
| 65 : | self.chunksize = struct.unpack(strflag+'L', file.read(4))[0] | ||
| 66 : | except struct.error: | ||
| 67 : | raise EOFError | ||
| 68 : | if inclheader: | ||
| 69 : | self.chunksize = self.chunksize - 8 # subtract header | ||
| 70 : | self.size_read = 0 | ||
| 71 : | try: | ||
| 72 : | self.offset = self.file.tell() | ||
| 73 : | except (AttributeError, IOError): | ||
| 74 : | self.seekable = False | ||
| 75 : | else: | ||
| 76 : | self.seekable = True | ||
| 77 : | |||
| 78 : | def getname(self): | ||
| 79 : | """Return the name (ID) of the current chunk.""" | ||
| 80 : | return self.chunkname | ||
| 81 : | |||
| 82 : | def getsize(self): | ||
| 83 : | """Return the size of the current chunk.""" | ||
| 84 : | return self.chunksize | ||
| 85 : | |||
| 86 : | def close(self): | ||
| 87 : | if not self.closed: | ||
| 88 : | self.skip() | ||
| 89 : | self.closed = True | ||
| 90 : | |||
| 91 : | def isatty(self): | ||
| 92 : | if self.closed: | ||
| 93 : | raise ValueError, "I/O operation on closed file" | ||
| 94 : | return False | ||
| 95 : | |||
| 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. | ||
| 100 : | """ | ||
| 101 : | |||
| 102 : | if self.closed: | ||
| 103 : | raise ValueError, "I/O operation on closed file" | ||
| 104 : | if not self.seekable: | ||
| 105 : | raise IOError, "cannot seek" | ||
| 106 : | if whence == 1: | ||
| 107 : | pos = pos + self.size_read | ||
| 108 : | elif whence == 2: | ||
| 109 : | pos = pos + self.chunksize | ||
| 110 : | if pos < 0 or pos > self.chunksize: | ||
| 111 : | raise RuntimeError | ||
| 112 : | self.file.seek(self.offset + pos, 0) | ||
| 113 : | self.size_read = pos | ||
| 114 : | |||
| 115 : | def tell(self): | ||
| 116 : | if self.closed: | ||
| 117 : | raise ValueError, "I/O operation on closed file" | ||
| 118 : | return self.size_read | ||
| 119 : | |||
| 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 | ||
| 123 : | of the chunk. | ||
| 124 : | """ | ||
| 125 : | |||
| 126 : | if self.closed: | ||
| 127 : | raise ValueError, "I/O operation on closed file" | ||
| 128 : | if self.size_read >= self.chunksize: | ||
| 129 : | return '' | ||
| 130 : | if size < 0: | ||
| 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 \ | ||
| 137 : | self.align and \ | ||
| 138 : | (self.chunksize & 1): | ||
| 139 : | dummy = self.file.read(1) | ||
| 140 : | self.size_read = self.size_read + len(dummy) | ||
| 141 : | return data | ||
| 142 : | |||
| 143 : | def skip(self): | ||
| 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. | ||
| 148 : | """ | ||
| 149 : | |||
| 150 : | if self.closed: | ||
| 151 : | raise ValueError, "I/O operation on closed file" | ||
| 152 : | if self.seekable: | ||
| 153 : | try: | ||
| 154 : | n = self.chunksize - self.size_read | ||
| 155 : | # maybe fix alignment | ||
| 156 : | if self.align and (self.chunksize & 1): | ||
| 157 : | n = n + 1 | ||
| 158 : | self.file.seek(n, 1) | ||
| 159 : | self.size_read = self.size_read + n | ||
| 160 : | return | ||
| 161 : | except IOError: | ||
| 162 : | pass | ||
| 163 : | while self.size_read < self.chunksize: | ||
| 164 : | n = min(8192, self.chunksize - self.size_read) | ||
| 165 : | dummy = self.read(n) | ||
| 166 : | if not dummy: | ||
| 167 : | raise EOFError |
| ViewVC Help | |
| Powered by ViewVC 1.0.0 |

