David Rose 21 лет назад
Родитель
Сommit
803712f986

+ 2 - 0
direct/src/dcparser/Sources.pp

@@ -26,6 +26,7 @@
      dcPackerInterface.h dcPackerInterface.I \
      dcParameter.h dcClassParameter.h dcArrayParameter.h dcSimpleParameter.h \
      dcNumericRange.h dcNumericRange.I \
+     dcSwitch.h \
      dcTypedef.h \
      dcPython.h \
      dcbase.h dcindent.h hashGenerator.h  \
@@ -42,6 +43,7 @@
      dcPackerInterface.cxx \
      dcParameter.cxx dcClassParameter.cxx \
      dcArrayParameter.cxx dcSimpleParameter.cxx \
+     dcSwitch.cxx \
      dcTypedef.cxx \
      dcindent.cxx  \
      hashGenerator.cxx primeNumberGenerator.cxx 

+ 1 - 0
direct/src/dcparser/dcArrayParameter.cxx

@@ -40,6 +40,7 @@ DCArrayParameter(DCParameter *element_type, const DCUnsignedIntRange &size) :
   if (_array_size >= 0 && _element_type->has_fixed_byte_size()) {
     _has_fixed_byte_size = true;
     _fixed_byte_size = _array_size * _element_type->get_fixed_byte_size();
+    _has_fixed_structure = true;
     
   } else {
     // We only need to store the length bytes if the array has a

+ 4 - 1
direct/src/dcparser/dcAtomicField.cxx

@@ -317,7 +317,7 @@ write(ostream &out, bool brief, int indent_level) const {
   }
 
   out << ";";
-  if (!brief) {
+  if (!brief && _number >= 0) {
     out << "  // field " << _number;
   }
   out << "\n";
@@ -372,6 +372,9 @@ add_element(const DCAtomicField::ElementType &element) {
     _has_fixed_byte_size = element._param->has_fixed_byte_size();
     _fixed_byte_size += element._param->get_fixed_byte_size();
   }
+  if (_has_fixed_structure) {
+    _has_fixed_structure = element._param->has_fixed_structure();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 36 - 34
direct/src/dcparser/dcClass.cxx

@@ -22,6 +22,41 @@
 #include "dcindent.h"
 #include "dcmsgtypes.h"
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DCClass::
+DCClass(const string &name, bool is_struct, bool bogus_class) : 
+  _name(name),
+  _is_struct(is_struct),
+  _bogus_class(bogus_class)
+{
+  _number = -1;
+      
+#ifdef HAVE_PYTHON
+  _class_def = NULL;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DCClass::
+~DCClass() {
+  Fields::iterator fi;
+  for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
+    delete (*fi);
+  }
+
+#ifdef HAVE_PYTHON
+  Py_XDECREF(_class_def);
+#endif
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::get_name
 //       Access: Published
@@ -540,39 +575,6 @@ ai_format_generate(PyObject *distobj, int do_id,
 }
 #endif  // HAVE_PYTHON
 
-////////////////////////////////////////////////////////////////////
-//     Function: DCClass::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-DCClass::
-DCClass(const string &name, bool is_struct, bool bogus_class) : 
-  _name(name),
-  _is_struct(is_struct),
-  _bogus_class(bogus_class)
-{
-#ifdef HAVE_PYTHON
-  _class_def = NULL;
-#endif
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCClass::Destructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-DCClass::
-~DCClass() {
-  Fields::iterator fi;
-  for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
-    delete (*fi);
-  }
-
-#ifdef HAVE_PYTHON
-  Py_XDECREF(_class_def);
-#endif
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::write
 //       Access: Public, Virtual
@@ -599,7 +601,7 @@ write(ostream &out, bool brief, int indent_level) const {
   }
 
   out << " {";
-  if (!brief) {
+  if (!brief && _number >= 0) {
     out << "  // index " << _number;
   }
   out << "\n";

+ 4 - 3
direct/src/dcparser/dcClass.h

@@ -33,6 +33,10 @@ class DCParameter;
 //               input .dc file.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_DIRECT DCClass : public DCDeclaration {
+public:
+  DCClass(const string &name, bool is_struct, bool bogus_class);
+  ~DCClass();
+
 PUBLISHED:
   const string &get_name() const;
   int get_number() const;
@@ -76,9 +80,6 @@ PUBLISHED:
 #endif 
 
 public:
-  DCClass(const string &name, bool is_struct, bool bogus_class);
-  ~DCClass();
-
   virtual void write(ostream &out, bool brief, int indent_level) const;
   void generate_hash(HashGenerator &hashgen) const;
 

+ 3 - 1
direct/src/dcparser/dcClassParameter.cxx

@@ -40,10 +40,12 @@ DCClassParameter(DCClass *dclass) :
   // fields).
   _has_fixed_byte_size = true;
   _fixed_byte_size = 0;
+  _has_fixed_structure = true;
   for (int i = 0; i < _num_nested_fields && _has_fixed_byte_size; i++) {
     DCPackerInterface *field = get_nested_field(i);
-    _has_fixed_byte_size = field->has_fixed_byte_size();
+    _has_fixed_byte_size = _has_fixed_byte_size && field->has_fixed_byte_size();
     _fixed_byte_size += field->get_fixed_byte_size();
+    _has_fixed_structure = _has_fixed_structure && field->has_fixed_structure();
   }
 }
 

+ 13 - 1
direct/src/dcparser/dcField.cxx

@@ -28,13 +28,15 @@
 ////////////////////////////////////////////////////////////////////
 DCField::
 DCField(const string &name) : DCPackerInterface(name) {
-  _number = 0;
+  _number = -1;
+
   _has_nested_fields = true;
   _num_nested_fields = 0;
   _pack_type = PT_field;
 
   _has_fixed_byte_size = true;
   _fixed_byte_size = 0;
+  _has_fixed_structure = true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -92,6 +94,16 @@ as_parameter() {
   return (DCParameter *)NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCField::as_switch
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCSwitch *DCField::
+as_switch() {
+  return (DCSwitch *)NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCField::format_data
 //       Access: Published

+ 2 - 0
direct/src/dcparser/dcField.h

@@ -26,6 +26,7 @@
 class DCAtomicField;
 class DCMolecularField;
 class DCParameter;
+class DCSwitch;
 class HashGenerator;
 
 ////////////////////////////////////////////////////////////////////
@@ -44,6 +45,7 @@ PUBLISHED:
   virtual DCAtomicField *as_atomic_field();
   virtual DCMolecularField *as_molecular_field();
   virtual DCParameter *as_parameter();
+  virtual DCSwitch *as_switch();
 
   string format_data(const string &packed_data);
   string parse_string(const string &formatted_string);

+ 10 - 0
direct/src/dcparser/dcLexer.lxx

@@ -370,6 +370,16 @@ REALNUM          ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
   return KW_TYPEDEF;
 }
 
+"switch" {
+  accept();
+  return KW_SWITCH;
+}
+
+"case" {
+  accept();
+  return KW_CASE;
+}
+
 "int8" {
   accept();
   return KW_INT8;

+ 3 - 0
direct/src/dcparser/dcMolecularField.cxx

@@ -96,6 +96,9 @@ add_atomic(DCAtomicField *atomic) {
     _has_fixed_byte_size = atomic->has_fixed_byte_size();
     _fixed_byte_size += atomic->get_fixed_byte_size();
   }
+  if (_has_fixed_structure) {
+    _has_fixed_structure = atomic->has_fixed_structure();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 47 - 2
direct/src/dcparser/dcPacker.I

@@ -45,6 +45,12 @@ has_nested_fields() const {
 //               The return value may be -1 to indicate that a
 //               variable number of nested fields are accepted by this
 //               field type (e.g. a variable-length array).
+//
+//               Note that this method is unreliable to determine how
+//               many fields you must traverse before you can call
+//               pop(), since particularly in the presence of a
+//               DCSwitch, it may change during traversal.  Use
+//               more_nested_fields() instead.
 ////////////////////////////////////////////////////////////////////
 INLINE int DCPacker::
 get_num_nested_fields() const {
@@ -63,6 +69,19 @@ more_nested_fields() const {
   return (_current_field != (DCPackerInterface *)NULL);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::get_current_parent
+//       Access: Published
+//  Description: Returns the field that we left in our last call to
+//               push(): the owner of the current level of fields.
+//               This may be NULL at the beginning of the pack
+//               operation.
+////////////////////////////////////////////////////////////////////
+INLINE const DCPackerInterface *DCPacker::
+get_current_parent() const {
+  return _current_parent;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCPacker::get_current_field
 //       Access: Published
@@ -76,6 +95,24 @@ get_current_field() const {
   return _current_field;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::get_last_switch
+//       Access: Published
+//  Description: Returns a pointer to the last DCSwitch instance that
+//               we have passed by and selected one case of during the
+//               pack/unpack process.  Each time we encounter a new
+//               DCSwitch and select a case, this will change state.
+//
+//               This may be used to detect when a DCSwitch has been
+//               selected.  At the moment this changes state,
+//               get_current_parent() will contain the particular
+//               SwitchCase that was selected by the switch.
+////////////////////////////////////////////////////////////////////
+INLINE const DCSwitch *DCPacker::
+get_last_switch() const {
+  return _last_switch;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCPacker::get_pack_type
 //       Access: Published
@@ -470,8 +507,16 @@ advance() {
     // call pop().
     _current_field = NULL;
 
-  } else if (_mode == M_unpack && _push_marker != 0 &&
-             _unpack_p >= _push_marker) {
+    // But if the parent is a switch record, we make a special case so
+    // we can get the alternate fields.
+    if (_current_parent != (DCPackerInterface *)NULL) {
+      const DCSwitch *dswitch = ((DCPackerInterface *)_current_parent)->as_switch();
+      if (dswitch != (DCSwitch *)NULL) {
+        handle_switch(dswitch);
+      }
+    }
+
+  } else if (_pop_marker != 0 && _unpack_p >= _pop_marker) {
     // Done with all the fields on this parent.  The caller must now
     // call pop().
     _current_field = NULL;

+ 92 - 16
direct/src/dcparser/dcPacker.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "dcPacker.h"
+#include "dcSwitch.h"
 #include "dcParserDefs.h"
 #include "dcLexerDefs.h"
 
@@ -32,15 +33,11 @@ DCPacker() {
   _unpack_length = 0;
   _owns_unpack_data = false;
   _unpack_p = 0;
-  _root = NULL;
-  _catalog = NULL;
   _live_catalog = NULL;
-  _current_field = NULL;
-  _current_parent = NULL;
-  _current_field_index = 0;
-  _num_nested_fields = 0;
   _pack_error = false;
   _range_error = false;
+  
+  clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -272,14 +269,14 @@ seek(const string &field_name) {
     }
     nassertr(_catalog != (DCPackerCatalog *)NULL, false);
 
-    int entry_index = _catalog->find_entry_by_name(field_name);
+    int entry_index = _live_catalog->find_entry_by_name(field_name);
     if (entry_index < 0) {
       // The field was not known.
       _pack_error = true;
       return false;
     }
 
-    const DCPackerCatalog::Entry &entry = _catalog->get_entry(entry_index);
+    const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(entry_index);
 
     // If we are seeking, we don't need to remember our current stack
     // position.
@@ -290,6 +287,13 @@ seek(const string &field_name) {
     _num_nested_fields = _current_parent->get_num_nested_fields();
     _unpack_p = _live_catalog->get_begin(entry_index);
 
+    // We don't really need _push_marker and _pop_marker now, except
+    // that we should set _push_marker in case we have just seeked to
+    // a switch parameter, and we should set _pop_marker to 0 just so
+    // it won't get in the way.
+    _push_marker = _unpack_p;
+    _pop_marker = 0;
+
     return true;
 
   } else if (_mode == M_repack) {
@@ -302,14 +306,24 @@ seek(const string &field_name) {
       return false;
     }
 
-    int entry_index = _catalog->find_entry_by_name(field_name);
+    int entry_index = _live_catalog->find_entry_by_name(field_name);
     if (entry_index < 0) {
       // The field was not known.
       _pack_error = true;
       return false;
     }
 
-    const DCPackerCatalog::Entry &entry = _catalog->get_entry(entry_index);
+    const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(entry_index);
+
+    if (((DCPackerInterface *)entry._parent)->as_switch() != (DCSwitch *)NULL) {
+      // If the parent is a DCSwitch, that can only mean that the
+      // seeked field is a switch parameter.  We can't support seeking
+      // to a switch parameter and modifying it directly--what would
+      // happen to all of the related fields?  Instead, you'll have to
+      // seek to the switch itself and repack the whole entity.
+      _pack_error = true;
+      return false;
+    }
 
     size_t begin = _live_catalog->get_begin(entry_index);
     if (begin < _unpack_p) {
@@ -337,9 +351,14 @@ seek(const string &field_name) {
     _current_field = entry._field;
     _current_parent = entry._parent;
     _current_field_index = entry._field_index;
-    _num_nested_fields = 0;  // this makes advance() stop after this field.
+    _num_nested_fields = 1;
     _unpack_p = _live_catalog->get_end(entry_index);
 
+    // Set up push_marker and pop_marker so we won't try to advance
+    // beyond this field.
+    _push_marker = begin;
+    _pop_marker = _live_catalog->get_end(entry_index);
+
     return true;
   }
 
@@ -370,6 +389,7 @@ push() {
     element._current_parent = _current_parent;
     element._current_field_index = _current_field_index;
     element._push_marker = _push_marker;
+    element._pop_marker = _pop_marker;
     _stack.push_back(element);
     _current_parent = _current_field;
 
@@ -383,12 +403,14 @@ push() {
       // Reserve length_bytes for when we figure out what the length
       // is.
       _push_marker = _pack_data.get_length();
+      _pop_marker = 0;
       _pack_data.append_junk(length_bytes);
 
     } else if (_mode == M_unpack) {
       // Read length_bytes to determine the end of this nested
       // sequence.
-      _push_marker = 0;
+      _push_marker = _unpack_p;
+      _pop_marker = 0;
 
       if (length_bytes != 0) {
         if (_unpack_p + length_bytes > _unpack_length) {
@@ -400,13 +422,13 @@ push() {
             length = DCPackerInterface::do_unpack_uint32
               (_unpack_data + _unpack_p);
             _unpack_p += 4;
-            _push_marker = _unpack_p + length;
+            _pop_marker = _unpack_p + length;
           } else {
             length = DCPackerInterface::do_unpack_uint16
               (_unpack_data + _unpack_p);
             _unpack_p += 2;
           }
-          _push_marker = _unpack_p + length;
+          _pop_marker = _unpack_p + length;
         
           // The explicit length trumps the number of nested fields
           // reported by get_num_nested_fields().
@@ -452,8 +474,8 @@ pop() {
     // Oops, didn't pack or unpack enough values.
     _pack_error = true;
 
-  } else if (_mode == M_unpack && _push_marker != 0 && 
-             _unpack_p != _push_marker) {
+  } else if (_mode == M_unpack && _pop_marker != 0 && 
+             _unpack_p != _pop_marker) {
     // Didn't unpack the right number of values.
     _pack_error = true;
   }
@@ -488,6 +510,7 @@ pop() {
     _current_parent = _stack.back()._current_parent;
     _current_field_index = _stack.back()._current_field_index;
     _push_marker = _stack.back()._push_marker;
+    _pop_marker = _stack.back()._pop_marker;
     _num_nested_fields = (_current_parent == NULL) ? 0 : _current_parent->get_num_nested_fields();
     _stack.pop_back();
   }
@@ -791,6 +814,7 @@ unpack_and_format(ostream &out) {
         break;
 
       case PT_field:
+      case PT_switch:
         out << '(';
         break;
 
@@ -816,6 +840,7 @@ unpack_and_format(ostream &out) {
         break;
 
       case PT_field:
+      case PT_switch:
         out << ')';
         break;
 
@@ -829,6 +854,54 @@ unpack_and_format(ostream &out) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::handle_switch
+//       Access: Private
+//  Description: When we advance past the key field on a switch
+//               record, we suddenly have more fields available--all
+//               the appropriate alternate fields in the switch.
+//
+//               This function is called when we detect this
+//               condition; it switches the _current_parent to the
+//               appropriate case of the switch record.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+handle_switch(const DCSwitch *dswitch) {
+  // First, get the value from the key.  This is either found in the
+  // unpack or the pack data, depending on what mode we're in.
+  const DCPackerInterface *new_parent = NULL;
+
+  if (_mode == M_pack || _mode == M_repack) {
+    const char *data = _pack_data.get_data();
+    new_parent = dswitch->apply_switch(data + _push_marker,
+                                       _pack_data.get_length() - _push_marker);
+
+  } else if (_mode == M_unpack) {
+    new_parent = dswitch->apply_switch(_unpack_data + _push_marker,
+                                       _unpack_p - _push_marker);
+
+  }
+
+  if (new_parent == (DCPackerInterface *)NULL) {
+    // This means an invalid value was packed for the key.
+    _pack_error = true;
+    return;
+  }
+
+  _last_switch = dswitch;
+
+  // Now substitute in the switch case for the previous parent (which
+  // replaces the switch node itself).  This will suddenly make a slew
+  // of new fields appear.
+  _current_parent = new_parent;
+  _num_nested_fields = _current_parent->get_num_nested_fields();
+
+  if (_num_nested_fields < 0 ||
+      _current_field_index < _num_nested_fields) {
+    _current_field = _current_parent->get_nested_field(_current_field_index);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCPacker::clear
 //       Access: Private
@@ -842,6 +915,9 @@ clear() {
   _current_parent = NULL;
   _current_field_index = 0;
   _num_nested_fields = 0;
+  _push_marker = 0;
+  _pop_marker = 0;
+  _last_switch = NULL;
 
   if (_live_catalog != (DCPackerCatalog::LiveCatalog *)NULL) {
     _catalog->release_live_catalog(_live_catalog);

+ 15 - 4
direct/src/dcparser/dcPacker.h

@@ -26,6 +26,8 @@
 #include "dcPackerCatalog.h"
 #include "dcPython.h"
 
+class DCSwitch;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : DCPacker
 // Description : This class can be used for packing a series of
@@ -64,7 +66,9 @@ PUBLISHED:
   INLINE int get_num_nested_fields() const;
   INLINE bool more_nested_fields() const;
 
+  INLINE const DCPackerInterface *get_current_parent() const;
   INLINE const DCPackerInterface *get_current_field() const;
+  INLINE const DCSwitch *get_last_switch() const;
   INLINE DCPackType get_pack_type() const;
 
   void push();
@@ -109,6 +113,7 @@ public:
 
 private:
   INLINE void advance();
+  void handle_switch(const DCSwitch *dswitch);
   void clear();
   void set_unpack_data(const char *unpack_data, size_t unpack_length, 
                        bool owns_unpack_data);
@@ -138,6 +143,7 @@ private:
     const DCPackerInterface *_current_parent;
     int _current_field_index;
     size_t _push_marker;
+    size_t _pop_marker;
   };
   typedef pvector<StackElement> Stack;
 
@@ -146,12 +152,17 @@ private:
   const DCPackerInterface *_current_parent;
   int _current_field_index;
 
-  // In pack mode, _push_marker marks the beginning of the push record
-  // (so we can go back and write in the length later).  In unpack
-  // mode, it marks the end of the push record (so we know when we've
-  // reached the end).
+  // _push_marker marks the beginning of the push record (so we can go
+  // back and write in the length later, or figure out the switch
+  // parameter).
   size_t _push_marker;
+  // _pop_marker is used in unpack mode with certain data structures
+  // (like dynamic arrays) to mark the end of the push record (so we
+  // know when we've reached the end).  It is zero when it is not in
+  // use.
+  size_t _pop_marker;
   int _num_nested_fields;
+  const DCSwitch *_last_switch;
 
   bool _pack_error;
   bool _range_error;

+ 46 - 0
direct/src/dcparser/dcPackerCatalog.I

@@ -42,6 +42,52 @@ get_end(int n) const {
   return _live_entries[n]._end;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCPackerCatalog::LiveCatalog::get_num_entries
+//       Access: Public
+//  Description: Returns the number of entries in the catalog.
+////////////////////////////////////////////////////////////////////
+INLINE int DCPackerCatalog::LiveCatalog::
+get_num_entries() const {
+  return _catalog->get_num_entries();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPackerCatalog::LiveCatalog::get_entry
+//       Access: Public
+//  Description: Returns the nth entry in the catalog.
+////////////////////////////////////////////////////////////////////
+INLINE const DCPackerCatalog::Entry &DCPackerCatalog::LiveCatalog::
+get_entry(int n) const {
+  return _catalog->get_entry(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPackerCatalog::LiveCatalog::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::LiveCatalog::
+find_entry_by_name(const string &name) const {
+  return _catalog->find_entry_by_name(name);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPackerCatalog::LiveCatalog::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::LiveCatalog::
+find_entry_by_field(const DCPackerInterface *field) const {
+  return _catalog->find_entry_by_field(field);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCPackerCatalog::get_num_entries
 //       Access: Public

+ 158 - 7
direct/src/dcparser/dcPackerCatalog.cxx

@@ -19,6 +19,7 @@
 #include "dcPackerCatalog.h"
 #include "dcPackerInterface.h"
 #include "dcPacker.h"
+#include "dcSwitch.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCPackerCatalog::Constructor
@@ -31,6 +32,22 @@ 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 &copy) :
+  _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
@@ -42,6 +59,11 @@ 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;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -95,11 +117,13 @@ find_entry_by_field(const DCPackerInterface *field) const {
 const DCPackerCatalog::LiveCatalog *DCPackerCatalog::
 get_live_catalog(const char *data, size_t length) const {
   if (_live_catalog != (LiveCatalog *)NULL) {
-    // Return the previously-allocated static catalog.
+    // 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;
@@ -110,13 +134,14 @@ get_live_catalog(const char *data, size_t length) const {
   
   DCPacker packer;
   packer.begin_unpack(data, length, _root);
-  r_fill_live_catalog(live_catalog, packer);
+  const DCSwitch *last_switch = NULL;
+  r_fill_live_catalog(live_catalog, packer, last_switch);
   bool okflag = packer.end_unpack();
 
   nassertr(okflag, live_catalog);
 
-  if (_root->has_fixed_byte_size()) {
-    // If our root field has a fixed byte size, then the live catalog
+  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;
@@ -165,6 +190,50 @@ add_entry(const string &name, const DCPackerInterface *field,
   _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 (!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 DCSwitch *dswitch = ((DCPackerInterface *)field)->as_switch();
+  if (dswitch != (DCSwitch *)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[dswitch] = 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
@@ -173,18 +242,20 @@ add_entry(const string &name, const DCPackerInterface *field,
 //               appropriate offsets.
 ////////////////////////////////////////////////////////////////////
 void DCPackerCatalog::
-r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer) const {
+r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer,
+                    const DCSwitch *&last_switch) const {
   const DCPackerInterface *current_field = packer.get_current_field();
 
-  int field_index = find_entry_by_field(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.push();
     while (packer.more_nested_fields()) {
-      r_fill_live_catalog(live_catalog, packer);
+      r_fill_live_catalog(live_catalog, packer, last_switch);
     }
     packer.pop();
 
@@ -195,4 +266,84 @@ r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer) const {
   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);
+    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 DCSwitch *dswitch,
+                     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(dswitch);
+  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;
 }

+ 25 - 2
direct/src/dcparser/dcPackerCatalog.h

@@ -23,6 +23,7 @@
 
 class DCPackerInterface;
 class DCPacker;
+class DCSwitch;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : DCPackerCatalog
@@ -35,6 +36,7 @@ class DCPacker;
 class EXPCL_DIRECT DCPackerCatalog {
 private:
   DCPackerCatalog(const DCPackerInterface *root);
+  DCPackerCatalog(const DCPackerCatalog &copy);
   ~DCPackerCatalog();
 
 public:
@@ -62,9 +64,16 @@ public:
     INLINE size_t get_begin(int n) const;
     INLINE size_t get_end(int n) const;
 
+    INLINE int get_num_entries() const;
+    INLINE const Entry &get_entry(int n) const;
+    INLINE int find_entry_by_name(const string &name) const;
+    INLINE int find_entry_by_field(const DCPackerInterface *field) const;
+
   private:
     typedef pvector<LiveCatalogEntry> LiveEntries;
     LiveEntries _live_entries;
+
+    const DCPackerCatalog *_catalog;
     friend class DCPackerCatalog;
   };
 
@@ -75,11 +84,19 @@ public:
 
   const LiveCatalog *get_live_catalog(const char *data, size_t length) const;
   void release_live_catalog(const LiveCatalog *live_catalog) const;
-    
+
 private:
   void add_entry(const string &name, const DCPackerInterface *field,
                  const DCPackerInterface *parent, int field_index);
-  void r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer) const;
+  
+  void r_fill_catalog(const string &name_prefix, const DCPackerInterface *field,
+                      const DCPackerInterface *parent, int field_index);
+  void r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer,
+                           const DCSwitch *&last_switch) const;
+
+  const DCPackerCatalog *update_switch_fields(const DCSwitch *dswitch,
+                                              const DCPackerInterface *switch_case) const;
+    
 
   const DCPackerInterface *_root;
   LiveCatalog *_live_catalog;
@@ -93,6 +110,12 @@ private:
   typedef pmap<const DCPackerInterface *, int> EntriesByField;
   EntriesByField _entries_by_field;
 
+  typedef pmap<const DCPackerInterface *, DCPackerCatalog *> SwitchCatalogs;
+  SwitchCatalogs _switch_catalogs;
+
+  typedef pmap<const DCSwitch *, string> SwitchPrefixes;
+  SwitchPrefixes _switch_prefixes;
+
   friend class DCPackerInterface;
 };
 

+ 18 - 0
direct/src/dcparser/dcPackerInterface.I

@@ -60,6 +60,24 @@ get_fixed_byte_size() const {
   return _fixed_byte_size;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCPackerInterface::has_fixed_structure
+//       Access: Public
+//  Description: Returns true if this field type always has the same
+//               structure regardless of the data in the stream, or
+//               false if its structure may vary.  This is almost, but
+//               not quite, the same thing as has_fixed_byte_size.
+//               The difference is that a DCSwitch may have multiple
+//               cases all with the same byte size, but they will
+//               still (presumably) have different structures, in the
+//               sense that the actual list of fields varies according
+//               to the live data.
+////////////////////////////////////////////////////////////////////
+INLINE bool DCPackerInterface::
+has_fixed_structure() const {
+  return _has_fixed_structure;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCPackerInterface::get_num_length_bytes
 //       Access: Public

+ 13 - 33
direct/src/dcparser/dcPackerInterface.cxx

@@ -30,6 +30,7 @@ DCPackerInterface(const string &name) :
 {
   _has_fixed_byte_size = false;
   _fixed_byte_size = 0;
+  _has_fixed_structure = false;
   _num_length_bytes = 0;
   _has_nested_fields = false;
   _num_nested_fields = -1;
@@ -47,6 +48,7 @@ DCPackerInterface(const DCPackerInterface &copy) :
   _name(copy._name),
   _has_fixed_byte_size(copy._has_fixed_byte_size),
   _fixed_byte_size(copy._fixed_byte_size),
+  _has_fixed_structure(copy._has_fixed_structure),
   _num_length_bytes(copy._num_length_bytes),
   _has_nested_fields(copy._has_nested_fields),
   _num_nested_fields(copy._num_nested_fields),
@@ -67,6 +69,16 @@ DCPackerInterface::
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCPackerInterface::as_switch
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCSwitch *DCPackerInterface::
+as_switch() {
+  return (DCSwitch *)NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCPackerInterface::calc_num_nested_fields
 //       Access: Public, Virtual
@@ -303,39 +315,7 @@ make_catalog() {
     for (int i = 0; i < num_nested; i++) {
       DCPackerInterface *nested = get_nested_field(i);
       if (nested != (DCPackerInterface *)NULL) {
-        nested->r_fill_catalog(_catalog, "", this, i);
-      }
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPackerInterface::r_fill_catalog
-//       Access: Private
-//  Description: Called internally to recursively fill up the new
-//               DCPackerCatalog object.
-////////////////////////////////////////////////////////////////////
-void DCPackerInterface::
-r_fill_catalog(DCPackerCatalog *catalog, const string &name_prefix,
-               DCPackerInterface *parent, int field_index) {
-  string next_name_prefix = name_prefix;
-
-  if (!get_name().empty()) {
-    // Record this entry in the catalog.
-    next_name_prefix += get_name();
-    catalog->add_entry(next_name_prefix, this, parent, field_index);
-
-    next_name_prefix += ".";
-  }
-
-  // Add any children.
-  if (has_nested_fields()) {
-    int num_nested = get_num_nested_fields();
-    // As above, it's ok if num_nested is -1.
-    for (int i = 0; i < num_nested; i++) {
-      DCPackerInterface *nested = get_nested_field(i);
-      if (nested != (DCPackerInterface *)NULL) {
-        nested->r_fill_catalog(catalog, next_name_prefix, this, i);
+        _catalog->r_fill_catalog("", nested, this, i);
       }
     }
   }

+ 6 - 3
direct/src/dcparser/dcPackerInterface.h

@@ -22,6 +22,7 @@
 #include "dcbase.h"
 #include "dcSubatomicType.h"
 
+class DCSwitch;
 class DCPackData;
 class DCPackerCatalog;
 
@@ -48,6 +49,7 @@ enum DCPackType {
   PT_array,
   PT_field,
   PT_class,
+  PT_switch,
 };
 END_PUBLISH
 
@@ -70,10 +72,13 @@ public:
 PUBLISHED:
   INLINE const string &get_name() const;
 
+  virtual DCSwitch *as_switch();
+
 public:
   INLINE void set_name(const string &name);
   INLINE bool has_fixed_byte_size() const;
   INLINE size_t get_fixed_byte_size() const;
+  INLINE bool has_fixed_structure() const;
   INLINE size_t get_num_length_bytes() const;
 
   INLINE bool has_nested_fields() const;
@@ -152,6 +157,7 @@ protected:
   string _name;
   bool _has_fixed_byte_size;
   size_t _fixed_byte_size;
+  bool _has_fixed_structure;
   size_t _num_length_bytes;
   bool _has_nested_fields;
   int _num_nested_fields;
@@ -159,9 +165,6 @@ protected:
 
 private:
   void make_catalog();
-  void r_fill_catalog(DCPackerCatalog *catalog, const string &name_prefix,
-                      DCPackerInterface *parent, int field_index);
-                    
 
   DCPackerCatalog *_catalog;
 };

+ 8 - 2
direct/src/dcparser/dcParameter.cxx

@@ -31,6 +31,7 @@ DCParameter::
 DCParameter() {
   _typedef = NULL;
   _has_fixed_byte_size = false;
+  _has_fixed_structure = false;
   _num_nested_fields = -1;
 }
 
@@ -139,9 +140,12 @@ output(ostream &out, bool brief) const {
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 void DCParameter::
-write(ostream &out, bool brief, int indent_level) const {
+write(ostream &out, bool, int indent_level) const {
+  // we must always output the name when the parameter occurs by
+  // itself within a class, so we ignore brief and pass false up to
+  // output().
   indent(out, indent_level);
-  output(out, brief);
+  output(out, false);
   out << ";\n";
 }
 
@@ -168,4 +172,6 @@ output_typedef_name(ostream &out, const string &prename, const string &name,
 ////////////////////////////////////////////////////////////////////
 void DCParameter::
 generate_hash(HashGenerator &) const {
+  // We specifically don't call up to DCField::generate_hash(), since
+  // the parameter name is not actually significant to the hash.
 }

+ 110 - 28
direct/src/dcparser/dcParser.yxx

@@ -8,6 +8,7 @@
 #include "dcParserDefs.h"
 #include "dcFile.h"
 #include "dcClass.h"
+#include "dcSwitch.h"
 #include "dcAtomicField.h"
 #include "dcMolecularField.h"
 #include "dcClassParameter.h"
@@ -27,6 +28,7 @@
 
 static DCFile *dc_file = (DCFile *)NULL;
 static DCClass *current_class = (DCClass *)NULL;
+static DCSwitch *current_switch = (DCSwitch *)NULL;
 static DCAtomicField *current_atomic = (DCAtomicField *)NULL;
 static DCMolecularField *current_molecular = (DCMolecularField *)NULL;
 static DCAtomicField::ElementType atomic_element(new DCSimpleParameter(ST_invalid));
@@ -71,6 +73,8 @@ dc_cleanup_parser() {
 %token KW_FROM 
 %token KW_IMPORT 
 %token KW_TYPEDEF 
+%token KW_SWITCH
+%token KW_CASE
 
 %token KW_INT8
 %token KW_INT16
@@ -111,6 +115,11 @@ dc_cleanup_parser() {
 %type <u.flag> kw_struct_or_kw_dclass
 %type <u.dclass> dclass_name
 %type <u.atomic> atomic_name
+%type <u.field> dclass_field
+%type <u.field> switch_field
+%type <u.field> atomic_field
+%type <u.field> molecular_field
+%type <u.field> switch
 %type <u.subatomic> type_token
 %type <u.parameter> type_name
 %type <u.parameter> type_definition
@@ -120,6 +129,7 @@ dc_cleanup_parser() {
 %type <u.parameter> parameter_definition
 %type <str> import_identifier
 %type <str> slash_identifier
+%type <str> optional_name
 %type <u.real> number
 
 %%
@@ -263,13 +273,7 @@ base_list:
 dclass_fields:
         empty
         | dclass_fields ';'
-        | dclass_fields atomic_field
-        | dclass_fields molecular_field
-        | dclass_fields unnamed_parameter ';'
-{
-  current_class->add_field($2);
-}
-        | dclass_fields named_parameter
+        | dclass_fields dclass_field
 {
   if (!current_class->add_field($2)) {
     yyerror("Duplicate field name: " + $2->get_name());
@@ -277,32 +281,30 @@ dclass_fields:
 }
         ;
 
+dclass_field:
+	atomic_field
+	| molecular_field
+	| switch
+	| unnamed_parameter ';'
+{
+  $$ = $1;
+}
+	| named_parameter
+{
+  $$ = $1;
+}
+	;
+
 atomic_field:
         IDENTIFIER '('
 {
   current_atomic = new DCAtomicField($1);
-  if (!current_class->add_field(current_atomic)) {
-    yyerror("Duplicate field name: " + current_atomic->get_name());
-  }
 }
         parameter_list ')' atomic_flags
-        ;
-
-atomic_name:
-        IDENTIFIER
 {
-  DCField *field = current_class->get_field_by_name($1);
-  $$ = (DCAtomicField *)NULL;
-  if (field == (DCField *)NULL) {
-    yyerror("Unknown field: " + $1);
-  } else {
-    $$ = field->as_atomic_field();
-    if ($$ == (DCAtomicField *)NULL) {
-      yyerror("Not an atomic field: " + $1);
-    }
-  }
+  $$ = current_atomic;
 }
-	;
+        ;
 
 parameter_list:
         empty
@@ -794,13 +796,29 @@ molecular_field:
         IDENTIFIER ':'
 {
   current_molecular = new DCMolecularField($1);
-  if (!current_class->add_field(current_molecular)) {
-    yyerror("Duplicate field name: " + current_molecular->get_name());
-  }
 }
         molecular_atom_list
+{
+  $$ = current_molecular;
+}
         ;
 
+atomic_name:
+        IDENTIFIER
+{
+  DCField *field = current_class->get_field_by_name($1);
+  $$ = (DCAtomicField *)NULL;
+  if (field == (DCField *)NULL) {
+    yyerror("Unknown field: " + $1);
+  } else {
+    $$ = field->as_atomic_field();
+    if ($$ == (DCAtomicField *)NULL) {
+      yyerror("Not an atomic field: " + $1);
+    }
+  }
+}
+	;
+
 molecular_atom_list:
         atomic_name
 {
@@ -821,6 +839,70 @@ molecular_atom_list:
 }
         ;
 
+optional_name:
+        empty
+{
+  $$ = "";
+}
+	| IDENTIFIER
+	;
+
+switch:
+	KW_SWITCH optional_name '(' parameter ')' '{'
+{
+  $$ = current_switch;
+  current_switch = new DCSwitch($2, $4);
+}
+	switch_fields '}'
+{
+  $$ = current_switch;
+  current_switch = (DCSwitch *)$<u.parameter>7;
+}
+	;
+
+
+switch_fields:
+        empty
+        | switch_fields ';'
+        | switch_fields switch_case
+        | switch_fields switch_field
+{
+  if (current_switch->get_num_cases() == 0) {
+    yyerror("case declaration required before first element");
+  } else if (!current_switch->add_field($2)) {
+    yyerror("Duplicate field name: " + $2->get_name());
+  }
+}
+        ;
+
+switch_case:
+	KW_CASE
+{
+  current_packer = &default_packer;
+  current_packer->begin_pack(current_switch->get_switch_parameter());
+}
+	parameter_value ':'
+{
+  if (!current_packer->end_pack()) {
+    yyerror("Invalid value for switch parameter");
+  } else {
+    current_switch->add_case(current_packer->get_string());
+  }
+}
+	;
+
+switch_field:
+	switch
+	| unnamed_parameter ';'
+{
+  $$ = $1;
+}
+	| named_parameter
+{
+  $$ = $1;
+}
+	;
+
 empty:
         ;
 

+ 2 - 0
direct/src/dcparser/dcParserDefs.h

@@ -24,6 +24,7 @@
 
 class DCFile;
 class DCClass;
+class DCField;
 class DCAtomicField;
 class DCParameter;
 class DCPacker;
@@ -48,6 +49,7 @@ public:
     double real;
     bool flag;
     DCClass *dclass;
+    DCField *field;
     DCAtomicField *atomic;
     DCSubatomicType subatomic;
     DCParameter *parameter;

+ 5 - 0
direct/src/dcparser/dcSimpleParameter.cxx

@@ -166,6 +166,7 @@ DCSimpleParameter(DCSubatomicType type, int divisor) :
   case ST_invalid:
     break;
   }
+  _has_fixed_structure = _has_fixed_byte_size;
 
   set_divisor(divisor);
 
@@ -412,9 +413,11 @@ set_range(const DCDoubleRange &range) {
       _num_length_bytes = 0;
       _has_fixed_byte_size = true;
       _fixed_byte_size = _uint_range.get_one_value();
+      _has_fixed_structure = true;
     } else {
       _num_length_bytes = 2;
       _has_fixed_byte_size = false;
+      _has_fixed_structure = false;
     }
     break;
 
@@ -431,9 +434,11 @@ set_range(const DCDoubleRange &range) {
       _num_length_bytes = 0;
       _has_fixed_byte_size = true;
       _fixed_byte_size = _uint_range.get_one_value();
+      _has_fixed_structure = true;
     } else {
       _num_length_bytes = 4;
       _has_fixed_byte_size = false;
+      _has_fixed_structure = false;
     }
     break;
 

+ 397 - 0
direct/src/dcparser/dcSwitch.cxx

@@ -0,0 +1,397 @@
+// Filename: dcSwitch.cxx
+// Created by:  drose (23Jun04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dcSwitch.h"
+#include "dcField.h"
+#include "dcindent.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::Constructor
+//       Access: Public
+//  Description: The switch_parameter must be recently allocated via
+//               new; it will be deleted via delete when the switch
+//               destructs.
+////////////////////////////////////////////////////////////////////
+DCSwitch::
+DCSwitch(const string &name, DCParameter *switch_parameter) :
+  DCField(name),
+  _switch_parameter(switch_parameter)
+{
+  _has_fixed_byte_size = _switch_parameter->has_fixed_byte_size();
+  _fixed_byte_size = _switch_parameter->get_fixed_byte_size();
+  _has_fixed_structure = false;
+
+  // The DCSwitch presents just one nested field initially, which is
+  // the switch parameter.  When we pack or unpack that, the DCPacker
+  // calls apply_switch(), which returns a new record that presents
+  // the remaining nested fields.
+  _has_nested_fields = true;
+  _num_nested_fields = 1;
+
+  _pack_type = PT_switch;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+DCSwitch::
+~DCSwitch() {
+  delete _switch_parameter;
+
+  Cases::iterator ci;
+  for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
+    const SwitchCase *dcase = (*ci);
+    delete dcase;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::as_switch
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCSwitch *DCSwitch::
+as_switch() {
+  return this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::get_switch_parameter
+//       Access: Published
+//  Description: Returns the key parameter on which the switch is
+//               based.  The value of this parameter in the record
+//               determines which one of the several cases within the
+//               switch will be used.
+////////////////////////////////////////////////////////////////////
+DCParameter *DCSwitch::
+get_switch_parameter() const {
+  return _switch_parameter;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::get_num_cases
+//       Access: Published
+//  Description: Returns the number of different cases within the
+//               switch.  The legal values for case_index range from 0
+//               to get_num_cases() - 1.
+////////////////////////////////////////////////////////////////////
+int DCSwitch::
+get_num_cases() const {
+  return _cases.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::get_case_by_value
+//       Access: Published
+//  Description: Returns the index number of the case with the
+//               indicated packed value, or -1 if no case has this
+//               value.
+////////////////////////////////////////////////////////////////////
+int DCSwitch::
+get_case_by_value(const string &case_value) const {
+  CasesByValue::const_iterator vi;
+  vi = _cases_by_value.find(case_value);
+  if (vi != _cases_by_value.end()) {
+    return (*vi).second;
+  }
+
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::get_value
+//       Access: Published
+//  Description: Returns the packed value associated with the
+//               indicated case.
+////////////////////////////////////////////////////////////////////
+string DCSwitch::
+get_value(int case_index) const {
+  nassertr(case_index >= 0 && case_index < (int)_cases.size(), string());
+  return _cases[case_index]->_value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::get_num_fields
+//       Access: Published
+//  Description: Returns the number of fields in the indicated case.
+////////////////////////////////////////////////////////////////////
+int DCSwitch::
+get_num_fields(int case_index) const {
+  nassertr(case_index >= 0 && case_index < (int)_cases.size(), 0);
+  return _cases[case_index]->_fields.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::get_num_fields
+//       Access: Published
+//  Description: Returns the nth field in the indicated case.
+////////////////////////////////////////////////////////////////////
+DCField *DCSwitch::
+get_field(int case_index, int n) const {
+  nassertr(case_index >= 0 && case_index < (int)_cases.size(), NULL);
+  nassertr(n >= 0 && n < (int)_cases[case_index]->_fields.size(), NULL);
+  return _cases[case_index]->_fields[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::get_field_by_name
+//       Access: Published
+//  Description: Returns the field with the given name from the
+//               indicated case, or NULL if no field has this name.
+////////////////////////////////////////////////////////////////////
+DCField *DCSwitch::
+get_field_by_name(int case_index, const string &name) const {
+  nassertr(case_index >= 0 && case_index < (int)_cases.size(), NULL);
+
+  const FieldsByName &fields_by_name = _cases[case_index]->_fields_by_name;
+  FieldsByName::const_iterator ni;
+  ni = fields_by_name.find(name);
+  if (ni != fields_by_name.end()) {
+    return (*ni).second;
+  }
+
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::get_nested_field
+//       Access: Public, Virtual
+//  Description: Returns the DCPackerInterface object that represents
+//               the nth nested field.  This may return NULL if there
+//               is no such field (but it shouldn't do this if n is in
+//               the range 0 <= n < get_num_nested_fields()).
+////////////////////////////////////////////////////////////////////
+DCPackerInterface *DCSwitch::
+get_nested_field(int) const {
+  // The DCSwitch presents just one nested field initially, which is
+  // the switch parameter.  When we pack or unpack that, the DCPacker
+  // calls apply_switch(), which returns a new record that presents
+  // the remaining nested fields.
+  return _switch_parameter;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::add_case
+//       Access: Public
+//  Description: Adds a new case to the switch with the indicated
+//               value, and returns the new case_index.  If the value
+//               has already been used for another case, returns -1.
+//               This is normally called only by the parser.
+////////////////////////////////////////////////////////////////////
+int DCSwitch::
+add_case(const string &value) {
+  int case_index = (int)_cases.size();
+  if (!_cases_by_value.insert(CasesByValue::value_type(value, case_index)).second) {
+    return -1;
+  }
+
+  SwitchCase *dcase = new SwitchCase(_name, value);
+  dcase->add_field(_switch_parameter);
+  _cases.push_back(dcase);
+  return case_index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::add_field
+//       Access: Public
+//  Description: Adds a field to the case most recently added via
+//               add_case().  Returns true if successful, false if the
+//               field duplicates a field already named within this
+//               case.  It is an error to call this before calling
+//               add_case().  This is normally called only by the
+//               parser.
+////////////////////////////////////////////////////////////////////
+bool DCSwitch::
+add_field(DCField *field) {
+  nassertr(!_cases.empty(), false);
+
+  if (!_cases.back()->add_field(field)) {
+    return false;
+  }
+
+  // See if we now have a fixed byte size for the overall switch.
+  // This will be true only if all of the individual cases have the
+  // same fixed byte size.
+  _fixed_byte_size = _cases.back()->get_fixed_byte_size();
+
+  Cases::const_iterator ci;
+  for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
+    const SwitchCase *dcase = (*ci);
+    if (!dcase->has_fixed_byte_size() || 
+        dcase->get_fixed_byte_size() != _fixed_byte_size) {
+
+      // Nope, we have a variable byte size.
+      _has_fixed_byte_size = false;
+      return true;
+    }
+  }
+
+  // Sonofagun, we do have a fixed byte size.
+  _has_fixed_byte_size = true;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::apply_switch
+//       Access: Public
+//  Description: Returns the DCPackerInterface that presents the
+//               alternative fields for the case indicated by the
+//               given packed value string, or NULL if the value
+//               string does not match one of the expected cases.
+////////////////////////////////////////////////////////////////////
+const DCPackerInterface *DCSwitch::
+apply_switch(const char *value_data, size_t length) const {
+  CasesByValue::const_iterator vi;
+  vi = _cases_by_value.find(string(value_data, length));
+  if (vi != _cases_by_value.end()) {
+    return _cases[(*vi).second];
+  }
+
+  // Invalid value.
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::write
+//       Access: Public, Virtual
+//  Description: Generates a parseable description of the object to
+//               the indicated output stream.
+////////////////////////////////////////////////////////////////////
+void DCSwitch::
+write(ostream &out, bool brief, int indent_level) const {
+  indent(out, indent_level)
+    << "switch";
+  if (!_name.empty()) {
+    out << " " << _name;
+  }
+  out << " (";
+  _switch_parameter->output(out, brief);
+  out << ") {\n";
+
+  Cases::const_iterator ci;
+  for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
+    const SwitchCase *dcase = (*ci);
+    indent(out, indent_level)
+      << "case " << _switch_parameter->format_data(dcase->_value) << ":\n";
+    
+    Fields::const_iterator fi;
+    if (!dcase->_fields.empty()) {
+      fi = dcase->_fields.begin();
+      ++fi;
+      while (fi != dcase->_fields.end()) {
+        (*fi)->write(out, brief, indent_level + 2);
+        ++fi;
+      }
+    }
+  }
+  indent(out, indent_level)
+    << "};\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::generate_hash
+//       Access: Public, Virtual
+//  Description: Accumulates the properties of this switch into the
+//               hash.
+////////////////////////////////////////////////////////////////////
+void DCSwitch::
+generate_hash(HashGenerator &hashgen) const {
+  DCField::generate_hash(hashgen);
+
+  _switch_parameter->generate_hash(hashgen);
+
+  hashgen.add_int(_cases.size());
+  Cases::const_iterator ci;
+  for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
+    const SwitchCase *dcase = (*ci);
+    hashgen.add_string(dcase->_value);
+
+    hashgen.add_int(dcase->_fields.size());
+    Fields::const_iterator fi;
+    for (fi = dcase->_fields.begin(); fi != dcase->_fields.end(); ++fi) {
+      (*fi)->generate_hash(hashgen);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::SwitchCase::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCSwitch::SwitchCase::
+SwitchCase(const string &name, const string &value) :
+  DCPackerInterface(name),
+  _value(value)
+{
+  _has_nested_fields = true;
+  _num_nested_fields = 0;
+  _pack_type = PT_switch;
+
+  _has_fixed_byte_size = true;
+  _fixed_byte_size = 0;
+  _has_fixed_structure = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::SwitchCase::get_nested_field
+//       Access: Public, Virtual
+//  Description: Returns the DCPackerInterface object that represents
+//               the nth nested field.  This may return NULL if there
+//               is no such field (but it shouldn't do this if n is in
+//               the range 0 <= n < get_num_nested_fields()).
+////////////////////////////////////////////////////////////////////
+DCPackerInterface *DCSwitch::SwitchCase::
+get_nested_field(int n) const {
+  nassertr(n >= 0 && n < (int)_fields.size(), NULL);
+  return _fields[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::SwitchCase::add_field
+//       Access: Public
+//  Description: Adds a field to this case.  Returns true if
+//               successful, false if the field duplicates a field
+//               already named within this case.  This is normally
+//               called only by the parser.
+////////////////////////////////////////////////////////////////////
+bool DCSwitch::SwitchCase::
+add_field(DCField *field) {
+  bool inserted = _fields_by_name.insert
+    (FieldsByName::value_type(field->get_name(), field)).second;
+
+  if (!inserted) {
+    return false;
+  }
+
+  _fields.push_back(field);
+
+  _num_nested_fields = (int)_fields.size();
+
+  // See if we still have a fixed byte size.
+  if (_has_fixed_byte_size) {
+    _has_fixed_byte_size = field->has_fixed_byte_size();
+    _fixed_byte_size += field->get_fixed_byte_size();
+  }
+  if (_has_fixed_structure) {
+    _has_fixed_structure = field->has_fixed_structure();
+  }
+  return true;
+}

+ 86 - 0
direct/src/dcparser/dcSwitch.h

@@ -0,0 +1,86 @@
+// Filename: dcSwitch.h
+// Created by:  drose (23Jun04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DCSWITCH_H
+#define DCSWITCH_H
+
+#include "dcbase.h"
+#include "dcField.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : DCSwitch
+// Description : This represents a switch statement, which can appear
+//               inside a class body and represents two or more
+//               alternative unpacking schemes based on the first
+//               field read.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DIRECT DCSwitch : public DCField {
+public:
+  DCSwitch(const string &name, DCParameter *switch_parameter);
+  virtual ~DCSwitch();
+
+PUBLISHED:
+  virtual DCSwitch *as_switch();
+
+  DCParameter *get_switch_parameter() const;
+
+  int get_num_cases() const;
+  int get_case_by_value(const string &case_value) const;
+
+  string get_value(int case_index) const;
+  int get_num_fields(int case_index) const;
+  DCField *get_field(int case_index, int n) const;
+  DCField *get_field_by_name(int case_index, const string &name) const;
+
+public:
+  virtual DCPackerInterface *get_nested_field(int n) const;
+
+  int add_case(const string &value);
+  bool add_field(DCField *field);
+
+  const DCPackerInterface *apply_switch(const char *value_data, size_t length) const;
+
+  virtual void write(ostream &out, bool brief, int indent_level) const;
+  virtual void generate_hash(HashGenerator &hashgen) const;
+
+private:
+  DCParameter *_switch_parameter;
+
+  typedef pvector<DCField *> Fields;
+  typedef pmap<string, DCField *> FieldsByName;
+
+  class SwitchCase : public DCPackerInterface {
+  public:
+    SwitchCase(const string &name, const string &value);
+    virtual DCPackerInterface *get_nested_field(int n) const;
+
+    bool add_field(DCField *field);
+
+    string _value;
+    Fields _fields;
+    FieldsByName _fields_by_name;
+  };
+
+  typedef pvector<SwitchCase *> Cases;
+  Cases _cases;
+
+  typedef pmap<string, int> CasesByValue;
+  CasesByValue _cases_by_value;
+};
+
+#endif

+ 1 - 0
direct/src/dcparser/dcparser_composite2.cxx

@@ -7,4 +7,5 @@
 #include "dcFile.cxx"
 #include "dcMolecularField.cxx"
 #include "dcSubatomicType.cxx"
+#include "dcSwitch.cxx"
 #include "dcTypedef.cxx"