blob: 320339ef74199f8173e38d69f3417a8c7e063e24 [file] [log] [blame]
Guido van Rossum8ea7bb81999-06-09 13:32:28 +00001"""Simple class to read IFF chunks.
2
3An IFF chunk (used in formats such as AIFF, TIFF, RMFF (RealMedia File
4Format)) has the following structure:
5
6+----------------+
7| ID (4 bytes) |
8+----------------+
9| size (4 bytes) |
10+----------------+
11| data |
12| ... |
13+----------------+
14
15The ID is a 4-byte string which identifies the type of chunk.
16
17The size field (a 32-bit value, encoded using big-endian byte order)
18gives the size of the whole chunk, including the 8-byte header.
19
20Usually a IFF-type file consists of one or more chunks. The proposed
21usage of the Chunk class defined here is to instantiate an instance at
22the start of each chunk and read from the instance until it reaches
23the end, after which a new instance can be instantiated. At the end
24of the file, creating a new instance will fail with a EOFError
25exception.
26
27Usage:
28while 1:
29 try:
30 chunk = Chunk(file)
31 except EOFError:
32 break
33 chunktype = chunk.getname()
34 while 1:
35 data = chunk.read(nbytes)
36 if not data:
37 pass
38 # do something with data
39
40The interface is file-like. The implemented methods are:
41read, close, seek, tell, isatty.
42Extra methods are: skip() (called by close, skips to the end of the chunk),
43getname() (returns the name (ID) of the chunk)
44
45The __init__ method has one required argument, a file-like object
46(including a chunk instance), and one optional argument, a flag which
47specifies whether or not chunks are aligned on 2-byte boundaries. The
48default is 1, i.e. aligned.
49"""
50
51class Chunk:
52 def __init__(self, file, align = 1):
53 import struct
54 self.closed = 0
55 self.align = align # whether to align to word (2-byte) boundaries
56 self.file = file
57 self.chunkname = file.read(4)
58 if len(self.chunkname) < 4:
59 raise EOFError
60 try:
61 self.chunksize = struct.unpack('>l', file.read(4))[0]
62 except struct.error:
63 raise EOFError
Guido van Rossum56b20591999-06-09 13:41:18 +000064 self.chunksize = self.chunksize - 8 # subtract header
Guido van Rossum8ea7bb81999-06-09 13:32:28 +000065 self.size_read = 0
66 self.offset = self.file.tell()
67
68 def getname(self):
69 """Return the name (ID) of the current chunk."""
70 return self.chunkname
71
72 def close(self):
73 if not self.closed:
74 self.skip()
75 self.closed = 1
76
77 def isatty(self):
78 if self.closed:
79 raise ValueError, "I/O operation on closed file"
80 return 0
81
82 def seek(self, pos, mode = 0):
83 """Seek to specified position into the chunk.
84 Default position is 0 (start of chunk).
85 If the file is not seekable, this will result in an error.
86 """
87
88 if self.closed:
89 raise ValueError, "I/O operation on closed file"
90 if mode == 1:
91 pos = pos + self.size_read
92 elif mode == 2:
93 pos = pos + self.chunk_size
94 if pos < 0 or pos > self.chunksize:
95 raise RuntimeError
96 self.file.seek(self.offset + pos, 0)
97 self.size_read = pos
98
99 def tell(self):
100 if self.closed:
101 raise ValueError, "I/O operation on closed file"
102 return self.size_read
103
104 def read(self, n = -1):
105 """Read at most n bytes from the chunk.
106 If n is omitted or negative, read until the end
107 of the chunk.
108 """
109
110 if self.closed:
111 raise ValueError, "I/O operation on closed file"
112 if self.size_read >= self.chunksize:
113 return ''
114 if n < 0:
115 n = self.chunksize - self.size_read
116 if n > self.chunksize - self.size_read:
117 n = self.chunksize - self.size_read
118 data = self.file.read(n)
119 self.size_read = self.size_read + len(data)
120 if self.size_read == self.chunksize and \
121 self.align and \
122 (self.chunksize & 1):
123 dummy = self.file.read(1)
124 self.size_read = self.size_read + len(dummy)
125 return data
126
127 def skip(self):
128 """Skip the rest of the chunk.
129 If you are not interested in the contents of the chunk,
130 this method should be called so that the file points to
131 the start of the next chunk.
132 """
133
134 if self.closed:
135 raise ValueError, "I/O operation on closed file"
136 try:
137 self.file.seek(self.chunksize - self.size_read, 1)
138 except RuntimeError:
139 while self.size_read < self.chunksize:
140 dummy = self.read(8192)
141 if not dummy:
142 raise EOFError
143