Panda3D
iffInputFile.cxx
1 // Filename: iffInputFile.cxx
2 // Created by: drose (24Apr01)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "iffInputFile.h"
16 #include "iffGenericChunk.h"
17 #include "datagram.h"
18 #include "datagramIterator.h"
19 #include "virtualFileSystem.h"
20 
21 TypeHandle IffInputFile::_type_handle;
22 
23 ////////////////////////////////////////////////////////////////////
24 // Function: IffInputFile::Constructor
25 // Access: Public
26 // Description:
27 ////////////////////////////////////////////////////////////////////
28 IffInputFile::
29 IffInputFile() {
30  _input = (istream *)NULL;
31  _owns_istream = false;
32  _eof = true;
33  _unexpected_eof = false;
34  _bytes_read = 0;
35 }
36 
37 ////////////////////////////////////////////////////////////////////
38 // Function: IffInputFile::Destructor
39 // Access: Public, Virtual
40 // Description:
41 ////////////////////////////////////////////////////////////////////
42 IffInputFile::
43 ~IffInputFile() {
44  if (_owns_istream) {
46  vfs->close_read_file(_input);
47  }
48 }
49 
50 ////////////////////////////////////////////////////////////////////
51 // Function: IffInputFile::open_read
52 // Access: Public
53 // Description: Attempts to open the indicated filename for reading.
54 // Returns true if successful, false otherwise.
55 ////////////////////////////////////////////////////////////////////
56 bool IffInputFile::
57 open_read(Filename filename) {
58  filename.set_binary();
59 
61  istream *in = vfs->open_read_file(filename, true);
62  if (in == (istream *)NULL) {
63  return false;
64  }
65 
66  set_input(in, true);
67  set_filename(filename);
68 
69  return true;
70 }
71 
72 ////////////////////////////////////////////////////////////////////
73 // Function: IffInputFile::set_input
74 // Access: Public
75 // Description: Sets up the input to use an arbitrary istream. If
76 // owns_istream is true, the istream will be deleted
77 // (via vfs->close_read_file()) when the IffInputFile
78 // destructs.
79 ////////////////////////////////////////////////////////////////////
80 void IffInputFile::
81 set_input(istream *input, bool owns_istream) {
82  if (_owns_istream) {
84  vfs->close_read_file(_input);
85  }
86  _input = input;
87  _owns_istream = owns_istream;
88  _eof = false;
89  _unexpected_eof = false;
90  _bytes_read = 0;
91 }
92 
93 ////////////////////////////////////////////////////////////////////
94 // Function: IffInputFile::get_int8
95 // Access: Public
96 // Description: Extracts a signed 8-bit integer.
97 ////////////////////////////////////////////////////////////////////
98 PN_int8 IffInputFile::
100  Datagram dg;
101  if (!read_bytes(dg, 1)) {
102  return 0;
103  }
104  DatagramIterator dgi(dg);
105  return dgi.get_int8();
106 }
107 
108 ////////////////////////////////////////////////////////////////////
109 // Function: IffInputFile::get_uint8
110 // Access: Public
111 // Description: Extracts an unsigned 8-bit integer.
112 ////////////////////////////////////////////////////////////////////
113 PN_uint8 IffInputFile::
115  Datagram dg;
116  if (!read_bytes(dg, 1)) {
117  return 0;
118  }
119  DatagramIterator dgi(dg);
120  return dgi.get_int8();
121 }
122 
123 ////////////////////////////////////////////////////////////////////
124 // Function: IffInputFile::get_be_int16
125 // Access: Public
126 // Description: Extracts a signed 16-bit big-endian integer.
127 ////////////////////////////////////////////////////////////////////
128 PN_int16 IffInputFile::
130  Datagram dg;
131  if (!read_bytes(dg, 2)) {
132  return 0;
133  }
134  DatagramIterator dgi(dg);
135  return dgi.get_be_int16();
136 }
137 
138 ////////////////////////////////////////////////////////////////////
139 // Function: IffInputFile::get_be_int32
140 // Access: Public
141 // Description: Extracts a signed 32-bit big-endian integer.
142 ////////////////////////////////////////////////////////////////////
143 PN_int32 IffInputFile::
145  Datagram dg;
146  if (!read_bytes(dg, 4)) {
147  return 0;
148  }
149  DatagramIterator dgi(dg);
150  return dgi.get_be_int32();
151 }
152 
153 ////////////////////////////////////////////////////////////////////
154 // Function: IffInputFile::get_be_uint16
155 // Access: Public
156 // Description: Extracts an unsigned 16-bit big-endian integer.
157 ////////////////////////////////////////////////////////////////////
158 PN_uint16 IffInputFile::
160  Datagram dg;
161  if (!read_bytes(dg, 2)) {
162  return 0;
163  }
164  DatagramIterator dgi(dg);
165  return dgi.get_be_uint16();
166 }
167 
168 ////////////////////////////////////////////////////////////////////
169 // Function: IffInputFile::get_be_uint32
170 // Access: Public
171 // Description: Extracts an unsigned 32-bit big-endian integer.
172 ////////////////////////////////////////////////////////////////////
173 PN_uint32 IffInputFile::
175  Datagram dg;
176  if (!read_bytes(dg, 4)) {
177  return 0;
178  }
179  DatagramIterator dgi(dg);
180  return dgi.get_be_uint32();
181 }
182 
183 ////////////////////////////////////////////////////////////////////
184 // Function: IffInputFile::get_be_float32
185 // Access: Public
186 // Description: Extracts a 32-bit big-endian single-precision
187 // floating-point number.
188 ////////////////////////////////////////////////////////////////////
189 PN_stdfloat IffInputFile::
191  Datagram dg;
192  if (!read_bytes(dg, 4)) {
193  return 0;
194  }
195  DatagramIterator dgi(dg);
196  return dgi.get_be_float32();
197 }
198 
199 ////////////////////////////////////////////////////////////////////
200 // Function: IffInputFile::get_string
201 // Access: Public
202 // Description: Extracts a null-terminated string.
203 ////////////////////////////////////////////////////////////////////
204 string IffInputFile::
206  string result;
207  char byte;
208  while (read_byte(byte)) {
209  if (byte == 0) {
210  break;
211  }
212  result += byte;
213  }
214 
215  align();
216  return result;
217 }
218 
219 ////////////////////////////////////////////////////////////////////
220 // Function: IffInputFile::get_id
221 // Access: Public
222 // Description: Extracts a 4-character IFF ID.
223 ////////////////////////////////////////////////////////////////////
226  Datagram dg;
227  if (!read_bytes(dg, 4)) {
228  return IffId();
229  }
230  const char *id = (const char *)dg.get_data();
231  return IffId(id);
232 }
233 
234 ////////////////////////////////////////////////////////////////////
235 // Function: IffInputFile::get_chunk
236 // Access: Public
237 // Description: Reads a single IffChunk, determining its type based
238 // on its ID. Allocates and returns a new IffChunk
239 // object of the appropriate type. Returns NULL if EOF
240 // is reached before the chunk can be read completely,
241 // or if there is some other error in reading the chunk.
242 ////////////////////////////////////////////////////////////////////
243 PT(IffChunk) IffInputFile::
244 get_chunk() {
245  if (is_eof()) {
246  return (IffChunk *)NULL;
247  }
248 
249  IffId id = get_id();
250  PN_uint32 length = get_be_uint32();
251 
252  if (!is_eof()) {
253  PT(IffChunk) chunk = make_new_chunk(id);
254  chunk->set_id(id);
255 
256  size_t start_point = get_bytes_read();
257  size_t end_point = start_point + length;
258 
259  if (chunk->read_iff(this, end_point)) {
260  if (is_eof()) {
261  if (!_unexpected_eof) {
262  nout << "Unexpected EOF on file reading " << *chunk << "\n";
263  _unexpected_eof = true;
264  }
265  return (IffChunk *)NULL;
266  }
267 
268  size_t num_bytes_read = get_bytes_read() - start_point;
269  if (num_bytes_read > length) {
270  nout << *chunk << " read " << num_bytes_read
271  << " instead of " << length << " bytes.\n";
272  return (IffChunk *)NULL;
273 
274  } else if (num_bytes_read < length) {
275  size_t skip_count = length - num_bytes_read;
276  nout << "Ignoring " << skip_count << " bytes at the end of "
277  << *chunk << "\n";
278  skip_bytes(skip_count);
279  }
280  return chunk;
281  }
282  }
283 
284  return (IffChunk *)NULL;
285 }
286 
287 ////////////////////////////////////////////////////////////////////
288 // Function: IffInputFile::get_subchunk
289 // Access: Public
290 // Description: Similar to get_chunk(), except the chunk size is only
291 // a 16-bit number instead of 32-bit, and it takes a
292 // context, which is the chunk in which this chunk is
293 // encountered. The parent chunk may (or may not)
294 // decide what kind of chunk is meant by the various
295 // id's encountered.
296 ////////////////////////////////////////////////////////////////////
297 PT(IffChunk) IffInputFile::
298 get_subchunk(IffChunk *context) {
299  if (is_eof()) {
300  return (IffChunk *)NULL;
301  }
302 
303  IffId id = get_id();
304  PN_uint16 length = get_be_uint16();
305 
306  if (!is_eof()) {
307  PT(IffChunk) chunk = context->make_new_chunk(this, id);
308  chunk->set_id(id);
309 
310  size_t start_point = get_bytes_read();
311  size_t end_point = start_point + length;
312 
313  if (chunk->read_iff(this, end_point)) {
314  if (is_eof()) {
315  if (!_unexpected_eof) {
316  nout << "Unexpected EOF on file reading " << *chunk << "\n";
317  _unexpected_eof = true;
318  }
319  return (IffChunk *)NULL;
320  }
321 
322  size_t num_bytes_read = get_bytes_read() - start_point;
323  if (num_bytes_read > length) {
324  nout << *chunk << " read " << num_bytes_read
325  << " instead of " << length << " bytes.\n";
326  return (IffChunk *)NULL;
327 
328  } else if (num_bytes_read < length) {
329  size_t skip_count = length - num_bytes_read;
330  nout << "Ignoring " << skip_count << " bytes at the end of "
331  << *chunk << "\n";
332  skip_bytes(skip_count);
333  }
334  return chunk;
335  }
336  }
337 
338  return (IffChunk *)NULL;
339 }
340 
341 ////////////////////////////////////////////////////////////////////
342 // Function: IffInputFile::read_byte
343 // Access: Public
344 // Description: Reads a single byte. Returns true if successful,
345 // false otherwise.
346 ////////////////////////////////////////////////////////////////////
347 bool IffInputFile::
348 read_byte(char &byte) {
349  if (is_eof()) {
350  return false;
351  }
352 
353  _input->get(byte);
354  _bytes_read++;
355  _eof = _input->eof() || _input->fail();
356  return !is_eof();
357 }
358 
359 ////////////////////////////////////////////////////////////////////
360 // Function: IffInputFile::read_bytes
361 // Access: Public
362 // Description: Reads a series of bytes, and stores them in the
363 // indicated Datagram. Returns true if successful,
364 // false otherwise.
365 ////////////////////////////////////////////////////////////////////
366 bool IffInputFile::
367 read_bytes(Datagram &datagram, int length) {
368  if (is_eof()) {
369  return false;
370  }
371 
372  char *buffer = new char[length];
373  _input->read(buffer, length);
374  _eof = (_input->gcount() != length);
375  if (is_eof()) {
376  return false;
377  }
378 
379  _bytes_read += length;
380  datagram = Datagram(buffer, length);
381  delete[] buffer;
382  return true;
383 }
384 
385 ////////////////////////////////////////////////////////////////////
386 // Function: IffInputFile::skip_bytes
387 // Access: Public
388 // Description: Reads a series of bytes, but does not store them.
389 // Returns true if successful, false otherwise.
390 ////////////////////////////////////////////////////////////////////
391 bool IffInputFile::
392 skip_bytes(int length) {
393  if (is_eof()) {
394  return false;
395  }
396 
397  char byte;
398  while (length > 0 && !is_eof()) {
399  read_byte(byte);
400  length--;
401  }
402 
403  return !is_eof();
404 }
405 
406 ////////////////////////////////////////////////////////////////////
407 // Function: IffInputFile::make_new_chunk
408 // Access: Protected, Virtual
409 // Description: Allocates and returns a new chunk of the appropriate
410 // type based on the given ID.
411 ////////////////////////////////////////////////////////////////////
412 IffChunk *IffInputFile::
413 make_new_chunk(IffId) {
414  return new IffGenericChunk;
415 }
bool skip_bytes(int length)
Reads a series of bytes, but does not store them.
void align()
If the current file pointer is not positioned on an even-byte boundary, reads and discards one byte s...
Definition: iffInputFile.I:68
PN_int8 get_int8()
Extracts a signed 8-bit integer.
void set_filename(const Filename &filename)
Indicates the filename that the InputFile is currently opened on.
Definition: iffInputFile.I:23
bool read_bytes(Datagram &datagram, int length)
Reads a series of bytes, and stores them in the indicated Datagram.
string get_string()
Extracts a null-terminated string.
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read...
virtual IffChunk * make_new_chunk(IffInputFile *in, IffId id)
Allocates and returns a new chunk of the appropriate type based on the given ID, according to the con...
Definition: iffChunk.cxx:50
size_t get_bytes_read() const
Returns the number of bytes read so far from the input file.
Definition: iffInputFile.I:56
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:494
PN_float32 get_be_float32()
Extracts a 32-bit big-endian single-precision floating-point number.
PN_uint16 get_be_uint16()
Extracts an unsigned 16-bit big-endian integer.
PN_int16 get_be_int16()
Extracts a signed 16-bit big-endian integer.
PN_int32 get_be_int32()
Extracts a signed 32-bit big-endian integer.
static void close_read_file(istream *stream)
Closes a file opened by a previous call to open_read_file().
bool open_read(Filename filename)
Attempts to open the indicated filename for reading.
bool read_byte(char &byte)
Reads a single byte.
The basic kind of record in an EA "IFF" file, which the LightWave object file is based on...
Definition: iffChunk.h:32
void set_input(istream *input, bool owns_istream)
Sets up the input to use an arbitrary istream.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PN_uint32 get_be_uint32()
Extracts an unsigned 32-bit big-endian integer.
PN_uint32 get_be_uint32()
Extracts an unsigned 32-bit big-endian integer.
PN_int16 get_be_int16()
Extracts a signed 16-bit big-endian integer.
void set_id(IffId id)
Changes the ID associated with this chunk.
Definition: iffChunk.I:41
PN_stdfloat get_be_float32()
Extracts a 32-bit big-endian single-precision floating-point number.
PN_int8 get_int8()
Extracts a signed 8-bit integer.
PN_uint8 get_uint8()
Extracts an unsigned 8-bit integer.
A class to retrieve the individual data elements previously stored in a Datagram. ...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
A four-byte chunk ID appearing in an "IFF" file.
Definition: iffId.h:29
PN_uint16 get_be_uint16()
Extracts an unsigned 16-bit big-endian integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
PN_int32 get_be_int32()
Extracts a signed 32-bit big-endian integer.
bool is_eof() const
Returns true if the last read operation failed because of reaching EOF, false otherwise.
Definition: iffInputFile.I:45
const void * get_data() const
Returns a pointer to the beginning of the datagram&#39;s data.
Definition: datagram.I:447
IffId get_id()
Extracts a 4-character IFF ID.
A class for a generic kind of IffChunk that is not understood by a particular IffReader.