瀏覽代碼

python 2.4 stuff

Dave Schuyler 20 年之前
父節點
當前提交
b3fa4b906c

+ 1365 - 1359
direct/src/dcparser/dcPacker.cxx

@@ -1,1359 +1,1365 @@
-// Filename: dcPacker.cxx
-// Created by:  drose (15Jun04)
-//
-////////////////////////////////////////////////////////////////////
-//
-// 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 "dcPacker.h"
-#include "dcSwitch.h"
-#include "dcParserDefs.h"
-#include "dcLexerDefs.h"
-#include "dcClassParameter.h"
-#include "dcSwitchParameter.h"
-#include "dcClass.h"
-
-DCPacker::StackElement *DCPacker::StackElement::_deleted_chain = NULL;
-int DCPacker::StackElement::_num_ever_allocated = 0;
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::Constructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-DCPacker::
-DCPacker() {
-  _mode = M_idle;
-  _unpack_data = NULL;
-  _unpack_length = 0;
-  _owns_unpack_data = false;
-  _unpack_p = 0;
-  _live_catalog = NULL;
-  _parse_error = false;
-  _pack_error = false;
-  _range_error = false;
-  _stack = NULL;
-  
-  clear();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::Destructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-DCPacker::
-~DCPacker() {
-  clear_data();
-  clear();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::begin_pack
-//       Access: Published
-//  Description: Begins a packing session.  The parameter is the DC
-//               object that describes the packing format; it may be a
-//               DCParameter or DCField.
-//
-//               Unless you call clear_data() between sessions,
-//               multiple packing sessions will be concatenated
-//               together into the same buffer.  If you wish to add
-//               bytes to the buffer between packing sessions, use
-//               append_data() or get_write_pointer().
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-begin_pack(const DCPackerInterface *root) {
-  nassertv(_mode == M_idle);
-  
-  _mode = M_pack;
-  _parse_error = false;
-  _pack_error = false;
-  _range_error = false;
-
-  _root = root;
-  _catalog = NULL;
-  _live_catalog = NULL;
-
-  _current_field = root;
-  _current_parent = NULL;
-  _current_field_index = 0;
-  _num_nested_fields = 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::end_pack
-//       Access: Published, Virtual
-//  Description: Finishes a packing session.
-//
-//               The return value is true on success, or false if
-//               there has been some error during packing.
-////////////////////////////////////////////////////////////////////
-bool DCPacker::
-end_pack() {
-  nassertr(_mode == M_pack, false);
-  
-  _mode = M_idle;
-
-  if (_stack != NULL || _current_field != NULL || _current_parent != NULL) {
-    _pack_error = true;
-  }
-
-  clear();
-
-  return !had_error();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::set_unpack_data
-//       Access: Public
-//  Description: Sets up the unpack_data pointer.  You may call this
-//               before calling the version of begin_unpack() that
-//               takes only one parameter.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-set_unpack_data(const string &data) {
-  nassertv(_mode == M_idle);
-
-  char *buffer = new char[data.length()];
-  memcpy(buffer, data.data(), data.length());
-  set_unpack_data(buffer, data.length(), true);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::set_unpack_data
-//       Access: Public
-//  Description: Sets up the unpack_data pointer.  You may call this
-//               before calling the version of begin_unpack() that
-//               takes only one parameter.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-set_unpack_data(const char *unpack_data, size_t unpack_length, 
-                bool owns_unpack_data) {
-  nassertv(_mode == M_idle);
-
-  if (_owns_unpack_data) {
-    delete[] _unpack_data;
-  }
-  _unpack_data = unpack_data;
-  _unpack_length = unpack_length;
-  _owns_unpack_data = owns_unpack_data;
-  _unpack_p = 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::begin_unpack
-//       Access: Public
-//  Description: Begins an unpacking session.  You must have
-//               previously called set_unpack_data() to specify a
-//               buffer to unpack.
-//
-//               If there was data left in the buffer after a previous
-//               begin_unpack() .. end_unpack() session, the new
-//               session will resume from the current point.  This
-//               method may be used, therefore, to unpack a sequence
-//               of objects from the same buffer.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-begin_unpack(const DCPackerInterface *root) {
-  nassertv(_mode == M_idle);
-  nassertv(_unpack_data != NULL);
-  
-  _mode = M_unpack;
-  _parse_error = false;
-  _pack_error = false;
-  _range_error = false;
-
-  _root = root;
-  _catalog = NULL;
-  _live_catalog = NULL;
-
-  _current_field = root;
-  _current_parent = NULL;
-  _current_field_index = 0;
-  _num_nested_fields = 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::end_unpack
-//       Access: Published
-//  Description: Finishes the unpacking session.
-//
-//               The return value is true on success, or false if
-//               there has been some error during unpacking (or if all
-//               fields have not been unpacked).
-////////////////////////////////////////////////////////////////////
-bool DCPacker::
-end_unpack() {
-  nassertr(_mode == M_unpack, false);
-  
-  _mode = M_idle;
-
-  if (_stack != NULL || _current_field != NULL || _current_parent != NULL) {
-    // This happens if we have not unpacked all of the fields.
-    // However, this is not an error if we have called seek() during
-    // the unpack session (in which case the _catalog will be
-    // non-NULL).  On the other hand, if the catalog is still NULL,
-    // then we have never called seek() and it is an error not to
-    // unpack all values.
-    if (_catalog == (DCPackerCatalog *)NULL) {
-      _pack_error = true;
-    }
-  }
-
-  clear();
-
-  return !had_error();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::begin_repack
-//       Access: Public
-//  Description: Begins a repacking session.  You must have previously
-//               called set_unpack_data() to specify a buffer to
-//               unpack.
-//
-//               Unlike begin_pack() or begin_unpack() you may not
-//               concatenate the results of multiple begin_repack()
-//               sessions in one buffer.
-//
-//               Also, unlike in packing or unpacking modes, you may
-//               not walk through the fields from beginning to end, or
-//               even pack two consecutive fields at once.  Instead,
-//               you must call seek() for each field you wish to
-//               modify and pack only that one field; then call seek()
-//               again to modify another field.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-begin_repack(const DCPackerInterface *root) {
-  nassertv(_mode == M_idle);
-  nassertv(_unpack_data != NULL);
-  nassertv(_unpack_p == 0);
-  
-  _mode = M_repack;
-  _parse_error = false;
-  _pack_error = false;
-  _range_error = false;
-  _pack_data.clear();
-
-  // In repack mode, we immediately get the catalog, since we know
-  // we'll need it.
-  _root = root;
-  _catalog = _root->get_catalog();
-  _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
-  if (_live_catalog == NULL) {
-    _pack_error = true;
-  }
-
-  // We don't begin at the first field in repack mode.  Instead, you
-  // must explicitly call seek().
-  _current_field = NULL;
-  _current_parent = NULL;
-  _current_field_index = 0;
-  _num_nested_fields = 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::end_repack
-//       Access: Published
-//  Description: Finishes the repacking session.
-//
-//               The return value is true on success, or false if
-//               there has been some error during repacking (or if all
-//               fields have not been repacked).
-////////////////////////////////////////////////////////////////////
-bool DCPacker::
-end_repack() {
-  nassertr(_mode == M_repack, false);
-
-  // Put the rest of the data onto the pack stream.
-  _pack_data.append_data(_unpack_data + _unpack_p, _unpack_length - _unpack_p);
-  
-  _mode = M_idle;
-  clear();
-
-  return !had_error();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::seek
-//       Access: Published
-//  Description: Sets the current unpack (or repack) position to the
-//               named field.  In unpack mode, the next call to
-//               unpack_*() or push() will begin to read the named
-//               field.  In repack mode, the next call to pack_*() or
-//               push() will modify the named field.
-//
-//               Returns true if successful, false if the field is not
-//               known (or if the packer is in an invalid mode).
-////////////////////////////////////////////////////////////////////
-bool DCPacker::
-seek(const string &field_name) {
-  if (_catalog == (DCPackerCatalog *)NULL) {
-    _catalog = _root->get_catalog();
-    _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
-  }
-  nassertr(_catalog != (DCPackerCatalog *)NULL, false);
-  if (_live_catalog == NULL) {
-    _pack_error = true;
-    return false;
-  }
-  
-  int seek_index = _live_catalog->find_entry_by_name(field_name);
-  if (seek_index < 0) {
-    // The field was not known.
-    _pack_error = true;
-    return false;
-  }
-
-  return seek(seek_index);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::seek
-//       Access: Published
-//  Description: Seeks to the field indentified by seek_index, which
-//               was returned by an earlier call to
-//               DCField::find_seek_index() to get the index of some
-//               nested field.  Also see the version of seek() that
-//               accepts a field name.
-//
-//               Returns true if successful, false if the field is not
-//               known (or if the packer is in an invalid mode).
-////////////////////////////////////////////////////////////////////
-bool DCPacker::
-seek(int seek_index) {
-  if (_catalog == (DCPackerCatalog *)NULL) {
-    _catalog = _root->get_catalog();
-    _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
-  }
-  nassertr(_catalog != (DCPackerCatalog *)NULL, false);
-  if (_live_catalog == NULL) {
-    _pack_error = true;
-    return false;
-  }
-  
-  if (_mode == M_unpack) {
-    const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(seek_index);
-
-    // If we are seeking, we don't need to remember our current stack
-    // position.
-    clear_stack();
-    _current_field = entry._field;
-    _current_parent = entry._parent;
-    _current_field_index = entry._field_index;
-    _num_nested_fields = _current_parent->get_num_nested_fields();
-    _unpack_p = _live_catalog->get_begin(seek_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) {
-    nassertr(_catalog != (DCPackerCatalog *)NULL, false);
-
-    if (_stack != NULL || _current_field != NULL) {
-      // It is an error to reseek while the stack is nonempty--that
-      // means we haven't finished packing the current field.
-      _pack_error = true;
-      return false;
-    }
-    const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(seek_index);
-
-    if (entry._parent->as_switch_parameter() != (DCSwitchParameter *)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(seek_index);
-    if (begin < _unpack_p) {
-      // Whoops, we are seeking fields out-of-order.  That means we
-      // need to write the entire record and start again. 
-      _pack_data.append_data(_unpack_data + _unpack_p, _unpack_length - _unpack_p);
-      size_t length = _pack_data.get_length();
-      char *buffer = _pack_data.take_data();
-      set_unpack_data(buffer, length, true);
-      _unpack_p = 0;
-
-      _catalog->release_live_catalog(_live_catalog);
-      _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
-
-      if (_live_catalog == NULL) {
-        _pack_error = true;
-        return false;
-      }
-
-      begin = _live_catalog->get_begin(seek_index);
-    }
-
-    // Now copy the bytes from _unpack_p to begin from the
-    // _unpack_data to the _pack_data.  These are the bytes we just
-    // skipped over with the call to seek().
-    _pack_data.append_data(_unpack_data + _unpack_p, begin - _unpack_p);
-
-    // And set the packer up to pack the indicated field (but no
-    // subsequent fields).
-    _current_field = entry._field;
-    _current_parent = entry._parent;
-    _current_field_index = entry._field_index;
-    _num_nested_fields = 1;
-    _unpack_p = _live_catalog->get_end(seek_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(seek_index);
-
-    return true;
-  }
-
-  // Invalid mode.
-  _pack_error = true;
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::push
-//       Access: Published
-//  Description: Marks the beginning of a nested series of fields.
-//
-//               This must be called before filling the elements of an
-//               array or the individual fields in a structure field.
-//               It must also be balanced by a matching pop().
-//
-//               It is necessary to use push() / pop() only if
-//               has_nested_fields() returns true.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-push() {
-  if (!has_nested_fields()) {
-    _pack_error = true;
-
-  } else {
-    StackElement *element = new StackElement;
-    element->_current_parent = _current_parent;
-    element->_current_field_index = _current_field_index;
-    element->_push_marker = _push_marker;
-    element->_pop_marker = _pop_marker;
-    element->_next = _stack;
-    _stack = element;
-    _current_parent = _current_field;
-
-
-    // Now deal with the length prefix that might or might not be
-    // before a sequence of nested fields.
-    int num_nested_fields = _current_parent->get_num_nested_fields();
-    size_t length_bytes = _current_parent->get_num_length_bytes();
-    
-    if (_mode == M_pack || _mode == M_repack) {
-      // 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 = _unpack_p;
-      _pop_marker = 0;
-
-      if (length_bytes != 0) {
-        if (_unpack_p + length_bytes > _unpack_length) {
-          _pack_error = true;
-
-        } else {
-          size_t length;
-          if (length_bytes == 4) {
-            length = DCPackerInterface::do_unpack_uint32
-              (_unpack_data + _unpack_p);
-            _unpack_p += 4;
-          } else {
-            length = DCPackerInterface::do_unpack_uint16
-              (_unpack_data + _unpack_p);
-            _unpack_p += 2;
-          }
-          _pop_marker = _unpack_p + length;
-        
-          // The explicit length trumps the number of nested fields
-          // reported by get_num_nested_fields().
-          if (length == 0) {
-            num_nested_fields = 0;
-          } else {
-            num_nested_fields = _current_parent->calc_num_nested_fields(length);
-          }
-        }
-      }
-    } else {
-      _pack_error = true;
-    }
-
-
-    // Now point to the first field in the nested range.
-    _num_nested_fields = num_nested_fields;
-    _current_field_index = 0;
-
-    if (_num_nested_fields >= 0 &&
-        _current_field_index >= _num_nested_fields) {
-      _current_field = NULL;
-      
-    } else {
-      _current_field = _current_parent->get_nested_field(_current_field_index);
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::pop
-//       Access: Published
-//  Description: Marks the end of a nested series of fields.
-//
-//               This must be called to match a previous push() only
-//               after all the expected number of nested fields have
-//               been packed.  It is an error to call it too early, or
-//               too late.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-pop() {
-  if (_current_field != NULL && _num_nested_fields >= 0) {
-    // Oops, didn't pack or unpack enough values.
-    _pack_error = true;
-
-  } else if (_mode == M_unpack && _pop_marker != 0 && 
-             _unpack_p != _pop_marker) {
-    // Didn't unpack the right number of values.
-    _pack_error = true;
-  }
-
-  if (_stack == NULL) {
-    // Unbalanced pop().
-    _pack_error = true;
-
-  } else {
-    if (!_current_parent->validate_num_nested_fields(_current_field_index)) {
-      // Incorrect number of nested elements.
-      _pack_error = true;
-    }
-
-    if (_mode == M_pack || _mode == M_repack) {
-      size_t length_bytes = _current_parent->get_num_length_bytes();
-      if (length_bytes != 0) {
-        // Now go back and fill in the length of the array.
-        size_t length = _pack_data.get_length() - _push_marker - length_bytes;
-        if (length_bytes == 4) {
-          DCPackerInterface::do_pack_uint32
-            (_pack_data.get_rewrite_pointer(_push_marker, 4), length);
-        } else {
-          DCPackerInterface::validate_uint_limits(length, 16, _range_error);
-          DCPackerInterface::do_pack_uint16
-            (_pack_data.get_rewrite_pointer(_push_marker, 2), length);
-        }
-      }
-    }
-
-    _current_field = _current_parent;
-    _current_parent = _stack->_current_parent;
-    _current_field_index = _stack->_current_field_index;
-    _push_marker = _stack->_push_marker;
-    _pop_marker = _stack->_pop_marker;
-    _num_nested_fields = (_current_parent == NULL) ? 0 : _current_parent->get_num_nested_fields();
-
-    StackElement *next = _stack->_next;
-    delete _stack;
-    _stack = next;
-  }
-
-  advance();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::pack_default_value
-//       Access: Published
-//  Description: Adds the default value for the current element into
-//               the stream.  If no default has been set for the
-//               current element, creates a sensible default.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-pack_default_value() {
-  nassertv(_mode == M_pack || _mode == M_repack);
-  if (_current_field == NULL) {
-    _pack_error = true;
-  } else {
-    if (_current_field->pack_default_value(_pack_data, _pack_error)) {
-      advance();
-
-    } else {
-      // If the single field didn't know how to pack a default value,
-      // try packing nested fields.
-      push();
-      while (more_nested_fields()) {
-        pack_default_value();
-      }
-      pop();
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::unpack_validate
-//       Access: Published
-//  Description: Internally unpacks the current numeric or string
-//               value and validates it against the type range limits,
-//               but does not return the value.  If the current field
-//               contains nested fields, validates all of them.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-unpack_validate() {
-  nassertv(_mode == M_unpack);
-  if (_current_field == NULL) {
-    _pack_error = true;
-
-  } else {
-    if (_current_field->unpack_validate(_unpack_data, _unpack_length, _unpack_p,
-                                        _pack_error, _range_error)) {
-      advance();
-    } else {
-      // If the single field couldn't be validated, try validating
-      // nested fields.
-      push();
-      while (more_nested_fields()) {
-        unpack_validate();
-      }
-      pop();
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::unpack_skip
-//       Access: Published
-//  Description: Skips the current field without unpacking it and
-//               advances to the next field.  If the current field
-//               contains nested fields, skips all of them.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-unpack_skip() {
-  nassertv(_mode == M_unpack);
-  if (_current_field == NULL) {
-    _pack_error = true;
-
-  } else {
-    if (_current_field->unpack_skip(_unpack_data, _unpack_length, _unpack_p,
-                                    _pack_error)) {
-      advance();
-
-    } else {
-      // If the single field couldn't be skipped, try skipping nested fields.
-      push();
-      while (more_nested_fields()) {
-        unpack_skip();
-      }
-      pop();
-    }
-  }
-}
-
-#ifdef HAVE_PYTHON
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::pack_object
-//       Access: Published
-//  Description: Packs the Python object of whatever type into the
-//               packer.  Each numeric object and string object maps
-//               to the corresponding pack_value() call; a tuple or
-//               sequence maps to a push() followed by all of the
-//               tuple's contents followed by a pop().
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-pack_object(PyObject *object) {
-  nassertv(_mode == M_pack || _mode == M_repack);
-
-  if (PyInt_Check(object)) {
-    pack_int(PyInt_AS_LONG(object));
-  } else if (PyFloat_Check(object)) {
-    pack_double(PyFloat_AS_DOUBLE(object));
-  } else if (PyLong_Check(object)) {
-    pack_int64(PyLong_AsLongLong(object));
-  } else if (PyString_Check(object) || PyUnicode_Check(object)) {
-    char *buffer;
-    int length;
-    PyString_AsStringAndSize(object, &buffer, &length);
-    if (buffer) {
-      pack_string(string(buffer, length));
-    }
-  } else {
-    // For some reason, PySequence_Check() is incorrectly reporting
-    // that a class instance is a sequence, even if it doesn't provide
-    // __len__, so we double-check by testing for __len__ explicitly.
-    bool is_sequence = 
-      (PySequence_Check(object) != 0) &&
-      (PyObject_HasAttrString(object, "__len__") != 0);
-    bool is_instance = false;
-
-    const DCClass *dclass = NULL;
-    const DCPackerInterface *current_field = get_current_field();
-    if (current_field != (DCPackerInterface *)NULL) {
-      const DCClassParameter *class_param = get_current_field()->as_class_parameter();
-      if (class_param != (DCClassParameter *)NULL) {
-        dclass = class_param->get_class();
-        
-        if (dclass->has_class_def()) {
-          PyObject *class_def = dclass->get_class_def();
-          is_instance = (PyObject_IsInstance(object, dclass->get_class_def()) != 0);
-          Py_DECREF(class_def);
-        }
-      }
-    }
-
-    // If dclass is not NULL, the packer is expecting a class object.
-    // There are then two cases: (1) the user has supplied a matching
-    // class object, or (2) the user has supplied a sequence object.
-    // Unfortunately, it may be difficult to differentiate these two
-    // cases, since a class object may also be a sequence object.
-
-    // The rule to differentiate them is:
-
-    // (1) If the supplied class object is an instance of the expected
-    // class object, it is considered to be a class object.
-
-    // (2) Otherwise, if the supplied class object has a __len__()
-    // method (i.e. PySequence_Check() returns true), then it is
-    // considered to be a sequence.
-
-    // (3) Otherwise, it is considered to be a class object.
-
-    if (dclass != (DCClass *)NULL && (is_instance || !is_sequence)) {
-      // The supplied object is either an instance of the expected
-      // class object, or it is not a sequence--this is case (1) or
-      // (3).
-      pack_class_object(dclass, object);
-    } else if (is_sequence) {
-      // The supplied object is not an instance of the expected class
-      // object, but it is a sequence.  This is case (2).
-      push();
-      int size = PySequence_Size(object);
-      for (int i = 0; i < size; ++i) {
-        PyObject *element = PySequence_GetItem(object, i);
-        if (element != (PyObject *)NULL) {
-          pack_object(element);
-          Py_DECREF(element);
-        } else {
-          cerr << "Unable to extract item " << i << " from sequence.\n";
-        }
-      }
-      pop();
-    } else {
-      // The supplied object is not a sequence, and we weren't
-      // expecting a class parameter.  This is none of the above, an
-      // error.
-      ostringstream strm;
-      PyObject *str = PyObject_Str(object);
-      strm << "Don't know how to pack object: "
-           << PyString_AsString(str) << "\n";
-      Py_DECREF(str);
-      nassert_raise(strm.str());
-      _pack_error = true;
-    }
-  }
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::unpack_object
-//       Access: Published
-//  Description: Unpacks a Python object of the appropriate type from
-//               the stream for the current field.  This may be an
-//               integer or a string for a simple field object; if the
-//               current field represents a list of fields it will be
-//               a tuple.
-////////////////////////////////////////////////////////////////////
-PyObject *DCPacker::
-unpack_object() {
-  PyObject *object = NULL;
-
-  DCPackType pack_type = get_pack_type();
-
-  switch (pack_type) {
-  case PT_invalid:
-    object = Py_None;
-    unpack_skip();
-    break;
-
-  case PT_double:
-    {
-      double value = unpack_double();
-      object = PyFloat_FromDouble(value);
-    }
-    break;
-      
-  case PT_int:
-    {
-      int value = unpack_int();
-      object = PyInt_FromLong(value);
-    }
-    break;
-      
-  case PT_uint:
-    {
-      unsigned int value = unpack_uint();
-      if (value & 0x80000000) {
-        object = PyLong_FromUnsignedLong(value);
-      } else {
-        object = PyInt_FromLong(value);
-      }
-    }
-    break;
-      
-  case PT_int64:
-    {
-      PN_int64 value = unpack_int64();
-      object = PyLong_FromLongLong(value);
-    }
-    break;
-      
-  case PT_uint64:
-    {
-      PN_uint64 value = unpack_uint64();
-      object = PyLong_FromUnsignedLongLong(value);
-    }
-    break;
-
-  case PT_string:
-  case PT_blob:
-    {
-      string str;
-      unpack_string(str);
-      object = PyString_FromStringAndSize(str.data(), str.size());
-    }
-    break;
-
-  case PT_class:
-    {
-      const DCClassParameter *class_param = get_current_field()->as_class_parameter();
-      if (class_param != (DCClassParameter *)NULL) {
-        const DCClass *dclass = class_param->get_class();
-        if (dclass->has_class_def()) {
-          // If we know what kind of class object this is and it has a
-          // valid constructor, create the class object instead of
-          // just a tuple.
-          object = unpack_class_object(dclass);
-          break;
-        }
-      }
-    }
-    // If we don't know what kind of class object it is, or it doesn't
-    // have a constructor, fall through and make a tuple.
-  default:
-    {
-      // First, build up a list from the nested objects.
-      object = PyList_New(0);
-
-      push();
-      while (more_nested_fields()) {
-        PyObject *element = unpack_object();
-        PyList_Append(object, element);
-        Py_DECREF(element);
-      }
-      pop();
-
-      if (pack_type != PT_array) {
-        // For these other kinds of objects, we'll convert the list
-        // into a tuple.
-        PyObject *tuple = PyList_AsTuple(object);
-        Py_DECREF(object);
-        object = tuple;
-      }
-    }
-    break;
-  }
-
-  return object;
-}
-#endif  // HAVE_PYTHON
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::parse_and_pack
-//       Access: Published
-//  Description: Parses an object's value according to the DC file
-//               syntax (e.g. as a default value string) and packs it.
-//               Returns true on success, false on a parse error.
-////////////////////////////////////////////////////////////////////
-bool DCPacker::
-parse_and_pack(const string &formatted_object) {
-  istringstream strm(formatted_object);
-  return parse_and_pack(strm);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::parse_and_pack
-//       Access: Published
-//  Description: Parses an object's value according to the DC file
-//               syntax (e.g. as a default value string) and packs it.
-//               Returns true on success, false on a parse error.
-////////////////////////////////////////////////////////////////////
-bool DCPacker::
-parse_and_pack(istream &in) {
-  dc_init_parser_parameter_value(in, "parse_and_pack", *this);
-  dcyyparse();
-  dc_cleanup_parser();
-
-  bool parse_error = (dc_error_count() != 0);
-  if (parse_error) {
-    _parse_error = true;
-  }
-
-  return !parse_error;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::unpack_and_format
-//       Access: Published
-//  Description: Unpacks an object and formats its value into a syntax
-//               suitable for parsing in the dc file (e.g. as a
-//               default value), or as an input to parse_object.
-////////////////////////////////////////////////////////////////////
-string DCPacker::
-unpack_and_format(bool show_field_names) {
-  ostringstream strm;
-  unpack_and_format(strm, show_field_names);
-  return strm.str();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::unpack_and_format
-//       Access: Published
-//  Description: Unpacks an object and formats its value into a syntax
-//               suitable for parsing in the dc file (e.g. as a
-//               default value), or as an input to parse_object.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-unpack_and_format(ostream &out, bool show_field_names) {
-  DCPackType pack_type = get_pack_type();
-
-  if (show_field_names && !get_current_field_name().empty()) {
-    nassertv(_current_field != (DCPackerInterface *)NULL);
-    const DCField *field = _current_field->as_field();
-    if (field != (DCField *)NULL && 
-        field->as_parameter() != (DCParameter *)NULL) {
-      out << field->get_name() << " = ";
-    }
-  }
-
-  switch (pack_type) {
-  case PT_invalid:
-    out << "<invalid>";
-    break;
-
-  case PT_double:
-    out << unpack_double();
-    break;
-      
-  case PT_int:
-    out << unpack_int();
-    break;
-      
-  case PT_uint:
-    out << unpack_uint();
-    break;
-      
-  case PT_int64:
-    out << unpack_int64();
-    break;
-      
-  case PT_uint64:
-    out << unpack_uint64();
-    break;
-
-  case PT_string:
-    enquote_string(out, '"', unpack_string());
-    break;
-
-  case PT_blob:
-    output_hex_string(out, unpack_literal_value());
-    break;
-
-  default:
-    {
-      switch (pack_type) {
-      case PT_array:
-        out << '[';
-        break;
-
-      case PT_field:
-      case PT_switch:
-        out << '(';
-        break;
-
-      case PT_class:
-      default:
-        out << '{';
-        break;
-      }
-
-      push();
-      while (more_nested_fields() && !had_pack_error()) {
-        unpack_and_format(out, show_field_names);
-
-        if (more_nested_fields()) {
-          out << ", ";
-        }
-      }
-      pop();
-
-      switch (pack_type) {
-      case PT_array:
-        out << ']';
-        break;
-
-      case PT_field:
-      case PT_switch:
-        out << ')';
-        break;
-
-      case PT_class:
-      default:
-        out << '}';
-        break;
-      }
-    }
-    break;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::enquote_string
-//       Access: Public, Static
-//  Description: Outputs the indicated string within quotation marks.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-enquote_string(ostream &out, char quote_mark, const string &str) {
-  out << quote_mark;
-  for (string::const_iterator pi = str.begin();
-       pi != str.end();
-       ++pi) {
-    if ((*pi) == quote_mark || (*pi) == '\\') {
-      out << '\\' << (*pi);
-
-    } else if (!isprint(*pi)) {
-      char buffer[10];
-      sprintf(buffer, "%02x", (unsigned char)(*pi));
-      out << "\\x" << buffer;
-
-    } else {
-      out << (*pi);
-    }
-  }
-  out << quote_mark;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::output_hex_string
-//       Access: Public, Static
-//  Description: Outputs the indicated string as a hex constant.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-output_hex_string(ostream &out, const string &str) {
-  out << '<';
-  for (string::const_iterator pi = str.begin();
-       pi != str.end();
-       ++pi) {
-    char buffer[10];
-    sprintf(buffer, "%02x", (unsigned char)(*pi));
-    out << buffer;
-  }
-  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 DCSwitchParameter *switch_parameter) {
-  // 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 = switch_parameter->apply_switch
-      (data + _push_marker, _pack_data.get_length() - _push_marker);
-
-  } else if (_mode == M_unpack) {
-    new_parent = switch_parameter->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.
-    _range_error = true;
-    return;
-  }
-
-  _last_switch = switch_parameter;
-
-  // 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
-//  Description: Resets the data structures after a pack or unpack
-//               sequence.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-clear() {
-  clear_stack();
-  _current_field = NULL;
-  _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);
-    _live_catalog = NULL;
-  }
-  _catalog = NULL;
-  _root = NULL;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::clear_stack
-//       Access: Private
-//  Description: Empties the stack.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-clear_stack() {
-  while (_stack != (StackElement *)NULL) {
-    StackElement *next = _stack->_next;
-    delete _stack;
-    _stack = next;
-  }
-}
-
-#ifdef HAVE_PYTHON
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::pack_class_object
-//       Access: Private
-//  Description: Given that the current element is a ClassParameter
-//               for a Python class object, try to extract the
-//               appropriate values from the class object and pack in.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-pack_class_object(const DCClass *dclass, PyObject *object) {
-  push();
-  while (more_nested_fields() && !_pack_error) {
-    const DCField *field = get_current_field()->as_field();
-    nassertv(field != (DCField *)NULL);
-    get_class_element(dclass, object, field);
-  }
-  pop();
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::unpack_class_object
-//       Access: Private
-//  Description: Given that the current element is a ClassParameter
-//               for a Python class for which we have a valid
-//               constructor, unpack it and fill in its values.
-////////////////////////////////////////////////////////////////////
-PyObject *DCPacker::
-unpack_class_object(const DCClass *dclass) {
-  PyObject *class_def = dclass->get_class_def();
-  nassertr(class_def != (PyObject *)NULL, NULL);
-
-  PyObject *object = NULL;
-
-  if (!dclass->has_constructor()) {
-    // If the class uses a default constructor, go ahead and create
-    // the Python object for it now.
-    object = PyObject_CallObject(class_def, NULL);
-    if (object == (PyObject *)NULL) {
-      return NULL;
-    }
-  }
-
-  push();
-  if (object == (PyObject *)NULL && more_nested_fields()) {
-    // The first nested field will be the constructor.
-    const DCField *field = get_current_field()->as_field();
-    nassertr(field != (DCField *)NULL, object);
-    nassertr(field == dclass->get_constructor(), object);
-
-    set_class_element(class_def, object, field);
-
-    // By now, the object should have been constructed.
-    if (object == (PyObject *)NULL) {
-      return NULL;
-    }
-  }
-  while (more_nested_fields()) {
-    const DCField *field = get_current_field()->as_field();
-    nassertr(field != (DCField *)NULL, object);
-
-    set_class_element(class_def, object, field);
-  }
-  pop();
-
-  return object;
-}
-#endif  // HAVE_PYTHON
-
-
-#ifdef HAVE_PYTHON
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::set_class_element
-//       Access: Private
-//  Description: Unpacks the current element and stuffs it on the
-//               Python class object in whatever way is appropriate.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-set_class_element(PyObject *class_def, PyObject *&object, 
-                  const DCField *field) {
-  string field_name = field->get_name();
-  DCPackType pack_type = get_pack_type();
-
-  if (field_name.empty()) {
-    switch (pack_type) {
-    case PT_class:
-    case PT_switch:
-      // If the field has no name, but it is one of these container
-      // objects, we want to unpack its nested objects directly into
-      // the class.
-      push();
-      while (more_nested_fields()) {
-        const DCField *field = get_current_field()->as_field();
-        nassertv(field != (DCField *)NULL);
-        nassertv(object != (PyObject *)NULL);
-        set_class_element(class_def, object, field);
-      }
-      pop();
-      break;
-
-    default:
-      // Otherwise, we just skip over the field.
-      unpack_skip();
-    }
-
-  } else {
-    // If the field does have a name, we will want to store it on the
-    // class, either by calling a method (for a PT_field pack_type) or
-    // by setting a value (for any other kind of pack_type).
-
-    PyObject *element = unpack_object();
-
-    if (pack_type == PT_field) {
-      if (object == (PyObject *)NULL) {
-        // If the object hasn't been constructed yet, assume this is
-        // the constructor.
-        object = PyObject_CallObject(class_def, element);
-
-      } else {
-        if (PyObject_HasAttrString(object, (char *)field_name.c_str())) {
-          PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
-          if (func != (PyObject *)NULL) {
-            PyObject *result = PyObject_CallObject(func, element);
-            Py_XDECREF(result);
-            Py_DECREF(func);
-          }
-        }
-      }
-      
-    } else {
-      nassertv(object != (PyObject *)NULL);
-      PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
-    }
-
-    Py_DECREF(element);
-  }
-}
-#endif  // HAVE_PYTHON
-
-
-#ifdef HAVE_PYTHON
-////////////////////////////////////////////////////////////////////
-//     Function: DCPacker::get_class_element
-//       Access: Private
-//  Description: Gets the current element from the Python object and
-//               packs it.
-////////////////////////////////////////////////////////////////////
-void DCPacker::
-get_class_element(const DCClass *dclass, PyObject *object, 
-                  const DCField *field) {
-  string field_name = field->get_name();
-  DCPackType pack_type = get_pack_type();
-
-  if (field_name.empty()) {
-    switch (pack_type) {
-    case PT_class:
-    case PT_switch:
-      // If the field has no name, but it is one of these container
-      // objects, we want to get its nested objects directly from
-      // the class.
-      push();
-      while (more_nested_fields() && !_pack_error) {
-        const DCField *field = get_current_field()->as_field();
-        nassertv(field != (DCField *)NULL);
-        get_class_element(dclass, object, field);
-      }
-      pop();
-      break;
-
-    default:
-      // Otherwise, we just pack the default value.
-      pack_default_value();
-    }
-
-  } else {
-    // If the field does have a name, we will want to get it from the
-    // class and pack it.  It just so happens that there's already a
-    // method that does this on DCClass.
-
-    if (!dclass->pack_required_field(*this, object, field)) {
-      _pack_error = true;
-    }
-  }
-}
-#endif  // HAVE_PYTHON
+// Filename: dcPacker.cxx
+// Created by:  drose (15Jun04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "dcPacker.h"
+#include "dcSwitch.h"
+#include "dcParserDefs.h"
+#include "dcLexerDefs.h"
+#include "dcClassParameter.h"
+#include "dcSwitchParameter.h"
+#include "dcClass.h"
+
+DCPacker::StackElement *DCPacker::StackElement::_deleted_chain = NULL;
+int DCPacker::StackElement::_num_ever_allocated = 0;
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCPacker::
+DCPacker() {
+  _mode = M_idle;
+  _unpack_data = NULL;
+  _unpack_length = 0;
+  _owns_unpack_data = false;
+  _unpack_p = 0;
+  _live_catalog = NULL;
+  _parse_error = false;
+  _pack_error = false;
+  _range_error = false;
+  _stack = NULL;
+  
+  clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCPacker::
+~DCPacker() {
+  clear_data();
+  clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::begin_pack
+//       Access: Published
+//  Description: Begins a packing session.  The parameter is the DC
+//               object that describes the packing format; it may be a
+//               DCParameter or DCField.
+//
+//               Unless you call clear_data() between sessions,
+//               multiple packing sessions will be concatenated
+//               together into the same buffer.  If you wish to add
+//               bytes to the buffer between packing sessions, use
+//               append_data() or get_write_pointer().
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+begin_pack(const DCPackerInterface *root) {
+  nassertv(_mode == M_idle);
+  
+  _mode = M_pack;
+  _parse_error = false;
+  _pack_error = false;
+  _range_error = false;
+
+  _root = root;
+  _catalog = NULL;
+  _live_catalog = NULL;
+
+  _current_field = root;
+  _current_parent = NULL;
+  _current_field_index = 0;
+  _num_nested_fields = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::end_pack
+//       Access: Published, Virtual
+//  Description: Finishes a packing session.
+//
+//               The return value is true on success, or false if
+//               there has been some error during packing.
+////////////////////////////////////////////////////////////////////
+bool DCPacker::
+end_pack() {
+  nassertr(_mode == M_pack, false);
+  
+  _mode = M_idle;
+
+  if (_stack != NULL || _current_field != NULL || _current_parent != NULL) {
+    _pack_error = true;
+  }
+
+  clear();
+
+  return !had_error();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::set_unpack_data
+//       Access: Public
+//  Description: Sets up the unpack_data pointer.  You may call this
+//               before calling the version of begin_unpack() that
+//               takes only one parameter.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+set_unpack_data(const string &data) {
+  nassertv(_mode == M_idle);
+
+  char *buffer = new char[data.length()];
+  memcpy(buffer, data.data(), data.length());
+  set_unpack_data(buffer, data.length(), true);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::set_unpack_data
+//       Access: Public
+//  Description: Sets up the unpack_data pointer.  You may call this
+//               before calling the version of begin_unpack() that
+//               takes only one parameter.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+set_unpack_data(const char *unpack_data, size_t unpack_length, 
+                bool owns_unpack_data) {
+  nassertv(_mode == M_idle);
+
+  if (_owns_unpack_data) {
+    delete[] _unpack_data;
+  }
+  _unpack_data = unpack_data;
+  _unpack_length = unpack_length;
+  _owns_unpack_data = owns_unpack_data;
+  _unpack_p = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::begin_unpack
+//       Access: Public
+//  Description: Begins an unpacking session.  You must have
+//               previously called set_unpack_data() to specify a
+//               buffer to unpack.
+//
+//               If there was data left in the buffer after a previous
+//               begin_unpack() .. end_unpack() session, the new
+//               session will resume from the current point.  This
+//               method may be used, therefore, to unpack a sequence
+//               of objects from the same buffer.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+begin_unpack(const DCPackerInterface *root) {
+  nassertv(_mode == M_idle);
+  nassertv(_unpack_data != NULL);
+  
+  _mode = M_unpack;
+  _parse_error = false;
+  _pack_error = false;
+  _range_error = false;
+
+  _root = root;
+  _catalog = NULL;
+  _live_catalog = NULL;
+
+  _current_field = root;
+  _current_parent = NULL;
+  _current_field_index = 0;
+  _num_nested_fields = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::end_unpack
+//       Access: Published
+//  Description: Finishes the unpacking session.
+//
+//               The return value is true on success, or false if
+//               there has been some error during unpacking (or if all
+//               fields have not been unpacked).
+////////////////////////////////////////////////////////////////////
+bool DCPacker::
+end_unpack() {
+  nassertr(_mode == M_unpack, false);
+  
+  _mode = M_idle;
+
+  if (_stack != NULL || _current_field != NULL || _current_parent != NULL) {
+    // This happens if we have not unpacked all of the fields.
+    // However, this is not an error if we have called seek() during
+    // the unpack session (in which case the _catalog will be
+    // non-NULL).  On the other hand, if the catalog is still NULL,
+    // then we have never called seek() and it is an error not to
+    // unpack all values.
+    if (_catalog == (DCPackerCatalog *)NULL) {
+      _pack_error = true;
+    }
+  }
+
+  clear();
+
+  return !had_error();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::begin_repack
+//       Access: Public
+//  Description: Begins a repacking session.  You must have previously
+//               called set_unpack_data() to specify a buffer to
+//               unpack.
+//
+//               Unlike begin_pack() or begin_unpack() you may not
+//               concatenate the results of multiple begin_repack()
+//               sessions in one buffer.
+//
+//               Also, unlike in packing or unpacking modes, you may
+//               not walk through the fields from beginning to end, or
+//               even pack two consecutive fields at once.  Instead,
+//               you must call seek() for each field you wish to
+//               modify and pack only that one field; then call seek()
+//               again to modify another field.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+begin_repack(const DCPackerInterface *root) {
+  nassertv(_mode == M_idle);
+  nassertv(_unpack_data != NULL);
+  nassertv(_unpack_p == 0);
+  
+  _mode = M_repack;
+  _parse_error = false;
+  _pack_error = false;
+  _range_error = false;
+  _pack_data.clear();
+
+  // In repack mode, we immediately get the catalog, since we know
+  // we'll need it.
+  _root = root;
+  _catalog = _root->get_catalog();
+  _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
+  if (_live_catalog == NULL) {
+    _pack_error = true;
+  }
+
+  // We don't begin at the first field in repack mode.  Instead, you
+  // must explicitly call seek().
+  _current_field = NULL;
+  _current_parent = NULL;
+  _current_field_index = 0;
+  _num_nested_fields = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::end_repack
+//       Access: Published
+//  Description: Finishes the repacking session.
+//
+//               The return value is true on success, or false if
+//               there has been some error during repacking (or if all
+//               fields have not been repacked).
+////////////////////////////////////////////////////////////////////
+bool DCPacker::
+end_repack() {
+  nassertr(_mode == M_repack, false);
+
+  // Put the rest of the data onto the pack stream.
+  _pack_data.append_data(_unpack_data + _unpack_p, _unpack_length - _unpack_p);
+  
+  _mode = M_idle;
+  clear();
+
+  return !had_error();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::seek
+//       Access: Published
+//  Description: Sets the current unpack (or repack) position to the
+//               named field.  In unpack mode, the next call to
+//               unpack_*() or push() will begin to read the named
+//               field.  In repack mode, the next call to pack_*() or
+//               push() will modify the named field.
+//
+//               Returns true if successful, false if the field is not
+//               known (or if the packer is in an invalid mode).
+////////////////////////////////////////////////////////////////////
+bool DCPacker::
+seek(const string &field_name) {
+  if (_catalog == (DCPackerCatalog *)NULL) {
+    _catalog = _root->get_catalog();
+    _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
+  }
+  nassertr(_catalog != (DCPackerCatalog *)NULL, false);
+  if (_live_catalog == NULL) {
+    _pack_error = true;
+    return false;
+  }
+  
+  int seek_index = _live_catalog->find_entry_by_name(field_name);
+  if (seek_index < 0) {
+    // The field was not known.
+    _pack_error = true;
+    return false;
+  }
+
+  return seek(seek_index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::seek
+//       Access: Published
+//  Description: Seeks to the field indentified by seek_index, which
+//               was returned by an earlier call to
+//               DCField::find_seek_index() to get the index of some
+//               nested field.  Also see the version of seek() that
+//               accepts a field name.
+//
+//               Returns true if successful, false if the field is not
+//               known (or if the packer is in an invalid mode).
+////////////////////////////////////////////////////////////////////
+bool DCPacker::
+seek(int seek_index) {
+  if (_catalog == (DCPackerCatalog *)NULL) {
+    _catalog = _root->get_catalog();
+    _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
+  }
+  nassertr(_catalog != (DCPackerCatalog *)NULL, false);
+  if (_live_catalog == NULL) {
+    _pack_error = true;
+    return false;
+  }
+  
+  if (_mode == M_unpack) {
+    const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(seek_index);
+
+    // If we are seeking, we don't need to remember our current stack
+    // position.
+    clear_stack();
+    _current_field = entry._field;
+    _current_parent = entry._parent;
+    _current_field_index = entry._field_index;
+    _num_nested_fields = _current_parent->get_num_nested_fields();
+    _unpack_p = _live_catalog->get_begin(seek_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) {
+    nassertr(_catalog != (DCPackerCatalog *)NULL, false);
+
+    if (_stack != NULL || _current_field != NULL) {
+      // It is an error to reseek while the stack is nonempty--that
+      // means we haven't finished packing the current field.
+      _pack_error = true;
+      return false;
+    }
+    const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(seek_index);
+
+    if (entry._parent->as_switch_parameter() != (DCSwitchParameter *)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(seek_index);
+    if (begin < _unpack_p) {
+      // Whoops, we are seeking fields out-of-order.  That means we
+      // need to write the entire record and start again. 
+      _pack_data.append_data(_unpack_data + _unpack_p, _unpack_length - _unpack_p);
+      size_t length = _pack_data.get_length();
+      char *buffer = _pack_data.take_data();
+      set_unpack_data(buffer, length, true);
+      _unpack_p = 0;
+
+      _catalog->release_live_catalog(_live_catalog);
+      _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
+
+      if (_live_catalog == NULL) {
+        _pack_error = true;
+        return false;
+      }
+
+      begin = _live_catalog->get_begin(seek_index);
+    }
+
+    // Now copy the bytes from _unpack_p to begin from the
+    // _unpack_data to the _pack_data.  These are the bytes we just
+    // skipped over with the call to seek().
+    _pack_data.append_data(_unpack_data + _unpack_p, begin - _unpack_p);
+
+    // And set the packer up to pack the indicated field (but no
+    // subsequent fields).
+    _current_field = entry._field;
+    _current_parent = entry._parent;
+    _current_field_index = entry._field_index;
+    _num_nested_fields = 1;
+    _unpack_p = _live_catalog->get_end(seek_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(seek_index);
+
+    return true;
+  }
+
+  // Invalid mode.
+  _pack_error = true;
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::push
+//       Access: Published
+//  Description: Marks the beginning of a nested series of fields.
+//
+//               This must be called before filling the elements of an
+//               array or the individual fields in a structure field.
+//               It must also be balanced by a matching pop().
+//
+//               It is necessary to use push() / pop() only if
+//               has_nested_fields() returns true.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+push() {
+  if (!has_nested_fields()) {
+    _pack_error = true;
+
+  } else {
+    StackElement *element = new StackElement;
+    element->_current_parent = _current_parent;
+    element->_current_field_index = _current_field_index;
+    element->_push_marker = _push_marker;
+    element->_pop_marker = _pop_marker;
+    element->_next = _stack;
+    _stack = element;
+    _current_parent = _current_field;
+
+
+    // Now deal with the length prefix that might or might not be
+    // before a sequence of nested fields.
+    int num_nested_fields = _current_parent->get_num_nested_fields();
+    size_t length_bytes = _current_parent->get_num_length_bytes();
+    
+    if (_mode == M_pack || _mode == M_repack) {
+      // 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 = _unpack_p;
+      _pop_marker = 0;
+
+      if (length_bytes != 0) {
+        if (_unpack_p + length_bytes > _unpack_length) {
+          _pack_error = true;
+
+        } else {
+          size_t length;
+          if (length_bytes == 4) {
+            length = DCPackerInterface::do_unpack_uint32
+              (_unpack_data + _unpack_p);
+            _unpack_p += 4;
+          } else {
+            length = DCPackerInterface::do_unpack_uint16
+              (_unpack_data + _unpack_p);
+            _unpack_p += 2;
+          }
+          _pop_marker = _unpack_p + length;
+        
+          // The explicit length trumps the number of nested fields
+          // reported by get_num_nested_fields().
+          if (length == 0) {
+            num_nested_fields = 0;
+          } else {
+            num_nested_fields = _current_parent->calc_num_nested_fields(length);
+          }
+        }
+      }
+    } else {
+      _pack_error = true;
+    }
+
+
+    // Now point to the first field in the nested range.
+    _num_nested_fields = num_nested_fields;
+    _current_field_index = 0;
+
+    if (_num_nested_fields >= 0 &&
+        _current_field_index >= _num_nested_fields) {
+      _current_field = NULL;
+      
+    } else {
+      _current_field = _current_parent->get_nested_field(_current_field_index);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::pop
+//       Access: Published
+//  Description: Marks the end of a nested series of fields.
+//
+//               This must be called to match a previous push() only
+//               after all the expected number of nested fields have
+//               been packed.  It is an error to call it too early, or
+//               too late.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+pop() {
+  if (_current_field != NULL && _num_nested_fields >= 0) {
+    // Oops, didn't pack or unpack enough values.
+    _pack_error = true;
+
+  } else if (_mode == M_unpack && _pop_marker != 0 && 
+             _unpack_p != _pop_marker) {
+    // Didn't unpack the right number of values.
+    _pack_error = true;
+  }
+
+  if (_stack == NULL) {
+    // Unbalanced pop().
+    _pack_error = true;
+
+  } else {
+    if (!_current_parent->validate_num_nested_fields(_current_field_index)) {
+      // Incorrect number of nested elements.
+      _pack_error = true;
+    }
+
+    if (_mode == M_pack || _mode == M_repack) {
+      size_t length_bytes = _current_parent->get_num_length_bytes();
+      if (length_bytes != 0) {
+        // Now go back and fill in the length of the array.
+        size_t length = _pack_data.get_length() - _push_marker - length_bytes;
+        if (length_bytes == 4) {
+          DCPackerInterface::do_pack_uint32
+            (_pack_data.get_rewrite_pointer(_push_marker, 4), length);
+        } else {
+          DCPackerInterface::validate_uint_limits(length, 16, _range_error);
+          DCPackerInterface::do_pack_uint16
+            (_pack_data.get_rewrite_pointer(_push_marker, 2), length);
+        }
+      }
+    }
+
+    _current_field = _current_parent;
+    _current_parent = _stack->_current_parent;
+    _current_field_index = _stack->_current_field_index;
+    _push_marker = _stack->_push_marker;
+    _pop_marker = _stack->_pop_marker;
+    _num_nested_fields = (_current_parent == NULL) ? 0 : _current_parent->get_num_nested_fields();
+
+    StackElement *next = _stack->_next;
+    delete _stack;
+    _stack = next;
+  }
+
+  advance();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::pack_default_value
+//       Access: Published
+//  Description: Adds the default value for the current element into
+//               the stream.  If no default has been set for the
+//               current element, creates a sensible default.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+pack_default_value() {
+  nassertv(_mode == M_pack || _mode == M_repack);
+  if (_current_field == NULL) {
+    _pack_error = true;
+  } else {
+    if (_current_field->pack_default_value(_pack_data, _pack_error)) {
+      advance();
+
+    } else {
+      // If the single field didn't know how to pack a default value,
+      // try packing nested fields.
+      push();
+      while (more_nested_fields()) {
+        pack_default_value();
+      }
+      pop();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::unpack_validate
+//       Access: Published
+//  Description: Internally unpacks the current numeric or string
+//               value and validates it against the type range limits,
+//               but does not return the value.  If the current field
+//               contains nested fields, validates all of them.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+unpack_validate() {
+  nassertv(_mode == M_unpack);
+  if (_current_field == NULL) {
+    _pack_error = true;
+
+  } else {
+    if (_current_field->unpack_validate(_unpack_data, _unpack_length, _unpack_p,
+                                        _pack_error, _range_error)) {
+      advance();
+    } else {
+      // If the single field couldn't be validated, try validating
+      // nested fields.
+      push();
+      while (more_nested_fields()) {
+        unpack_validate();
+      }
+      pop();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::unpack_skip
+//       Access: Published
+//  Description: Skips the current field without unpacking it and
+//               advances to the next field.  If the current field
+//               contains nested fields, skips all of them.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+unpack_skip() {
+  nassertv(_mode == M_unpack);
+  if (_current_field == NULL) {
+    _pack_error = true;
+
+  } else {
+    if (_current_field->unpack_skip(_unpack_data, _unpack_length, _unpack_p,
+                                    _pack_error)) {
+      advance();
+
+    } else {
+      // If the single field couldn't be skipped, try skipping nested fields.
+      push();
+      while (more_nested_fields()) {
+        unpack_skip();
+      }
+      pop();
+    }
+  }
+}
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::pack_object
+//       Access: Published
+//  Description: Packs the Python object of whatever type into the
+//               packer.  Each numeric object and string object maps
+//               to the corresponding pack_value() call; a tuple or
+//               sequence maps to a push() followed by all of the
+//               tuple's contents followed by a pop().
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+pack_object(PyObject *object) {
+  nassertv(_mode == M_pack || _mode == M_repack);
+
+  #ifdef USE_PYTHON_2_2_OR_EARLIER
+  if (PyInt_Check(object)) {
+  #else
+  if (PyLong_Check(object)) {
+    pack_int(PyLong_AsUnsignedLong(object));
+  } else if (PyInt_Check(object)) {
+  #endif
+    pack_int(PyInt_AS_LONG(object));
+  } else if (PyFloat_Check(object)) {
+    pack_double(PyFloat_AS_DOUBLE(object));
+  } else if (PyLong_Check(object)) {
+    pack_int64(PyLong_AsLongLong(object));
+  } else if (PyString_Check(object) || PyUnicode_Check(object)) {
+    char *buffer;
+    int length;
+    PyString_AsStringAndSize(object, &buffer, &length);
+    if (buffer) {
+      pack_string(string(buffer, length));
+    }
+  } else {
+    // For some reason, PySequence_Check() is incorrectly reporting
+    // that a class instance is a sequence, even if it doesn't provide
+    // __len__, so we double-check by testing for __len__ explicitly.
+    bool is_sequence = 
+      (PySequence_Check(object) != 0) &&
+      (PyObject_HasAttrString(object, "__len__") != 0);
+    bool is_instance = false;
+
+    const DCClass *dclass = NULL;
+    const DCPackerInterface *current_field = get_current_field();
+    if (current_field != (DCPackerInterface *)NULL) {
+      const DCClassParameter *class_param = get_current_field()->as_class_parameter();
+      if (class_param != (DCClassParameter *)NULL) {
+        dclass = class_param->get_class();
+        
+        if (dclass->has_class_def()) {
+          PyObject *class_def = dclass->get_class_def();
+          is_instance = (PyObject_IsInstance(object, dclass->get_class_def()) != 0);
+          Py_DECREF(class_def);
+        }
+      }
+    }
+
+    // If dclass is not NULL, the packer is expecting a class object.
+    // There are then two cases: (1) the user has supplied a matching
+    // class object, or (2) the user has supplied a sequence object.
+    // Unfortunately, it may be difficult to differentiate these two
+    // cases, since a class object may also be a sequence object.
+
+    // The rule to differentiate them is:
+
+    // (1) If the supplied class object is an instance of the expected
+    // class object, it is considered to be a class object.
+
+    // (2) Otherwise, if the supplied class object has a __len__()
+    // method (i.e. PySequence_Check() returns true), then it is
+    // considered to be a sequence.
+
+    // (3) Otherwise, it is considered to be a class object.
+
+    if (dclass != (DCClass *)NULL && (is_instance || !is_sequence)) {
+      // The supplied object is either an instance of the expected
+      // class object, or it is not a sequence--this is case (1) or
+      // (3).
+      pack_class_object(dclass, object);
+    } else if (is_sequence) {
+      // The supplied object is not an instance of the expected class
+      // object, but it is a sequence.  This is case (2).
+      push();
+      int size = PySequence_Size(object);
+      for (int i = 0; i < size; ++i) {
+        PyObject *element = PySequence_GetItem(object, i);
+        if (element != (PyObject *)NULL) {
+          pack_object(element);
+          Py_DECREF(element);
+        } else {
+          cerr << "Unable to extract item " << i << " from sequence.\n";
+        }
+      }
+      pop();
+    } else {
+      // The supplied object is not a sequence, and we weren't
+      // expecting a class parameter.  This is none of the above, an
+      // error.
+      ostringstream strm;
+      PyObject *str = PyObject_Str(object);
+      strm << "Don't know how to pack object: "
+           << PyString_AsString(str) << "\n";
+      Py_DECREF(str);
+      nassert_raise(strm.str());
+      _pack_error = true;
+    }
+  }
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::unpack_object
+//       Access: Published
+//  Description: Unpacks a Python object of the appropriate type from
+//               the stream for the current field.  This may be an
+//               integer or a string for a simple field object; if the
+//               current field represents a list of fields it will be
+//               a tuple.
+////////////////////////////////////////////////////////////////////
+PyObject *DCPacker::
+unpack_object() {
+  PyObject *object = NULL;
+
+  DCPackType pack_type = get_pack_type();
+
+  switch (pack_type) {
+  case PT_invalid:
+    object = Py_None;
+    unpack_skip();
+    break;
+
+  case PT_double:
+    {
+      double value = unpack_double();
+      object = PyFloat_FromDouble(value);
+    }
+    break;
+      
+  case PT_int:
+    {
+      int value = unpack_int();
+      object = PyInt_FromLong(value);
+    }
+    break;
+      
+  case PT_uint:
+    {
+      unsigned int value = unpack_uint();
+      if (value & 0x80000000) {
+        object = PyLong_FromUnsignedLong(value);
+      } else {
+        object = PyInt_FromLong(value);
+      }
+    }
+    break;
+      
+  case PT_int64:
+    {
+      PN_int64 value = unpack_int64();
+      object = PyLong_FromLongLong(value);
+    }
+    break;
+      
+  case PT_uint64:
+    {
+      PN_uint64 value = unpack_uint64();
+      object = PyLong_FromUnsignedLongLong(value);
+    }
+    break;
+
+  case PT_string:
+  case PT_blob:
+    {
+      string str;
+      unpack_string(str);
+      object = PyString_FromStringAndSize(str.data(), str.size());
+    }
+    break;
+
+  case PT_class:
+    {
+      const DCClassParameter *class_param = get_current_field()->as_class_parameter();
+      if (class_param != (DCClassParameter *)NULL) {
+        const DCClass *dclass = class_param->get_class();
+        if (dclass->has_class_def()) {
+          // If we know what kind of class object this is and it has a
+          // valid constructor, create the class object instead of
+          // just a tuple.
+          object = unpack_class_object(dclass);
+          break;
+        }
+      }
+    }
+    // If we don't know what kind of class object it is, or it doesn't
+    // have a constructor, fall through and make a tuple.
+  default:
+    {
+      // First, build up a list from the nested objects.
+      object = PyList_New(0);
+
+      push();
+      while (more_nested_fields()) {
+        PyObject *element = unpack_object();
+        PyList_Append(object, element);
+        Py_DECREF(element);
+      }
+      pop();
+
+      if (pack_type != PT_array) {
+        // For these other kinds of objects, we'll convert the list
+        // into a tuple.
+        PyObject *tuple = PyList_AsTuple(object);
+        Py_DECREF(object);
+        object = tuple;
+      }
+    }
+    break;
+  }
+
+  return object;
+}
+#endif  // HAVE_PYTHON
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::parse_and_pack
+//       Access: Published
+//  Description: Parses an object's value according to the DC file
+//               syntax (e.g. as a default value string) and packs it.
+//               Returns true on success, false on a parse error.
+////////////////////////////////////////////////////////////////////
+bool DCPacker::
+parse_and_pack(const string &formatted_object) {
+  istringstream strm(formatted_object);
+  return parse_and_pack(strm);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::parse_and_pack
+//       Access: Published
+//  Description: Parses an object's value according to the DC file
+//               syntax (e.g. as a default value string) and packs it.
+//               Returns true on success, false on a parse error.
+////////////////////////////////////////////////////////////////////
+bool DCPacker::
+parse_and_pack(istream &in) {
+  dc_init_parser_parameter_value(in, "parse_and_pack", *this);
+  dcyyparse();
+  dc_cleanup_parser();
+
+  bool parse_error = (dc_error_count() != 0);
+  if (parse_error) {
+    _parse_error = true;
+  }
+
+  return !parse_error;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::unpack_and_format
+//       Access: Published
+//  Description: Unpacks an object and formats its value into a syntax
+//               suitable for parsing in the dc file (e.g. as a
+//               default value), or as an input to parse_object.
+////////////////////////////////////////////////////////////////////
+string DCPacker::
+unpack_and_format(bool show_field_names) {
+  ostringstream strm;
+  unpack_and_format(strm, show_field_names);
+  return strm.str();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::unpack_and_format
+//       Access: Published
+//  Description: Unpacks an object and formats its value into a syntax
+//               suitable for parsing in the dc file (e.g. as a
+//               default value), or as an input to parse_object.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+unpack_and_format(ostream &out, bool show_field_names) {
+  DCPackType pack_type = get_pack_type();
+
+  if (show_field_names && !get_current_field_name().empty()) {
+    nassertv(_current_field != (DCPackerInterface *)NULL);
+    const DCField *field = _current_field->as_field();
+    if (field != (DCField *)NULL && 
+        field->as_parameter() != (DCParameter *)NULL) {
+      out << field->get_name() << " = ";
+    }
+  }
+
+  switch (pack_type) {
+  case PT_invalid:
+    out << "<invalid>";
+    break;
+
+  case PT_double:
+    out << unpack_double();
+    break;
+      
+  case PT_int:
+    out << unpack_int();
+    break;
+      
+  case PT_uint:
+    out << unpack_uint();
+    break;
+      
+  case PT_int64:
+    out << unpack_int64();
+    break;
+      
+  case PT_uint64:
+    out << unpack_uint64();
+    break;
+
+  case PT_string:
+    enquote_string(out, '"', unpack_string());
+    break;
+
+  case PT_blob:
+    output_hex_string(out, unpack_literal_value());
+    break;
+
+  default:
+    {
+      switch (pack_type) {
+      case PT_array:
+        out << '[';
+        break;
+
+      case PT_field:
+      case PT_switch:
+        out << '(';
+        break;
+
+      case PT_class:
+      default:
+        out << '{';
+        break;
+      }
+
+      push();
+      while (more_nested_fields() && !had_pack_error()) {
+        unpack_and_format(out, show_field_names);
+
+        if (more_nested_fields()) {
+          out << ", ";
+        }
+      }
+      pop();
+
+      switch (pack_type) {
+      case PT_array:
+        out << ']';
+        break;
+
+      case PT_field:
+      case PT_switch:
+        out << ')';
+        break;
+
+      case PT_class:
+      default:
+        out << '}';
+        break;
+      }
+    }
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::enquote_string
+//       Access: Public, Static
+//  Description: Outputs the indicated string within quotation marks.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+enquote_string(ostream &out, char quote_mark, const string &str) {
+  out << quote_mark;
+  for (string::const_iterator pi = str.begin();
+       pi != str.end();
+       ++pi) {
+    if ((*pi) == quote_mark || (*pi) == '\\') {
+      out << '\\' << (*pi);
+
+    } else if (!isprint(*pi)) {
+      char buffer[10];
+      sprintf(buffer, "%02x", (unsigned char)(*pi));
+      out << "\\x" << buffer;
+
+    } else {
+      out << (*pi);
+    }
+  }
+  out << quote_mark;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::output_hex_string
+//       Access: Public, Static
+//  Description: Outputs the indicated string as a hex constant.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+output_hex_string(ostream &out, const string &str) {
+  out << '<';
+  for (string::const_iterator pi = str.begin();
+       pi != str.end();
+       ++pi) {
+    char buffer[10];
+    sprintf(buffer, "%02x", (unsigned char)(*pi));
+    out << buffer;
+  }
+  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 DCSwitchParameter *switch_parameter) {
+  // 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 = switch_parameter->apply_switch
+      (data + _push_marker, _pack_data.get_length() - _push_marker);
+
+  } else if (_mode == M_unpack) {
+    new_parent = switch_parameter->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.
+    _range_error = true;
+    return;
+  }
+
+  _last_switch = switch_parameter;
+
+  // 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
+//  Description: Resets the data structures after a pack or unpack
+//               sequence.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+clear() {
+  clear_stack();
+  _current_field = NULL;
+  _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);
+    _live_catalog = NULL;
+  }
+  _catalog = NULL;
+  _root = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::clear_stack
+//       Access: Private
+//  Description: Empties the stack.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+clear_stack() {
+  while (_stack != (StackElement *)NULL) {
+    StackElement *next = _stack->_next;
+    delete _stack;
+    _stack = next;
+  }
+}
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::pack_class_object
+//       Access: Private
+//  Description: Given that the current element is a ClassParameter
+//               for a Python class object, try to extract the
+//               appropriate values from the class object and pack in.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+pack_class_object(const DCClass *dclass, PyObject *object) {
+  push();
+  while (more_nested_fields() && !_pack_error) {
+    const DCField *field = get_current_field()->as_field();
+    nassertv(field != (DCField *)NULL);
+    get_class_element(dclass, object, field);
+  }
+  pop();
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::unpack_class_object
+//       Access: Private
+//  Description: Given that the current element is a ClassParameter
+//               for a Python class for which we have a valid
+//               constructor, unpack it and fill in its values.
+////////////////////////////////////////////////////////////////////
+PyObject *DCPacker::
+unpack_class_object(const DCClass *dclass) {
+  PyObject *class_def = dclass->get_class_def();
+  nassertr(class_def != (PyObject *)NULL, NULL);
+
+  PyObject *object = NULL;
+
+  if (!dclass->has_constructor()) {
+    // If the class uses a default constructor, go ahead and create
+    // the Python object for it now.
+    object = PyObject_CallObject(class_def, NULL);
+    if (object == (PyObject *)NULL) {
+      return NULL;
+    }
+  }
+
+  push();
+  if (object == (PyObject *)NULL && more_nested_fields()) {
+    // The first nested field will be the constructor.
+    const DCField *field = get_current_field()->as_field();
+    nassertr(field != (DCField *)NULL, object);
+    nassertr(field == dclass->get_constructor(), object);
+
+    set_class_element(class_def, object, field);
+
+    // By now, the object should have been constructed.
+    if (object == (PyObject *)NULL) {
+      return NULL;
+    }
+  }
+  while (more_nested_fields()) {
+    const DCField *field = get_current_field()->as_field();
+    nassertr(field != (DCField *)NULL, object);
+
+    set_class_element(class_def, object, field);
+  }
+  pop();
+
+  return object;
+}
+#endif  // HAVE_PYTHON
+
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::set_class_element
+//       Access: Private
+//  Description: Unpacks the current element and stuffs it on the
+//               Python class object in whatever way is appropriate.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+set_class_element(PyObject *class_def, PyObject *&object, 
+                  const DCField *field) {
+  string field_name = field->get_name();
+  DCPackType pack_type = get_pack_type();
+
+  if (field_name.empty()) {
+    switch (pack_type) {
+    case PT_class:
+    case PT_switch:
+      // If the field has no name, but it is one of these container
+      // objects, we want to unpack its nested objects directly into
+      // the class.
+      push();
+      while (more_nested_fields()) {
+        const DCField *field = get_current_field()->as_field();
+        nassertv(field != (DCField *)NULL);
+        nassertv(object != (PyObject *)NULL);
+        set_class_element(class_def, object, field);
+      }
+      pop();
+      break;
+
+    default:
+      // Otherwise, we just skip over the field.
+      unpack_skip();
+    }
+
+  } else {
+    // If the field does have a name, we will want to store it on the
+    // class, either by calling a method (for a PT_field pack_type) or
+    // by setting a value (for any other kind of pack_type).
+
+    PyObject *element = unpack_object();
+
+    if (pack_type == PT_field) {
+      if (object == (PyObject *)NULL) {
+        // If the object hasn't been constructed yet, assume this is
+        // the constructor.
+        object = PyObject_CallObject(class_def, element);
+
+      } else {
+        if (PyObject_HasAttrString(object, (char *)field_name.c_str())) {
+          PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
+          if (func != (PyObject *)NULL) {
+            PyObject *result = PyObject_CallObject(func, element);
+            Py_XDECREF(result);
+            Py_DECREF(func);
+          }
+        }
+      }
+      
+    } else {
+      nassertv(object != (PyObject *)NULL);
+      PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
+    }
+
+    Py_DECREF(element);
+  }
+}
+#endif  // HAVE_PYTHON
+
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCPacker::get_class_element
+//       Access: Private
+//  Description: Gets the current element from the Python object and
+//               packs it.
+////////////////////////////////////////////////////////////////////
+void DCPacker::
+get_class_element(const DCClass *dclass, PyObject *object, 
+                  const DCField *field) {
+  string field_name = field->get_name();
+  DCPackType pack_type = get_pack_type();
+
+  if (field_name.empty()) {
+    switch (pack_type) {
+    case PT_class:
+    case PT_switch:
+      // If the field has no name, but it is one of these container
+      // objects, we want to get its nested objects directly from
+      // the class.
+      push();
+      while (more_nested_fields() && !_pack_error) {
+        const DCField *field = get_current_field()->as_field();
+        nassertv(field != (DCField *)NULL);
+        get_class_element(dclass, object, field);
+      }
+      pop();
+      break;
+
+    default:
+      // Otherwise, we just pack the default value.
+      pack_default_value();
+    }
+
+  } else {
+    // If the field does have a name, we will want to get it from the
+    // class and pack it.  It just so happens that there's already a
+    // method that does this on DCClass.
+
+    if (!dclass->pack_required_field(*this, object, field)) {
+      _pack_error = true;
+    }
+  }
+}
+#endif  // HAVE_PYTHON

+ 536 - 528
direct/src/distributed/cConnectionRepository.cxx

@@ -1,528 +1,536 @@
-// Filename: cConnectionRepository.cxx
-// Created by:  drose (17May04)
-//
-////////////////////////////////////////////////////////////////////
-//
-// 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 "cConnectionRepository.h"
-#include "dcmsgtypes.h"
-#include "dcClass.h"
-#include "dcPacker.h"
-
-#include "config_distributed.h"
-#include "httpChannel.h"
-#include "urlSpec.h"
-#include "datagramIterator.h"
-#include "throw_event.h"
-#include "pStatTimer.h"
-
-
-const string CConnectionRepository::_overflow_event_name = "CRDatagramOverflow";
-
-#ifndef CPPPARSER
-PStatCollector CConnectionRepository::_update_pcollector("App:Show code:readerPollTask:Update");
-#endif  // CPPPARSER
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::Constructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-CConnectionRepository::
-CConnectionRepository() :
-#ifdef HAVE_PYTHON
-  _python_repository(NULL),
-#endif
-#ifdef HAVE_SSL
-  _http_conn(NULL),
-#endif
-#ifdef HAVE_NSPR
-  _cw(&_qcm, 0),
-  _qcr(&_qcm, 0),
-#endif
-  _client_datagram(true),
-  _simulated_disconnect(false),
-  _verbose(distributed_cat.is_spam()),
-  _msg_channel(0),
-  _msg_sender(0),
-  _sec_code(0),
-  _msg_type(0)
-{
-#if defined(HAVE_NSPR) && defined(SIMULATE_NETWORK_DELAY)
-  if (min_lag != 0.0 || max_lag != 0.0) {
-    _qcr.start_delay(min_lag, max_lag);
-  }
-#endif
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::Destructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-CConnectionRepository::
-~CConnectionRepository() {
-  disconnect();
-}
-
-#ifdef HAVE_SSL
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::set_connection_http
-//       Access: Published
-//  Description: Once a connection has been established via the HTTP
-//               interface, gets the connection and uses it.  The
-//               supplied HTTPChannel object must have a connection
-//               available via get_connection().
-////////////////////////////////////////////////////////////////////
-void CConnectionRepository::
-set_connection_http(HTTPChannel *channel) {
-  disconnect();
-  nassertv(channel->is_connection_ready());
-  _http_conn = channel->get_connection();
-}
-#endif  // HAVE_SSL
-
-
-#ifdef HAVE_NSPR
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::try_connect_nspr
-//       Access: Published
-//  Description: Uses NSPR to try to connect to the server and port
-//               named in the indicated URL.  Returns true if
-//               successful, false otherwise.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-try_connect_nspr(const URLSpec &url) {
-  disconnect();
-
-  _nspr_conn = 
-    _qcm.open_TCP_client_connection(url.get_server(), url.get_port(),
-                                    game_server_timeout_ms);
-
-  if (_nspr_conn != (Connection *)NULL) {
-    _nspr_conn->set_no_delay(true);
-    _qcr.add_connection(_nspr_conn);
-    return true;
-  }
-
-  return false;
-}
-#endif  // HAVE_NSPR
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::check_datagram
-//       Access: Published
-//  Description: Returns true if a new datagram is available, false
-//               otherwise.  If the return value is true, the new
-//               datagram may be retrieved via get_datagram(), or
-//               preferably, with get_datagram_iterator() and
-//               get_msg_type().
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-check_datagram() {
-  if (_simulated_disconnect) {
-    return false;
-  }
-  while (do_check_datagram()) {
-#ifndef NDEBUG
-    if (get_verbose()) {
-      describe_message(nout, "receive ", _dg.get_message());
-    }
-#endif  // NDEBUG
-
-    // Start breaking apart the datagram.
-    _di = DatagramIterator(_dg);
-
-    if (!_client_datagram) {
-      _msg_channel = _di.get_uint64();
-      _msg_sender = _di.get_uint64();
-      _sec_code = _di.get_uint8();
-      
-#ifdef HAVE_PYTHON
-      // For now, we need to stuff this field onto the Python
-      // structure, to support legacy code that expects to find it
-      // there.
-      if (_python_repository != (PyObject *)NULL) {
-        PyObject *value = PyLong_FromUnsignedLongLong(_msg_sender);
-        PyObject_SetAttrString(_python_repository, "msgSender", value);
-        Py_DECREF(value);
-      }
-#endif  // HAVE_PYTHON
-    }
-
-    _msg_type = _di.get_uint16();
-    // Is this a message that we can process directly?
-    switch (_msg_type) {
-#ifdef HAVE_PYTHON
-    case CLIENT_OBJECT_UPDATE_FIELD:
-    case STATESERVER_OBJECT_UPDATE_FIELD:
-      if (!handle_update_field()) {
-        return false;
-      }
-      break;
-#endif  // HAVE_PYTHON
-
-    default:
-      // Some unknown message; let the caller deal with it.
-      return true;
-    }
-  }
-
-  // No datagrams available.
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::is_connected
-//       Access: Published
-//  Description: Returns true if the connection to the gameserver is
-//               established and still good, false if we are not
-//               connected.  A false value means either (a) we never
-//               successfully connected, (b) we explicitly called
-//               disconnect(), or (c) we were connected, but the
-//               connection was spontaneously lost.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-is_connected() {
-#ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    if (_qcm.reset_connection_available()) {
-      PT(Connection) reset_connection;
-      if (_qcm.get_reset_connection(reset_connection)) {
-        _qcm.close_connection(reset_connection);
-        if (reset_connection == _nspr_conn) {
-          // Whoops, lost our connection.
-          _nspr_conn = NULL;
-          return false;
-        }
-      }
-    }
-    return true;
-  }
-#endif  // HAVE_NSPR
-
-#ifdef HAVE_SSL
-  if (_http_conn) {
-    if (!_http_conn->is_closed()) {
-      return true;
-    }
-
-    // Connection lost.
-    delete _http_conn;
-    _http_conn = NULL;
-  }
-#endif  // HAVE_SSL
-
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::send_datagram
-//       Access: Published
-//  Description: Queues the indicated datagram for sending to the
-//               server.  It may not get send immediately if
-//               collect_tcp is in effect; call flush() to guarantee
-//               it is sent now.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-send_datagram(const Datagram &dg) {
-  if (_simulated_disconnect) {
-    distributed_cat.warning()
-      << "Unable to send datagram during simulated disconnect.\n";
-    return false;
-  }
-
-#ifndef NDEBUG
-  if (get_verbose()) {
-    describe_message(nout, "send ", dg.get_message());
-  }
-#endif  // NDEBUG
-
-#ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    _cw.send(dg, _nspr_conn);
-    return true;
-  }
-#endif  // HAVE_NSPR
-
-#ifdef HAVE_SSL
-  if (_http_conn) {
-    if (!_http_conn->send_datagram(dg)) {
-      distributed_cat.warning()
-        << "Could not send datagram.\n";
-      return false;
-    }
-
-    return true;
-  }
-#endif  // HAVE_SSL
-
-  distributed_cat.warning()
-    << "Unable to send datagram after connection is closed.\n";
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::consider_flush
-//       Access: Published
-//  Description: Sends the most recently queued data if enough time
-//               has elapsed.  This only has meaning if
-//               set_collect_tcp() has been set to true.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-consider_flush() {
-  if (_simulated_disconnect) {
-    return false;
-  }
-
-#ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    return _nspr_conn->consider_flush();
-  }
-#endif  // HAVE_NSPR
-
-#ifdef HAVE_SSL
-  if (_http_conn) {
-    return _http_conn->consider_flush();
-  }
-#endif  // HAVE_SSL
-
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::flush
-//       Access: Published
-//  Description: Sends the most recently queued data now.  This only
-//               has meaning if set_collect_tcp() has been set to
-//               true.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-flush() {
-  if (_simulated_disconnect) {
-    return false;
-  }
-
-#ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    return _nspr_conn->flush();
-  }
-#endif  // HAVE_NSPR
-
-#ifdef HAVE_SSL
-  if (_http_conn) {
-    return _http_conn->flush();
-  }
-#endif  // HAVE_SSL
-
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::disconnect
-//       Access: Published
-//  Description: Closes the connection to the server.
-////////////////////////////////////////////////////////////////////
-void CConnectionRepository::
-disconnect() {
-#ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    _qcm.close_connection(_nspr_conn);
-    _nspr_conn = NULL;
-  }
-#endif  // HAVE_NSPR
-
-#ifdef HAVE_SSL
-  if (_http_conn) {
-    _http_conn->close();
-    delete _http_conn;
-    _http_conn = NULL;
-  }
-#endif  // HAVE_SSL
-
-  _simulated_disconnect = false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::do_check_datagram
-//       Access: Private
-//  Description: The private implementation of check_datagram(), this
-//               gets one datagram if it is available.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-do_check_datagram() {
-#ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    _nspr_conn->consider_flush();
-    if (_qcr.get_overflow_flag()) {
-      throw_event(get_overflow_event_name());
-      _qcr.reset_overflow_flag();
-    }
-    return (_qcr.data_available() && _qcr.get_data(_dg));
-  }
-#endif  // HAVE_NSPR
-
-#ifdef HAVE_SSL
-  if (_http_conn) {
-    _http_conn->consider_flush();
-    return _http_conn->receive_datagram(_dg);
-  }
-#endif  // HAVE_SSL
-
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::handle_update_field
-//       Access: Private
-//  Description: Directly handles an update message on a field.
-//               Python never touches the datagram; it just gets its
-//               distributed method called with the appropriate
-//               parameters.  Returns true if everything is ok, false
-//               if there was an error processing the field's update
-//               method.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-handle_update_field() {
-#ifdef HAVE_PYTHON
-  PStatTimer timer(_update_pcollector);
-  int do_id = _di.get_uint32();
-  if (_python_repository != (PyObject *)NULL) {
-    PyObject *doId2do =
-      PyObject_GetAttrString(_python_repository, "doId2do");
-    nassertr(doId2do != NULL, false);
-
-    PyObject *doId = PyInt_FromLong(do_id);
-    PyObject *distobj = PyDict_GetItem(doId2do, doId);
-    Py_DECREF(doId);
-    Py_DECREF(doId2do);
-
-    if (distobj != NULL) {
-      PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
-      nassertr(dclass_obj != NULL, false);
-
-      PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
-      Py_DECREF(dclass_obj);
-      nassertr(dclass_this != NULL, false);
-
-      DCClass *dclass = (DCClass *)PyInt_AsLong(dclass_this);
-      Py_DECREF(dclass_this);
-
-      // It's a good idea to ensure the reference count to distobj is
-      // raised while we call the update method--otherwise, the update
-      // method might get into trouble if it tried to delete the
-      // object from the doId2do map.
-      Py_INCREF(distobj);
-      dclass->receive_update(distobj, _di); 
-      Py_DECREF(distobj);
-      
-      if (PyErr_Occurred()) {
-        return false;
-      }
-    }
-  }
-#endif  // HAVE_PYTHON  
-
-  return true;
-}
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::describe_message
-//       Access: Private
-//  Description: Unpacks the message and reformats it for user
-//               consumption, writing a description on the indicated
-//               output stream.
-////////////////////////////////////////////////////////////////////
-void CConnectionRepository::
-describe_message(ostream &out, const string &prefix, 
-                 const string &message_data) const {
-  DCPacker packer;
-  packer.set_unpack_data(message_data);
-  CHANNEL_TYPE do_id;
-  int msg_type;
-  bool is_update = false;
-
-  if (!_client_datagram) {
-    packer.RAW_UNPACK_CHANNEL();  // msg_channel
-    packer.RAW_UNPACK_CHANNEL();  // msg_sender
-    packer.raw_unpack_uint8();    // sec_code
-    msg_type = packer.raw_unpack_uint16();
-    is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD);
-    
-  } else {
-    msg_type = packer.raw_unpack_uint16();
-    is_update = (msg_type == CLIENT_OBJECT_UPDATE_FIELD);
-  }
-
-  if (!is_update) {
-    out << prefix << "message " << msg_type << "\n";
-
-  } else {
-    // It's an update message.  Figure out what dclass the object is
-    // based on its doId, so we can decode the rest of the message.
-    do_id = packer.raw_unpack_uint32();
-    DCClass *dclass = NULL;
-
-#ifdef HAVE_PYTHON
-    if (_python_repository != (PyObject *)NULL) {
-      PyObject *doId2do =
-        PyObject_GetAttrString(_python_repository, "doId2do");
-      nassertv(doId2do != NULL);
-
-      PyObject *doId = PyInt_FromLong(do_id);
-      PyObject *distobj = PyDict_GetItem(doId2do, doId);
-      Py_DECREF(doId);
-      Py_DECREF(doId2do);
-
-      if (distobj != NULL) {
-        PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
-        nassertv(dclass_obj != NULL);
-
-        PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
-        Py_DECREF(dclass_obj);
-        nassertv(dclass_this != NULL);
-        
-        dclass = (DCClass *)PyInt_AsLong(dclass_this);
-        Py_DECREF(dclass_this);
-      }
-    }
-#endif  // HAVE_PYTHON  
-
-    int field_id = packer.raw_unpack_uint16();
-
-    if (dclass == (DCClass *)NULL) {
-      out << prefix << "update for unknown object " << do_id 
-          << ", field " << field_id << "\n";
-
-    } else {
-      out << prefix << "update for " << dclass->get_name()
-          << " " << do_id << ": ";
-      DCField *field = dclass->get_field_by_index(field_id);
-      if (field == (DCField *)NULL) {
-        out << "unknown field " << field_id << "\n";
-        
-      } else {
-        out << field->get_name();
-        packer.begin_unpack(field);
-        packer.unpack_and_format(out);
-        packer.end_unpack();
-        out << "\n";
-      }
-    }
-  }
-}
-#endif  // NDEBUG
-
+// Filename: cConnectionRepository.cxx
+// Created by:  drose (17May04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "cConnectionRepository.h"
+#include "dcmsgtypes.h"
+#include "dcClass.h"
+#include "dcPacker.h"
+
+#include "config_distributed.h"
+#include "httpChannel.h"
+#include "urlSpec.h"
+#include "datagramIterator.h"
+#include "throw_event.h"
+#include "pStatTimer.h"
+
+
+const string CConnectionRepository::_overflow_event_name = "CRDatagramOverflow";
+
+#ifndef CPPPARSER
+PStatCollector CConnectionRepository::_update_pcollector("App:Show code:readerPollTask:Update");
+#endif  // CPPPARSER
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CConnectionRepository::
+CConnectionRepository() :
+#ifdef HAVE_PYTHON
+  _python_repository(NULL),
+#endif
+#ifdef HAVE_SSL
+  _http_conn(NULL),
+#endif
+#ifdef HAVE_NSPR
+  _cw(&_qcm, 0),
+  _qcr(&_qcm, 0),
+#endif
+  _client_datagram(true),
+  _simulated_disconnect(false),
+  _verbose(distributed_cat.is_spam()),
+  _msg_channel(0),
+  _msg_sender(0),
+  _sec_code(0),
+  _msg_type(0)
+{
+#if defined(HAVE_NSPR) && defined(SIMULATE_NETWORK_DELAY)
+  if (min_lag != 0.0 || max_lag != 0.0) {
+    _qcr.start_delay(min_lag, max_lag);
+  }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CConnectionRepository::
+~CConnectionRepository() {
+  disconnect();
+}
+
+#ifdef HAVE_SSL
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::set_connection_http
+//       Access: Published
+//  Description: Once a connection has been established via the HTTP
+//               interface, gets the connection and uses it.  The
+//               supplied HTTPChannel object must have a connection
+//               available via get_connection().
+////////////////////////////////////////////////////////////////////
+void CConnectionRepository::
+set_connection_http(HTTPChannel *channel) {
+  disconnect();
+  nassertv(channel->is_connection_ready());
+  _http_conn = channel->get_connection();
+}
+#endif  // HAVE_SSL
+
+
+#ifdef HAVE_NSPR
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::try_connect_nspr
+//       Access: Published
+//  Description: Uses NSPR to try to connect to the server and port
+//               named in the indicated URL.  Returns true if
+//               successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+try_connect_nspr(const URLSpec &url) {
+  disconnect();
+
+  _nspr_conn = 
+    _qcm.open_TCP_client_connection(url.get_server(), url.get_port(),
+                                    game_server_timeout_ms);
+
+  if (_nspr_conn != (Connection *)NULL) {
+    _nspr_conn->set_no_delay(true);
+    _qcr.add_connection(_nspr_conn);
+    return true;
+  }
+
+  return false;
+}
+#endif  // HAVE_NSPR
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::check_datagram
+//       Access: Published
+//  Description: Returns true if a new datagram is available, false
+//               otherwise.  If the return value is true, the new
+//               datagram may be retrieved via get_datagram(), or
+//               preferably, with get_datagram_iterator() and
+//               get_msg_type().
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+check_datagram() {
+  if (_simulated_disconnect) {
+    return false;
+  }
+  while (do_check_datagram()) {
+#ifndef NDEBUG
+    if (get_verbose()) {
+      describe_message(nout, "receive ", _dg.get_message());
+    }
+#endif  // NDEBUG
+
+    // Start breaking apart the datagram.
+    _di = DatagramIterator(_dg);
+
+    if (!_client_datagram) {
+      _msg_channel = _di.get_uint64();
+      _msg_sender = _di.get_uint64();
+      _sec_code = _di.get_uint8();
+      
+#ifdef HAVE_PYTHON
+      // For now, we need to stuff this field onto the Python
+      // structure, to support legacy code that expects to find it
+      // there.
+      if (_python_repository != (PyObject *)NULL) {
+        PyObject *value = PyLong_FromUnsignedLongLong(_msg_sender);
+        PyObject_SetAttrString(_python_repository, "msgSender", value);
+        Py_DECREF(value);
+      }
+#endif  // HAVE_PYTHON
+    }
+
+    _msg_type = _di.get_uint16();
+    // Is this a message that we can process directly?
+    switch (_msg_type) {
+#ifdef HAVE_PYTHON
+    case CLIENT_OBJECT_UPDATE_FIELD:
+    case STATESERVER_OBJECT_UPDATE_FIELD:
+      if (!handle_update_field()) {
+        return false;
+      }
+      break;
+#endif  // HAVE_PYTHON
+
+    default:
+      // Some unknown message; let the caller deal with it.
+      return true;
+    }
+  }
+
+  // No datagrams available.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::is_connected
+//       Access: Published
+//  Description: Returns true if the connection to the gameserver is
+//               established and still good, false if we are not
+//               connected.  A false value means either (a) we never
+//               successfully connected, (b) we explicitly called
+//               disconnect(), or (c) we were connected, but the
+//               connection was spontaneously lost.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+is_connected() {
+#ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    if (_qcm.reset_connection_available()) {
+      PT(Connection) reset_connection;
+      if (_qcm.get_reset_connection(reset_connection)) {
+        _qcm.close_connection(reset_connection);
+        if (reset_connection == _nspr_conn) {
+          // Whoops, lost our connection.
+          _nspr_conn = NULL;
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_SSL
+  if (_http_conn) {
+    if (!_http_conn->is_closed()) {
+      return true;
+    }
+
+    // Connection lost.
+    delete _http_conn;
+    _http_conn = NULL;
+  }
+#endif  // HAVE_SSL
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::send_datagram
+//       Access: Published
+//  Description: Queues the indicated datagram for sending to the
+//               server.  It may not get send immediately if
+//               collect_tcp is in effect; call flush() to guarantee
+//               it is sent now.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+send_datagram(const Datagram &dg) {
+  if (_simulated_disconnect) {
+    distributed_cat.warning()
+      << "Unable to send datagram during simulated disconnect.\n";
+    return false;
+  }
+
+#ifndef NDEBUG
+  if (get_verbose()) {
+    describe_message(nout, "send ", dg.get_message());
+  }
+#endif  // NDEBUG
+
+#ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    _cw.send(dg, _nspr_conn);
+    return true;
+  }
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_SSL
+  if (_http_conn) {
+    if (!_http_conn->send_datagram(dg)) {
+      distributed_cat.warning()
+        << "Could not send datagram.\n";
+      return false;
+    }
+
+    return true;
+  }
+#endif  // HAVE_SSL
+
+  distributed_cat.warning()
+    << "Unable to send datagram after connection is closed.\n";
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::consider_flush
+//       Access: Published
+//  Description: Sends the most recently queued data if enough time
+//               has elapsed.  This only has meaning if
+//               set_collect_tcp() has been set to true.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+consider_flush() {
+  if (_simulated_disconnect) {
+    return false;
+  }
+
+#ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    return _nspr_conn->consider_flush();
+  }
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_SSL
+  if (_http_conn) {
+    return _http_conn->consider_flush();
+  }
+#endif  // HAVE_SSL
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::flush
+//       Access: Published
+//  Description: Sends the most recently queued data now.  This only
+//               has meaning if set_collect_tcp() has been set to
+//               true.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+flush() {
+  if (_simulated_disconnect) {
+    return false;
+  }
+
+#ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    return _nspr_conn->flush();
+  }
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_SSL
+  if (_http_conn) {
+    return _http_conn->flush();
+  }
+#endif  // HAVE_SSL
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::disconnect
+//       Access: Published
+//  Description: Closes the connection to the server.
+////////////////////////////////////////////////////////////////////
+void CConnectionRepository::
+disconnect() {
+#ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    _qcm.close_connection(_nspr_conn);
+    _nspr_conn = NULL;
+  }
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_SSL
+  if (_http_conn) {
+    _http_conn->close();
+    delete _http_conn;
+    _http_conn = NULL;
+  }
+#endif  // HAVE_SSL
+
+  _simulated_disconnect = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::do_check_datagram
+//       Access: Private
+//  Description: The private implementation of check_datagram(), this
+//               gets one datagram if it is available.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+do_check_datagram() {
+#ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    _nspr_conn->consider_flush();
+    if (_qcr.get_overflow_flag()) {
+      throw_event(get_overflow_event_name());
+      _qcr.reset_overflow_flag();
+    }
+    return (_qcr.data_available() && _qcr.get_data(_dg));
+  }
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_SSL
+  if (_http_conn) {
+    _http_conn->consider_flush();
+    return _http_conn->receive_datagram(_dg);
+  }
+#endif  // HAVE_SSL
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::handle_update_field
+//       Access: Private
+//  Description: Directly handles an update message on a field.
+//               Python never touches the datagram; it just gets its
+//               distributed method called with the appropriate
+//               parameters.  Returns true if everything is ok, false
+//               if there was an error processing the field's update
+//               method.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+handle_update_field() {
+#ifdef HAVE_PYTHON
+  PStatTimer timer(_update_pcollector);
+  unsigned int do_id = _di.get_uint32();
+  if (_python_repository != (PyObject *)NULL) {
+    PyObject *doId2do =
+      PyObject_GetAttrString(_python_repository, "doId2do");
+    nassertr(doId2do != NULL, false);
+
+    #ifdef USE_PYTHON_2_2_OR_EARLIER
+    PyObject *doId = PyInt_FromLong(do_id);
+    #else
+    PyObject *doId = PyLong_FromUnsignedLong(do_id);
+    #endif
+    PyObject *distobj = PyDict_GetItem(doId2do, doId);
+    Py_DECREF(doId);
+    Py_DECREF(doId2do);
+
+    if (distobj != NULL) {
+      PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
+      nassertr(dclass_obj != NULL, false);
+
+      PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
+      Py_DECREF(dclass_obj);
+      nassertr(dclass_this != NULL, false);
+
+      DCClass *dclass = (DCClass *)PyInt_AsLong(dclass_this);
+      Py_DECREF(dclass_this);
+
+      // It's a good idea to ensure the reference count to distobj is
+      // raised while we call the update method--otherwise, the update
+      // method might get into trouble if it tried to delete the
+      // object from the doId2do map.
+      Py_INCREF(distobj);
+      dclass->receive_update(distobj, _di); 
+      Py_DECREF(distobj);
+      
+      if (PyErr_Occurred()) {
+        return false;
+      }
+    }
+  }
+#endif  // HAVE_PYTHON  
+
+  return true;
+}
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::describe_message
+//       Access: Private
+//  Description: Unpacks the message and reformats it for user
+//               consumption, writing a description on the indicated
+//               output stream.
+////////////////////////////////////////////////////////////////////
+void CConnectionRepository::
+describe_message(ostream &out, const string &prefix, 
+                 const string &message_data) const {
+  DCPacker packer;
+  packer.set_unpack_data(message_data);
+  CHANNEL_TYPE do_id;
+  int msg_type;
+  bool is_update = false;
+
+  if (!_client_datagram) {
+    packer.RAW_UNPACK_CHANNEL();  // msg_channel
+    packer.RAW_UNPACK_CHANNEL();  // msg_sender
+    packer.raw_unpack_uint8();    // sec_code
+    msg_type = packer.raw_unpack_uint16();
+    is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD);
+    
+  } else {
+    msg_type = packer.raw_unpack_uint16();
+    is_update = (msg_type == CLIENT_OBJECT_UPDATE_FIELD);
+  }
+
+  if (!is_update) {
+    out << prefix << "message " << msg_type << "\n";
+
+  } else {
+    // It's an update message.  Figure out what dclass the object is
+    // based on its doId, so we can decode the rest of the message.
+    do_id = packer.raw_unpack_uint32();
+    DCClass *dclass = NULL;
+
+#ifdef HAVE_PYTHON
+    if (_python_repository != (PyObject *)NULL) {
+      PyObject *doId2do =
+        PyObject_GetAttrString(_python_repository, "doId2do");
+      nassertv(doId2do != NULL);
+
+      #ifdef USE_PYTHON_2_2_OR_EARLIER
+      PyObject *doId = PyInt_FromLong(do_id);
+      #else
+      PyObject *doId = PyLong_FromUnsignedLong(do_id);
+      #endif
+      PyObject *distobj = PyDict_GetItem(doId2do, doId);
+      Py_DECREF(doId);
+      Py_DECREF(doId2do);
+
+      if (distobj != NULL) {
+        PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
+        nassertv(dclass_obj != NULL);
+
+        PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
+        Py_DECREF(dclass_obj);
+        nassertv(dclass_this != NULL);
+        
+        dclass = (DCClass *)PyInt_AsLong(dclass_this);
+        Py_DECREF(dclass_this);
+      }
+    }
+#endif  // HAVE_PYTHON  
+
+    int field_id = packer.raw_unpack_uint16();
+
+    if (dclass == (DCClass *)NULL) {
+      out << prefix << "update for unknown object " << do_id 
+          << ", field " << field_id << "\n";
+
+    } else {
+      out << prefix << "update for " << dclass->get_name()
+          << " " << do_id << ": ";
+      DCField *field = dclass->get_field_by_index(field_id);
+      if (field == (DCField *)NULL) {
+        out << "unknown field " << field_id << "\n";
+        
+      } else {
+        out << field->get_name();
+        packer.begin_unpack(field);
+        packer.unpack_and_format(out);
+        packer.end_unpack();
+        out << "\n";
+      }
+    }
+  }
+}
+#endif  // NDEBUG
+

+ 650 - 637
dtool/src/interrogate/interfaceMakerPythonObj.cxx

@@ -1,637 +1,650 @@
-// Filename: interfaceMakerPythonObj.cxx
-// Created by:  drose (19Sep01)
-//
-////////////////////////////////////////////////////////////////////
-//
-// 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 "interfaceMakerPythonObj.h"
-#include "interrogateBuilder.h"
-#include "interrogate.h"
-#include "functionRemap.h"
-#include "parameterRemapUnchanged.h"
-#include "typeManager.h"
-#include "functionWriterPtrFromPython.h"
-#include "functionWriterPtrToPython.h"
-
-#include "interrogateDatabase.h"
-#include "interrogateType.h"
-#include "interrogateFunction.h"
-#include "cppFunctionType.h"
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-InterfaceMakerPythonObj::
-InterfaceMakerPythonObj(InterrogateModuleDef *def) :
-  InterfaceMakerPython(def)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::Destructor
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-InterfaceMakerPythonObj::
-~InterfaceMakerPythonObj() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::write_prototypes
-//       Access: Public, Virtual
-//  Description: Generates the list of function prototypes
-//               corresponding to the functions that will be output in
-//               write_functions().
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonObj::
-write_prototypes(ostream &out,ostream *out_h) {
-  Functions::iterator fi;
-  for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
-    Function *func = (*fi);
-    write_prototype_for(out, func);
-  }
-
-  out << "\n";
-  InterfaceMakerPython::write_prototypes(out,out_h);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::write_functions
-//       Access: Public, Virtual
-//  Description: Generates the list of functions that are appropriate
-//               for this interface.  This function is called *before*
-//               write_prototypes(), above.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonObj::
-write_functions(ostream &out) {
-  Functions::iterator fi;
-  for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
-    Function *func = (*fi);
-    write_function_for(out, func);
-  }
-
-  InterfaceMakerPython::write_functions(out);
-
-  // Finally, generate all the make-class-wrapper functions.
-  Objects::iterator oi;
-  for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
-    Object *object = (*oi).second;
-
-    write_class_wrapper(out, object);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::write_module
-//       Access: Public, Virtual
-//  Description: Generates whatever additional code is required to
-//               support a module file.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonObj::
-write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) {
-  InterfaceMakerPython::write_module(out,out_h, def);
-
-  out << "static PyMethodDef python_obj_funcs[] = {\n";
-
-  Objects::iterator oi;
-  for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
-    Object *object = (*oi).second;
-
-    Functions::iterator fi;
-    for (fi = object->_constructors.begin(); 
-         fi != object->_constructors.end(); 
-         ++fi) {
-      Function *func = (*fi);
-      out << "  { \"" << func->_ifunc.get_name() << "\", &" << func->_name 
-          << ", METH_VARARGS },\n";
-    }
-  }  
-  out << "  { NULL, NULL }\n"
-      << "};\n\n"
-
-      << "#ifdef _WIN32\n"
-      << "extern \"C\" __declspec(dllexport) void init" << def->library_name << "();\n"
-      << "#else\n"
-      << "extern \"C\" void init" << def->library_name << "();\n"
-      << "#endif\n\n"
-    
-      << "void init" << def->library_name << "() {\n"
-      << "  Py_InitModule(\"" << def->library_name
-      << "\", python_obj_funcs);\n"
-      << "}\n\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::synthesize_this_parameter
-//       Access: Public, Virtual
-//  Description: This method should be overridden and redefined to
-//               return true for interfaces that require the implicit
-//               "this" parameter, if present, to be passed as the
-//               first parameter to any wrapper functions.
-////////////////////////////////////////////////////////////////////
-bool InterfaceMakerPythonObj::
-synthesize_this_parameter() {
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::get_builder_name
-//       Access: Public, Static
-//  Description: Returns the name of the InterfaceMaker function
-//               generated to define the Python class for the
-//               indicated struct type.
-////////////////////////////////////////////////////////////////////
-string InterfaceMakerPythonObj::
-get_builder_name(CPPType *struct_type) {
-  return "get_python_class_" + 
-    InterrogateBuilder::clean_identifier(struct_type->get_local_name(&parser));
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::get_wrapper_prefix
-//       Access: Protected, Virtual
-//  Description: Returns the prefix string used to generate wrapper
-//               function names.
-////////////////////////////////////////////////////////////////////
-string InterfaceMakerPythonObj::
-get_wrapper_prefix() {
-  return "wpo_";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::write_class_wrapper
-//       Access: Private
-//  Description: Writes a function that will define the Python class.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonObj::
-write_class_wrapper(ostream &out, InterfaceMaker::Object *object) {
-  CPPType *struct_type = object->_itype._cpptype;
-  if (struct_type == (CPPType *)NULL) {
-    return;
-  }
-
-  string name = get_builder_name(struct_type);
-  string python_name =
-    InterrogateBuilder::clean_identifier(struct_type->get_simple_name());
-
-  out << "/*\n"
-      << " * Generate unique Python class for "
-      << struct_type->get_local_name(&parser) << "\n"
-      << " */\n"
-      << "PyObject *\n"
-      << name << "() {\n"
-      << "  static PyObject *wrapper = (PyObject *)NULL;\n"
-      << "  static PyMethodDef methods[] = {\n";
-
-  int methods_size = 0;
-  int class_methods_size = 0;
-
-  Functions::iterator fi;
-  for (fi = object->_methods.begin(); fi != object->_methods.end(); ++fi) {
-    Function *func = (*fi);
-    if (func->_has_this) {
-      out << "  { \"" << func->_ifunc.get_name() << "\", &" << func->_name 
-          << ", METH_VARARGS },\n";
-      methods_size++;
-    }
-  }
-
-  out << "  };\n"
-      << "  static const int methods_size = " << methods_size << ";\n\n"
-      << "  static PyMethodDef class_methods[] = {\n";
-
-  for (fi = object->_methods.begin(); fi != object->_methods.end(); ++fi) {
-    Function *func = (*fi);
-    if (!func->_has_this) {
-      out << "  { \"" << func->_ifunc.get_name() << "\", &" << func->_name 
-          << ", METH_VARARGS },\n";
-      class_methods_size++;
-    }
-  }
-
-  out << "  };\n"
-      << "  static const int class_methods_size = " << class_methods_size << ";\n\n"
-      << "  if (wrapper == (PyObject *)NULL) {\n"
-      << "    int i;\n"
-      << "    PyObject *bases = PyTuple_New(0);\n"
-      << "    PyObject *dict = PyDict_New();\n"
-      << "    PyObject *name = PyString_FromString(\""
-      << python_name << "\");\n"
-      << "    wrapper = PyClass_New(bases, dict, name);\n"
-      << "    for (i = 0; i < methods_size; i++) {\n"
-      << "      PyObject *function, *method;\n"
-      << "      function = PyCFunction_New(&methods[i], (PyObject *)NULL);\n"
-      << "      method = PyMethod_New(function, (PyObject *)NULL, wrapper);\n"
-      << "      PyDict_SetItemString(dict, methods[i].ml_name, method);\n"
-      << "    }\n"
-      << "    for (i = 0; i < class_methods_size; i++) {\n"
-      << "      PyObject *function;\n"
-      << "      function = PyCFunction_New(&class_methods[i], (PyObject *)NULL);\n"
-      << "      PyDict_SetItemString(dict, class_methods[i].ml_name, function);\n"
-      << "    }\n"
-      << "  }\n"
-      << "  return wrapper;\n"
-      << "}\n\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::write_prototype_for
-//       Access: Private
-//  Description: Writes the prototype for the indicated function.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonObj::
-write_prototype_for(ostream &out, InterfaceMaker::Function *func) {
-  out << "static PyObject *"
-      << func->_name << "(PyObject *self, PyObject *args);\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::write_function_for
-//       Access: Private
-//  Description: Writes the definition for a function that will call
-//               the indicated C++ function or method.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonObj::
-write_function_for(ostream &out, InterfaceMaker::Function *func) {
-  Function::Remaps::const_iterator ri;
-  out << "/*\n"
-      << " * Python object wrapper for\n";
-  for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-    FunctionRemap *remap = (*ri);
-    out << " * ";
-    remap->write_orig_prototype(out, 0);
-    out << "\n";
-  }
-  out << " */\n";
-
-  out << "static PyObject *"
-      << func->_name << "(PyObject *, PyObject *args) {\n";
-
-  // Now write out each instance of the overloaded function.
-  string expected_params = "Arguments must match one of:";
-
-  for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-    FunctionRemap *remap = (*ri);
-    expected_params += "\\n  ";
-    write_function_instance(out, 2, func, remap, expected_params);
-  }
-
-  // If we get here in the generated code, none of the parameters were
-  // valid.  Generate an error exception.  (We don't rely on the error
-  // already generated by ParseTuple(), because it only reports the
-  // error for one flavor of the function, whereas we might accept
-  // multiple flavors for the different overloaded C++ function
-  // signatures.
-
-  out << "  PyErr_SetString(PyExc_TypeError, \"" << expected_params << "\");\n"
-      << "  return (PyObject *)NULL;\n";
-
-  out << "}\n\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::write_function_instance
-//       Access: Private
-//  Description: Writes out the part of a function that handles a
-//               single instance of an overloaded function.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonObj::
-write_function_instance(ostream &out, int indent_level,
-                        InterfaceMaker::Function *func,
-                        FunctionRemap *remap, string &expected_params) {
-  indent(out, indent_level) << "{\n";
-  indent(out, indent_level + 2) << "/* ";
-  remap->write_orig_prototype(out, 0);
-  out << " */\n\n";
-  
-  string format_specifiers;
-  string parameter_list;
-  vector_string pexprs;
-  string extra_convert;
-  string extra_param_check;
-  string extra_cleanup;
-
-  // Make one pass through the parameter list.  We will output a
-  // one-line temporary variable definition for each parameter, while
-  // simultaneously building the ParseTuple() function call and also
-  // the parameter expression list for call_function().
-
-  expected_params += remap->_cppfunc->get_simple_name();
-  expected_params += "(";
-
-  int pn;
-  for (pn = 0; pn < (int)remap->_parameters.size(); pn++) {
-    if (pn != 0) {
-      expected_params += ", ";
-    }
-
-    indent(out, indent_level + 2);
-    CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type();
-    CPPType *type = remap->_parameters[pn]._remap->get_new_type();
-    string param_name = remap->get_parameter_name(pn);
-    
-    // This is the string to convert our local variable to the
-    // appropriate C++ type.  Normally this is just a cast.
-    string pexpr_string =
-      "(" + type->get_local_name(&parser) + ")" + param_name;
-    
-    if (remap->_parameters[pn]._remap->new_type_is_atomic_string()) {
-      if (TypeManager::is_char_pointer(orig_type)) {
-        out << "char *" << param_name;
-        format_specifiers += "s";
-        parameter_list += ", &" + param_name;
-        
-      } else {
-        out << "char *" << param_name
-            << "_str; int " << param_name << "_len";
-        format_specifiers += "s#";
-        parameter_list += ", &" + param_name
-          + "_str, &" + param_name + "_len";
-        pexpr_string = "basic_string<char>(" +
-          param_name + "_str, " +
-          param_name + "_len)";
-      }
-      expected_params += "string";
-      
-    } else if (TypeManager::is_bool(type)) {
-      out << "PyObject *" << param_name;
-      format_specifiers += "O";
-      parameter_list += ", &" + param_name;
-      pexpr_string = "(PyObject_IsTrue(" + param_name + ")!=0)";
-      expected_params += "bool";
-
-    } else if (TypeManager::is_unsigned_longlong(type)) {
-      out << "PyObject *" << param_name;
-      format_specifiers += "O";
-      parameter_list += ", &" + param_name;
-      extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
-      extra_param_check += "|| (" + param_name + "_long == NULL)";
-      pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)";
-      extra_cleanup += " Py_XDECREF(" + param_name + "_long);";
-      expected_params += "long";
-
-    } else if (TypeManager::is_longlong(type)) {
-      out << "PyObject *" << param_name;
-      format_specifiers += "O";
-      parameter_list += ", &" + param_name;
-      extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
-      extra_param_check += "|| (" + param_name + "_long == NULL)";
-      pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)";
-      extra_cleanup += " Py_XDECREF(" + param_name + "_long);";
-      expected_params += "long";
-
-    } else if (TypeManager::is_integer(type)) {
-      out << "int " << param_name;
-      format_specifiers += "i";
-      parameter_list += ", &" + param_name;
-      expected_params += "integer";
-
-    } else if (TypeManager::is_float(type)) {
-      out << "double " << param_name;
-      format_specifiers += "d";
-      parameter_list += ", &" + param_name;
-      expected_params += "float";
-
-    } else if (TypeManager::is_char_pointer(type)) {
-      out << "char *" << param_name;
-      format_specifiers += "s";
-      parameter_list += ", &" + param_name;
-      expected_params += "string";
-
-    } else if (TypeManager::is_pointer(type)) {
-      FunctionWriterPtrFromPython *writer = get_ptr_from_python(type);
-      writer->get_pointer_type()->output_instance(out, param_name, &parser);
-      format_specifiers += "O&";
-      parameter_list += ", &" + writer->get_name() + ", &" + param_name;
-      expected_params += writer->get_type()->get_preferred_name();
-
-    } else {
-      // Ignore a parameter.
-      out << "PyObject *" << param_name;
-      format_specifiers += "O";
-      parameter_list += ", &" + param_name;
-      expected_params += "any";
-    }
-
-    if (remap->_parameters[pn]._has_name) {
-      expected_params += " " + remap->_parameters[pn]._name;
-    }
-
-    out << ";\n";
-    pexprs.push_back(pexpr_string);
-  }
-  expected_params += ")";
-
-  indent(out, indent_level + 2)
-    << "if (PyArg_ParseTuple(args, \"" << format_specifiers
-    << "\"" << parameter_list << ")) {\n";
-
-  if (!extra_convert.empty()) {
-    indent(out, indent_level + 3)
-      << extra_convert << "\n";
-  }
-
-  if (!extra_param_check.empty()) {
-    indent(out, indent_level + 4)
-      << "if (" << extra_param_check.substr(3) << ") {\n";
-    if (!extra_cleanup.empty()) {
-      indent(out, indent_level + 5)
-        << extra_cleanup << "\n";
-    }
-    indent(out, indent_level + 6)
-      << "PyErr_SetString(PyExc_TypeError, \"Invalid parameters.\");\n";
-    indent(out, indent_level + 6)
-      << "return (PyObject *)NULL;\n";
-    indent(out, indent_level + 4)
-      << "}\n";
-  }
-
-  if (track_interpreter) {
-    indent(out, indent_level + 4)
-      << "in_interpreter = 0;\n";
-  }
-
-  if (!remap->_void_return && 
-      remap->_return_type->new_type_is_atomic_string()) {
-    // Treat strings as a special case.  We don't want to format the
-    // return expression.
-    string return_expr = 
-      remap->call_function(out, indent_level + 4, false, "param0", pexprs);
-
-    CPPType *type = remap->_return_type->get_orig_type();
-    indent(out, indent_level + 4);
-    type->output_instance(out, "return_value", &parser);
-    out << " = " << return_expr << ";\n";
-
-    if (track_interpreter) {
-      indent(out, indent_level + 4)
-        << "in_interpreter = 1;\n";
-    }
-    if (!extra_cleanup.empty()) {
-      indent(out, indent_level + 3)
-        << extra_cleanup << "\n";
-    }
-
-    return_expr = manage_return_value(out, indent_level + 4, remap, "return_value");
-    test_assert(out, indent_level + 4);
-    pack_return_value(out, indent_level + 4, remap, return_expr);
-
-  } else {
-    string return_expr = 
-      remap->call_function(out, indent_level + 4, true, "param0", pexprs);
-    if (return_expr.empty()) {
-      if (track_interpreter) {
-        indent(out, indent_level + 4)
-          << "in_interpreter = 1;\n";
-      }
-      if (!extra_cleanup.empty()) {
-        indent(out, indent_level + 3)
-          << extra_cleanup << "\n";
-      }
-      test_assert(out, indent_level + 4);
-      indent(out, indent_level + 4)
-        << "return Py_BuildValue(\"\");\n";
-
-    } else {
-      CPPType *type = remap->_return_type->get_temporary_type();
-      indent(out, indent_level + 4);
-      type->output_instance(out, "return_value", &parser);
-      out << " = " << return_expr << ";\n";
-      if (track_interpreter) {
-        indent(out, indent_level + 4)
-          << "in_interpreter = 1;\n";
-      }
-      if (!extra_cleanup.empty()) {
-        indent(out, indent_level + 3)
-          << extra_cleanup << "\n";
-      }
-
-      return_expr = manage_return_value(out, indent_level + 4, remap, "return_value");
-      test_assert(out, indent_level + 4);
-      pack_return_value(out, indent_level + 4, remap, remap->_return_type->temporary_to_return(return_expr));
-    }
-  }
-
-  indent(out, indent_level + 2) << "}\n";
-  indent(out, indent_level + 2)
-    << "PyErr_Clear();\n";  // Clear the error generated by ParseTuple()
-  indent(out, indent_level)
-    << "}\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::pack_return_value
-//       Access: Private
-//  Description: Outputs a command to pack the indicated expression,
-//               of the return_type type, as a Python return value.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonObj::
-pack_return_value(ostream &out, int indent_level,
-                  FunctionRemap *remap, string return_expr) {
-  CPPType *orig_type = remap->_return_type->get_orig_type();
-  CPPType *type = remap->_return_type->get_new_type();
-
-  if (remap->_return_type->new_type_is_atomic_string()) {
-    if (TypeManager::is_char_pointer(orig_type)) {
-      indent(out, indent_level)
-        << "return PyString_FromString(" << return_expr << ");\n";
-
-    } else {
-      indent(out, indent_level)
-        << "return PyString_FromStringAndSize("
-        << return_expr << ".data(), " << return_expr << ".length());\n";
-    }
-
-  } else if (TypeManager::is_unsigned_longlong(type)) {
-    indent(out, indent_level)
-      << "return PyLong_FromUnsignedLongLong(" << return_expr << ");\n";
-
-  } else if (TypeManager::is_longlong(type)) {
-    indent(out, indent_level)
-      << "return PyLong_FromLongLong(" << return_expr << ");\n";
-
-  } else if (TypeManager::is_integer(type)) {
-    indent(out, indent_level)
-      << "return PyInt_FromLong(" << return_expr << ");\n";
-
-  } else if (TypeManager::is_float(type)) {
-    indent(out, indent_level)
-      << "return PyFloat_FromDouble(" << return_expr << ");\n";
-
-  } else if (TypeManager::is_char_pointer(type)) {
-    indent(out, indent_level)
-      << "return PyString_FromString(" << return_expr << ");\n";
-
-  } else if (TypeManager::is_pointer(type)) {
-    bool caller_manages = remap->_return_value_needs_management;
-
-    FunctionWriterPtrToPython *writer = get_ptr_to_python(type);
-    indent(out, indent_level)
-      << "return " << writer->get_name() << "((" 
-      << writer->get_pointer_type()->get_local_name(&parser) << ")"
-      << return_expr << ", " << caller_manages << ");\n";
-
-  } else {
-    // Return None.
-    indent(out, indent_level)
-      << "return Py_BuildValue(\"\");\n";
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::get_ptr_from_python
-//       Access: Private
-//  Description: Returns a FunctionWriter pointer suitable for
-//               converting from a Python wrapper of the indicated
-//               type to the corresponding C++ pointer.
-////////////////////////////////////////////////////////////////////
-FunctionWriterPtrFromPython *InterfaceMakerPythonObj::
-get_ptr_from_python(CPPType *type) {
-  PtrConverter::iterator ci;
-  ci = _from_python.find(type);
-  if (ci != _from_python.end()) {
-    // We've previously used this type.
-    return (FunctionWriterPtrFromPython *)(*ci).second;
-  }
-
-  FunctionWriter *writer = 
-    _function_writers.add_writer(new FunctionWriterPtrFromPython(type));
-  _from_python.insert(PtrConverter::value_type(type, writer));
-  return (FunctionWriterPtrFromPython *)writer;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonObj::get_ptr_to_python
-//       Access: Private
-//  Description: Returns a FunctionWriter pointer suitable for
-//               converting from a C++ pointer of the indicated
-//               type to the corresponding Python wrapper.
-////////////////////////////////////////////////////////////////////
-FunctionWriterPtrToPython *InterfaceMakerPythonObj::
-get_ptr_to_python(CPPType *type) {
-  PtrConverter::iterator ci;
-  ci = _to_python.find(type);
-  if (ci != _to_python.end()) {
-    // We've previously used this type.
-    return (FunctionWriterPtrToPython *)(*ci).second;
-  }
-
-  FunctionWriter *writer = 
-    _function_writers.add_writer(new FunctionWriterPtrToPython(type));
-  _to_python.insert(PtrConverter::value_type(type, writer));
-  return (FunctionWriterPtrToPython *)writer;
-}
+// Filename: interfaceMakerPythonObj.cxx
+// Created by:  drose (19Sep01)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "interfaceMakerPythonObj.h"
+#include "interrogateBuilder.h"
+#include "interrogate.h"
+#include "functionRemap.h"
+#include "parameterRemapUnchanged.h"
+#include "typeManager.h"
+#include "functionWriterPtrFromPython.h"
+#include "functionWriterPtrToPython.h"
+
+#include "interrogateDatabase.h"
+#include "interrogateType.h"
+#include "interrogateFunction.h"
+#include "cppFunctionType.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+InterfaceMakerPythonObj::
+InterfaceMakerPythonObj(InterrogateModuleDef *def) :
+  InterfaceMakerPython(def)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+InterfaceMakerPythonObj::
+~InterfaceMakerPythonObj() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::write_prototypes
+//       Access: Public, Virtual
+//  Description: Generates the list of function prototypes
+//               corresponding to the functions that will be output in
+//               write_functions().
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonObj::
+write_prototypes(ostream &out,ostream *out_h) {
+  Functions::iterator fi;
+  for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
+    Function *func = (*fi);
+    write_prototype_for(out, func);
+  }
+
+  out << "\n";
+  InterfaceMakerPython::write_prototypes(out,out_h);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::write_functions
+//       Access: Public, Virtual
+//  Description: Generates the list of functions that are appropriate
+//               for this interface.  This function is called *before*
+//               write_prototypes(), above.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonObj::
+write_functions(ostream &out) {
+  Functions::iterator fi;
+  for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
+    Function *func = (*fi);
+    write_function_for(out, func);
+  }
+
+  InterfaceMakerPython::write_functions(out);
+
+  // Finally, generate all the make-class-wrapper functions.
+  Objects::iterator oi;
+  for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
+    Object *object = (*oi).second;
+
+    write_class_wrapper(out, object);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::write_module
+//       Access: Public, Virtual
+//  Description: Generates whatever additional code is required to
+//               support a module file.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonObj::
+write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) {
+  InterfaceMakerPython::write_module(out,out_h, def);
+
+  out << "static PyMethodDef python_obj_funcs[] = {\n";
+
+  Objects::iterator oi;
+  for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
+    Object *object = (*oi).second;
+
+    Functions::iterator fi;
+    for (fi = object->_constructors.begin(); 
+         fi != object->_constructors.end(); 
+         ++fi) {
+      Function *func = (*fi);
+      out << "  { \"" << func->_ifunc.get_name() << "\", &" << func->_name 
+          << ", METH_VARARGS },\n";
+    }
+  }  
+  out << "  { NULL, NULL }\n"
+      << "};\n\n"
+
+      << "#ifdef _WIN32\n"
+      << "extern \"C\" __declspec(dllexport) void init" << def->library_name << "();\n"
+      << "#else\n"
+      << "extern \"C\" void init" << def->library_name << "();\n"
+      << "#endif\n\n"
+    
+      << "void init" << def->library_name << "() {\n"
+      << "  Py_InitModule(\"" << def->library_name
+      << "\", python_obj_funcs);\n"
+      << "}\n\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::synthesize_this_parameter
+//       Access: Public, Virtual
+//  Description: This method should be overridden and redefined to
+//               return true for interfaces that require the implicit
+//               "this" parameter, if present, to be passed as the
+//               first parameter to any wrapper functions.
+////////////////////////////////////////////////////////////////////
+bool InterfaceMakerPythonObj::
+synthesize_this_parameter() {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::get_builder_name
+//       Access: Public, Static
+//  Description: Returns the name of the InterfaceMaker function
+//               generated to define the Python class for the
+//               indicated struct type.
+////////////////////////////////////////////////////////////////////
+string InterfaceMakerPythonObj::
+get_builder_name(CPPType *struct_type) {
+  return "get_python_class_" + 
+    InterrogateBuilder::clean_identifier(struct_type->get_local_name(&parser));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::get_wrapper_prefix
+//       Access: Protected, Virtual
+//  Description: Returns the prefix string used to generate wrapper
+//               function names.
+////////////////////////////////////////////////////////////////////
+string InterfaceMakerPythonObj::
+get_wrapper_prefix() {
+  return "wpo_";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::write_class_wrapper
+//       Access: Private
+//  Description: Writes a function that will define the Python class.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonObj::
+write_class_wrapper(ostream &out, InterfaceMaker::Object *object) {
+  CPPType *struct_type = object->_itype._cpptype;
+  if (struct_type == (CPPType *)NULL) {
+    return;
+  }
+
+  string name = get_builder_name(struct_type);
+  string python_name =
+    InterrogateBuilder::clean_identifier(struct_type->get_simple_name());
+
+  out << "/*\n"
+      << " * Generate unique Python class for "
+      << struct_type->get_local_name(&parser) << "\n"
+      << " */\n"
+      << "PyObject *\n"
+      << name << "() {\n"
+      << "  static PyObject *wrapper = (PyObject *)NULL;\n"
+      << "  static PyMethodDef methods[] = {\n";
+
+  int methods_size = 0;
+  int class_methods_size = 0;
+
+  Functions::iterator fi;
+  for (fi = object->_methods.begin(); fi != object->_methods.end(); ++fi) {
+    Function *func = (*fi);
+    if (func->_has_this) {
+      out << "  { \"" << func->_ifunc.get_name() << "\", &" << func->_name 
+          << ", METH_VARARGS },\n";
+      methods_size++;
+    }
+  }
+
+  out << "  };\n"
+      << "  static const int methods_size = " << methods_size << ";\n\n"
+      << "  static PyMethodDef class_methods[] = {\n";
+
+  for (fi = object->_methods.begin(); fi != object->_methods.end(); ++fi) {
+    Function *func = (*fi);
+    if (!func->_has_this) {
+      out << "  { \"" << func->_ifunc.get_name() << "\", &" << func->_name 
+          << ", METH_VARARGS },\n";
+      class_methods_size++;
+    }
+  }
+
+  out << "  };\n"
+      << "  static const int class_methods_size = " << class_methods_size << ";\n\n"
+      << "  if (wrapper == (PyObject *)NULL) {\n"
+      << "    int i;\n"
+      << "    PyObject *bases = PyTuple_New(0);\n"
+      << "    PyObject *dict = PyDict_New();\n"
+      << "    PyObject *name = PyString_FromString(\""
+      << python_name << "\");\n"
+      << "    wrapper = PyClass_New(bases, dict, name);\n"
+      << "    for (i = 0; i < methods_size; ++i) {\n"
+      << "      PyObject *function, *method;\n"
+      << "      function = PyCFunction_New(&methods[i], (PyObject *)NULL);\n"
+      << "      method = PyMethod_New(function, (PyObject *)NULL, wrapper);\n"
+      << "      PyDict_SetItemString(dict, methods[i].ml_name, method);\n"
+      << "    }\n"
+      << "    for (i = 0; i < class_methods_size; ++i) {\n"
+      << "      PyObject *function;\n"
+      << "      function = PyCFunction_New(&class_methods[i], (PyObject *)NULL);\n"
+      << "      PyDict_SetItemString(dict, class_methods[i].ml_name, function);\n"
+      << "    }\n"
+      << "  }\n"
+      << "  return wrapper;\n"
+      << "}\n\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::write_prototype_for
+//       Access: Private
+//  Description: Writes the prototype for the indicated function.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonObj::
+write_prototype_for(ostream &out, InterfaceMaker::Function *func) {
+  out << "static PyObject *"
+      << func->_name << "(PyObject *self, PyObject *args);\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::write_function_for
+//       Access: Private
+//  Description: Writes the definition for a function that will call
+//               the indicated C++ function or method.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonObj::
+write_function_for(ostream &out, InterfaceMaker::Function *func) {
+  Function::Remaps::const_iterator ri;
+  out << "/*\n"
+      << " * Python object wrapper for\n";
+  for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
+    FunctionRemap *remap = (*ri);
+    out << " * ";
+    remap->write_orig_prototype(out, 0);
+    out << "\n";
+  }
+  out << " */\n";
+
+  out << "static PyObject *"
+      << func->_name << "(PyObject *, PyObject *args) {\n";
+
+  // Now write out each instance of the overloaded function.
+  string expected_params = "Arguments must match one of:";
+
+  for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
+    FunctionRemap *remap = (*ri);
+    expected_params += "\\n  ";
+    write_function_instance(out, 2, func, remap, expected_params);
+  }
+
+  // If we get here in the generated code, none of the parameters were
+  // valid.  Generate an error exception.  (We don't rely on the error
+  // already generated by ParseTuple(), because it only reports the
+  // error for one flavor of the function, whereas we might accept
+  // multiple flavors for the different overloaded C++ function
+  // signatures.
+
+  out << "  PyErr_SetString(PyExc_TypeError, \"" << expected_params << "\");\n"
+      << "  return (PyObject *)NULL;\n";
+
+  out << "}\n\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::write_function_instance
+//       Access: Private
+//  Description: Writes out the part of a function that handles a
+//               single instance of an overloaded function.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonObj::
+write_function_instance(ostream &out, int indent_level,
+                        InterfaceMaker::Function *func,
+                        FunctionRemap *remap, string &expected_params) {
+  indent(out, indent_level) << "{\n";
+  indent(out, indent_level + 2) << "/* ";
+  remap->write_orig_prototype(out, 0);
+  out << " */\n\n";
+  
+  string format_specifiers;
+  string parameter_list;
+  vector_string pexprs;
+  string extra_convert;
+  string extra_param_check;
+  string extra_cleanup;
+
+  // Make one pass through the parameter list.  We will output a
+  // one-line temporary variable definition for each parameter, while
+  // simultaneously building the ParseTuple() function call and also
+  // the parameter expression list for call_function().
+
+  expected_params += remap->_cppfunc->get_simple_name();
+  expected_params += "(";
+
+  int pn;
+  for (pn = 0; pn < (int)remap->_parameters.size(); pn++) {
+    if (pn != 0) {
+      expected_params += ", ";
+    }
+
+    indent(out, indent_level + 2);
+    CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type();
+    CPPType *type = remap->_parameters[pn]._remap->get_new_type();
+    string param_name = remap->get_parameter_name(pn);
+    
+    // This is the string to convert our local variable to the
+    // appropriate C++ type.  Normally this is just a cast.
+    string pexpr_string =
+      "(" + type->get_local_name(&parser) + ")" + param_name;
+    
+    if (remap->_parameters[pn]._remap->new_type_is_atomic_string()) {
+      if (TypeManager::is_char_pointer(orig_type)) {
+        out << "char *" << param_name;
+        format_specifiers += "s";
+        parameter_list += ", &" + param_name;
+        
+      } else {
+        out << "char *" << param_name
+            << "_str; int " << param_name << "_len";
+        format_specifiers += "s#";
+        parameter_list += ", &" + param_name
+          + "_str, &" + param_name + "_len";
+        pexpr_string = "basic_string<char>(" +
+          param_name + "_str, " +
+          param_name + "_len)";
+      }
+      expected_params += "string";
+      
+    } else if (TypeManager::is_bool(type)) {
+      out << "PyObject *" << param_name;
+      format_specifiers += "O";
+      parameter_list += ", &" + param_name;
+      pexpr_string = "(PyObject_IsTrue(" + param_name + ")!=0)";
+      expected_params += "bool";
+
+    } else if (TypeManager::is_unsigned_longlong(type)) {
+      out << "PyObject *" << param_name;
+      format_specifiers += "O";
+      parameter_list += ", &" + param_name;
+      extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
+      extra_param_check += "|| (" + param_name + "_long == NULL)";
+      pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)";
+      extra_cleanup += " Py_XDECREF(" + param_name + "_long);";
+      expected_params += "long";
+
+    } else if (TypeManager::is_longlong(type)) {
+      out << "PyObject *" << param_name;
+      format_specifiers += "O";
+      parameter_list += ", &" + param_name;
+      extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
+      extra_param_check += "|| (" + param_name + "_long == NULL)";
+      pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)";
+      extra_cleanup += " Py_XDECREF(" + param_name + "_long);";
+      expected_params += "long";
+
+    #ifndef USE_PYTHON_2_2_OR_EARLIER
+    } else if (TypeManager::is_unsigned_integer(type)) {
+      out << "unsigned int " << param_name;
+      format_specifiers += "I";  // This requires Python 2.3 or better
+      parameter_list += ", &" + param_name;
+    #endif
+
+    } else if (TypeManager::is_integer(type)) {
+      out << "int " << param_name;
+      format_specifiers += "i";
+      parameter_list += ", &" + param_name;
+      expected_params += "integer";
+
+    } else if (TypeManager::is_float(type)) {
+      out << "double " << param_name;
+      format_specifiers += "d";
+      parameter_list += ", &" + param_name;
+      expected_params += "float";
+
+    } else if (TypeManager::is_char_pointer(type)) {
+      out << "char *" << param_name;
+      format_specifiers += "s";
+      parameter_list += ", &" + param_name;
+      expected_params += "string";
+
+    } else if (TypeManager::is_pointer(type)) {
+      FunctionWriterPtrFromPython *writer = get_ptr_from_python(type);
+      writer->get_pointer_type()->output_instance(out, param_name, &parser);
+      format_specifiers += "O&";
+      parameter_list += ", &" + writer->get_name() + ", &" + param_name;
+      expected_params += writer->get_type()->get_preferred_name();
+
+    } else {
+      // Ignore a parameter.
+      out << "PyObject *" << param_name;
+      format_specifiers += "O";
+      parameter_list += ", &" + param_name;
+      expected_params += "any";
+    }
+
+    if (remap->_parameters[pn]._has_name) {
+      expected_params += " " + remap->_parameters[pn]._name;
+    }
+
+    out << ";\n";
+    pexprs.push_back(pexpr_string);
+  }
+  expected_params += ")";
+
+  indent(out, indent_level + 2)
+    << "if (PyArg_ParseTuple(args, \"" << format_specifiers
+    << "\"" << parameter_list << ")) {\n";
+
+  if (!extra_convert.empty()) {
+    indent(out, indent_level + 3)
+      << extra_convert << "\n";
+  }
+
+  if (!extra_param_check.empty()) {
+    indent(out, indent_level + 4)
+      << "if (" << extra_param_check.substr(3) << ") {\n";
+    if (!extra_cleanup.empty()) {
+      indent(out, indent_level + 5)
+        << extra_cleanup << "\n";
+    }
+    indent(out, indent_level + 6)
+      << "PyErr_SetString(PyExc_TypeError, \"Invalid parameters.\");\n";
+    indent(out, indent_level + 6)
+      << "return (PyObject *)NULL;\n";
+    indent(out, indent_level + 4)
+      << "}\n";
+  }
+
+  if (track_interpreter) {
+    indent(out, indent_level + 4)
+      << "in_interpreter = 0;\n";
+  }
+
+  if (!remap->_void_return && 
+      remap->_return_type->new_type_is_atomic_string()) {
+    // Treat strings as a special case.  We don't want to format the
+    // return expression.
+    string return_expr = 
+      remap->call_function(out, indent_level + 4, false, "param0", pexprs);
+
+    CPPType *type = remap->_return_type->get_orig_type();
+    indent(out, indent_level + 4);
+    type->output_instance(out, "return_value", &parser);
+    out << " = " << return_expr << ";\n";
+
+    if (track_interpreter) {
+      indent(out, indent_level + 4)
+        << "in_interpreter = 1;\n";
+    }
+    if (!extra_cleanup.empty()) {
+      indent(out, indent_level + 3)
+        << extra_cleanup << "\n";
+    }
+
+    return_expr = manage_return_value(out, indent_level + 4, remap, "return_value");
+    test_assert(out, indent_level + 4);
+    pack_return_value(out, indent_level + 4, remap, return_expr);
+
+  } else {
+    string return_expr = 
+      remap->call_function(out, indent_level + 4, true, "param0", pexprs);
+    if (return_expr.empty()) {
+      if (track_interpreter) {
+        indent(out, indent_level + 4)
+          << "in_interpreter = 1;\n";
+      }
+      if (!extra_cleanup.empty()) {
+        indent(out, indent_level + 3)
+          << extra_cleanup << "\n";
+      }
+      test_assert(out, indent_level + 4);
+      indent(out, indent_level + 4)
+        << "return Py_BuildValue(\"\");\n";
+
+    } else {
+      CPPType *type = remap->_return_type->get_temporary_type();
+      indent(out, indent_level + 4);
+      type->output_instance(out, "return_value", &parser);
+      out << " = " << return_expr << ";\n";
+      if (track_interpreter) {
+        indent(out, indent_level + 4)
+          << "in_interpreter = 1;\n";
+      }
+      if (!extra_cleanup.empty()) {
+        indent(out, indent_level + 3)
+          << extra_cleanup << "\n";
+      }
+
+      return_expr = manage_return_value(out, indent_level + 4, remap, "return_value");
+      test_assert(out, indent_level + 4);
+      pack_return_value(out, indent_level + 4, remap, remap->_return_type->temporary_to_return(return_expr));
+    }
+  }
+
+  indent(out, indent_level + 2) << "}\n";
+  indent(out, indent_level + 2)
+    << "PyErr_Clear();\n";  // Clear the error generated by ParseTuple()
+  indent(out, indent_level)
+    << "}\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::pack_return_value
+//       Access: Private
+//  Description: Outputs a command to pack the indicated expression,
+//               of the return_type type, as a Python return value.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonObj::
+pack_return_value(ostream &out, int indent_level,
+                  FunctionRemap *remap, string return_expr) {
+  CPPType *orig_type = remap->_return_type->get_orig_type();
+  CPPType *type = remap->_return_type->get_new_type();
+
+  if (remap->_return_type->new_type_is_atomic_string()) {
+    if (TypeManager::is_char_pointer(orig_type)) {
+      indent(out, indent_level)
+        << "return PyString_FromString(" << return_expr << ");\n";
+
+    } else {
+      indent(out, indent_level)
+        << "return PyString_FromStringAndSize("
+        << return_expr << ".data(), " << return_expr << ".length());\n";
+    }
+
+  } else if (TypeManager::is_unsigned_longlong(type)) {
+    indent(out, indent_level)
+      << "return PyLong_FromUnsignedLongLong(" << return_expr << ");\n";
+
+  } else if (TypeManager::is_longlong(type)) {
+    indent(out, indent_level)
+      << "return PyLong_FromLongLong(" << return_expr << ");\n";
+
+  #ifndef USE_PYTHON_2_2_OR_EARLIER
+  } else if (TypeManager::is_unsigned_integer(type)) {
+    indent(out, indent_level)
+      << "return PyLong_FromUnsignedLong(" << return_expr << ");\n";
+  #endif
+
+  } else if (TypeManager::is_integer(type)) {
+    indent(out, indent_level)
+      << "return PyInt_FromLong(" << return_expr << ");\n";
+
+  } else if (TypeManager::is_float(type)) {
+    indent(out, indent_level)
+      << "return PyFloat_FromDouble(" << return_expr << ");\n";
+
+  } else if (TypeManager::is_char_pointer(type)) {
+    indent(out, indent_level)
+      << "return PyString_FromString(" << return_expr << ");\n";
+
+  } else if (TypeManager::is_pointer(type)) {
+    bool caller_manages = remap->_return_value_needs_management;
+
+    FunctionWriterPtrToPython *writer = get_ptr_to_python(type);
+    indent(out, indent_level)
+      << "return " << writer->get_name() << "((" 
+      << writer->get_pointer_type()->get_local_name(&parser) << ")"
+      << return_expr << ", " << caller_manages << ");\n";
+
+  } else {
+    // Return None.
+    indent(out, indent_level)
+      << "return Py_BuildValue(\"\");\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::get_ptr_from_python
+//       Access: Private
+//  Description: Returns a FunctionWriter pointer suitable for
+//               converting from a Python wrapper of the indicated
+//               type to the corresponding C++ pointer.
+////////////////////////////////////////////////////////////////////
+FunctionWriterPtrFromPython *InterfaceMakerPythonObj::
+get_ptr_from_python(CPPType *type) {
+  PtrConverter::iterator ci;
+  ci = _from_python.find(type);
+  if (ci != _from_python.end()) {
+    // We've previously used this type.
+    return (FunctionWriterPtrFromPython *)(*ci).second;
+  }
+
+  FunctionWriter *writer = 
+    _function_writers.add_writer(new FunctionWriterPtrFromPython(type));
+  _from_python.insert(PtrConverter::value_type(type, writer));
+  return (FunctionWriterPtrFromPython *)writer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonObj::get_ptr_to_python
+//       Access: Private
+//  Description: Returns a FunctionWriter pointer suitable for
+//               converting from a C++ pointer of the indicated
+//               type to the corresponding Python wrapper.
+////////////////////////////////////////////////////////////////////
+FunctionWriterPtrToPython *InterfaceMakerPythonObj::
+get_ptr_to_python(CPPType *type) {
+  PtrConverter::iterator ci;
+  ci = _to_python.find(type);
+  if (ci != _to_python.end()) {
+    // We've previously used this type.
+    return (FunctionWriterPtrToPython *)(*ci).second;
+  }
+
+  FunctionWriter *writer = 
+    _function_writers.add_writer(new FunctionWriterPtrToPython(type));
+  _to_python.insert(PtrConverter::value_type(type, writer));
+  return (FunctionWriterPtrToPython *)writer;
+}

+ 497 - 484
dtool/src/interrogate/interfaceMakerPythonSimple.cxx

@@ -1,484 +1,497 @@
-// Filename: interfaceMakerPythonSimple.cxx
-// Created by:  drose (01Oct01)
-//
-////////////////////////////////////////////////////////////////////
-//
-// 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 "interfaceMakerPythonSimple.h"
-#include "interrogateBuilder.h"
-#include "interrogate.h"
-#include "functionRemap.h"
-#include "parameterRemapUnchanged.h"
-#include "typeManager.h"
-
-#include "interrogateDatabase.h"
-#include "interrogateType.h"
-#include "interrogateFunction.h"
-#include "cppFunctionType.h"
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-InterfaceMakerPythonSimple::
-InterfaceMakerPythonSimple(InterrogateModuleDef *def) :
-  InterfaceMakerPython(def)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::Destructor
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-InterfaceMakerPythonSimple::
-~InterfaceMakerPythonSimple() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::write_prototypes
-//       Access: Public, Virtual
-//  Description: Generates the list of function prototypes
-//               corresponding to the functions that will be output in
-//               write_functions().
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonSimple::
-write_prototypes(ostream &out,ostream *out_h) {
-  Functions::iterator fi;
-  for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
-    Function *func = (*fi);
-    write_prototype_for(out, func);
-  }
-
-  out << "\n";
-  InterfaceMakerPython::write_prototypes(out,out_h);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::write_functions
-//       Access: Public, Virtual
-//  Description: Generates the list of functions that are appropriate
-//               for this interface.  This function is called *before*
-//               write_prototypes(), above.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonSimple::
-write_functions(ostream &out) {
-  Functions::iterator fi;
-  for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
-    Function *func = (*fi);
-    write_function_for(out, func);
-  }
-
-  InterfaceMakerPython::write_functions(out);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::write_module
-//       Access: Public, Virtual
-//  Description: Generates whatever additional code is required to
-//               support a module file.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonSimple::
-write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) {
-  InterfaceMakerPython::write_module(out,out_h, def);
-
-  out << "static PyMethodDef python_simple_funcs[] = {\n";
-
-  Functions::iterator fi;
-  for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
-    Function *func = (*fi);
-    Function::Remaps::const_iterator ri;
-    for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-      FunctionRemap *remap = (*ri);
-      out << "  { \"" << remap->_reported_name << "\", &" 
-          << remap->_wrapper_name << ", METH_VARARGS },\n";
-    }
-  }  
-  out << "  { NULL, NULL }\n"
-      << "};\n\n"
-
-      << "#ifdef _WIN32\n"
-      << "extern \"C\" __declspec(dllexport) void init" << def->library_name << "();\n"
-      << "#else\n"
-      << "extern \"C\" void init" << def->library_name << "();\n"
-      << "#endif\n\n"
-    
-      << "void init" << def->library_name << "() {\n"
-      << "  Py_InitModule(\"" << def->library_name
-      << "\", python_simple_funcs);\n"
-      << "}\n\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::synthesize_this_parameter
-//       Access: Public, Virtual
-//  Description: This method should be overridden and redefined to
-//               return true for interfaces that require the implicit
-//               "this" parameter, if present, to be passed as the
-//               first parameter to any wrapper functions.
-////////////////////////////////////////////////////////////////////
-bool InterfaceMakerPythonSimple::
-synthesize_this_parameter() {
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::get_wrapper_prefix
-//       Access: Protected, Virtual
-//  Description: Returns the prefix string used to generate wrapper
-//               function names.
-////////////////////////////////////////////////////////////////////
-string InterfaceMakerPythonSimple::
-get_wrapper_prefix() {
-  return "_inP";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::get_unique_prefix
-//       Access: Protected, Virtual
-//  Description: Returns the prefix string used to generate unique
-//               symbolic names, which are not necessarily C-callable
-//               function names.
-////////////////////////////////////////////////////////////////////
-string InterfaceMakerPythonSimple::
-get_unique_prefix() {
-  return "p";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::record_function_wrapper
-//       Access: Protected, Virtual
-//  Description: Associates the function wrapper with its function in
-//               the appropriate structures in the database.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonSimple::
-record_function_wrapper(InterrogateFunction &ifunc, 
-                        FunctionWrapperIndex wrapper_index) {
-  ifunc._python_wrappers.push_back(wrapper_index);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::write_prototype_for
-//       Access: Private
-//  Description: Writes the prototype for the indicated function.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonSimple::
-write_prototype_for(ostream &out, InterfaceMaker::Function *func) {
-  Function::Remaps::const_iterator ri;
-
-  for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-    FunctionRemap *remap = (*ri);
-    if (!output_function_names) {
-      // If we're not saving the function names, don't export it from
-      // the library.
-      out << "static ";
-    } else {
-      out << "extern \"C\" ";
-    }
-    out << "PyObject *"
-        << remap->_wrapper_name << "(PyObject *self, PyObject *args);\n";
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::write_function_for
-//       Access: Private
-//  Description: Writes the definition for a function that will call
-//               the indicated C++ function or method.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonSimple::
-write_function_for(ostream &out, InterfaceMaker::Function *func) {
-  Function::Remaps::const_iterator ri;
-
-  for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-    FunctionRemap *remap = (*ri);
-    write_function_instance(out, func, remap);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::write_function_instance
-//       Access: Private
-//  Description: Writes out the particular function that handles a
-//               single instance of an overloaded function.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonSimple::write_function_instance(ostream &out, InterfaceMaker::Function *func,
-                        FunctionRemap *remap) {
-  out << "/*\n"
-      << " * Python simple wrapper for\n"
-      << " * ";
-  remap->write_orig_prototype(out, 0);
-  out << "\n"
-      << " */\n";
-
-  if (!output_function_names) {
-    // If we're not saving the function names, don't export it from
-    // the library.
-    out << "static ";
-  }
-
-  out << "PyObject *\n"
-      << remap->_wrapper_name << "(PyObject *, PyObject *args) {\n";
-
-  if (generate_spam) {
-    write_spam_message(out, remap);
-  }
-
-  string format_specifiers;
-  string parameter_list;
-  string container;
-  vector_string pexprs;
-  string extra_convert;
-  string extra_param_check;
-  string extra_cleanup;
-
-  // Make one pass through the parameter list.  We will output a
-  // one-line temporary variable definition for each parameter, while
-  // simultaneously building the ParseTuple() function call and also
-  // the parameter expression list for call_function().
-
-  int pn;
-  for (pn = 0; pn < (int)remap->_parameters.size(); pn++) {
-    indent(out, 2);
-    CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type();
-    CPPType *type = remap->_parameters[pn]._remap->get_new_type();
-    string param_name = remap->get_parameter_name(pn);
-    
-    // This is the string to convert our local variable to the
-    // appropriate C++ type.  Normally this is just a cast.
-    string pexpr_string =
-      "(" + type->get_local_name(&parser) + ")" + param_name;
-    
-    if (remap->_parameters[pn]._remap->new_type_is_atomic_string()) {
-      if (TypeManager::is_char_pointer(orig_type)) {
-        out << "char *" << param_name;
-        format_specifiers += "s";
-        parameter_list += ", &" + param_name;
-        
-      } else {
-        out << "char *" << param_name
-            << "_str; int " << param_name << "_len";
-        format_specifiers += "s#";
-        parameter_list += ", &" + param_name
-          + "_str, &" + param_name + "_len";
-        pexpr_string = "basic_string<char>(" +
-          param_name + "_str, " +
-          param_name + "_len)";
-      }
-      
-    } else if (TypeManager::is_bool(type)) {
-      out << "PyObject *" << param_name;
-      format_specifiers += "O";
-      parameter_list += ", &" + param_name;
-      pexpr_string = "(PyObject_IsTrue(" + param_name + ")!=0)";
-
-    } else if (TypeManager::is_unsigned_longlong(type)) {
-      out << "PyObject *" << param_name;
-      format_specifiers += "O";
-      parameter_list += ", &" + param_name;
-      extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
-      extra_param_check += "|| (" + param_name + "_long == NULL)";
-      pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)";
-      extra_cleanup += " Py_XDECREF(" + param_name + "_long);";
-
-    } else if (TypeManager::is_longlong(type)) {
-      out << "PyObject *" << param_name;
-      format_specifiers += "O";
-      parameter_list += ", &" + param_name;
-      extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
-      extra_param_check += "|| (" + param_name + "_long == NULL)";
-      pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)";
-      extra_cleanup += " Py_XDECREF(" + param_name + "_long);";
-
-    } else if (TypeManager::is_integer(type)) {
-      out << "int " << param_name;
-      format_specifiers += "i";
-      parameter_list += ", &" + param_name;
-
-    } else if (TypeManager::is_float(type)) {
-      out << "double " << param_name;
-      format_specifiers += "d";
-      parameter_list += ", &" + param_name;
-
-    } else if (TypeManager::is_char_pointer(type)) {
-      out << "char *" << param_name;
-      format_specifiers += "s";
-      parameter_list += ", &" + param_name;
-
-    } else if (TypeManager::is_pointer_to_PyObject(type)) {
-      out << "PyObject *" << param_name;
-      format_specifiers += "O";
-      parameter_list += ", &" + param_name;
-      pexpr_string = param_name;
-
-    } else if (TypeManager::is_pointer(type)) {
-      out << "int " << param_name;
-      format_specifiers += "i";
-      parameter_list += ", &" + param_name;
-
-    } else {
-      // Ignore a parameter.
-      out << "PyObject *" << param_name;
-      format_specifiers += "O";
-      parameter_list += ", &" + param_name;
-    }
-
-    out << ";\n";
-    if (remap->_has_this && pn == 0) {
-      // The "this" parameter gets passed in separately.
-      container = pexpr_string;
-    }
-    pexprs.push_back(pexpr_string);
-  }
-
-  out << "  if (PyArg_ParseTuple(args, \"" << format_specifiers
-      << "\"" << parameter_list << ")) {\n";
-
-  if (!extra_convert.empty()) {
-    out << "   " << extra_convert << "\n";
-  }
-
-  if (!extra_param_check.empty()) {
-    out << "    if (" << extra_param_check.substr(3) << ") {\n";
-    if (!extra_cleanup.empty()) {
-      out << "     " << extra_cleanup << "\n";
-    }
-    out << "      PyErr_SetString(PyExc_TypeError, \"Invalid parameters.\");\n"
-        << "      return (PyObject *)NULL;\n"
-        << "    }\n";
-  }
-  
-  if (track_interpreter) {
-    out << "    in_interpreter = 0;\n";
-  }
-
-  if (!remap->_void_return && 
-      remap->_return_type->new_type_is_atomic_string()) {
-    // Treat strings as a special case.  We don't want to format the
-    // return expression.
-    string return_expr = remap->call_function(out, 4, false, container, pexprs);
-    CPPType *type = remap->_return_type->get_orig_type();
-    out << "    ";
-    type->output_instance(out, "return_value", &parser);
-    out << " = " << return_expr << ";\n";
-    
-    if (track_interpreter) {
-      out << "    in_interpreter = 1;\n";
-    }
-    if (!extra_cleanup.empty()) {
-      out << "   " << extra_cleanup << "\n";
-    }
-    
-    return_expr = manage_return_value(out, 4, remap, "return_value");
-    test_assert(out, 4);
-    pack_return_value(out, 4, remap, return_expr);
-    
-  } else {
-    string return_expr = remap->call_function(out, 4, true, container, pexprs);
-    if (return_expr.empty()) {
-      if (track_interpreter) {
-        out << "    in_interpreter = 1;\n";
-      }
-      if (!extra_cleanup.empty()) {
-        out << "   " << extra_cleanup << "\n";
-      }
-      test_assert(out, 4);
-      out << "    return Py_BuildValue(\"\");\n";
-      
-    } else {
-      CPPType *type = remap->_return_type->get_temporary_type();
-      out << "    ";
-      type->output_instance(out, "return_value", &parser);
-      out << " = " << return_expr << ";\n";
-      if (track_interpreter) {
-        out << "    in_interpreter = 1;\n";
-      }
-      if (!extra_cleanup.empty()) {
-        out << "   " << extra_cleanup << "\n";
-      }
-      
-      return_expr = manage_return_value(out, 4, remap, "return_value");
-      test_assert(out, 4);
-      pack_return_value(out, 4, remap, remap->_return_type->temporary_to_return(return_expr));
-    }
-  }
-  
-  out << "  }\n";
-  
-  out << "  return (PyObject *)NULL;\n";
-  out << "}\n\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InterfaceMakerPythonSimple::pack_return_value
-//       Access: Private
-//  Description: Outputs a command to pack the indicated expression,
-//               of the return_type type, as a Python return value.
-////////////////////////////////////////////////////////////////////
-void InterfaceMakerPythonSimple::
-pack_return_value(ostream &out, int indent_level,
-                  FunctionRemap *remap, string return_expr) {
-  CPPType *orig_type = remap->_return_type->get_orig_type();
-  CPPType *type = remap->_return_type->get_new_type();
-
-  if (remap->_return_type->new_type_is_atomic_string()) 
-  {
-    if (TypeManager::is_char_pointer(orig_type)) {
-      indent(out, indent_level)
-        << "return PyString_FromString(" << return_expr << ");\n";
-
-    } else {
-      indent(out, indent_level)
-        << "return PyString_FromStringAndSize("
-        << return_expr << ".data(), " << return_expr << ".length());\n";
-    }
-
-  } else if (TypeManager::is_unsigned_longlong(type)) 
-  {
-    indent(out, indent_level)
-      << "return PyLong_FromUnsignedLongLong(" << return_expr << ");\n";
-
-  } else if (TypeManager::is_longlong(type)) 
-  {
-    indent(out, indent_level)
-      << "return PyLong_FromLongLong(" << return_expr << ");\n";
-
-  } else if (TypeManager::is_integer(type)) {
-    indent(out, indent_level)
-      << "return PyInt_FromLong(" << return_expr << ");\n";
-
-  } else if (TypeManager::is_float(type)) {
-    indent(out, indent_level)
-      << "return PyFloat_FromDouble(" << return_expr << ");\n";
-
-  } else if (TypeManager::is_char_pointer(type)) {
-    indent(out, indent_level)
-      << "return PyString_FromString(" << return_expr << ");\n";
-
-  } else if (TypeManager::is_pointer_to_PyObject(type)) {
-    indent(out, indent_level)
-      << "return " << return_expr << ";\n";
-    
-  } else if (TypeManager::is_pointer(type)) {
-    indent(out, indent_level)
-      << "return PyInt_FromLong((int)" << return_expr << ");\n";
-
-  } else {
-    // Return None.
-    indent(out, indent_level)
-      << "return Py_BuildValue(\"\");\n";
-  }
-}
+// Filename: interfaceMakerPythonSimple.cxx
+// Created by:  drose (01Oct01)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "interfaceMakerPythonSimple.h"
+#include "interrogateBuilder.h"
+#include "interrogate.h"
+#include "functionRemap.h"
+#include "parameterRemapUnchanged.h"
+#include "typeManager.h"
+
+#include "interrogateDatabase.h"
+#include "interrogateType.h"
+#include "interrogateFunction.h"
+#include "cppFunctionType.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+InterfaceMakerPythonSimple::
+InterfaceMakerPythonSimple(InterrogateModuleDef *def) :
+  InterfaceMakerPython(def)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+InterfaceMakerPythonSimple::
+~InterfaceMakerPythonSimple() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::write_prototypes
+//       Access: Public, Virtual
+//  Description: Generates the list of function prototypes
+//               corresponding to the functions that will be output in
+//               write_functions().
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonSimple::
+write_prototypes(ostream &out,ostream *out_h) {
+  Functions::iterator fi;
+  for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
+    Function *func = (*fi);
+    write_prototype_for(out, func);
+  }
+
+  out << "\n";
+  InterfaceMakerPython::write_prototypes(out,out_h);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::write_functions
+//       Access: Public, Virtual
+//  Description: Generates the list of functions that are appropriate
+//               for this interface.  This function is called *before*
+//               write_prototypes(), above.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonSimple::
+write_functions(ostream &out) {
+  Functions::iterator fi;
+  for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
+    Function *func = (*fi);
+    write_function_for(out, func);
+  }
+
+  InterfaceMakerPython::write_functions(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::write_module
+//       Access: Public, Virtual
+//  Description: Generates whatever additional code is required to
+//               support a module file.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonSimple::
+write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) {
+  InterfaceMakerPython::write_module(out,out_h, def);
+
+  out << "static PyMethodDef python_simple_funcs[] = {\n";
+
+  Functions::iterator fi;
+  for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
+    Function *func = (*fi);
+    Function::Remaps::const_iterator ri;
+    for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
+      FunctionRemap *remap = (*ri);
+      out << "  { \"" << remap->_reported_name << "\", &" 
+          << remap->_wrapper_name << ", METH_VARARGS },\n";
+    }
+  }  
+  out << "  { NULL, NULL }\n"
+      << "};\n\n"
+
+      << "#ifdef _WIN32\n"
+      << "extern \"C\" __declspec(dllexport) void init" << def->library_name << "();\n"
+      << "#else\n"
+      << "extern \"C\" void init" << def->library_name << "();\n"
+      << "#endif\n\n"
+    
+      << "void init" << def->library_name << "() {\n"
+      << "  Py_InitModule(\"" << def->library_name
+      << "\", python_simple_funcs);\n"
+      << "}\n\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::synthesize_this_parameter
+//       Access: Public, Virtual
+//  Description: This method should be overridden and redefined to
+//               return true for interfaces that require the implicit
+//               "this" parameter, if present, to be passed as the
+//               first parameter to any wrapper functions.
+////////////////////////////////////////////////////////////////////
+bool InterfaceMakerPythonSimple::
+synthesize_this_parameter() {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::get_wrapper_prefix
+//       Access: Protected, Virtual
+//  Description: Returns the prefix string used to generate wrapper
+//               function names.
+////////////////////////////////////////////////////////////////////
+string InterfaceMakerPythonSimple::
+get_wrapper_prefix() {
+  return "_inP";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::get_unique_prefix
+//       Access: Protected, Virtual
+//  Description: Returns the prefix string used to generate unique
+//               symbolic names, which are not necessarily C-callable
+//               function names.
+////////////////////////////////////////////////////////////////////
+string InterfaceMakerPythonSimple::
+get_unique_prefix() {
+  return "p";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::record_function_wrapper
+//       Access: Protected, Virtual
+//  Description: Associates the function wrapper with its function in
+//               the appropriate structures in the database.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonSimple::
+record_function_wrapper(InterrogateFunction &ifunc, 
+                        FunctionWrapperIndex wrapper_index) {
+  ifunc._python_wrappers.push_back(wrapper_index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::write_prototype_for
+//       Access: Private
+//  Description: Writes the prototype for the indicated function.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonSimple::
+write_prototype_for(ostream &out, InterfaceMaker::Function *func) {
+  Function::Remaps::const_iterator ri;
+
+  for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
+    FunctionRemap *remap = (*ri);
+    if (!output_function_names) {
+      // If we're not saving the function names, don't export it from
+      // the library.
+      out << "static ";
+    } else {
+      out << "extern \"C\" ";
+    }
+    out << "PyObject *"
+        << remap->_wrapper_name << "(PyObject *self, PyObject *args);\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::write_function_for
+//       Access: Private
+//  Description: Writes the definition for a function that will call
+//               the indicated C++ function or method.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonSimple::
+write_function_for(ostream &out, InterfaceMaker::Function *func) {
+  Function::Remaps::const_iterator ri;
+
+  for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
+    FunctionRemap *remap = (*ri);
+    write_function_instance(out, func, remap);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::write_function_instance
+//       Access: Private
+//  Description: Writes out the particular function that handles a
+//               single instance of an overloaded function.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonSimple::write_function_instance(ostream &out, InterfaceMaker::Function *func,
+                        FunctionRemap *remap) {
+  out << "/*\n"
+      << " * Python simple wrapper for\n"
+      << " * ";
+  remap->write_orig_prototype(out, 0);
+  out << "\n"
+      << " */\n";
+
+  if (!output_function_names) {
+    // If we're not saving the function names, don't export it from
+    // the library.
+    out << "static ";
+  }
+
+  out << "PyObject *\n"
+      << remap->_wrapper_name << "(PyObject *, PyObject *args) {\n";
+
+  if (generate_spam) {
+    write_spam_message(out, remap);
+  }
+
+  string format_specifiers;
+  string parameter_list;
+  string container;
+  vector_string pexprs;
+  string extra_convert;
+  string extra_param_check;
+  string extra_cleanup;
+
+  // Make one pass through the parameter list.  We will output a
+  // one-line temporary variable definition for each parameter, while
+  // simultaneously building the ParseTuple() function call and also
+  // the parameter expression list for call_function().
+
+  int pn;
+  for (pn = 0; pn < (int)remap->_parameters.size(); ++pn) {
+    indent(out, 2);
+    CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type();
+    CPPType *type = remap->_parameters[pn]._remap->get_new_type();
+    string param_name = remap->get_parameter_name(pn);
+    
+    // This is the string to convert our local variable to the
+    // appropriate C++ type.  Normally this is just a cast.
+    string pexpr_string =
+      "(" + type->get_local_name(&parser) + ")" + param_name;
+    
+    if (remap->_parameters[pn]._remap->new_type_is_atomic_string()) {
+      if (TypeManager::is_char_pointer(orig_type)) {
+        out << "char *" << param_name;
+        format_specifiers += "s";
+        parameter_list += ", &" + param_name;
+        
+      } else {
+        out << "char *" << param_name
+            << "_str; int " << param_name << "_len";
+        format_specifiers += "s#";
+        parameter_list += ", &" + param_name
+          + "_str, &" + param_name + "_len";
+        pexpr_string = "basic_string<char>(" +
+          param_name + "_str, " +
+          param_name + "_len)";
+      }
+      
+    } else if (TypeManager::is_bool(type)) {
+      out << "PyObject *" << param_name;
+      format_specifiers += "O";
+      parameter_list += ", &" + param_name;
+      pexpr_string = "(PyObject_IsTrue(" + param_name + ")!=0)";
+
+    } else if (TypeManager::is_unsigned_longlong(type)) {
+      out << "PyObject *" << param_name;
+      format_specifiers += "O";
+      parameter_list += ", &" + param_name;
+      extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
+      extra_param_check += "|| (" + param_name + "_long == NULL)";
+      pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)";
+      extra_cleanup += " Py_XDECREF(" + param_name + "_long);";
+
+    } else if (TypeManager::is_longlong(type)) {
+      out << "PyObject *" << param_name;
+      format_specifiers += "O";
+      parameter_list += ", &" + param_name;
+      extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
+      extra_param_check += "|| (" + param_name + "_long == NULL)";
+      pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)";
+      extra_cleanup += " Py_XDECREF(" + param_name + "_long);";
+
+    #ifndef USE_PYTHON_2_2_OR_EARLIER
+    } else if (TypeManager::is_unsigned_integer(type)) {
+      out << "unsigned int " << param_name;
+      format_specifiers += "I";  // This requires Python 2.3 or better
+      parameter_list += ", &" + param_name;
+    #endif
+
+    } else if (TypeManager::is_integer(type)) {
+      out << "int " << param_name;
+      format_specifiers += "i";
+      parameter_list += ", &" + param_name;
+
+    } else if (TypeManager::is_float(type)) {
+      out << "double " << param_name;
+      format_specifiers += "d";
+      parameter_list += ", &" + param_name;
+
+    } else if (TypeManager::is_char_pointer(type)) {
+      out << "char *" << param_name;
+      format_specifiers += "s";
+      parameter_list += ", &" + param_name;
+
+    } else if (TypeManager::is_pointer_to_PyObject(type)) {
+      out << "PyObject *" << param_name;
+      format_specifiers += "O";
+      parameter_list += ", &" + param_name;
+      pexpr_string = param_name;
+
+    } else if (TypeManager::is_pointer(type)) {
+      out << "int " << param_name;
+      format_specifiers += "i";
+      parameter_list += ", &" + param_name;
+
+    } else {
+      // Ignore a parameter.
+      out << "PyObject *" << param_name;
+      format_specifiers += "O";
+      parameter_list += ", &" + param_name;
+    }
+
+    out << ";\n";
+    if (remap->_has_this && pn == 0) {
+      // The "this" parameter gets passed in separately.
+      container = pexpr_string;
+    }
+    pexprs.push_back(pexpr_string);
+  }
+
+  out << "  if (PyArg_ParseTuple(args, \"" << format_specifiers
+      << "\"" << parameter_list << ")) {\n";
+
+  if (!extra_convert.empty()) {
+    out << "   " << extra_convert << "\n";
+  }
+
+  if (!extra_param_check.empty()) {
+    out << "    if (" << extra_param_check.substr(3) << ") {\n";
+    if (!extra_cleanup.empty()) {
+      out << "     " << extra_cleanup << "\n";
+    }
+    out << "      PyErr_SetString(PyExc_TypeError, \"Invalid parameters.\");\n"
+        << "      return (PyObject *)NULL;\n"
+        << "    }\n";
+  }
+  
+  if (track_interpreter) {
+    out << "    in_interpreter = 0;\n";
+  }
+
+  if (!remap->_void_return && 
+      remap->_return_type->new_type_is_atomic_string()) {
+    // Treat strings as a special case.  We don't want to format the
+    // return expression.
+    string return_expr = remap->call_function(out, 4, false, container, pexprs);
+    CPPType *type = remap->_return_type->get_orig_type();
+    out << "    ";
+    type->output_instance(out, "return_value", &parser);
+    out << " = " << return_expr << ";\n";
+    
+    if (track_interpreter) {
+      out << "    in_interpreter = 1;\n";
+    }
+    if (!extra_cleanup.empty()) {
+      out << "   " << extra_cleanup << "\n";
+    }
+    
+    return_expr = manage_return_value(out, 4, remap, "return_value");
+    test_assert(out, 4);
+    pack_return_value(out, 4, remap, return_expr);
+    
+  } else {
+    string return_expr = remap->call_function(out, 4, true, container, pexprs);
+    if (return_expr.empty()) {
+      if (track_interpreter) {
+        out << "    in_interpreter = 1;\n";
+      }
+      if (!extra_cleanup.empty()) {
+        out << "   " << extra_cleanup << "\n";
+      }
+      test_assert(out, 4);
+      out << "    return Py_BuildValue(\"\");\n";
+      
+    } else {
+      CPPType *type = remap->_return_type->get_temporary_type();
+      out << "    ";
+      type->output_instance(out, "return_value", &parser);
+      out << " = " << return_expr << ";\n";
+      if (track_interpreter) {
+        out << "    in_interpreter = 1;\n";
+      }
+      if (!extra_cleanup.empty()) {
+        out << "   " << extra_cleanup << "\n";
+      }
+      
+      return_expr = manage_return_value(out, 4, remap, "return_value");
+      test_assert(out, 4);
+      pack_return_value(out, 4, remap, remap->_return_type->temporary_to_return(return_expr));
+    }
+  }
+  
+  out << "  }\n";
+  
+  out << "  return (PyObject *)NULL;\n";
+  out << "}\n\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InterfaceMakerPythonSimple::pack_return_value
+//       Access: Private
+//  Description: Outputs a command to pack the indicated expression,
+//               of the return_type type, as a Python return value.
+////////////////////////////////////////////////////////////////////
+void InterfaceMakerPythonSimple::
+pack_return_value(ostream &out, int indent_level,
+                  FunctionRemap *remap, string return_expr) {
+  CPPType *orig_type = remap->_return_type->get_orig_type();
+  CPPType *type = remap->_return_type->get_new_type();
+
+  if (remap->_return_type->new_type_is_atomic_string()) 
+  {
+    if (TypeManager::is_char_pointer(orig_type)) {
+      indent(out, indent_level)
+        << "return PyString_FromString(" << return_expr << ");\n";
+
+    } else {
+      indent(out, indent_level)
+        << "return PyString_FromStringAndSize("
+        << return_expr << ".data(), " << return_expr << ".length());\n";
+    }
+
+  } else if (TypeManager::is_unsigned_longlong(type)) 
+  {
+    indent(out, indent_level)
+      << "return PyLong_FromUnsignedLongLong(" << return_expr << ");\n";
+
+  } else if (TypeManager::is_longlong(type)) 
+  {
+    indent(out, indent_level)
+      << "return PyLong_FromLongLong(" << return_expr << ");\n";
+
+  #ifndef USE_PYTHON_2_2_OR_EARLIER
+  } else if (TypeManager::is_unsigned_integer(type)) {
+    indent(out, indent_level)
+      << "return PyLong_FromUnsignedLong(" << return_expr << ");\n";
+  #endif
+
+  } else if (TypeManager::is_integer(type)) {
+    indent(out, indent_level)
+      << "return PyInt_FromLong(" << return_expr << ");\n";
+
+  } else if (TypeManager::is_float(type)) {
+    indent(out, indent_level)
+      << "return PyFloat_FromDouble(" << return_expr << ");\n";
+
+  } else if (TypeManager::is_char_pointer(type)) {
+    indent(out, indent_level)
+      << "return PyString_FromString(" << return_expr << ");\n";
+
+  } else if (TypeManager::is_pointer_to_PyObject(type)) {
+    indent(out, indent_level)
+      << "return " << return_expr << ";\n";
+    
+  } else if (TypeManager::is_pointer(type)) {
+    indent(out, indent_level)
+      << "return PyInt_FromLong((int)" << return_expr << ");\n";
+
+  } else {
+    // Return None.
+    indent(out, indent_level)
+      << "return Py_BuildValue(\"\");\n";
+  }
+}