mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
356 lines
14 KiB
C++
356 lines
14 KiB
C++
// Filename: dcPackerCatalog.cxx
|
|
// Created by: drose (21Jun04)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d-general@lists.sourceforge.net .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "dcPackerCatalog.h"
|
|
#include "dcPackerInterface.h"
|
|
#include "dcPacker.h"
|
|
#include "dcSwitchParameter.h"
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::Constructor
|
|
// Access: Private
|
|
// Description: The catalog is created only by
|
|
// DCPackerInterface::get_catalog().
|
|
////////////////////////////////////////////////////////////////////
|
|
DCPackerCatalog::
|
|
DCPackerCatalog(const DCPackerInterface *root) : _root(root) {
|
|
_live_catalog = NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::Copy Constructor
|
|
// Access: Private
|
|
// Description: The copy constructor is used only internally, in
|
|
// update_switch_fields().
|
|
////////////////////////////////////////////////////////////////////
|
|
DCPackerCatalog::
|
|
DCPackerCatalog(const DCPackerCatalog ©) :
|
|
_root(copy._root),
|
|
_entries(copy._entries),
|
|
_entries_by_name(copy._entries_by_name),
|
|
_entries_by_field(copy._entries_by_field)
|
|
{
|
|
_live_catalog = NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::Destructor
|
|
// Access: Private
|
|
// Description: The catalog is destroyed only by
|
|
// ~DCPackerInterface().
|
|
////////////////////////////////////////////////////////////////////
|
|
DCPackerCatalog::
|
|
~DCPackerCatalog() {
|
|
if (_live_catalog != (LiveCatalog *)NULL) {
|
|
delete _live_catalog;
|
|
}
|
|
|
|
SwitchCatalogs::iterator si;
|
|
for (si = _switch_catalogs.begin(); si != _switch_catalogs.end(); ++si) {
|
|
delete (*si).second;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::find_entry_by_name
|
|
// Access: Public
|
|
// Description: Returns the index number of the entry with the
|
|
// indicated name, or -1 if no entry has the indicated
|
|
// name. The return value is suitable for passing to
|
|
// get_entry().
|
|
////////////////////////////////////////////////////////////////////
|
|
int DCPackerCatalog::
|
|
find_entry_by_name(const string &name) const {
|
|
EntriesByName::const_iterator ni;
|
|
ni = _entries_by_name.find(name);
|
|
if (ni != _entries_by_name.end()) {
|
|
return (*ni).second;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::find_entry_by_field
|
|
// Access: Public
|
|
// Description: Returns the index number of the entry with the
|
|
// indicated field, or -1 if no entry has the indicated
|
|
// field. The return value is suitable for passing to
|
|
// get_entry().
|
|
////////////////////////////////////////////////////////////////////
|
|
int DCPackerCatalog::
|
|
find_entry_by_field(const DCPackerInterface *field) const {
|
|
EntriesByField::const_iterator ni;
|
|
ni = _entries_by_field.find(field);
|
|
if (ni != _entries_by_field.end()) {
|
|
return (*ni).second;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::get_live_catalog
|
|
// Access: Public
|
|
// Description: Returns a LiveCatalog object indicating the positions
|
|
// within the indicated data record of each field within
|
|
// the catalog. If the catalog's fields are all
|
|
// fixed-width, this may return a statically-allocated
|
|
// LiveCatalog object that is the same for all data
|
|
// records; otherwise, it will allocate a new
|
|
// LiveCatalog object that must be freed with a later
|
|
// call to release_live_catalog().
|
|
////////////////////////////////////////////////////////////////////
|
|
const DCPackerCatalog::LiveCatalog *DCPackerCatalog::
|
|
get_live_catalog(const char *data, size_t length) const {
|
|
if (_live_catalog != (LiveCatalog *)NULL) {
|
|
// Return the previously-allocated live catalog; it will be the
|
|
// same as this one since it's based on a fixed-length field.
|
|
return _live_catalog;
|
|
}
|
|
|
|
LiveCatalog *live_catalog = new LiveCatalog;
|
|
live_catalog->_catalog = this;
|
|
live_catalog->_live_entries.reserve(_entries.size());
|
|
LiveCatalogEntry zero_entry;
|
|
zero_entry._begin = 0;
|
|
zero_entry._end = 0;
|
|
for (size_t i = 0; i < _entries.size(); i++) {
|
|
live_catalog->_live_entries.push_back(zero_entry);
|
|
}
|
|
|
|
DCPacker packer;
|
|
packer.set_unpack_data(data, length, false);
|
|
packer.begin_unpack(_root);
|
|
const DCSwitchParameter *last_switch = NULL;
|
|
r_fill_live_catalog(live_catalog, packer, last_switch);
|
|
bool okflag = packer.end_unpack();
|
|
|
|
if (!okflag) {
|
|
delete live_catalog;
|
|
return NULL;
|
|
}
|
|
|
|
if (_root->has_fixed_structure()) {
|
|
// If our root field has a fixed structure, then the live catalog
|
|
// will always be the same every time, so we might as well keep
|
|
// this one around as an optimization.
|
|
((DCPackerCatalog *)this)->_live_catalog = live_catalog;
|
|
}
|
|
|
|
return live_catalog;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::release_live_catalog
|
|
// Access: Public
|
|
// Description: Releases the LiveCatalog object that was returned by
|
|
// an earlier call to get_live_catalog(). If this
|
|
// represents a newly-allocated live catalog, it will
|
|
// free it; otherwise, it will do nothing.
|
|
//
|
|
// It is therefore always correct (and necessary) to
|
|
// match a call to get_live_catalog() with a later call
|
|
// to release_live_catalog().
|
|
////////////////////////////////////////////////////////////////////
|
|
void DCPackerCatalog::
|
|
release_live_catalog(const DCPackerCatalog::LiveCatalog *live_catalog) const {
|
|
if (live_catalog != _live_catalog) {
|
|
delete (LiveCatalog *)live_catalog;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::add_entry
|
|
// Access: Private
|
|
// Description: Called only by DCPackerInterface::r_fill_catalog(),
|
|
// this adds a new entry to the catalog.
|
|
////////////////////////////////////////////////////////////////////
|
|
void DCPackerCatalog::
|
|
add_entry(const string &name, const DCPackerInterface *field,
|
|
const DCPackerInterface *parent, int field_index) {
|
|
Entry entry;
|
|
entry._name = name;
|
|
entry._field = field;
|
|
entry._parent = parent;
|
|
entry._field_index = field_index;
|
|
|
|
int entry_index = (int)_entries.size();
|
|
_entries.push_back(entry);
|
|
_entries_by_name.insert(EntriesByName::value_type(name, entry_index));
|
|
_entries_by_field.insert(EntriesByField::value_type(field, entry_index));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::r_fill_catalog
|
|
// Access: Private
|
|
// Description: Called by DCPackerInterface to recursively fill up a
|
|
// newly-allocated reference catalog. Also called by
|
|
// update_switch_fields to append fields to a catalog
|
|
// after a DCSwitch node is selected.
|
|
////////////////////////////////////////////////////////////////////
|
|
void DCPackerCatalog::
|
|
r_fill_catalog(const string &name_prefix, const DCPackerInterface *field,
|
|
const DCPackerInterface *parent, int field_index) {
|
|
string next_name_prefix = name_prefix;
|
|
|
|
if (parent != (const DCPackerInterface *)NULL && !field->get_name().empty()) {
|
|
// Record this entry in the catalog.
|
|
next_name_prefix += field->get_name();
|
|
add_entry(next_name_prefix, field, parent, field_index);
|
|
|
|
next_name_prefix += ".";
|
|
}
|
|
|
|
const DCSwitchParameter *switch_parameter = field->as_switch_parameter();
|
|
if (switch_parameter != (DCSwitchParameter *)NULL) {
|
|
// If we come upon a DCSwitch while building the catalog, save the
|
|
// name_prefix at this point so we'll have it again when we later
|
|
// encounter the switch while unpacking a live record (and so we
|
|
// can return to this point in the recursion from
|
|
// update_switch_fields).
|
|
_switch_prefixes[switch_parameter] = next_name_prefix;
|
|
}
|
|
|
|
// Add any children.
|
|
if (field->has_nested_fields()) {
|
|
int num_nested = field->get_num_nested_fields();
|
|
// It's ok if num_nested is -1.
|
|
for (int i = 0; i < num_nested; i++) {
|
|
DCPackerInterface *nested = field->get_nested_field(i);
|
|
if (nested != (DCPackerInterface *)NULL) {
|
|
r_fill_catalog(next_name_prefix, nested, field, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::r_fill_live_catalog
|
|
// Access: Private
|
|
// Description: Recursively walks through all of the fields on the
|
|
// catalog and fills the live catalog with the
|
|
// appropriate offsets.
|
|
////////////////////////////////////////////////////////////////////
|
|
void DCPackerCatalog::
|
|
r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer,
|
|
const DCSwitchParameter *&last_switch) const {
|
|
const DCPackerInterface *current_field = packer.get_current_field();
|
|
|
|
int field_index = live_catalog->find_entry_by_field(current_field);
|
|
if (field_index >= 0) {
|
|
nassertv(field_index < (int)live_catalog->_live_entries.size());
|
|
live_catalog->_live_entries[field_index]._begin = packer.get_num_unpacked_bytes();
|
|
}
|
|
|
|
if (packer.has_nested_fields() &&
|
|
(packer.get_pack_type() != PT_string && packer.get_pack_type() != PT_blob)) {
|
|
packer.push();
|
|
while (packer.more_nested_fields()) {
|
|
r_fill_live_catalog(live_catalog, packer, last_switch);
|
|
}
|
|
packer.pop();
|
|
|
|
} else {
|
|
packer.unpack_skip();
|
|
}
|
|
|
|
if (field_index >= 0) {
|
|
live_catalog->_live_entries[field_index]._end = packer.get_num_unpacked_bytes();
|
|
}
|
|
|
|
if (last_switch != packer.get_last_switch()) {
|
|
// We've just invoked a new DCSwitch. That means we must add the
|
|
// new fields revealed by the switch to the reference catalog.
|
|
last_switch = packer.get_last_switch();
|
|
|
|
const DCPackerInterface *switch_case = packer.get_current_parent();
|
|
nassertv(switch_case != (DCPackerInterface *)NULL);
|
|
const DCPackerCatalog *switch_catalog =
|
|
live_catalog->_catalog->update_switch_fields(last_switch, switch_case);
|
|
nassertv(switch_catalog != (DCPackerCatalog *)NULL);
|
|
live_catalog->_catalog = switch_catalog;
|
|
|
|
// And we also have to expand the live catalog to hold the new
|
|
// entries.
|
|
LiveCatalogEntry zero_entry;
|
|
zero_entry._begin = 0;
|
|
zero_entry._end = 0;
|
|
for (size_t i = live_catalog->_live_entries.size();
|
|
i < switch_catalog->_entries.size();
|
|
i++) {
|
|
live_catalog->_live_entries.push_back(zero_entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: DCPackerCatalog::update_switch_fields
|
|
// Access: Private
|
|
// Description: Returns a new DCPackerCatalog that includes all of
|
|
// the fields in this object, with the addition of the
|
|
// fields named by switch_case.
|
|
//
|
|
// This is used to implement switches, which change the
|
|
// set of fields they make available according to the
|
|
// data in the record, and therefore present a different
|
|
// catalog under different circumstances.
|
|
//
|
|
// This returned pointer is allocated one time for each
|
|
// different switch_case instance; if a given same
|
|
// switch_case is supplied twice, the same pointer is
|
|
// returned both times. The ownership of the returned
|
|
// pointer is kept by this object.
|
|
////////////////////////////////////////////////////////////////////
|
|
const DCPackerCatalog *DCPackerCatalog::
|
|
update_switch_fields(const DCSwitchParameter *switch_parameter,
|
|
const DCPackerInterface *switch_case) const {
|
|
SwitchCatalogs::const_iterator si = _switch_catalogs.find(switch_case);
|
|
if (si != _switch_catalogs.end()) {
|
|
return (*si).second;
|
|
}
|
|
|
|
// Look up the name_prefix will we use for all of the fields that
|
|
// descend from this switch. This should be stored in this record
|
|
// because we must have come across the DCSwitch when building the
|
|
// catalog the first time.
|
|
SwitchPrefixes::const_iterator pi = _switch_prefixes.find(switch_parameter);
|
|
nassertr(pi != _switch_prefixes.end(), NULL);
|
|
string name_prefix = (*pi).second;
|
|
|
|
// Start by creating a new DCPackerCatalog object that contains all
|
|
// of the fields that this one contains.
|
|
DCPackerCatalog *switch_catalog = new DCPackerCatalog(*this);
|
|
|
|
// Now record all of the fields of the switch case in the new
|
|
// catalog. We start with the second field of the switch case,
|
|
// since the first field will be the switch parameter itself, which
|
|
// we would have already recorded the first time around.
|
|
int num_nested = switch_case->get_num_nested_fields();
|
|
for (int i = 1; i < num_nested; i++) {
|
|
DCPackerInterface *nested = switch_case->get_nested_field(i);
|
|
if (nested != (DCPackerInterface *)NULL) {
|
|
switch_catalog->r_fill_catalog(name_prefix, nested, switch_case, i);
|
|
}
|
|
}
|
|
|
|
// Store the newly-generated switch catalog in the record so the
|
|
// same pointer can be returned in the future.
|
|
((DCPackerCatalog *)this)->_switch_catalogs[switch_case] = switch_catalog;
|
|
|
|
return switch_catalog;
|
|
}
|