open_toontown_panda3d/panda/src/putil/bamReader.cxx

538 lines
16 KiB
C++

// Filename: bamReader.cxx
// Created by: jason (12Jun00)
//
#include <pandabase.h>
#include <notify.h>
#include "bam.h"
#include "bamReader.h"
#include <datagramIterator.h>
#include "config_util.h"
WriteableFactory *BamReader::_factory = (WriteableFactory*)0L;
BamReader* const BamReader::Null = (BamReader*)0L;
WriteableFactory* const BamReader::NullFactory = (WriteableFactory*)0L;
const int BamReader::_cur_major = _bam_major_ver;
const int BamReader::_cur_minor = _bam_minor_ver;
////////////////////////////////////////////////////////////////////
// Function: BamReader::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
BamReader::
~BamReader(void)
{
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::init
// Access: Public
// Description: Does all initialization necessary for BamReader to work
// Current this means reading in the Type Map stored as
// the header of the file. This TypeMap contains the
// name of all types written out and their type indeces
// as set when the file was created. This map is used
// for later determing the current TypeHandle for each
// object written. Each Object is identified in the file
// with the old index for space reason.
//
// This returns true if the BamReader successfully
// initialized, false otherwise.
////////////////////////////////////////////////////////////////////
bool BamReader::
init(void)
{
Datagram header;
if (_source->is_error())
{
return false;
}
if (!_source->get_datagram(header)) {
bam_cat.error()
<< "Unable to read Bam header.\n";
return false;
}
DatagramIterator scan(header);
_file_major = scan.get_uint16();
_file_minor = scan.get_uint16();
// If the major version is different, or the minor version is
// *newer*, we can't safely load the file.
if (_file_major != _bam_major_ver || _file_minor > _bam_minor_ver)
{
bam_cat.error()
<< "Bam file is version " << _file_major << "." << _file_minor
<< ".\n";
if (_bam_minor_ver == 0) {
bam_cat.error()
<< "This program can only load version "
<< _bam_major_ver << ".0 bams.\n";
} else {
bam_cat.error()
<< "This program can only load version "
<< _bam_major_ver << ".0 through "
<< _bam_major_ver << "." << _bam_minor_ver << " bams.\n";
}
return false;
}
if (bam_cat.is_debug()) {
bam_cat.debug()
<< "Bam file is version " << _file_major << "." << _file_minor
<< ".\n";
if (_file_minor != _bam_minor_ver) {
bam_cat.debug()
<< "(Current version is " << _bam_major_ver << "." << _bam_minor_ver
<< ".)\n";
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::read_handle
// Access: Public
// Description: Reads a TypeHandle out of the Datagram. If the Type
// has not been registered yet, BamReader will register
// the Type with TypeRegistry
////////////////////////////////////////////////////////////////////
TypeHandle BamReader::
read_handle(DatagramIterator& scan)
{
int id = scan.get_uint16();
if (id == 0)
{
//This indicates an object that should have already been read in,
//so return TypeHandle::none() to indicate this.
if (bam_cat.is_spam()) {
bam_cat.spam()
<< "Reading previously read TypeHandle.\n";
}
return TypeHandle::none();
}
if (_index_map.find(id) == _index_map.end())
{
//Have not loaded this type before
string name = scan.get_string();
TypeHandle type = TypeRegistry::ptr()->find_type(name);
bool new_type = false;
if (type == TypeHandle::none())
{
//This is a new type we've never heard of before so register it
//with the type registry
type = TypeRegistry::ptr()->register_dynamic_type(name);
bam_cat.warning()
<< "Bam file contains objects of unknown type: " << type << "\n";
new_type = true;
}
_index_map[id] = type;
// Now pick up the derivation information.
int num_parent_classes = scan.get_uint8();
for (int i = 0; i < num_parent_classes; i++) {
TypeHandle parent_type = read_handle(scan);
if (new_type) {
TypeRegistry::ptr()->record_derivation(type, parent_type);
} else {
if (type.get_parent_towards(parent_type) != parent_type) {
bam_cat.warning()
<< "Bam file indicates a derivation of " << type
<< " from " << parent_type << " which is no longer true.\n";
}
}
}
}
if (bam_cat.is_spam()) {
bam_cat.spam()
<< "Reading TypeHandle for " << _index_map[id] << ".\n";
}
return _index_map[id];
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::read_object
// Access: Public
// Description: Reads an object definition from the DatagramGenerator
// and generates an object of the correct type, and returns
// a pointer to that object.
////////////////////////////////////////////////////////////////////
TypedWriteable* BamReader::
read_object(void)
{
Datagram packet, body;
//if (_source->empty()) //If the source is empty, then we merely have
//{ //back reference read's queue, so clear the queue
// clear_queue();
// return TypedWriteable::Null;
//}
if (_source->is_error())
{
return TypedWriteable::Null;
}
if (!_source->get_datagram(packet)) {
// The datagram source is empty.
if (bam_cat.is_debug()) {
bam_cat.debug()
<< "Reached end of bam source.\n";
}
return TypedWriteable::Null;
}
//Now pull out the type ID and Object ID from the
//datagram and pass the remaining data in that
//datagram, the body to the WriteableParam for
//creation of the object
DatagramIterator scan(packet);
TypeHandle type = read_handle(scan);
PN_uint16 objId = scan.get_uint16();
//Before anything else, check to see if type is none
//If that is the case, then we SHOULD have already read
//in and created the object (although pointers in that object
//may not be fully instanciated yet), so just return a pointer
//to that already created obj and don't try to re-create it
if (type != TypeHandle::none())
{
string message = scan.get_remaining_bytes();
body.append_data(message.data(), message.size());
//Generate FactoryParams object to pass to the factory for
//creating the object
FactoryParams list;
list.add_param(new WriteableParam(body));
list.add_param(new BamReaderParam(this));
//Due to the recursive nature of reading in objects (and their pointers)
//this line is to ensure that objId gets into the map _created_objs
//before we call make_instance. It works due to the nature of map.
//What happens is that a pair consistenting of objId and some random
//pointer is put into the map. But this is okay, since in the
//recursion we only refer to this objId for purposes of whether
//it is there or not and we immediately override it with a good
//value otherwise
_created_objs[objId];
_created_objs[objId] = _factory->make_instance_more_general(type, list);
//Just some sanity checks
if (_created_objs[objId] == (TypedWriteable *)NULL) {
bam_cat.error()
<< "Unable to create an object of type " << type << endl;
} else if (_created_objs[objId]->get_type() != type) {
bam_cat.warning()
<< "Attempted to create a " << type.get_name() \
<< " but a " << _created_objs[objId]->get_type() \
<< " was created instead." << endl;
} else {
if (bam_cat.is_spam()) {
bam_cat.spam()
<< "Read a " << _created_objs[objId]->get_type() << "\n";
}
}
}
empty_queue();
return _created_objs[objId];
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::read_pointer
// Access: Public
// Description: The interface for reading a pointer to another object
// from a Bam file. Objects reading themselves from a
// Bam file should call this when they expect to read a
// pointer, passing in the datagram iterator and a
// pointer to their own object, i.e. 'this'.
//
// Rather than returning a pointer immediately, this
// function reads the internal pointer information from
// the datagram and queues up the request. The pointer
// itself may not be available until later (it may be a
// pointer to an object that appears later in the Bam
// file). Later, when all pointers are available, the
// complete_pointers() callback function will be called
// with an array of actual pointers, one for time
// read_pointer() was called. It is then the calling
// object's responsibilty to store these pointers in the
// object properly.
////////////////////////////////////////////////////////////////////
void BamReader::
read_pointer(DatagramIterator& scan, TypedWriteable* forWhom)
{
PN_uint16 objId = scan.get_uint16();
_deferred_pointers[forWhom].push_back(objId);
//This is safe since we have already read the full datagram
//for the object requesting this object. So there can be
//no collision on that front.
//IMPORTANT NOTE: This does make the assumption that
//that objects are requested by other objects in the same
//order that they wrote them originally.
//Don't queue a read of a null pointer
//or if the object has already been read
if ((objId != 0) && (_created_objs.find(objId) == _created_objs.end()))
queue(objId);
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::read_pointers
// Access: Public
// Description: A convenience function to read a contiguous list of
// pointers. This is equivalent to calling
// read_pointer() count times.
////////////////////////////////////////////////////////////////////
void BamReader::
read_pointers(DatagramIterator &scan, TypedWriteable *forWhom, int count) {
for (int i = 0; i < count; i++) {
read_pointer(scan, forWhom);
}
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::register_finalize
// Access: Public
// Description: Register for later finalization
////////////////////////////////////////////////////////////////////
void BamReader::
register_finalize(TypedWriteable *whom)
{
if (whom == TypedWriteable::Null)
{
bam_cat.error() << "Can't register a null pointer to finalize!" << endl;
return;
}
_finalize_list.insert(whom);
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::finalize_now
// Access: Public
// Description: Force finalization of a particular object
////////////////////////////////////////////////////////////////////
void BamReader::
finalize_now(TypedWriteable *whom)
{
if (whom == TypedWriteable::Null)
{
bam_cat.error() << "Can't finalize null pointer!" << endl;
return;
}
if (_finalize_list.find(whom) != _finalize_list.end())
{
whom->finalize();
_finalize_list.erase(whom);
}
else
{
bam_cat.warning() << "Request to finalize object of type "
<< whom->get_type().get_name() << " failed"
<< endl;
}
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::empty_queue
// Access: Private
// Description: For every objId in the queue, reads an object
// from the datagram source
////////////////////////////////////////////////////////////////////
void BamReader::
empty_queue(void) {
size_t num_reads = _deferred_reads.size();
_deferred_reads.clear();
while (num_reads > 0) {
read_object();
num_reads--;
}
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::finalize_this
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
void BamReader::
finalize_this(TypedWriteable *whom)
{
whom->finalize();
_finalize_list.erase(whom);
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::finalize
// Access: Private
// Description: Call finalize_this on all objects stored
////////////////////////////////////////////////////////////////////
void BamReader::
finalize(void)
{
if (bam_cat.is_debug()) {
bam_cat.debug()
<< "Finalizing bam source\n";
}
Finalize::iterator fi = _finalize_list.begin();
while(fi != _finalize_list.end())
{
if (*fi == TypedWriteable::Null)
{
bam_cat.error() << "Spun off into the weeds. "
<< "somehow a null was registered to be "
<< "finalized." << endl;
_finalize_list.erase(fi);
}
else
{
finalize_this(*fi);
}
fi = _finalize_list.begin();
}
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::get_pta
// Access: Public
// Description: Requests an already read PTA from BamReader. If
// the PTA has not already been read and registered,
// returns a NULL
////////////////////////////////////////////////////////////////////
void* BamReader::
get_pta(DatagramIterator &scan)
{
int _id = scan.get_uint16();
if (_id == 0)
{
_pta_id = -1;
return (void*)NULL;
}
if (_ptamap.find(_id) == _ptamap.end())
{
_pta_id = _id;
return (void*)NULL;
}
else
{
//Just to enforce that register_pta should do nothing if
//get_pta returns a good value or a NULL was what was
//actually written
_pta_id = -1;
}
return _ptamap[_id];
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::register_pta
// Access: Public
// Description: Utility function to be called by the objects reading
// themselves from a Datagram. Registers a PTA with
// BamReader, so that if there are any shared ptr references,
// BamReader can detect and resolve those. To be used
// in conjunction with get_pta
////////////////////////////////////////////////////////////////////
void BamReader::
register_pta(void *ptr)
{
//Just a sanity check to make sure that register_pta
//does nothing if get_pta has not been called first
if (_pta_id != -1)
{
_ptamap[_pta_id] = ptr;
}
_pta_id = -1;
}
////////////////////////////////////////////////////////////////////
// Function: BamReader::resolve
// Access: Public
// Description: Calling this function, asks BamReader to go ahead
// and resolve all pointer requests that can be resolved
////////////////////////////////////////////////////////////////////
bool BamReader::
resolve(void)
{
Requests::iterator whom, old;
vector_ushort::iterator request;
bool objReferencesComplete;
vector_typedWriteable references;
//Loop through the list of all objects who registered themselves as wanting
//a pointer to another object to be completed
whom = _deferred_pointers.begin();
while(whom != _deferred_pointers.end())
{
//For each of those objects, loop through its list of objects that it is
//interested in. If any one of those is not complete, then do nothing
//for that object, as when it is passed the list of pointers, it will
//expect them in a certain order and in totality.
objReferencesComplete = true;
references.clear();
for(request = (*whom).second.begin(); request != (*whom).second.end(); request++)
{
//Check and see if the request was for a null pointer
if (*request == 0)
{
references.push_back(TypedWriteable::Null);
}
else
{
//Test to see if the object requested has been created or not yet
if (_created_objs.find((*request)) == _created_objs.end())
{
objReferencesComplete = false;
break;
}
references.push_back(_created_objs[(*request)]);
}
}
if (objReferencesComplete)
{
(*whom).first->complete_pointers(references, this);
//The request list has been completed, so delete the
//list froms storage
//Annoying STL design flaw forces this delete trick
old = whom;
whom++;
_deferred_pointers.erase(old);
}
else
{
// nassertv(objReferencesComplete);
bam_cat.warning()
<< "Unable to complete " << (*whom).first->get_type() << "\n";
whom++;
}
}
finalize();
return true;
}