open_toontown_panda3d/pandatool/src/lwo/iffInputFile.cxx

370 lines
7.7 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file iffInputFile.cxx
* @author drose
* @date 2001-04-24
*/
#include "iffInputFile.h"
#include "iffGenericChunk.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "virtualFileSystem.h"
TypeHandle IffInputFile::_type_handle;
/**
*
*/
IffInputFile::
IffInputFile() {
_input = (istream *)NULL;
_owns_istream = false;
_eof = true;
_unexpected_eof = false;
_bytes_read = 0;
}
/**
*
*/
IffInputFile::
~IffInputFile() {
if (_owns_istream) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
vfs->close_read_file(_input);
}
}
/**
* Attempts to open the indicated filename for reading. Returns true if
* successful, false otherwise.
*/
bool IffInputFile::
open_read(Filename filename) {
filename.set_binary();
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
istream *in = vfs->open_read_file(filename, true);
if (in == (istream *)NULL) {
return false;
}
set_input(in, true);
set_filename(filename);
return true;
}
/**
* Sets up the input to use an arbitrary istream. If owns_istream is true,
* the istream will be deleted (via vfs->close_read_file()) when the
* IffInputFile destructs.
*/
void IffInputFile::
set_input(istream *input, bool owns_istream) {
if (_owns_istream) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
vfs->close_read_file(_input);
}
_input = input;
_owns_istream = owns_istream;
_eof = false;
_unexpected_eof = false;
_bytes_read = 0;
}
/**
* Extracts a signed 8-bit integer.
*/
int8_t IffInputFile::
get_int8() {
Datagram dg;
if (!read_bytes(dg, 1)) {
return 0;
}
DatagramIterator dgi(dg);
return dgi.get_int8();
}
/**
* Extracts an unsigned 8-bit integer.
*/
uint8_t IffInputFile::
get_uint8() {
Datagram dg;
if (!read_bytes(dg, 1)) {
return 0;
}
DatagramIterator dgi(dg);
return dgi.get_int8();
}
/**
* Extracts a signed 16-bit big-endian integer.
*/
int16_t IffInputFile::
get_be_int16() {
Datagram dg;
if (!read_bytes(dg, 2)) {
return 0;
}
DatagramIterator dgi(dg);
return dgi.get_be_int16();
}
/**
* Extracts a signed 32-bit big-endian integer.
*/
int32_t IffInputFile::
get_be_int32() {
Datagram dg;
if (!read_bytes(dg, 4)) {
return 0;
}
DatagramIterator dgi(dg);
return dgi.get_be_int32();
}
/**
* Extracts an unsigned 16-bit big-endian integer.
*/
uint16_t IffInputFile::
get_be_uint16() {
Datagram dg;
if (!read_bytes(dg, 2)) {
return 0;
}
DatagramIterator dgi(dg);
return dgi.get_be_uint16();
}
/**
* Extracts an unsigned 32-bit big-endian integer.
*/
uint32_t IffInputFile::
get_be_uint32() {
Datagram dg;
if (!read_bytes(dg, 4)) {
return 0;
}
DatagramIterator dgi(dg);
return dgi.get_be_uint32();
}
/**
* Extracts a 32-bit big-endian single-precision floating-point number.
*/
PN_stdfloat IffInputFile::
get_be_float32() {
Datagram dg;
if (!read_bytes(dg, 4)) {
return 0;
}
DatagramIterator dgi(dg);
return dgi.get_be_float32();
}
/**
* Extracts a null-terminated string.
*/
string IffInputFile::
get_string() {
string result;
char byte;
while (read_byte(byte)) {
if (byte == 0) {
break;
}
result += byte;
}
align();
return result;
}
/**
* Extracts a 4-character IFF ID.
*/
IffId IffInputFile::
get_id() {
Datagram dg;
if (!read_bytes(dg, 4)) {
return IffId();
}
const char *id = (const char *)dg.get_data();
return IffId(id);
}
/**
* Reads a single IffChunk, determining its type based on its ID. Allocates
* and returns a new IffChunk object of the appropriate type. Returns NULL if
* EOF is reached before the chunk can be read completely, or if there is some
* other error in reading the chunk.
*/
PT(IffChunk) IffInputFile::
get_chunk() {
if (is_eof()) {
return (IffChunk *)NULL;
}
IffId id = get_id();
uint32_t length = get_be_uint32();
if (!is_eof()) {
PT(IffChunk) chunk = make_new_chunk(id);
chunk->set_id(id);
size_t start_point = get_bytes_read();
size_t end_point = start_point + length;
if (chunk->read_iff(this, end_point)) {
if (is_eof()) {
if (!_unexpected_eof) {
nout << "Unexpected EOF on file reading " << *chunk << "\n";
_unexpected_eof = true;
}
return (IffChunk *)NULL;
}
size_t num_bytes_read = get_bytes_read() - start_point;
if (num_bytes_read > length) {
nout << *chunk << " read " << num_bytes_read
<< " instead of " << length << " bytes.\n";
return (IffChunk *)NULL;
} else if (num_bytes_read < length) {
size_t skip_count = length - num_bytes_read;
nout << "Ignoring " << skip_count << " bytes at the end of "
<< *chunk << "\n";
skip_bytes(skip_count);
}
return chunk;
}
}
return (IffChunk *)NULL;
}
/**
* Similar to get_chunk(), except the chunk size is only a 16-bit number
* instead of 32-bit, and it takes a context, which is the chunk in which this
* chunk is encountered. The parent chunk may (or may not) decide what kind
* of chunk is meant by the various id's encountered.
*/
PT(IffChunk) IffInputFile::
get_subchunk(IffChunk *context) {
if (is_eof()) {
return (IffChunk *)NULL;
}
IffId id = get_id();
uint16_t length = get_be_uint16();
if (!is_eof()) {
PT(IffChunk) chunk = context->make_new_chunk(this, id);
chunk->set_id(id);
size_t start_point = get_bytes_read();
size_t end_point = start_point + length;
if (chunk->read_iff(this, end_point)) {
if (is_eof()) {
if (!_unexpected_eof) {
nout << "Unexpected EOF on file reading " << *chunk << "\n";
_unexpected_eof = true;
}
return (IffChunk *)NULL;
}
size_t num_bytes_read = get_bytes_read() - start_point;
if (num_bytes_read > length) {
nout << *chunk << " read " << num_bytes_read
<< " instead of " << length << " bytes.\n";
return (IffChunk *)NULL;
} else if (num_bytes_read < length) {
size_t skip_count = length - num_bytes_read;
nout << "Ignoring " << skip_count << " bytes at the end of "
<< *chunk << "\n";
skip_bytes(skip_count);
}
return chunk;
}
}
return (IffChunk *)NULL;
}
/**
* Reads a single byte. Returns true if successful, false otherwise.
*/
bool IffInputFile::
read_byte(char &byte) {
if (is_eof()) {
return false;
}
_input->get(byte);
_bytes_read++;
_eof = _input->eof() || _input->fail();
return !is_eof();
}
/**
* Reads a series of bytes, and stores them in the indicated Datagram.
* Returns true if successful, false otherwise.
*/
bool IffInputFile::
read_bytes(Datagram &datagram, int length) {
if (is_eof()) {
return false;
}
char *buffer = new char[length];
_input->read(buffer, length);
_eof = (_input->gcount() != length);
if (is_eof()) {
return false;
}
_bytes_read += length;
datagram = Datagram(buffer, length);
delete[] buffer;
return true;
}
/**
* Reads a series of bytes, but does not store them. Returns true if
* successful, false otherwise.
*/
bool IffInputFile::
skip_bytes(int length) {
if (is_eof()) {
return false;
}
char byte;
while (length > 0 && !is_eof()) {
read_byte(byte);
length--;
}
return !is_eof();
}
/**
* Allocates and returns a new chunk of the appropriate type based on the
* given ID.
*/
IffChunk *IffInputFile::
make_new_chunk(IffId) {
return new IffGenericChunk;
}