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