Browse Source

Merge branch 'master' into cmake

Sam Edwards 6 years ago
parent
commit
19ed9f6b47
100 changed files with 2491 additions and 5934 deletions
  1. 2 0
      .github/FUNDING.yml
  2. 6 0
      BACKERS.md
  3. 4 4
      README.md
  4. 3 0
      contrib/src/ai/aiBehaviors.h
  5. 2 1
      direct/src/actor/Actor.py
  6. 5 689
      direct/src/dcparser/dcClass.cxx
  7. 58 44
      direct/src/dcparser/dcClass.h
  8. 665 0
      direct/src/dcparser/dcClass_ext.cxx
  9. 93 0
      direct/src/dcparser/dcClass_ext.h
  10. 6 313
      direct/src/dcparser/dcField.cxx
  11. 13 15
      direct/src/dcparser/dcField.h
  12. 305 0
      direct/src/dcparser/dcField_ext.cxx
  13. 48 0
      direct/src/dcparser/dcField_ext.h
  14. 0 508
      direct/src/dcparser/dcPacker.cxx
  15. 17 14
      direct/src/dcparser/dcPacker.h
  16. 508 0
      direct/src/dcparser/dcPacker_ext.cxx
  17. 45 0
      direct/src/dcparser/dcPacker_ext.h
  18. 0 42
      direct/src/dcparser/dcPython.h
  19. 3 0
      direct/src/dcparser/p3dcparser_ext_composite.cxx
  20. 2 0
      direct/src/directtools/DirectUtil.py
  21. 14 7
      direct/src/dist/FreezeTool.py
  22. 56 0
      direct/src/dist/commands.py
  23. 4 3
      direct/src/distributed/cConnectionRepository.cxx
  24. 1 1
      direct/src/distributed/cConnectionRepository.h
  25. 4 0
      direct/src/distributed/cDistributedSmoothNodeBase.cxx
  26. 1 2
      direct/src/distributed/cDistributedSmoothNodeBase.h
  27. 4 4
      direct/src/distributed/config_distributed.h
  28. 2 1
      direct/src/gui/DirectDialog.py
  29. 9 7
      direct/src/gui/DirectEntry.py
  30. 25 3
      direct/src/gui/DirectEntryScroll.py
  31. 3 3
      direct/src/gui/DirectGuiBase.py
  32. 4 3
      direct/src/gui/DirectOptionMenu.py
  33. 6 1
      direct/src/gui/DirectScrolledFrame.py
  34. 5 4
      direct/src/gui/DirectScrolledList.py
  35. 1 19
      direct/src/interval/SoundInterval.py
  36. 199 62
      direct/src/p3d/DeploymentTools.py
  37. 18 0
      direct/src/showbase/BufferViewer.py
  38. 17 1
      direct/src/showbase/Loader.py
  39. 5 0
      direct/src/showbase/PythonUtil.py
  40. 4 1
      direct/src/showbase/ShowBase.py
  41. 1 0
      direct/src/showbase/ShowBaseGlobal.py
  42. 2 1
      direct/src/showbase/Transitions.py
  43. 2 1
      direct/src/tkpanels/MopathRecorder.py
  44. 25 15
      direct/src/tkpanels/ParticlePanel.py
  45. 0 108
      dmodels/src/level_editor/donaldsDockColors.txt
  46. 0 64
      dmodels/src/level_editor/donaldsDockStyles.txt
  47. BIN
      dmodels/src/level_editor/donalds_dock_layout.flt
  48. 0 61
      dmodels/src/level_editor/minniesMelodyLandColors.txt
  49. 0 79
      dmodels/src/level_editor/minniesMelodyLandStyles.txt
  50. BIN
      dmodels/src/level_editor/minnies_melody_land_layout.flt
  51. 0 65
      dmodels/src/level_editor/theBurrrghColors.txt
  52. 0 90
      dmodels/src/level_editor/theBurrrghStyles.txt
  53. BIN
      dmodels/src/level_editor/the_burrrgh_layout.flt
  54. 0 106
      dmodels/src/level_editor/toontownCentralColors.txt
  55. 0 84
      dmodels/src/level_editor/toontownCentralStyles.txt
  56. BIN
      dmodels/src/level_editor/toontown_central_layout.flt
  57. 43 0
      doc/ReleaseNotes
  58. 15 11
      dtool/src/cppparser/cppIdentifier.cxx
  59. 0 47
      dtool/src/dtoolbase/dllbase.txt
  60. 0 12
      dtool/src/dtoolbase/dtoolsymbols.h
  61. 6 6
      dtool/src/dtoolutil/iostream_ext.cxx
  62. 4 4
      dtool/src/dtoolutil/iostream_ext.h
  63. 10 1
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  64. 6 3
      dtool/src/interrogate/interrogate.cxx
  65. 8 1
      dtool/src/interrogatedb/dtool_super_base.cxx
  66. 1 1
      dtool/src/interrogatedb/py_compat.h
  67. 73 10
      dtool/src/interrogatedb/py_wrappers.cxx
  68. 4 4
      dtool/src/parser-inc/iostream
  69. 5 1
      dtool/src/prc/encryptStream.cxx
  70. 16 11
      dtool/src/prc/notify.cxx
  71. 6 0
      dtool/src/prc/notifyCategory.cxx
  72. 0 492
      dtool/src/pystub/pystub.cxx
  73. 0 38
      dtool/src/pystub/pystub.h
  74. 2 2
      makepanda/getversion.py
  75. 27 0
      makepanda/installer.nsi
  76. 1 1
      makepanda/installpanda.py
  77. 5 7
      makepanda/makepackage.py
  78. 31 56
      makepanda/makepanda.py
  79. 0 4
      makepanda/makepanda.vcproj
  80. 5 1
      makepanda/makepandacore.py
  81. 14 12
      makepanda/makewheel.py
  82. 8 3
      makepanda/test_wheel.py
  83. 0 8
      panda/src/audio/audioManager.cxx
  84. 0 3
      panda/src/audio/audioManager.h
  85. 0 17
      panda/src/audio/audioSound.cxx
  86. 1 8
      panda/src/audio/audioSound.h
  87. 0 23
      panda/src/audio/config_audio.cxx
  88. 1 13
      panda/src/audio/config_audio.h
  89. 1 1
      panda/src/audio/nullAudioManager.h
  90. 1 1
      panda/src/audio/nullAudioSound.h
  91. 0 102
      panda/src/audiotraits/config_milesAudio.cxx
  92. 0 37
      panda/src/audiotraits/config_milesAudio.h
  93. 0 39
      panda/src/audiotraits/globalMilesManager.I
  94. 0 446
      panda/src/audiotraits/globalMilesManager.cxx
  95. 0 117
      panda/src/audiotraits/globalMilesManager.h
  96. 0 1121
      panda/src/audiotraits/milesAudioManager.cxx
  97. 0 198
      panda/src/audiotraits/milesAudioManager.h
  98. 0 13
      panda/src/audiotraits/milesAudioSample.I
  99. 0 530
      panda/src/audiotraits/milesAudioSample.cxx
  100. 0 103
      panda/src/audiotraits/milesAudioSample.h

+ 2 - 0
.github/FUNDING.yml

@@ -0,0 +1,2 @@
+open_collective: panda3d
+

+ 6 - 0
BACKERS.md

@@ -24,6 +24,12 @@ This is a list of all the people who are contributing financially to Panda3D.  I
 * Sam Edwards
 * Max Voss
 
+## Enthusiasts
+
+![Benefactors](https://opencollective.com/panda3d/tiers/enthusiast.svg?avatarHeight=48&width=600)
+
+* Eric Thomson
+
 ## Backers
 
 ![Backers](https://opencollective.com/panda3d/tiers/backer.svg?avatarHeight=48&width=600)

+ 4 - 4
README.md

@@ -24,7 +24,7 @@ Installing Panda3D
 ==================
 
 The latest Panda3D SDK can be downloaded from
-[this page](https://www.panda3d.org/download/sdk-1-10-3/).
+[this page](https://www.panda3d.org/download/sdk-1-10-4-1/).
 If you are familiar with installing Python packages, you can use
 the following comand:
 
@@ -64,8 +64,8 @@ depending on whether you are on a 32-bit or 64-bit system, or you can
 [click here](https://github.com/rdb/panda3d-thirdparty) for instructions on
 building them from source.
 
-https://www.panda3d.org/download/panda3d-1.10.3/panda3d-1.10.3-tools-win64.zip
-https://www.panda3d.org/download/panda3d-1.10.3/panda3d-1.10.3-tools-win32.zip
+https://www.panda3d.org/download/panda3d-1.10.4.1/panda3d-1.10.4.1-tools-win64.zip
+https://www.panda3d.org/download/panda3d-1.10.4.1/panda3d-1.10.4.1-tools-win32.zip
 
 After acquiring these dependencies, you may simply build Panda3D from the
 command prompt using the following command.  (Change `14.1` to `14` if you are
@@ -135,7 +135,7 @@ macOS
 -----
 
 On macOS, you will need to download a set of precompiled thirdparty packages in order to
-compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.3/panda3d-1.10.3-tools-mac.tar.gz).
+compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.4.1/panda3d-1.10.4.1-tools-mac.tar.gz).
 
 After placing the thirdparty directory inside the panda3d source directory,
 you may build Panda3D using a command like the following:

+ 3 - 0
contrib/src/ai/aiBehaviors.h

@@ -28,6 +28,9 @@ class PathFollow;
 class PathFind;
 class ObstacleAvoidance;
 
+#include "flee.h"
+#include "evade.h"
+
 typedef std::list<Flee, std::allocator<Flee> > ListFlee;
 typedef std::list<Evade, std::allocator<Evade> > ListEvade;
 

+ 2 - 1
direct/src/actor/Actor.py

@@ -67,7 +67,7 @@ class Actor(DirectObject, NodePath):
 
         def __init__(self, filename = None, animBundle = None):
             self.filename = filename
-            self.animBundle = None
+            self.animBundle = animBundle
             self.animControl = None
 
         def makeCopy(self):
@@ -1947,6 +1947,7 @@ class Actor(DirectObject, NodePath):
                     animName = acc.getAnimName(i)
 
                     animDef = Actor.AnimDef()
+                    animDef.animBundle = animControl.getAnim()
                     animDef.animControl = animControl
                     self.__animControlDict[lodName][partName][animName] = animDef
 

+ 5 - 689
direct/src/dcparser/dcClass.cxx

@@ -16,22 +16,13 @@
 #include "dcAtomicField.h"
 #include "hashGenerator.h"
 #include "dcindent.h"
-#include "dcmsgtypes.h"
 
 #include "dcClassParameter.h"
 #include <algorithm>
 
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
-using std::ostream;
-using std::ostringstream;
 using std::string;
 
 #ifdef WITHIN_PANDA
-#include "pStatTimer.h"
-
 #ifndef CPPPARSER
 PStatCollector DCClass::_update_pcollector("App:Show code:readerPollTask:Update");
 PStatCollector DCClass::_generate_pcollector("App:Show code:readerPollTask:Generate");
@@ -86,10 +77,7 @@ DCClass(DCFile *dc_file, const string &name, bool is_struct, bool bogus_class) :
   _number = -1;
   _constructor = nullptr;
 
-#ifdef HAVE_PYTHON
-  _class_def = nullptr;
-  _owner_class_def = nullptr;
-#endif
+  _python_class_defs = nullptr;
 }
 
 /**
@@ -105,11 +93,6 @@ DCClass::
   for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
     delete (*fi);
   }
-
-#ifdef HAVE_PYTHON
-  Py_XDECREF(_class_def);
-  Py_XDECREF(_owner_class_def);
-#endif
 }
 
 /**
@@ -335,7 +318,7 @@ inherits_from_bogus_class() const {
  * Write a string representation of this instance to <out>.
  */
 void DCClass::
-output(ostream &out) const {
+output(std::ostream &out) const {
   if (_is_struct) {
     out << "struct";
   } else {
@@ -346,678 +329,11 @@ output(ostream &out) const {
   }
 }
 
-#ifdef HAVE_PYTHON
-/**
- * Returns true if the DCClass object has an associated Python class
- * definition, false otherwise.
- */
-bool DCClass::
-has_class_def() const {
-  return (_class_def != nullptr);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Sets the class object associated with this DistributedClass.  This object
- * will be used to construct new instances of the class.
- */
-void DCClass::
-set_class_def(PyObject *class_def) {
-  Py_XINCREF(class_def);
-  Py_XDECREF(_class_def);
-  _class_def = class_def;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns the class object that was previously associated with this
- * DistributedClass.  This will return a new reference to the object.
- */
-PyObject *DCClass::
-get_class_def() const {
-  if (_class_def == nullptr) {
-    Py_INCREF(Py_None);
-    return Py_None;
-  }
-
-  Py_INCREF(_class_def);
-  return _class_def;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns true if the DCClass object has an associated Python owner class
- * definition, false otherwise.
- */
-bool DCClass::
-has_owner_class_def() const {
-  return (_owner_class_def != nullptr);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Sets the owner class object associated with this DistributedClass.  This
- * object will be used to construct new owner instances of the class.
- */
-void DCClass::
-set_owner_class_def(PyObject *owner_class_def) {
-  Py_XINCREF(owner_class_def);
-  Py_XDECREF(_owner_class_def);
-  _owner_class_def = owner_class_def;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns the owner class object that was previously associated with this
- * DistributedClass.  This will return a new reference to the object.
- */
-PyObject *DCClass::
-get_owner_class_def() const {
-  if (_owner_class_def == nullptr) {
-    Py_INCREF(Py_None);
-    return Py_None;
-  }
-
-  Py_INCREF(_owner_class_def);
-  return _owner_class_def;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Extracts the update message out of the packer and applies it to the
- * indicated object by calling the appropriate method.
- */
-void DCClass::
-receive_update(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
-  PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
-    DCPacker packer;
-    const char *data = (const char *)di.get_datagram().get_data();
-    packer.set_unpack_data(data + di.get_current_index(),
-                           di.get_remaining_size(), false);
-
-    int field_id = packer.raw_unpack_uint16();
-    DCField *field = get_field_by_index(field_id);
-    if (field == nullptr) {
-            ostringstream strm;
-            strm
-                << "Received update for field " << field_id << ", not in class "
-                << get_name();
-            nassert_raise(strm.str());
-            return;
-    }
-
-    packer.begin_unpack(field);
-    field->receive_update(packer, distobj);
-    packer.end_unpack();
-
-    di.skip_bytes(packer.get_num_unpacked_bytes());
-
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a big datagram that includes all of the "required" fields that
- * are sent along with a normal "generate with required" message.  This is all
- * of the atomic fields that are marked "broadcast required".
- */
-void DCClass::
-receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
-  PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
-  DCPacker packer;
-  const char *data = (const char *)di.get_datagram().get_data();
-  packer.set_unpack_data(data + di.get_current_index(),
-                         di.get_remaining_size(), false);
-
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->as_molecular_field() == nullptr &&
-        field->is_required() && field->is_broadcast()) {
-      packer.begin_unpack(field);
-      field->receive_update(packer, distobj);
-      if (!packer.end_unpack()) {
-        break;
-      }
-    }
-  }
-
-  di.skip_bytes(packer.get_num_unpacked_bytes());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a big datagram that includes all of the "required" fields that
- * are sent along with a normal "generate with required" message.  This is all
- * of the atomic fields that are marked "broadcast ownrecv". Should be used
- * for 'owner-view' objects.
- */
-void DCClass::
-receive_update_broadcast_required_owner(PyObject *distobj,
-                                        DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
-  PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
-  DCPacker packer;
-  const char *data = (const char *)di.get_datagram().get_data();
-  packer.set_unpack_data(data + di.get_current_index(),
-                         di.get_remaining_size(), false);
-
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->as_molecular_field() == nullptr &&
-        field->is_required() && (field->is_ownrecv() || field->is_broadcast())) {
-      packer.begin_unpack(field);
-      field->receive_update(packer, distobj);
-      if (!packer.end_unpack()) {
-        break;
-      }
-    }
-  }
-
-  di.skip_bytes(packer.get_num_unpacked_bytes());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a big datagram that includes all of the "required" fields that
- * are sent when an avatar is created.  This is all of the atomic fields that
- * are marked "required", whether they are broadcast or not.
- */
-void DCClass::
-receive_update_all_required(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
-  PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
-  DCPacker packer;
-  const char *data = (const char *)di.get_datagram().get_data();
-  packer.set_unpack_data(data + di.get_current_index(),
-                         di.get_remaining_size(), false);
-
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->as_molecular_field() == nullptr &&
-        field->is_required()) {
-      packer.begin_unpack(field);
-      field->receive_update(packer, distobj);
-      if (!packer.end_unpack()) {
-        break;
-      }
-    }
-  }
-
-  di.skip_bytes(packer.get_num_unpacked_bytes());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a datagram that lists some additional fields that are broadcast
- * in one chunk.
- */
-void DCClass::
-receive_update_other(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
-  PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
-  int num_fields = di.get_uint16();
-  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
-    receive_update(distobj, di);
-  }
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes an update for a named field from a packed value blob.
- */
-void DCClass::
-direct_update(PyObject *distobj, const string &field_name,
-              const vector_uchar &value_blob) {
-  DCField *field = get_field_by_name(field_name);
-  nassertv_always(field != nullptr);
-
-  DCPacker packer;
-  packer.set_unpack_data(value_blob);
-  packer.begin_unpack(field);
-  field->receive_update(packer, distobj);
-  packer.end_unpack();
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes an update for a named field from a packed datagram.
- */
-void DCClass::
-direct_update(PyObject *distobj, const string &field_name,
-              const Datagram &datagram) {
-  DCField *field = get_field_by_name(field_name);
-  nassertv_always(field != nullptr);
-
-  DCPacker packer;
-  packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false);
-  packer.begin_unpack(field);
-  field->receive_update(packer, distobj);
-  packer.end_unpack();
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Looks up the current value of the indicated field by calling the
- * appropriate get*() function, then packs that value into the datagram.  This
- * field is presumably either a required field or a specified optional field,
- * and we are building up a datagram for the generate-with-required message.
- *
- * Returns true on success, false on failure.
- */
-bool DCClass::
-pack_required_field(Datagram &datagram, PyObject *distobj,
-                    const DCField *field) const {
-  DCPacker packer;
-  packer.begin_pack(field);
-  if (!pack_required_field(packer, distobj, field)) {
-    return false;
-  }
-  if (!packer.end_pack()) {
-    return false;
-  }
-
-  datagram.append_data(packer.get_data(), packer.get_length());
-  return true;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Looks up the current value of the indicated field by calling the
- * appropriate get*() function, then packs that value into the packer.  This
- * field is presumably either a required field or a specified optional field,
- * and we are building up a datagram for the generate-with-required message.
- *
- * Returns true on success, false on failure.
- */
-bool DCClass::
-pack_required_field(DCPacker &packer, PyObject *distobj,
-                    const DCField *field) const {
-  const DCParameter *parameter = field->as_parameter();
-  if (parameter != nullptr) {
-    // This is the easy case: to pack a parameter, we just look on the class
-    // object for the data element.
-    string field_name = field->get_name();
-
-    if (!PyObject_HasAttrString(distobj, (char *)field_name.c_str())) {
-      // If the attribute is not defined, but the field has a default value
-      // specified, quietly pack the default value.
-      if (field->has_default_value()) {
-        packer.pack_default_value();
-        return true;
-      }
-
-      // If there is no default value specified, it's an error.
-      ostringstream strm;
-      strm << "Data element " << field_name
-           << ", required by dc file for dclass " << get_name()
-           << ", not defined on object";
-      nassert_raise(strm.str());
-      return false;
-    }
-    PyObject *result =
-      PyObject_GetAttrString(distobj, (char *)field_name.c_str());
-    nassertr(result != nullptr, false);
-
-    // Now pack the value into the datagram.
-    bool pack_ok = parameter->pack_args(packer, result);
-    Py_DECREF(result);
-
-    return pack_ok;
-  }
-
-  if (field->as_molecular_field() != nullptr) {
-    ostringstream strm;
-    strm << "Cannot pack molecular field " << field->get_name()
-         << " for generate";
-    nassert_raise(strm.str());
-    return false;
-  }
-
-  const DCAtomicField *atom = field->as_atomic_field();
-  nassertr(atom != nullptr, false);
-
-  // We need to get the initial value of this field.  There isn't a good,
-  // robust way to get this; presently, we just mangle the "setFoo()" name of
-  // the required field into "getFoo()" and call that.
-  string setter_name = atom->get_name();
-
-  if (setter_name.empty()) {
-    ostringstream strm;
-    strm << "Required field is unnamed!";
-    nassert_raise(strm.str());
-    return false;
-  }
-
-  if (atom->get_num_elements() == 0) {
-    // It sure doesn't make sense to have a required field with no parameters.
-    // What data, exactly, is required?
-    ostringstream strm;
-    strm << "Required field " << setter_name << " has no parameters!";
-    nassert_raise(strm.str());
-    return false;
-  }
-
-  string getter_name = setter_name;
-  if (setter_name.substr(0, 3) == "set") {
-    // If the original method started with "set", we mangle this directly to
-    // "get".
-    getter_name[0] = 'g';
-
-  } else {
-    // Otherwise, we add a "get" prefix, and capitalize the next letter.
-    getter_name = "get" + setter_name;
-    getter_name[3] = toupper(getter_name[3]);
-  }
-
-  // Now we have to look up the getter on the distributed object and call it.
-  if (!PyObject_HasAttrString(distobj, (char *)getter_name.c_str())) {
-    // As above, if there's no getter but the field has a default value
-    // specified, quietly pack the default value.
-    if (field->has_default_value()) {
-      packer.pack_default_value();
-      return true;
-    }
-
-    // Otherwise, with no default value it's an error.
-    ostringstream strm;
-    strm << "Distributed class " << get_name()
-         << " doesn't have getter named " << getter_name
-         << " to match required field " << setter_name;
-    nassert_raise(strm.str());
-    return false;
-  }
-  PyObject *func =
-    PyObject_GetAttrString(distobj, (char *)getter_name.c_str());
-  nassertr(func != nullptr, false);
-
-  PyObject *empty_args = PyTuple_New(0);
-  PyObject *result = PyObject_CallObject(func, empty_args);
-  Py_DECREF(empty_args);
-  Py_DECREF(func);
-  if (result == nullptr) {
-    // We don't set this as an exception, since presumably the Python method
-    // itself has already triggered a Python exception.
-    std::cerr << "Error when calling " << getter_name << "\n";
-    return false;
-  }
-
-  if (atom->get_num_elements() == 1) {
-    // In this case, we expect the getter to return one object, which we wrap
-    // up in a tuple.
-    PyObject *tuple = PyTuple_New(1);
-    PyTuple_SET_ITEM(tuple, 0, result);
-    result = tuple;
-
-  } else {
-    // Otherwise, it had better already be a sequence or tuple of some sort.
-    if (!PySequence_Check(result)) {
-      ostringstream strm;
-      strm << "Since dclass " << get_name() << " method " << setter_name
-           << " is declared to have multiple parameters, Python function "
-           << getter_name << " must return a list or tuple.\n";
-      nassert_raise(strm.str());
-      return false;
-    }
-  }
-
-  // Now pack the arguments into the datagram.
-  bool pack_ok = atom->pack_args(packer, result);
-  Py_DECREF(result);
-
-  return pack_ok;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the client.
- */
-Datagram DCClass::
-client_format_update(const string &field_name, DOID_TYPE do_id,
-                     PyObject *args) const {
-  DCField *field = get_field_by_name(field_name);
-  if (field == nullptr) {
-    ostringstream strm;
-    strm << "No field named " << field_name << " in class " << get_name()
-         << "\n";
-    nassert_raise(strm.str());
-    return Datagram();
-  }
-
-  return field->client_format_update(do_id, args);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the AI.
- */
-Datagram DCClass::
-ai_format_update(const string &field_name, DOID_TYPE do_id,
-                 CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
-  DCField *field = get_field_by_name(field_name);
-  if (field == nullptr) {
-    ostringstream strm;
-    strm << "No field named " << field_name << " in class " << get_name()
-         << "\n";
-    nassert_raise(strm.str());
-    return Datagram();
-  }
-
-  return field->ai_format_update(do_id, to_id, from_id, args);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update,
- * using the indicated msg type for the indicated distributed object from the
- * AI.
- */
-Datagram DCClass::
-ai_format_update_msg_type(const string &field_name, DOID_TYPE do_id,
-                 CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
-  DCField *field = get_field_by_name(field_name);
-  if (field == nullptr) {
-    ostringstream strm;
-    strm << "No field named " << field_name << " in class " << get_name()
-         << "\n";
-    nassert_raise(strm.str());
-    return Datagram();
-  }
-
-  return field->ai_format_update_msg_type(do_id, to_id, from_id, msg_type, args);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to generate a new
- * distributed object from the client.  This requires querying the object for
- * the initial value of its required fields.
- *
- * optional_fields is a list of fieldNames to generate in addition to the
- * normal required fields.
- *
- * This method is only called by the CMU implementation.
- */
-Datagram DCClass::
-client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
-                           ZONEID_TYPE zone_id,
-                           PyObject *optional_fields) const {
-  DCPacker packer;
-
-  packer.raw_pack_uint16(CLIENT_OBJECT_GENERATE_CMU);
-
-  packer.raw_pack_uint32(zone_id);
-  packer.raw_pack_uint16(_number);
-  packer.raw_pack_uint32(do_id);
-
-  // Specify all of the required fields.
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields; ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->is_required() && field->as_molecular_field() == nullptr) {
-      packer.begin_pack(field);
-      if (!pack_required_field(packer, distobj, field)) {
-        return Datagram();
-      }
-      packer.end_pack();
-    }
-  }
-
-  // Also specify the optional fields.
-  int num_optional_fields = 0;
-  if (PyObject_IsTrue(optional_fields)) {
-    num_optional_fields = PySequence_Size(optional_fields);
-  }
-  packer.raw_pack_uint16(num_optional_fields);
-
-  for (int i = 0; i < num_optional_fields; i++) {
-    PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
-#if PY_MAJOR_VERSION >= 3
-    string field_name = PyUnicode_AsUTF8(py_field_name);
-#else
-    string field_name = PyString_AsString(py_field_name);
-#endif
-    Py_XDECREF(py_field_name);
-
-    DCField *field = get_field_by_name(field_name);
-    if (field == nullptr) {
-      ostringstream strm;
-      strm << "No field named " << field_name << " in class " << get_name()
-           << "\n";
-      nassert_raise(strm.str());
-      return Datagram();
-    }
-    packer.raw_pack_uint16(field->get_number());
-    packer.begin_pack(field);
-    if (!pack_required_field(packer, distobj, field)) {
-      return Datagram();
-    }
-    packer.end_pack();
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to generate a new
- * distributed object from the AI. This requires querying the object for the
- * initial value of its required fields.
- *
- * optional_fields is a list of fieldNames to generate in addition to the
- * normal required fields.
- */
-Datagram DCClass::
-ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
-                   DOID_TYPE parent_id, ZONEID_TYPE zone_id,
-                   CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id,
-                   PyObject *optional_fields) const {
-  DCPacker packer;
-
-  packer.raw_pack_uint8(1);
-  packer.RAW_PACK_CHANNEL(district_channel_id);
-  packer.RAW_PACK_CHANNEL(from_channel_id);
-    // packer.raw_pack_uint8('A');
-
-  bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0);
-
-  if (has_optional_fields) {
-    packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER);
-  } else {
-    packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED);
-  }
-
-  packer.raw_pack_uint32(do_id);
-  // Parent is a bit overloaded; this parent is not about inheritance, this
-  // one is about the visibility container parent, i.e.  the zone parent:
-  packer.raw_pack_uint32(parent_id);
-  packer.raw_pack_uint32(zone_id);
-  packer.raw_pack_uint16(_number);
-
-  // Specify all of the required fields.
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields; ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->is_required() && field->as_molecular_field() == nullptr) {
-      packer.begin_pack(field);
-      if (!pack_required_field(packer, distobj, field)) {
-        return Datagram();
-      }
-      packer.end_pack();
-    }
-  }
-
-  // Also specify the optional fields.
-  if (has_optional_fields) {
-    int num_optional_fields = PySequence_Size(optional_fields);
-    packer.raw_pack_uint16(num_optional_fields);
-
-    for (int i = 0; i < num_optional_fields; ++i) {
-      PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
-#if PY_MAJOR_VERSION >= 3
-      string field_name = PyUnicode_AsUTF8(py_field_name);
-#else
-      string field_name = PyString_AsString(py_field_name);
-#endif
-      Py_XDECREF(py_field_name);
-
-      DCField *field = get_field_by_name(field_name);
-      if (field == nullptr) {
-        ostringstream strm;
-        strm << "No field named " << field_name << " in class " << get_name()
-             << "\n";
-        nassert_raise(strm.str());
-        return Datagram();
-      }
-
-      packer.raw_pack_uint16(field->get_number());
-
-      packer.begin_pack(field);
-      if (!pack_required_field(packer, distobj, field)) {
-        return Datagram();
-      }
-      packer.end_pack();
-    }
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
 /**
  * Write a string representation of this instance to <out>.
  */
 void DCClass::
-output(ostream &out, bool brief) const {
+output(std::ostream &out, bool brief) const {
   output_instance(out, brief, "", "", "");
 }
 
@@ -1026,7 +342,7 @@ output(ostream &out, bool brief) const {
  * stream.
  */
 void DCClass::
-write(ostream &out, bool brief, int indent_level) const {
+write(std::ostream &out, bool brief, int indent_level) const {
   indent(out, indent_level);
   if (_is_struct) {
     out << "struct";
@@ -1086,7 +402,7 @@ write(ostream &out, bool brief, int indent_level) const {
  * stream.
  */
 void DCClass::
-output_instance(ostream &out, bool brief, const string &prename,
+output_instance(std::ostream &out, bool brief, const string &prename,
                 const string &name, const string &postname) const {
   if (_is_struct) {
     out << "struct";

+ 58 - 44
direct/src/dcparser/dcClass.h

@@ -17,11 +17,12 @@
 #include "dcbase.h"
 #include "dcField.h"
 #include "dcDeclaration.h"
-#include "dcPython.h"
 
 #ifdef WITHIN_PANDA
 #include "pStatCollector.h"
 #include "configVariableBool.h"
+#include "extension.h"
+#include "datagramIterator.h"
 
 extern ConfigVariableBool dc_multiple_inheritance;
 extern ConfigVariableBool dc_virtual_inheritance;
@@ -80,44 +81,52 @@ PUBLISHED:
 
   virtual void output(std::ostream &out) const;
 
-#ifdef HAVE_PYTHON
-  bool has_class_def() const;
-  void set_class_def(PyObject *class_def);
-  PyObject *get_class_def() const;
-  bool has_owner_class_def() const;
-  void set_owner_class_def(PyObject *owner_class_def);
-  PyObject *get_owner_class_def() const;
-
-  void receive_update(PyObject *distobj, DatagramIterator &di) const;
-  void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const;
-  void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const;
-  void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const;
-  void receive_update_other(PyObject *distobj, DatagramIterator &di) const;
-
-  void direct_update(PyObject *distobj, const std::string &field_name,
-                     const vector_uchar &value_blob);
-  void direct_update(PyObject *distobj, const std::string &field_name,
-                     const Datagram &datagram);
-  bool pack_required_field(Datagram &datagram, PyObject *distobj,
-                           const DCField *field) const;
-  bool pack_required_field(DCPacker &packer, PyObject *distobj,
-                           const DCField *field) const;
-
-
-
-  Datagram client_format_update(const std::string &field_name,
-                                DOID_TYPE do_id, PyObject *args) const;
-  Datagram ai_format_update(const std::string &field_name, DOID_TYPE do_id,
-                            CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const;
-  Datagram ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
-                            CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const;
-  Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id, ZONEID_TYPE parent_id, ZONEID_TYPE zone_id,
-                              CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id,
-                              PyObject *optional_fields) const;
-  Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
-                                      ZONEID_TYPE zone_id,                                                           PyObject *optional_fields) const;
+  EXTENSION(bool has_class_def() const);
+  EXTENSION(void set_class_def(PyObject *class_def));
+  EXTENSION(PyObject *get_class_def() const);
+  EXTENSION(bool has_owner_class_def() const);
+  EXTENSION(void set_owner_class_def(PyObject *owner_class_def));
+  EXTENSION(PyObject *get_owner_class_def() const);
+
+  EXTENSION(void receive_update(PyObject *distobj, DatagramIterator &di) const);
+  EXTENSION(void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const);
+  EXTENSION(void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const);
+  EXTENSION(void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const);
+  EXTENSION(void receive_update_other(PyObject *distobj, DatagramIterator &di) const);
+
+  EXTENSION(void direct_update(PyObject *distobj, const std::string &field_name,
+                               const vector_uchar &value_blob));
+  EXTENSION(void direct_update(PyObject *distobj, const std::string &field_name,
+                               const Datagram &datagram));
+  EXTENSION(bool pack_required_field(Datagram &datagram, PyObject *distobj,
+                                     const DCField *field) const);
+  EXTENSION(bool pack_required_field(DCPacker &packer, PyObject *distobj,
+                                     const DCField *field) const);
+
+
+
+  EXTENSION(Datagram client_format_update(const std::string &field_name,
+                                          DOID_TYPE do_id, PyObject *args) const);
+  EXTENSION(Datagram ai_format_update(const std::string &field_name,
+                                      DOID_TYPE do_id,
+                                      CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+                                      PyObject *args) const);
+  EXTENSION(Datagram ai_format_update_msg_type(const std::string &field_name,
+                                               DOID_TYPE do_id,
+                                               CHANNEL_TYPE to_id,
+                                               CHANNEL_TYPE from_id,
+                                               int msg_type,
+                                               PyObject *args) const);
+  EXTENSION(Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
+                                        ZONEID_TYPE parent_id,
+                                        ZONEID_TYPE zone_id,
+                                        CHANNEL_TYPE district_channel_id,
+                                        CHANNEL_TYPE from_channel_id,
+                                        PyObject *optional_fields) const);
+  EXTENSION(Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
+                                                ZONEID_TYPE zone_id,
+                                                PyObject *optional_fields) const);
 
-#endif
 
 public:
   virtual void output(std::ostream &out, bool brief) const;
@@ -136,8 +145,8 @@ private:
   void shadow_inherited_field(const std::string &name);
 
 #ifdef WITHIN_PANDA
-  PStatCollector _class_update_pcollector;
-  PStatCollector _class_generate_pcollector;
+  mutable PStatCollector _class_update_pcollector;
+  mutable PStatCollector _class_generate_pcollector;
   static PStatCollector _update_pcollector;
   static PStatCollector _generate_pcollector;
 #endif
@@ -163,12 +172,17 @@ private:
   typedef pmap<int, DCField *> FieldsByIndex;
   FieldsByIndex _fields_by_index;
 
-#ifdef HAVE_PYTHON
-  PyObject *_class_def;
-  PyObject *_owner_class_def;
-#endif
+  // See pandaNode.h for an explanation of this trick
+  class PythonClassDefs : public ReferenceCount {
+  public:
+    virtual ~PythonClassDefs() {};
+  };
+  PT(PythonClassDefs) _python_class_defs;
 
   friend class DCField;
+#ifdef WITHIN_PANDA
+  friend class Extension<DCClass>;
+#endif
 };
 
 #include "dcClass.I"

+ 665 - 0
direct/src/dcparser/dcClass_ext.cxx

@@ -0,0 +1,665 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcClass_ext.cxx
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#include "dcClass_ext.h"
+#include "dcField_ext.h"
+#include "dcAtomicField.h"
+#include "dcPacker.h"
+#include "dcmsgtypes.h"
+
+#include "datagram.h"
+#include "datagramIterator.h"
+#include "pStatTimer.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Returns true if the DCClass object has an associated Python class
+ * definition, false otherwise.
+ */
+bool Extension<DCClass>::
+has_class_def() const {
+  return _this->_python_class_defs != nullptr
+      && ((PythonClassDefsImpl *)_this->_python_class_defs.p())->_class_def != nullptr;
+}
+
+/**
+ * Sets the class object associated with this DistributedClass.  This object
+ * will be used to construct new instances of the class.
+ */
+void Extension<DCClass>::
+set_class_def(PyObject *class_def) {
+  PythonClassDefsImpl *defs = do_get_defs();
+
+  Py_XINCREF(class_def);
+  Py_XDECREF(defs->_class_def);
+  defs->_class_def = class_def;
+}
+
+/**
+ * Returns the class object that was previously associated with this
+ * DistributedClass.  This will return a new reference to the object.
+ */
+PyObject *Extension<DCClass>::
+get_class_def() const {
+  if (!has_class_def()) {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+
+  PythonClassDefsImpl *defs = do_get_defs();
+  Py_INCREF(defs->_class_def);
+  return defs->_class_def;
+}
+
+/**
+ * Returns true if the DCClass object has an associated Python owner class
+ * definition, false otherwise.
+ */
+bool Extension<DCClass>::
+has_owner_class_def() const {
+  return _this->_python_class_defs != nullptr
+      && ((PythonClassDefsImpl *)_this->_python_class_defs.p())->_owner_class_def != nullptr;
+}
+
+/**
+ * Sets the owner class object associated with this DistributedClass.  This
+ * object will be used to construct new owner instances of the class.
+ */
+void Extension<DCClass>::
+set_owner_class_def(PyObject *owner_class_def) {
+  PythonClassDefsImpl *defs = do_get_defs();
+
+  Py_XINCREF(owner_class_def);
+  Py_XDECREF(defs->_owner_class_def);
+  defs->_owner_class_def = owner_class_def;
+}
+
+/**
+ * Returns the owner class object that was previously associated with this
+ * DistributedClass.  This will return a new reference to the object.
+ */
+PyObject *Extension<DCClass>::
+get_owner_class_def() const {
+  if (!has_owner_class_def()) {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+
+  PythonClassDefsImpl *defs = do_get_defs();
+  Py_INCREF(defs->_owner_class_def);
+  return defs->_owner_class_def;
+}
+
+/**
+ * Extracts the update message out of the packer and applies it to the
+ * indicated object by calling the appropriate method.
+ */
+void Extension<DCClass>::
+receive_update(PyObject *distobj, DatagramIterator &di) const {
+  PStatTimer timer(_this->_class_update_pcollector);
+  DCPacker packer;
+  const char *data = (const char *)di.get_datagram().get_data();
+  packer.set_unpack_data(data + di.get_current_index(),
+                         di.get_remaining_size(), false);
+
+  int field_id = packer.raw_unpack_uint16();
+  DCField *field = _this->get_field_by_index(field_id);
+  if (field == nullptr) {
+    ostringstream strm;
+    strm
+        << "Received update for field " << field_id << ", not in class "
+        << _this->get_name();
+    nassert_raise(strm.str());
+    return;
+  }
+
+  packer.begin_unpack(field);
+  invoke_extension(field).receive_update(packer, distobj);
+  packer.end_unpack();
+
+  di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a big datagram that includes all of the "required" fields that
+ * are sent along with a normal "generate with required" message.  This is all
+ * of the atomic fields that are marked "broadcast required".
+ */
+void Extension<DCClass>::
+receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const {
+  PStatTimer timer(_this->_class_update_pcollector);
+  DCPacker packer;
+  const char *data = (const char *)di.get_datagram().get_data();
+  packer.set_unpack_data(data + di.get_current_index(),
+                         di.get_remaining_size(), false);
+
+  int num_fields = _this->get_num_inherited_fields();
+  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+    DCField *field = _this->get_inherited_field(i);
+    if (field->as_molecular_field() == nullptr &&
+        field->is_required() && field->is_broadcast()) {
+      packer.begin_unpack(field);
+      invoke_extension(field).receive_update(packer, distobj);
+      if (!packer.end_unpack()) {
+        break;
+      }
+    }
+  }
+
+  di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a big datagram that includes all of the "required" fields that
+ * are sent along with a normal "generate with required" message.  This is all
+ * of the atomic fields that are marked "broadcast ownrecv". Should be used
+ * for 'owner-view' objects.
+ */
+void Extension<DCClass>::
+receive_update_broadcast_required_owner(PyObject *distobj,
+                                        DatagramIterator &di) const {
+  PStatTimer timer(_this->_class_update_pcollector);
+  DCPacker packer;
+  const char *data = (const char *)di.get_datagram().get_data();
+  packer.set_unpack_data(data + di.get_current_index(),
+                         di.get_remaining_size(), false);
+
+  int num_fields = _this->get_num_inherited_fields();
+  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+    DCField *field = _this->get_inherited_field(i);
+    if (field->as_molecular_field() == nullptr &&
+        field->is_required() && (field->is_ownrecv() || field->is_broadcast())) {
+      packer.begin_unpack(field);
+      invoke_extension(field).receive_update(packer, distobj);
+      if (!packer.end_unpack()) {
+        break;
+      }
+    }
+  }
+
+  di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a big datagram that includes all of the "required" fields that
+ * are sent when an avatar is created.  This is all of the atomic fields that
+ * are marked "required", whether they are broadcast or not.
+ */
+void Extension<DCClass>::
+receive_update_all_required(PyObject *distobj, DatagramIterator &di) const {
+  PStatTimer timer(_this->_class_update_pcollector);
+  DCPacker packer;
+  const char *data = (const char *)di.get_datagram().get_data();
+  packer.set_unpack_data(data + di.get_current_index(),
+                         di.get_remaining_size(), false);
+
+  int num_fields = _this->get_num_inherited_fields();
+  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+    DCField *field = _this->get_inherited_field(i);
+    if (field->as_molecular_field() == nullptr &&
+        field->is_required()) {
+      packer.begin_unpack(field);
+      invoke_extension(field).receive_update(packer, distobj);
+      if (!packer.end_unpack()) {
+        break;
+      }
+    }
+  }
+
+  di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a datagram that lists some additional fields that are broadcast
+ * in one chunk.
+ */
+void Extension<DCClass>::
+receive_update_other(PyObject *distobj, DatagramIterator &di) const {
+  PStatTimer timer(_this->_class_update_pcollector);
+  int num_fields = di.get_uint16();
+  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+    receive_update(distobj, di);
+  }
+}
+
+/**
+ * Processes an update for a named field from a packed value blob.
+ */
+void Extension<DCClass>::
+direct_update(PyObject *distobj, const std::string &field_name,
+              const vector_uchar &value_blob) {
+  DCField *field = _this->get_field_by_name(field_name);
+  nassertv_always(field != nullptr);
+
+  DCPacker packer;
+  packer.set_unpack_data(value_blob);
+  packer.begin_unpack(field);
+  invoke_extension(field).receive_update(packer, distobj);
+  packer.end_unpack();
+}
+
+/**
+ * Processes an update for a named field from a packed datagram.
+ */
+void Extension<DCClass>::
+direct_update(PyObject *distobj, const std::string &field_name,
+              const Datagram &datagram) {
+  DCField *field = _this->get_field_by_name(field_name);
+  nassertv_always(field != nullptr);
+
+  DCPacker packer;
+  packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false);
+  packer.begin_unpack(field);
+  invoke_extension(field).receive_update(packer, distobj);
+  packer.end_unpack();
+}
+
+/**
+ * Looks up the current value of the indicated field by calling the
+ * appropriate get*() function, then packs that value into the datagram.  This
+ * field is presumably either a required field or a specified optional field,
+ * and we are building up a datagram for the generate-with-required message.
+ *
+ * Returns true on success, false on failure.
+ */
+bool Extension<DCClass>::
+pack_required_field(Datagram &datagram, PyObject *distobj,
+                    const DCField *field) const {
+  DCPacker packer;
+  packer.begin_pack(field);
+  if (!pack_required_field(packer, distobj, field)) {
+    return false;
+  }
+  if (!packer.end_pack()) {
+    return false;
+  }
+
+  datagram.append_data(packer.get_data(), packer.get_length());
+  return true;
+}
+
+/**
+ * Looks up the current value of the indicated field by calling the
+ * appropriate get*() function, then packs that value into the packer.  This
+ * field is presumably either a required field or a specified optional field,
+ * and we are building up a datagram for the generate-with-required message.
+ *
+ * Returns true on success, false on failure.
+ */
+bool Extension<DCClass>::
+pack_required_field(DCPacker &packer, PyObject *distobj,
+                    const DCField *field) const {
+  using std::ostringstream;
+
+  const DCParameter *parameter = field->as_parameter();
+  if (parameter != nullptr) {
+    // This is the easy case: to pack a parameter, we just look on the class
+    // object for the data element.
+    std::string field_name = field->get_name();
+
+    if (!PyObject_HasAttrString(distobj, (char *)field_name.c_str())) {
+      // If the attribute is not defined, but the field has a default value
+      // specified, quietly pack the default value.
+      if (field->has_default_value()) {
+        packer.pack_default_value();
+        return true;
+      }
+
+      // If there is no default value specified, it's an error.
+      ostringstream strm;
+      strm << "Data element " << field_name
+           << ", required by dc file for dclass " << _this->get_name()
+           << ", not defined on object";
+      nassert_raise(strm.str());
+      return false;
+    }
+    PyObject *result =
+      PyObject_GetAttrString(distobj, (char *)field_name.c_str());
+    nassertr(result != nullptr, false);
+
+    // Now pack the value into the datagram.
+    bool pack_ok = invoke_extension((DCField *)parameter).pack_args(packer, result);
+    Py_DECREF(result);
+
+    return pack_ok;
+  }
+
+  if (field->as_molecular_field() != nullptr) {
+    ostringstream strm;
+    strm << "Cannot pack molecular field " << field->get_name()
+         << " for generate";
+    nassert_raise(strm.str());
+    return false;
+  }
+
+  const DCAtomicField *atom = field->as_atomic_field();
+  nassertr(atom != nullptr, false);
+
+  // We need to get the initial value of this field.  There isn't a good,
+  // robust way to get this; presently, we just mangle the "setFoo()" name of
+  // the required field into "getFoo()" and call that.
+  std::string setter_name = atom->get_name();
+
+  if (setter_name.empty()) {
+    ostringstream strm;
+    strm << "Required field is unnamed!";
+    nassert_raise(strm.str());
+    return false;
+  }
+
+  if (atom->get_num_elements() == 0) {
+    // It sure doesn't make sense to have a required field with no parameters.
+    // What data, exactly, is required?
+    ostringstream strm;
+    strm << "Required field " << setter_name << " has no parameters!";
+    nassert_raise(strm.str());
+    return false;
+  }
+
+  std::string getter_name = setter_name;
+  if (setter_name.substr(0, 3) == "set") {
+    // If the original method started with "set", we mangle this directly to
+    // "get".
+    getter_name[0] = 'g';
+
+  } else {
+    // Otherwise, we add a "get" prefix, and capitalize the next letter.
+    getter_name = "get" + setter_name;
+    getter_name[3] = toupper(getter_name[3]);
+  }
+
+  // Now we have to look up the getter on the distributed object and call it.
+  if (!PyObject_HasAttrString(distobj, (char *)getter_name.c_str())) {
+    // As above, if there's no getter but the field has a default value
+    // specified, quietly pack the default value.
+    if (field->has_default_value()) {
+      packer.pack_default_value();
+      return true;
+    }
+
+    // Otherwise, with no default value it's an error.
+    ostringstream strm;
+    strm << "Distributed class " << _this->get_name()
+         << " doesn't have getter named " << getter_name
+         << " to match required field " << setter_name;
+    nassert_raise(strm.str());
+    return false;
+  }
+  PyObject *func =
+    PyObject_GetAttrString(distobj, (char *)getter_name.c_str());
+  nassertr(func != nullptr, false);
+
+  PyObject *empty_args = PyTuple_New(0);
+  PyObject *result = PyObject_CallObject(func, empty_args);
+  Py_DECREF(empty_args);
+  Py_DECREF(func);
+  if (result == nullptr) {
+    // We don't set this as an exception, since presumably the Python method
+    // itself has already triggered a Python exception.
+    std::cerr << "Error when calling " << getter_name << "\n";
+    return false;
+  }
+
+  if (atom->get_num_elements() == 1) {
+    // In this case, we expect the getter to return one object, which we wrap
+    // up in a tuple.
+    PyObject *tuple = PyTuple_New(1);
+    PyTuple_SET_ITEM(tuple, 0, result);
+    result = tuple;
+
+  } else {
+    // Otherwise, it had better already be a sequence or tuple of some sort.
+    if (!PySequence_Check(result)) {
+      ostringstream strm;
+      strm << "Since dclass " << _this->get_name() << " method " << setter_name
+           << " is declared to have multiple parameters, Python function "
+           << getter_name << " must return a list or tuple.\n";
+      nassert_raise(strm.str());
+      return false;
+    }
+  }
+
+  // Now pack the arguments into the datagram.
+  bool pack_ok = invoke_extension((DCField *)atom).pack_args(packer, result);
+  Py_DECREF(result);
+
+  return pack_ok;
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the client.
+ */
+Datagram Extension<DCClass>::
+client_format_update(const std::string &field_name, DOID_TYPE do_id,
+                     PyObject *args) const {
+  DCField *field = _this->get_field_by_name(field_name);
+  if (field == nullptr) {
+    std::ostringstream strm;
+    strm << "No field named " << field_name << " in class " << _this->get_name()
+         << "\n";
+    nassert_raise(strm.str());
+    return Datagram();
+  }
+
+  return invoke_extension(field).client_format_update(do_id, args);
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the AI.
+ */
+Datagram Extension<DCClass>::
+ai_format_update(const std::string &field_name, DOID_TYPE do_id,
+                 CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
+  DCField *field = _this->get_field_by_name(field_name);
+  if (field == nullptr) {
+    std::ostringstream strm;
+    strm << "No field named " << field_name << " in class " << _this->get_name()
+         << "\n";
+    nassert_raise(strm.str());
+    return Datagram();
+  }
+
+  return invoke_extension(field).ai_format_update(do_id, to_id, from_id, args);
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update,
+ * using the indicated msg type for the indicated distributed object from the
+ * AI.
+ */
+Datagram Extension<DCClass>::
+ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
+                          CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+                          int msg_type, PyObject *args) const {
+  DCField *field = _this->get_field_by_name(field_name);
+  if (field == nullptr) {
+    std::ostringstream strm;
+    strm << "No field named " << field_name << " in class " << _this->get_name()
+         << "\n";
+    nassert_raise(strm.str());
+    return Datagram();
+  }
+
+  return invoke_extension(field).ai_format_update_msg_type(do_id, to_id, from_id, msg_type, args);
+}
+
+/**
+ * Generates a datagram containing the message necessary to generate a new
+ * distributed object from the client.  This requires querying the object for
+ * the initial value of its required fields.
+ *
+ * optional_fields is a list of fieldNames to generate in addition to the
+ * normal required fields.
+ *
+ * This method is only called by the CMU implementation.
+ */
+Datagram Extension<DCClass>::
+client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
+                           ZONEID_TYPE zone_id,
+                           PyObject *optional_fields) const {
+  DCPacker packer;
+
+  packer.raw_pack_uint16(CLIENT_OBJECT_GENERATE_CMU);
+
+  packer.raw_pack_uint32(zone_id);
+  packer.raw_pack_uint16(_this->_number);
+  packer.raw_pack_uint32(do_id);
+
+  // Specify all of the required fields.
+  int num_fields = _this->get_num_inherited_fields();
+  for (int i = 0; i < num_fields; ++i) {
+    DCField *field = _this->get_inherited_field(i);
+    if (field->is_required() && field->as_molecular_field() == nullptr) {
+      packer.begin_pack(field);
+      if (!pack_required_field(packer, distobj, field)) {
+        return Datagram();
+      }
+      packer.end_pack();
+    }
+  }
+
+  // Also specify the optional fields.
+  int num_optional_fields = 0;
+  if (PyObject_IsTrue(optional_fields)) {
+    num_optional_fields = PySequence_Size(optional_fields);
+  }
+  packer.raw_pack_uint16(num_optional_fields);
+
+  for (int i = 0; i < num_optional_fields; i++) {
+    PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
+#if PY_MAJOR_VERSION >= 3
+    std::string field_name = PyUnicode_AsUTF8(py_field_name);
+#else
+    std::string field_name = PyString_AsString(py_field_name);
+#endif
+    Py_XDECREF(py_field_name);
+
+    DCField *field = _this->get_field_by_name(field_name);
+    if (field == nullptr) {
+      std::ostringstream strm;
+      strm << "No field named " << field_name << " in class " << _this->get_name()
+           << "\n";
+      nassert_raise(strm.str());
+      return Datagram();
+    }
+    packer.raw_pack_uint16(field->get_number());
+    packer.begin_pack(field);
+    if (!pack_required_field(packer, distobj, field)) {
+      return Datagram();
+    }
+    packer.end_pack();
+  }
+
+  return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Generates a datagram containing the message necessary to generate a new
+ * distributed object from the AI. This requires querying the object for the
+ * initial value of its required fields.
+ *
+ * optional_fields is a list of fieldNames to generate in addition to the
+ * normal required fields.
+ */
+Datagram Extension<DCClass>::
+ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
+                   DOID_TYPE parent_id, ZONEID_TYPE zone_id,
+                   CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id,
+                   PyObject *optional_fields) const {
+  DCPacker packer;
+
+  packer.raw_pack_uint8(1);
+  packer.RAW_PACK_CHANNEL(district_channel_id);
+  packer.RAW_PACK_CHANNEL(from_channel_id);
+    // packer.raw_pack_uint8('A');
+
+  bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0);
+
+  if (has_optional_fields) {
+    packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER);
+  } else {
+    packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED);
+  }
+
+  packer.raw_pack_uint32(do_id);
+  // Parent is a bit overloaded; this parent is not about inheritance, this
+  // one is about the visibility container parent, i.e.  the zone parent:
+  packer.raw_pack_uint32(parent_id);
+  packer.raw_pack_uint32(zone_id);
+  packer.raw_pack_uint16(_this->_number);
+
+  // Specify all of the required fields.
+  int num_fields = _this->get_num_inherited_fields();
+  for (int i = 0; i < num_fields; ++i) {
+    DCField *field = _this->get_inherited_field(i);
+    if (field->is_required() && field->as_molecular_field() == nullptr) {
+      packer.begin_pack(field);
+      if (!pack_required_field(packer, distobj, field)) {
+        return Datagram();
+      }
+      packer.end_pack();
+    }
+  }
+
+  // Also specify the optional fields.
+  if (has_optional_fields) {
+    int num_optional_fields = PySequence_Size(optional_fields);
+    packer.raw_pack_uint16(num_optional_fields);
+
+    for (int i = 0; i < num_optional_fields; ++i) {
+      PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
+#if PY_MAJOR_VERSION >= 3
+      std::string field_name = PyUnicode_AsUTF8(py_field_name);
+#else
+      std::string field_name = PyString_AsString(py_field_name);
+#endif
+      Py_XDECREF(py_field_name);
+
+      DCField *field = _this->get_field_by_name(field_name);
+      if (field == nullptr) {
+        std::ostringstream strm;
+        strm << "No field named " << field_name << " in class "
+             << _this->get_name() << "\n";
+        nassert_raise(strm.str());
+        return Datagram();
+      }
+
+      packer.raw_pack_uint16(field->get_number());
+
+      packer.begin_pack(field);
+      if (!pack_required_field(packer, distobj, field)) {
+        return Datagram();
+      }
+      packer.end_pack();
+    }
+  }
+
+  return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Returns the PythonClassDefsImpl object stored on the DCClass object,
+ * creating it if it didn't yet exist.
+ */
+Extension<DCClass>::PythonClassDefsImpl *Extension<DCClass>::
+do_get_defs() const {
+  if (!_this->_python_class_defs) {
+    _this->_python_class_defs = new PythonClassDefsImpl();
+  }
+  return (PythonClassDefsImpl *)_this->_python_class_defs.p();
+}
+
+#endif  // HAVE_PYTHON

+ 93 - 0
direct/src/dcparser/dcClass_ext.h

@@ -0,0 +1,93 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcClass_ext.h
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#ifndef DCCLASS_EXT_H
+#define DCCLASS_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "dcClass.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for DCClass, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<DCClass> : public ExtensionBase<DCClass> {
+public:
+  bool has_class_def() const;
+  void set_class_def(PyObject *class_def);
+  PyObject *get_class_def() const;
+  bool has_owner_class_def() const;
+  void set_owner_class_def(PyObject *owner_class_def);
+  PyObject *get_owner_class_def() const;
+
+  void receive_update(PyObject *distobj, DatagramIterator &di) const;
+  void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const;
+  void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const;
+  void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const;
+  void receive_update_other(PyObject *distobj, DatagramIterator &di) const;
+
+  void direct_update(PyObject *distobj, const std::string &field_name,
+                     const vector_uchar &value_blob);
+  void direct_update(PyObject *distobj, const std::string &field_name,
+                     const Datagram &datagram);
+  bool pack_required_field(Datagram &datagram, PyObject *distobj,
+                           const DCField *field) const;
+  bool pack_required_field(DCPacker &packer, PyObject *distobj,
+                           const DCField *field) const;
+
+
+
+  Datagram client_format_update(const std::string &field_name,
+                                DOID_TYPE do_id, PyObject *args) const;
+  Datagram ai_format_update(const std::string &field_name, DOID_TYPE do_id,
+                            CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const;
+  Datagram ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
+                            CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const;
+  Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
+                              ZONEID_TYPE parent_id, ZONEID_TYPE zone_id,
+                              CHANNEL_TYPE district_channel_id,
+                              CHANNEL_TYPE from_channel_id,
+                              PyObject *optional_fields) const;
+  Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
+                                      ZONEID_TYPE zone_id,
+                                      PyObject *optional_fields) const;
+
+private:
+  /**
+   * Implementation of DCClass::PythonClassDefs which actually stores the
+   * Python pointers.  This needs to be defined here rather than on DCClass
+   * itself, since DCClass cannot include Python.h or call Python functions.
+   */
+  class PythonClassDefsImpl : public DCClass::PythonClassDefs {
+  public:
+    virtual ~PythonClassDefsImpl() {
+      Py_XDECREF(_class_def);
+      Py_XDECREF(_owner_class_def);
+    }
+
+    PyObject *_class_def = nullptr;
+    PyObject *_owner_class_def = nullptr;
+  };
+
+  PythonClassDefsImpl *do_get_defs() const;
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // DCCLASS_EXT_H

+ 6 - 313
direct/src/dcparser/dcField.cxx

@@ -18,16 +18,6 @@
 #include "hashGenerator.h"
 #include "dcmsgtypes.h"
 
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
-#ifdef WITHIN_PANDA
-#include "pStatTimer.h"
-#endif
-
-using std::string;
-
 /**
  *
  */
@@ -58,7 +48,7 @@ DCField() :
  *
  */
 DCField::
-DCField(const string &name, DCClass *dclass) :
+DCField(const std::string &name, DCClass *dclass) :
   DCPackerInterface(name),
   _dclass(dclass)
 #ifdef WITHIN_PANDA
@@ -161,14 +151,14 @@ as_parameter() const {
  * string formatting it for human consumption.  Returns empty string if there
  * is an error.
  */
-string DCField::
+std::string DCField::
 format_data(const vector_uchar &packed_data, bool show_field_names) {
   DCPacker packer;
   packer.set_unpack_data(packed_data);
   packer.begin_unpack(this);
-  string result = packer.unpack_and_format(show_field_names);
+  std::string result = packer.unpack_and_format(show_field_names);
   if (!packer.end_unpack()) {
-    return string();
+    return std::string();
   }
   return result;
 }
@@ -179,7 +169,7 @@ format_data(const vector_uchar &packed_data, bool show_field_names) {
  * the corresponding packed data.  Returns empty string if there is an error.
  */
 vector_uchar DCField::
-parse_string(const string &formatted_string) {
+parse_string(const std::string &formatted_string) {
   DCPacker packer;
   packer.begin_pack(this);
   if (!packer.parse_and_pack(formatted_string)) {
@@ -212,254 +202,6 @@ validate_ranges(const vector_uchar &packed_data) const {
   return (packer.get_num_unpacked_bytes() == packed_data.size());
 }
 
-#ifdef HAVE_PYTHON
-/**
- * Packs the Python arguments from the indicated tuple into the packer.
- * Returns true on success, false on failure.
- *
- * It is assumed that the packer is currently positioned on this field.
- */
-bool DCField::
-pack_args(DCPacker &packer, PyObject *sequence) const {
-  nassertr(!packer.had_error(), false);
-  nassertr(packer.get_current_field() == this, false);
-
-  packer.pack_object(sequence);
-  if (!packer.had_error()) {
-    /*
-    cerr << "pack " << get_name() << get_pystr(sequence) << "\n";
-    */
-
-    return true;
-  }
-
-  if (!Notify::ptr()->has_assert_failed()) {
-    std::ostringstream strm;
-    PyObject *exc_type = PyExc_Exception;
-
-    if (as_parameter() != nullptr) {
-      // If it's a parameter-type field, the value may or may not be a
-      // sequence.
-      if (packer.had_pack_error()) {
-        strm << "Incorrect arguments to field: " << get_name()
-             << " = " << get_pystr(sequence);
-        exc_type = PyExc_TypeError;
-      } else {
-        strm << "Value out of range on field: " << get_name()
-             << " = " << get_pystr(sequence);
-        exc_type = PyExc_ValueError;
-      }
-
-    } else {
-      // If it's a molecular or atomic field, the value should be a sequence.
-      PyObject *tuple = PySequence_Tuple(sequence);
-      if (tuple == nullptr) {
-        strm << "Value for " << get_name() << " not a sequence: " \
-             << get_pystr(sequence);
-        exc_type = PyExc_TypeError;
-
-      } else {
-        if (packer.had_pack_error()) {
-          strm << "Incorrect arguments to field: " << get_name()
-               << get_pystr(sequence);
-          exc_type = PyExc_TypeError;
-        } else {
-          strm << "Value out of range on field: " << get_name()
-               << get_pystr(sequence);
-          exc_type = PyExc_ValueError;
-        }
-
-        Py_DECREF(tuple);
-      }
-    }
-
-    string message = strm.str();
-    PyErr_SetString(exc_type, message.c_str());
-  }
-  return false;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Unpacks the values from the packer, beginning at the current point in the
- * unpack_buffer, into a Python tuple and returns the tuple.
- *
- * It is assumed that the packer is currently positioned on this field.
- */
-PyObject *DCField::
-unpack_args(DCPacker &packer) const {
-  nassertr(!packer.had_error(), nullptr);
-  nassertr(packer.get_current_field() == this, nullptr);
-
-  size_t start_byte = packer.get_num_unpacked_bytes();
-  PyObject *object = packer.unpack_object();
-
-  if (!packer.had_error()) {
-    // Successfully unpacked.
-    /*
-    cerr << "recv " << get_name() << get_pystr(object) << "\n";
-    */
-
-    return object;
-  }
-
-  if (!Notify::ptr()->has_assert_failed()) {
-    std::ostringstream strm;
-    PyObject *exc_type = PyExc_Exception;
-
-    if (packer.had_pack_error()) {
-      strm << "Data error unpacking field ";
-      output(strm, true);
-      size_t length = packer.get_unpack_length() - start_byte;
-      strm << "\nGot data (" << (int)length << " bytes):\n";
-      Datagram dg(packer.get_unpack_data() + start_byte, length);
-      dg.dump_hex(strm);
-      size_t error_byte = packer.get_num_unpacked_bytes() - start_byte;
-      strm << "Error detected on byte " << error_byte
-           << " (" << std::hex << error_byte << std::dec << " hex)";
-
-      exc_type = PyExc_RuntimeError;
-    } else {
-      strm << "Value outside specified range when unpacking field "
-           << get_name() << ": " << get_pystr(object);
-      exc_type = PyExc_ValueError;
-    }
-
-    string message = strm.str();
-    PyErr_SetString(exc_type, message.c_str());
-  }
-
-  Py_XDECREF(object);
-  return nullptr;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Extracts the update message out of the datagram and applies it to the
- * indicated object by calling the appropriate method.
- */
-void DCField::
-receive_update(DCPacker &packer, PyObject *distobj) const {
-  if (as_parameter() != nullptr) {
-    // If it's a parameter-type field, just store a new value on the object.
-    PyObject *value = unpack_args(packer);
-    if (value != nullptr) {
-      PyObject_SetAttrString(distobj, (char *)_name.c_str(), value);
-    }
-    Py_DECREF(value);
-
-  } else {
-    // Otherwise, it must be an atomic or molecular field, so call the
-    // corresponding method.
-
-    if (!PyObject_HasAttrString(distobj, (char *)_name.c_str())) {
-      // If there's no Python method to receive this message, don't bother
-      // unpacking it to a Python tuple--just skip past the message.
-      packer.unpack_skip();
-
-    } else {
-      // Otherwise, get a Python tuple from the args and call the Python
-      // method.
-      PyObject *args = unpack_args(packer);
-
-      if (args != nullptr) {
-        PyObject *func = PyObject_GetAttrString(distobj, (char *)_name.c_str());
-        nassertv(func != nullptr);
-
-        PyObject *result;
-        {
-#ifdef WITHIN_PANDA
-          PStatTimer timer(((DCField *)this)->_field_update_pcollector);
-#endif
-          result = PyObject_CallObject(func, args);
-        }
-        Py_XDECREF(result);
-        Py_DECREF(func);
-        Py_DECREF(args);
-      }
-    }
-  }
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the client.
- */
-Datagram DCField::
-client_format_update(DOID_TYPE do_id, PyObject *args) const {
-  DCPacker packer;
-
-  packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
-  packer.raw_pack_uint32(do_id);
-  packer.raw_pack_uint16(_number);
-
-  packer.begin_pack(this);
-  pack_args(packer, args);
-  if (!packer.end_pack()) {
-    return Datagram();
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the AI.
- */
-Datagram DCField::
-ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
-  DCPacker packer;
-
-  packer.raw_pack_uint8(1);
-  packer.RAW_PACK_CHANNEL(to_id);
-  packer.RAW_PACK_CHANNEL(from_id);
-  packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
-  packer.raw_pack_uint32(do_id);
-  packer.raw_pack_uint16(_number);
-
-  packer.begin_pack(this);
-  pack_args(packer, args);
-  if (!packer.end_pack()) {
-    return Datagram();
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update,
- * with the msg type, for the indicated distributed object from the AI.
- */
-Datagram DCField::
-ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
-  DCPacker packer;
-
-  packer.raw_pack_uint8(1);
-  packer.RAW_PACK_CHANNEL(to_id);
-  packer.RAW_PACK_CHANNEL(from_id);
-  packer.raw_pack_uint16(msg_type);
-  packer.raw_pack_uint32(do_id);
-  packer.raw_pack_uint16(_number);
-
-  packer.begin_pack(this);
-  pack_args(packer, args);
-  if (!packer.end_pack()) {
-    return Datagram();
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
-
 /**
  * Accumulates the properties of this field into the hash.
  */
@@ -499,62 +241,13 @@ pack_default_value(DCPackData &pack_data, bool &) const {
  * Sets the name of this field.
  */
 void DCField::
-set_name(const string &name) {
+set_name(const std::string &name) {
   DCPackerInterface::set_name(name);
   if (_dclass != nullptr) {
     _dclass->_dc_file->mark_inherited_fields_stale();
   }
 }
 
-#ifdef HAVE_PYTHON
-/**
- * Returns the string representation of the indicated Python object.
- */
-string DCField::
-get_pystr(PyObject *value) {
-  if (value == nullptr) {
-    return "(null)";
-  }
-
-  PyObject *str = PyObject_Str(value);
-  if (str != nullptr) {
-#if PY_MAJOR_VERSION >= 3
-    string result = PyUnicode_AsUTF8(str);
-#else
-    string result = PyString_AsString(str);
-#endif
-    Py_DECREF(str);
-    return result;
-  }
-
-  PyObject *repr = PyObject_Repr(value);
-  if (repr != nullptr) {
-#if PY_MAJOR_VERSION >= 3
-    string result = PyUnicode_AsUTF8(repr);
-#else
-    string result = PyString_AsString(repr);
-#endif
-    Py_DECREF(repr);
-    return result;
-  }
-
-  if (value->ob_type != nullptr) {
-    PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
-    if (typestr != nullptr) {
-#if PY_MAJOR_VERSION >= 3
-      string result = PyUnicode_AsUTF8(typestr);
-#else
-      string result = PyString_AsString(typestr);
-#endif
-      Py_DECREF(typestr);
-      return result;
-    }
-  }
-
-  return "(invalid object)";
-}
-#endif  // HAVE_PYTHON
-
 /**
  * Recomputes the default value of the field by repacking it.
  */

+ 13 - 15
direct/src/dcparser/dcField.h

@@ -17,10 +17,11 @@
 #include "dcbase.h"
 #include "dcPackerInterface.h"
 #include "dcKeywordList.h"
-#include "dcPython.h"
 
 #ifdef WITHIN_PANDA
 #include "pStatCollector.h"
+#include "extension.h"
+#include "datagram.h"
 #endif
 
 class DCPacker;
@@ -76,18 +77,17 @@ PUBLISHED:
   INLINE void output(std::ostream &out) const;
   INLINE void write(std::ostream &out, int indent_level) const;
 
-#ifdef HAVE_PYTHON
-  bool pack_args(DCPacker &packer, PyObject *sequence) const;
-  PyObject *unpack_args(DCPacker &packer) const;
+  EXTENSION(bool pack_args(DCPacker &packer, PyObject *sequence) const);
+  EXTENSION(PyObject *unpack_args(DCPacker &packer) const);
 
-  void receive_update(DCPacker &packer, PyObject *distobj) const;
+  EXTENSION(void receive_update(DCPacker &packer, PyObject *distobj) const);
 
-  Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const;
-  Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
-                            PyObject *args) const;
-  Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
-                            int msg_type, PyObject *args) const;
-#endif
+  EXTENSION(Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const);
+  EXTENSION(Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id,
+                                      CHANNEL_TYPE from_id, PyObject *args) const);
+  EXTENSION(Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id,
+                                               CHANNEL_TYPE from_id, int msg_type,
+                                               PyObject *args) const);
 
 public:
   virtual void output(std::ostream &out, bool brief) const=0;
@@ -100,10 +100,6 @@ public:
   INLINE void set_class(DCClass *dclass);
   INLINE void set_default_value(vector_uchar default_value);
 
-#ifdef HAVE_PYTHON
-  static std::string get_pystr(PyObject *value);
-#endif
-
 protected:
   void refresh_default_value();
 
@@ -119,6 +115,8 @@ private:
 
 #ifdef WITHIN_PANDA
   PStatCollector _field_update_pcollector;
+
+  friend class Extension<DCField>;
 #endif
 };
 

+ 305 - 0
direct/src/dcparser/dcField_ext.cxx

@@ -0,0 +1,305 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcField_ext.cxx
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#include "dcField_ext.h"
+#include "dcPacker_ext.h"
+#include "dcmsgtypes.h"
+
+#include "datagram.h"
+#include "pStatTimer.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Packs the Python arguments from the indicated tuple into the packer.
+ * Returns true on success, false on failure.
+ *
+ * It is assumed that the packer is currently positioned on this field.
+ */
+bool Extension<DCField>::
+pack_args(DCPacker &packer, PyObject *sequence) const {
+  nassertr(!packer.had_error(), false);
+  nassertr(packer.get_current_field() == _this, false);
+
+  invoke_extension(&packer).pack_object(sequence);
+  if (!packer.had_error()) {
+    /*
+    cerr << "pack " << _this->get_name() << get_pystr(sequence) << "\n";
+    */
+
+    return true;
+  }
+
+  if (!Notify::ptr()->has_assert_failed()) {
+    std::ostringstream strm;
+    PyObject *exc_type = PyExc_Exception;
+
+    if (_this->as_parameter() != nullptr) {
+      // If it's a parameter-type field, the value may or may not be a
+      // sequence.
+      if (packer.had_pack_error()) {
+        strm << "Incorrect arguments to field: " << _this->get_name()
+             << " = " << get_pystr(sequence);
+        exc_type = PyExc_TypeError;
+      } else {
+        strm << "Value out of range on field: " << _this->get_name()
+             << " = " << get_pystr(sequence);
+        exc_type = PyExc_ValueError;
+      }
+
+    } else {
+      // If it's a molecular or atomic field, the value should be a sequence.
+      PyObject *tuple = PySequence_Tuple(sequence);
+      if (tuple == nullptr) {
+        strm << "Value for " << _this->get_name() << " not a sequence: " \
+             << get_pystr(sequence);
+        exc_type = PyExc_TypeError;
+
+      } else {
+        if (packer.had_pack_error()) {
+          strm << "Incorrect arguments to field: " << _this->get_name()
+               << get_pystr(sequence);
+          exc_type = PyExc_TypeError;
+        } else {
+          strm << "Value out of range on field: " << _this->get_name()
+               << get_pystr(sequence);
+          exc_type = PyExc_ValueError;
+        }
+
+        Py_DECREF(tuple);
+      }
+    }
+
+    std::string message = strm.str();
+    PyErr_SetString(exc_type, message.c_str());
+  }
+  return false;
+}
+
+/**
+ * Unpacks the values from the packer, beginning at the current point in the
+ * unpack_buffer, into a Python tuple and returns the tuple.
+ *
+ * It is assumed that the packer is currently positioned on this field.
+ */
+PyObject *Extension<DCField>::
+unpack_args(DCPacker &packer) const {
+  nassertr(!packer.had_error(), nullptr);
+  nassertr(packer.get_current_field() == _this, nullptr);
+
+  size_t start_byte = packer.get_num_unpacked_bytes();
+  PyObject *object = invoke_extension(&packer).unpack_object();
+
+  if (!packer.had_error()) {
+    // Successfully unpacked.
+    /*
+    cerr << "recv " << _this->get_name() << get_pystr(object) << "\n";
+    */
+
+    return object;
+  }
+
+  if (!Notify::ptr()->has_assert_failed()) {
+    std::ostringstream strm;
+    PyObject *exc_type = PyExc_Exception;
+
+    if (packer.had_pack_error()) {
+      strm << "Data error unpacking field ";
+      _this->output(strm, true);
+      size_t length = packer.get_unpack_length() - start_byte;
+      strm << "\nGot data (" << (int)length << " bytes):\n";
+      Datagram dg(packer.get_unpack_data() + start_byte, length);
+      dg.dump_hex(strm);
+      size_t error_byte = packer.get_num_unpacked_bytes() - start_byte;
+      strm << "Error detected on byte " << error_byte
+           << " (" << std::hex << error_byte << std::dec << " hex)";
+
+      exc_type = PyExc_RuntimeError;
+    } else {
+      strm << "Value outside specified range when unpacking field "
+           << _this->get_name() << ": " << get_pystr(object);
+      exc_type = PyExc_ValueError;
+    }
+
+    std::string message = strm.str();
+    PyErr_SetString(exc_type, message.c_str());
+  }
+
+  Py_XDECREF(object);
+  return nullptr;
+}
+
+/**
+ * Extracts the update message out of the datagram and applies it to the
+ * indicated object by calling the appropriate method.
+ */
+void Extension<DCField>::
+receive_update(DCPacker &packer, PyObject *distobj) const {
+  if (_this->as_parameter() != nullptr) {
+    // If it's a parameter-type field, just store a new value on the object.
+    PyObject *value = unpack_args(packer);
+    if (value != nullptr) {
+      PyObject_SetAttrString(distobj, (char *)_this->_name.c_str(), value);
+    }
+    Py_DECREF(value);
+
+  } else {
+    // Otherwise, it must be an atomic or molecular field, so call the
+    // corresponding method.
+
+    if (!PyObject_HasAttrString(distobj, (char *)_this->_name.c_str())) {
+      // If there's no Python method to receive this message, don't bother
+      // unpacking it to a Python tuple--just skip past the message.
+      packer.unpack_skip();
+
+    } else {
+      // Otherwise, get a Python tuple from the args and call the Python
+      // method.
+      PyObject *args = unpack_args(packer);
+
+      if (args != nullptr) {
+        PyObject *func = PyObject_GetAttrString(distobj, (char *)_this->_name.c_str());
+        nassertv(func != nullptr);
+
+        PyObject *result;
+        {
+#ifdef WITHIN_PANDA
+          PStatTimer timer(_this->_field_update_pcollector);
+#endif
+          result = PyObject_CallObject(func, args);
+        }
+        Py_XDECREF(result);
+        Py_DECREF(func);
+        Py_DECREF(args);
+      }
+    }
+  }
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the client.
+ */
+Datagram Extension<DCField>::
+client_format_update(DOID_TYPE do_id, PyObject *args) const {
+  DCPacker packer;
+
+  packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
+  packer.raw_pack_uint32(do_id);
+  packer.raw_pack_uint16(_this->_number);
+
+  packer.begin_pack(_this);
+  pack_args(packer, args);
+  if (!packer.end_pack()) {
+    return Datagram();
+  }
+
+  return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the AI.
+ */
+Datagram Extension<DCField>::
+ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
+  DCPacker packer;
+
+  packer.raw_pack_uint8(1);
+  packer.RAW_PACK_CHANNEL(to_id);
+  packer.RAW_PACK_CHANNEL(from_id);
+  packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
+  packer.raw_pack_uint32(do_id);
+  packer.raw_pack_uint16(_this->_number);
+
+  packer.begin_pack(_this);
+  pack_args(packer, args);
+  if (!packer.end_pack()) {
+    return Datagram();
+  }
+
+  return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update,
+ * with the msg type, for the indicated distributed object from the AI.
+ */
+Datagram Extension<DCField>::
+ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
+  DCPacker packer;
+
+  packer.raw_pack_uint8(1);
+  packer.RAW_PACK_CHANNEL(to_id);
+  packer.RAW_PACK_CHANNEL(from_id);
+  packer.raw_pack_uint16(msg_type);
+  packer.raw_pack_uint32(do_id);
+  packer.raw_pack_uint16(_this->_number);
+
+  packer.begin_pack(_this);
+  pack_args(packer, args);
+  if (!packer.end_pack()) {
+    return Datagram();
+  }
+
+  return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Returns the string representation of the indicated Python object.
+ */
+std::string Extension<DCField>::
+get_pystr(PyObject *value) {
+  if (value == nullptr) {
+    return "(null)";
+  }
+
+  PyObject *str = PyObject_Str(value);
+  if (str != nullptr) {
+#if PY_MAJOR_VERSION >= 3
+    std::string result = PyUnicode_AsUTF8(str);
+#else
+    std::string result = PyString_AsString(str);
+#endif
+    Py_DECREF(str);
+    return result;
+  }
+
+  PyObject *repr = PyObject_Repr(value);
+  if (repr != nullptr) {
+#if PY_MAJOR_VERSION >= 3
+    std::string result = PyUnicode_AsUTF8(repr);
+#else
+    std::string result = PyString_AsString(repr);
+#endif
+    Py_DECREF(repr);
+    return result;
+  }
+
+  if (value->ob_type != nullptr) {
+    PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
+    if (typestr != nullptr) {
+#if PY_MAJOR_VERSION >= 3
+      std::string result = PyUnicode_AsUTF8(typestr);
+#else
+      std::string result = PyString_AsString(typestr);
+#endif
+      Py_DECREF(typestr);
+      return result;
+    }
+  }
+
+  return "(invalid object)";
+}
+
+#endif  // HAVE_PYTHON

+ 48 - 0
direct/src/dcparser/dcField_ext.h

@@ -0,0 +1,48 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcField_ext.h
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#ifndef DCFIELD_EXT_H
+#define DCFIELD_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "dcField.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for DCField, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<DCField> : public ExtensionBase<DCField> {
+public:
+  bool pack_args(DCPacker &packer, PyObject *sequence) const;
+  PyObject *unpack_args(DCPacker &packer) const;
+
+  void receive_update(DCPacker &packer, PyObject *distobj) const;
+
+  Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const;
+  Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+                            PyObject *args) const;
+  Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+                            int msg_type, PyObject *args) const;
+
+  static std::string get_pystr(PyObject *value);
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // DCFIELD_EXT_H

+ 0 - 508
direct/src/dcparser/dcPacker.cxx

@@ -19,10 +19,6 @@
 #include "dcSwitchParameter.h"
 #include "dcClass.h"
 
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
 using std::istream;
 using std::istringstream;
 using std::ostream;
@@ -622,335 +618,6 @@ unpack_skip() {
   }
 }
 
-#ifdef HAVE_PYTHON
-/**
- * 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);
-  DCPackType pack_type = get_pack_type();
-
-  // had to add this for basic 64 and unsigned data to get packed right .. Not
-  // sure if we can just do the rest this way..
-
- switch(pack_type)
-  {
-  case PT_int64:
-      if(PyLong_Check(object))
-      {
-            pack_int64(PyLong_AsLongLong(object));
-            return;
-      }
-#if PY_MAJOR_VERSION < 3
-      else if (PyInt_Check(object))
-      {
-            pack_int64(PyInt_AsLong(object));
-            return;
-      }
-#endif
-      break;
-  case PT_uint64:
-      if(PyLong_Check(object))
-      {
-            pack_uint64(PyLong_AsUnsignedLongLong(object));
-            return;
-      }
-#if PY_MAJOR_VERSION < 3
-      else if(PyInt_Check(object))
-      {
-            PyObject  *obj1 = PyNumber_Long(object);
-            pack_int(PyLong_AsUnsignedLongLong(obj1));
-            Py_DECREF(obj1);
-            return;
-      }
-#endif
-      break;
-  case PT_int:
-      if(PyLong_Check(object))
-      {
-            pack_int(PyLong_AsLong(object));
-            return;
-      }
-#if PY_MAJOR_VERSION < 3
-      else if (PyInt_Check(object))
-      {
-            pack_int(PyInt_AsLong(object));
-            return;
-      }
-#endif
-      break;
-  case PT_uint:
-      if(PyLong_Check(object))
-      {
-            pack_uint(PyLong_AsUnsignedLong(object));
-            return;
-      }
-#if PY_MAJOR_VERSION < 3
-      else if (PyInt_Check(object))
-      {
-            PyObject *obj1 = PyNumber_Long(object);
-            pack_uint(PyLong_AsUnsignedLong(obj1));
-            Py_DECREF(obj1);
-            return;
-      }
-#endif
-      break;
-  default:
-      break;
-  }
-  if (PyLong_Check(object)) {
-    pack_int(PyLong_AsLong(object));
-#if PY_MAJOR_VERSION < 3
-  } else if (PyInt_Check(object)) {
-    pack_int(PyInt_AS_LONG(object));
-#endif
-  } else if (PyFloat_Check(object)) {
-    pack_double(PyFloat_AS_DOUBLE(object));
-  } else if (PyLong_Check(object)) {
-    pack_int64(PyLong_AsLongLong(object));
-#if PY_MAJOR_VERSION >= 3
-  } else if (PyUnicode_Check(object)) {
-    const char *buffer;
-    Py_ssize_t length;
-    buffer = PyUnicode_AsUTF8AndSize(object, &length);
-    if (buffer) {
-      pack_string(string(buffer, length));
-    }
-  } else if (PyBytes_Check(object)) {
-    const unsigned char *buffer;
-    Py_ssize_t length;
-    PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
-    if (buffer) {
-      pack_blob(vector_uchar(buffer, buffer + length));
-    }
-#else
-  } else if (PyString_Check(object) || PyUnicode_Check(object)) {
-    char *buffer;
-    Py_ssize_t length;
-    PyString_AsStringAndSize(object, &buffer, &length);
-    if (buffer) {
-      pack_string(string(buffer, length));
-    }
-#endif
-  } 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 = nullptr;
-    const DCPackerInterface *current_field = get_current_field();
-    if (current_field != nullptr) {
-      const DCClassParameter *class_param = get_current_field()->as_class_parameter();
-      if (class_param != nullptr) {
-        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 != nullptr && (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 != nullptr) {
-          pack_object(element);
-          Py_DECREF(element);
-        } else {
-          std::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;
-      strm << "Don't know how to pack object: "
-           << DCField::get_pystr(object);
-      nassert_raise(strm.str());
-      _pack_error = true;
-    }
-  }
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * 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 = nullptr;
-
-  DCPackType pack_type = get_pack_type();
-
-  switch (pack_type) {
-  case PT_invalid:
-    object = Py_None;
-    Py_INCREF(object);
-    unpack_skip();
-    break;
-
-  case PT_double:
-    {
-      double value = unpack_double();
-      object = PyFloat_FromDouble(value);
-    }
-    break;
-
-  case PT_int:
-    {
-      int value = unpack_int();
-#if PY_MAJOR_VERSION >= 3
-      object = PyLong_FromLong(value);
-#else
-      object = PyInt_FromLong(value);
-#endif
-    }
-    break;
-
-  case PT_uint:
-    {
-      unsigned int value = unpack_uint();
-#if PY_MAJOR_VERSION >= 3
-      object = PyLong_FromLong(value);
-#else
-      if (value & 0x80000000) {
-        object = PyLong_FromUnsignedLong(value);
-      } else {
-        object = PyInt_FromLong(value);
-      }
-#endif
-    }
-    break;
-
-  case PT_int64:
-    {
-      int64_t value = unpack_int64();
-      object = PyLong_FromLongLong(value);
-    }
-    break;
-
-  case PT_uint64:
-    {
-      uint64_t value = unpack_uint64();
-      object = PyLong_FromUnsignedLongLong(value);
-    }
-    break;
-
-  case PT_blob:
-#if PY_MAJOR_VERSION >= 3
-    {
-      string str;
-      unpack_string(str);
-      object = PyBytes_FromStringAndSize(str.data(), str.size());
-    }
-    break;
-#endif
-    // On Python 2, fall through to below.
-
-  case PT_string:
-    {
-      string str;
-      unpack_string(str);
-#if PY_MAJOR_VERSION >= 3
-      object = PyUnicode_FromStringAndSize(str.data(), str.size());
-#else
-      object = PyString_FromStringAndSize(str.data(), str.size());
-#endif
-    }
-    break;
-
-  case PT_class:
-    {
-      const DCClassParameter *class_param = get_current_field()->as_class_parameter();
-      if (class_param != nullptr) {
-        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);
-          if (object == nullptr) {
-            std::cerr << "Unable to construct object of class "
-                 << dclass->get_name() << "\n";
-          } else {
-            break;
-          }
-        }
-      }
-    }
-    // Fall through (if no constructor)
-
-    // 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;
-  }
-
-  nassertr(object != nullptr, nullptr);
-  return object;
-}
-#endif  // HAVE_PYTHON
-
-
 /**
  * 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
@@ -1206,178 +873,3 @@ clear_stack() {
     _stack = next;
   }
 }
-
-#ifdef HAVE_PYTHON
-/**
- * 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 != nullptr);
-    get_class_element(dclass, object, field);
-  }
-  pop();
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * 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 != nullptr, nullptr);
-
-  PyObject *object = nullptr;
-
-  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, nullptr);
-    if (object == nullptr) {
-      return nullptr;
-    }
-  }
-
-  push();
-  if (object == nullptr && more_nested_fields()) {
-    // The first nested field will be the constructor.
-    const DCField *field = get_current_field()->as_field();
-    nassertr(field != nullptr, object);
-    nassertr(field == dclass->get_constructor(), object);
-
-    set_class_element(class_def, object, field);
-
-    // By now, the object should have been constructed.
-    if (object == nullptr) {
-      return nullptr;
-    }
-  }
-  while (more_nested_fields()) {
-    const DCField *field = get_current_field()->as_field();
-    nassertr(field != nullptr, object);
-
-    set_class_element(class_def, object, field);
-  }
-  pop();
-
-  return object;
-}
-#endif  // HAVE_PYTHON
-
-
-#ifdef HAVE_PYTHON
-/**
- * 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 != nullptr);
-        nassertv(object != nullptr);
-        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 == nullptr) {
-        // 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 != nullptr) {
-            PyObject *result = PyObject_CallObject(func, element);
-            Py_XDECREF(result);
-            Py_DECREF(func);
-          }
-        }
-      }
-
-    } else {
-      nassertv(object != nullptr);
-      PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
-    }
-
-    Py_DECREF(element);
-  }
-}
-#endif  // HAVE_PYTHON
-
-
-#ifdef HAVE_PYTHON
-/**
- * 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 != nullptr);
-        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

+ 17 - 14
direct/src/dcparser/dcPacker.h

@@ -19,7 +19,10 @@
 #include "dcSubatomicType.h"
 #include "dcPackData.h"
 #include "dcPackerCatalog.h"
-#include "dcPython.h"
+
+#ifdef WITHIN_PANDA
+#include "extension.h"
+#endif
 
 class DCClass;
 class DCSwitchParameter;
@@ -104,10 +107,8 @@ public:
 
 PUBLISHED:
 
-#ifdef HAVE_PYTHON
-  void pack_object(PyObject *object);
-  PyObject *unpack_object();
-#endif
+  EXTENSION(void pack_object(PyObject *object));
+  EXTENSION(PyObject *unpack_object());
 
   bool parse_and_pack(const std::string &formatted_object);
   bool parse_and_pack(std::istream &in);
@@ -195,14 +196,12 @@ private:
   void clear();
   void clear_stack();
 
-#ifdef HAVE_PYTHON
-  void pack_class_object(const DCClass *dclass, PyObject *object);
-  PyObject *unpack_class_object(const DCClass *dclass);
-  void set_class_element(PyObject *class_def, PyObject *&object,
-                         const DCField *field);
-  void get_class_element(const DCClass *dclass, PyObject *object,
-                         const DCField *field);
-#endif
+  EXTENSION(void pack_class_object(const DCClass *dclass, PyObject *object));
+  EXTENSION(PyObject *unpack_class_object(const DCClass *dclass));
+  EXTENSION(void set_class_element(PyObject *class_def, PyObject *&object,
+                                   const DCField *field));
+  EXTENSION(void get_class_element(const DCClass *dclass, PyObject *object,
+                                   const DCField *field));
 
 private:
   enum Mode {
@@ -223,7 +222,7 @@ private:
   const DCPackerCatalog *_catalog;
   const DCPackerCatalog::LiveCatalog *_live_catalog;
 
-  class StackElement {
+  class EXPCL_DIRECT_DCPARSER StackElement {
   public:
     // As an optimization, we implement operator new and delete here to
     // minimize allocation overhead during push() and pop().
@@ -258,6 +257,10 @@ private:
   bool _parse_error;
   bool _pack_error;
   bool _range_error;
+
+#ifdef WITHIN_PANDA
+  friend class Extension<DCPacker>;
+#endif
 };
 
 #include "dcPacker.I"

+ 508 - 0
direct/src/dcparser/dcPacker_ext.cxx

@@ -0,0 +1,508 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcPacker_ext.cxx
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#include "dcPacker_ext.h"
+#include "dcClass_ext.h"
+#include "dcField_ext.h"
+
+#include "dcClassParameter.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * 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 Extension<DCPacker>::
+pack_object(PyObject *object) {
+  nassertv(_this->_mode == DCPacker::Mode::M_pack ||
+           _this->_mode == DCPacker::Mode::M_repack);
+  DCPackType pack_type = _this->get_pack_type();
+
+  // had to add this for basic 64 and unsigned data to get packed right .. Not
+  // sure if we can just do the rest this way..
+
+  switch(pack_type) {
+  case PT_int64:
+    if (PyLong_Check(object)) {
+      _this->pack_int64(PyLong_AsLongLong(object));
+      return;
+    }
+#if PY_MAJOR_VERSION < 3
+    else if (PyInt_Check(object)) {
+      _this->pack_int64(PyInt_AsLong(object));
+      return;
+    }
+#endif
+    break;
+
+  case PT_uint64:
+    if (PyLong_Check(object)) {
+      _this->pack_uint64(PyLong_AsUnsignedLongLong(object));
+      return;
+    }
+#if PY_MAJOR_VERSION < 3
+    else if (PyInt_Check(object)) {
+      PyObject *obj1 = PyNumber_Long(object);
+      _this->pack_int(PyLong_AsUnsignedLongLong(obj1));
+      Py_DECREF(obj1);
+      return;
+    }
+#endif
+    break;
+
+  case PT_int:
+    if (PyLong_Check(object)) {
+      _this->pack_int(PyLong_AsLong(object));
+      return;
+    }
+#if PY_MAJOR_VERSION < 3
+    else if (PyInt_Check(object)) {
+      _this->pack_int(PyInt_AsLong(object));
+      return;
+    }
+#endif
+    break;
+
+  case PT_uint:
+    if (PyLong_Check(object)) {
+      _this->pack_uint(PyLong_AsUnsignedLong(object));
+      return;
+    }
+#if PY_MAJOR_VERSION < 3
+    else if (PyInt_Check(object)) {
+      PyObject *obj1 = PyNumber_Long(object);
+      _this->pack_uint(PyLong_AsUnsignedLong(obj1));
+      Py_DECREF(obj1);
+      return;
+    }
+#endif
+    break;
+
+  default:
+    break;
+  }
+
+  if (PyLong_Check(object)) {
+    _this->pack_int(PyLong_AsLong(object));
+#if PY_MAJOR_VERSION < 3
+  } else if (PyInt_Check(object)) {
+    _this->pack_int(PyInt_AS_LONG(object));
+#endif
+  } else if (PyFloat_Check(object)) {
+    _this->pack_double(PyFloat_AS_DOUBLE(object));
+  } else if (PyLong_Check(object)) {
+    _this->pack_int64(PyLong_AsLongLong(object));
+#if PY_MAJOR_VERSION >= 3
+  } else if (PyUnicode_Check(object)) {
+    const char *buffer;
+    Py_ssize_t length;
+    buffer = PyUnicode_AsUTF8AndSize(object, &length);
+    if (buffer) {
+      _this->pack_string(std::string(buffer, length));
+    }
+  } else if (PyBytes_Check(object)) {
+    const unsigned char *buffer;
+    Py_ssize_t length;
+    PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
+    if (buffer) {
+      _this->pack_blob(vector_uchar(buffer, buffer + length));
+    }
+#else
+  } else if (PyString_Check(object) || PyUnicode_Check(object)) {
+    char *buffer;
+    Py_ssize_t length;
+    PyString_AsStringAndSize(object, &buffer, &length);
+    if (buffer) {
+      _this->pack_string(std::string(buffer, length));
+    }
+#endif
+  } 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 = nullptr;
+    const DCPackerInterface *current_field = _this->get_current_field();
+    if (current_field != nullptr) {
+      const DCClassParameter *class_param = _this->get_current_field()->as_class_parameter();
+      if (class_param != nullptr) {
+        dclass = class_param->get_class();
+
+        if (invoke_extension(dclass).has_class_def()) {
+          PyObject *class_def = invoke_extension(dclass).get_class_def();
+          is_instance = (PyObject_IsInstance(object, invoke_extension(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 != nullptr && (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).
+      _this->push();
+      int size = PySequence_Size(object);
+      for (int i = 0; i < size; ++i) {
+        PyObject *element = PySequence_GetItem(object, i);
+        if (element != nullptr) {
+          pack_object(element);
+          Py_DECREF(element);
+        } else {
+          std::cerr << "Unable to extract item " << i << " from sequence.\n";
+        }
+      }
+      _this->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.
+      std::ostringstream strm;
+      strm << "Don't know how to pack object: "
+           << Extension<DCField>::get_pystr(object);
+      nassert_raise(strm.str());
+      _this->_pack_error = true;
+    }
+  }
+}
+
+/**
+ * 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 *Extension<DCPacker>::
+unpack_object() {
+  PyObject *object = nullptr;
+
+  DCPackType pack_type = _this->get_pack_type();
+
+  switch (pack_type) {
+  case PT_invalid:
+    object = Py_None;
+    Py_INCREF(object);
+    _this->unpack_skip();
+    break;
+
+  case PT_double:
+    {
+      double value = _this->unpack_double();
+      object = PyFloat_FromDouble(value);
+    }
+    break;
+
+  case PT_int:
+    {
+      int value = _this->unpack_int();
+#if PY_MAJOR_VERSION >= 3
+      object = PyLong_FromLong(value);
+#else
+      object = PyInt_FromLong(value);
+#endif
+    }
+    break;
+
+  case PT_uint:
+    {
+      unsigned int value = _this->unpack_uint();
+#if PY_MAJOR_VERSION >= 3
+      object = PyLong_FromLong(value);
+#else
+      if (value & 0x80000000) {
+        object = PyLong_FromUnsignedLong(value);
+      } else {
+        object = PyInt_FromLong(value);
+      }
+#endif
+    }
+    break;
+
+  case PT_int64:
+    {
+      int64_t value = _this->unpack_int64();
+      object = PyLong_FromLongLong(value);
+    }
+    break;
+
+  case PT_uint64:
+    {
+      uint64_t value = _this->unpack_uint64();
+      object = PyLong_FromUnsignedLongLong(value);
+    }
+    break;
+
+  case PT_blob:
+#if PY_MAJOR_VERSION >= 3
+    {
+      std::string str;
+      _this->unpack_string(str);
+      object = PyBytes_FromStringAndSize(str.data(), str.size());
+    }
+    break;
+#endif
+    // On Python 2, fall through to below.
+
+  case PT_string:
+    {
+      std::string str;
+      _this->unpack_string(str);
+#if PY_MAJOR_VERSION >= 3
+      object = PyUnicode_FromStringAndSize(str.data(), str.size());
+#else
+      object = PyString_FromStringAndSize(str.data(), str.size());
+#endif
+    }
+    break;
+
+  case PT_class:
+    {
+      const DCClassParameter *class_param = _this->get_current_field()->as_class_parameter();
+      if (class_param != nullptr) {
+        const DCClass *dclass = class_param->get_class();
+        if (invoke_extension(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);
+          if (object == nullptr) {
+            std::cerr << "Unable to construct object of class "
+                 << dclass->get_name() << "\n";
+          } else {
+            break;
+          }
+        }
+      }
+    }
+    // Fall through (if no constructor)
+
+    // 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);
+
+      _this->push();
+      while (_this->more_nested_fields()) {
+        PyObject *element = unpack_object();
+        PyList_Append(object, element);
+        Py_DECREF(element);
+      }
+      _this->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;
+  }
+
+  nassertr(object != nullptr, nullptr);
+  return object;
+}
+
+/**
+ * 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 Extension<DCPacker>::
+pack_class_object(const DCClass *dclass, PyObject *object) {
+  _this->push();
+  while (_this->more_nested_fields() && !_this->_pack_error) {
+    const DCField *field = _this->get_current_field()->as_field();
+    nassertv(field != nullptr);
+    get_class_element(dclass, object, field);
+  }
+  _this->pop();
+}
+
+/**
+ * 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 *Extension<DCPacker>::
+unpack_class_object(const DCClass *dclass) {
+  PyObject *class_def = invoke_extension(dclass).get_class_def();
+  nassertr(class_def != nullptr, nullptr);
+
+  PyObject *object = nullptr;
+
+  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, nullptr);
+    if (object == nullptr) {
+      return nullptr;
+    }
+  }
+
+  _this->push();
+  if (object == nullptr && _this->more_nested_fields()) {
+    // The first nested field will be the constructor.
+    const DCField *field = _this->get_current_field()->as_field();
+    nassertr(field != nullptr, object);
+    nassertr(field == dclass->get_constructor(), object);
+
+    set_class_element(class_def, object, field);
+
+    // By now, the object should have been constructed.
+    if (object == nullptr) {
+      return nullptr;
+    }
+  }
+  while (_this->more_nested_fields()) {
+    const DCField *field = _this->get_current_field()->as_field();
+    nassertr(field != nullptr, object);
+
+    set_class_element(class_def, object, field);
+  }
+  _this->pop();
+
+  return object;
+}
+
+/**
+ * Unpacks the current element and stuffs it on the Python class object in
+ * whatever way is appropriate.
+ */
+void Extension<DCPacker>::
+set_class_element(PyObject *class_def, PyObject *&object,
+                  const DCField *field) {
+  std::string field_name = field->get_name();
+  DCPackType pack_type = _this->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.
+      _this->push();
+      while (_this->more_nested_fields()) {
+        const DCField *field = _this->get_current_field()->as_field();
+        nassertv(field != nullptr);
+        nassertv(object != nullptr);
+        set_class_element(class_def, object, field);
+      }
+      _this->pop();
+      break;
+
+    default:
+      // Otherwise, we just skip over the field.
+      _this->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 == nullptr) {
+        // 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 != nullptr) {
+            PyObject *result = PyObject_CallObject(func, element);
+            Py_XDECREF(result);
+            Py_DECREF(func);
+          }
+        }
+      }
+
+    } else {
+      nassertv(object != nullptr);
+      PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
+    }
+
+    Py_DECREF(element);
+  }
+}
+
+/**
+ * Gets the current element from the Python object and packs it.
+ */
+void Extension<DCPacker>::
+get_class_element(const DCClass *dclass, PyObject *object,
+                  const DCField *field) {
+  std::string field_name = field->get_name();
+  DCPackType pack_type = _this->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.
+      _this->push();
+      while (_this->more_nested_fields() && !_this->_pack_error) {
+        const DCField *field = _this->get_current_field()->as_field();
+        nassertv(field != nullptr);
+        get_class_element(dclass, object, field);
+      }
+      _this->pop();
+      break;
+
+    default:
+      // Otherwise, we just pack the default value.
+      _this->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 (!invoke_extension(dclass).pack_required_field(*_this, object, field)) {
+      _this->_pack_error = true;
+    }
+  }
+}
+
+#endif  // HAVE_PYTHON

+ 45 - 0
direct/src/dcparser/dcPacker_ext.h

@@ -0,0 +1,45 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcPacker_ext.h
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#ifndef DCPACKER_EXT_H
+#define DCPACKER_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "dcPacker.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for DCPacker, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<DCPacker> : public ExtensionBase<DCPacker> {
+public:
+  void pack_object(PyObject *object);
+  PyObject *unpack_object();
+
+  void pack_class_object(const DCClass *dclass, PyObject *object);
+  PyObject *unpack_class_object(const DCClass *dclass);
+  void set_class_element(PyObject *class_def, PyObject *&object,
+                         const DCField *field);
+  void get_class_element(const DCClass *dclass, PyObject *object,
+                         const DCField *field);
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // DCPACKER_EXT_H

+ 0 - 42
direct/src/dcparser/dcPython.h

@@ -1,42 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file dcPython.h
- * @author drose
- * @date 2004-06-22
- */
-
-#ifndef DCPYTHON_H
-#define DCPYTHON_H
-
-// The only purpose of this file is to serve as a common place to put the
-// nonsense associated with #including <Python.h>.
-
-#ifdef HAVE_PYTHON
-
-#define PY_SSIZE_T_CLEAN 1
-
-#undef _POSIX_C_SOURCE
-#undef _XOPEN_SOURCE
-#include <Python.h>
-
-// Python 2.5 adds Py_ssize_t; earlier versions don't have it.
-#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
-typedef int Py_ssize_t;
-#define PY_SSIZE_T_MAX INT_MAX
-#define PY_SSIZE_T_MIN INT_MIN
-#endif
-
-// Several interfaces in this module that use Python also require these header
-// files, so we might as well pick them up too.
-#include "datagram.h"
-#include "datagramIterator.h"
-
-#endif  // HAVE_PYTHON
-
-#endif

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

@@ -0,0 +1,3 @@
+#include "dcClass_ext.cxx"
+#include "dcField_ext.cxx"
+#include "dcPacker_ext.cxx"

+ 2 - 0
direct/src/directtools/DirectUtil.py

@@ -1,5 +1,7 @@
 
 from .DirectGlobals import *
+from panda3d.core import VBase4
+from direct.task.Task import Task
 
 # Routines to adjust values
 def ROUND_TO(value, divisor):

+ 14 - 7
direct/src/dist/FreezeTool.py

@@ -800,29 +800,36 @@ class Freezer:
                     self.moduleSuffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE)
         else:
             self.moduleSuffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)]
+
+            abi_version = '{0}{1}'.format(*sys.version_info)
+            abi_flags = ''
+            if sys.version_info < (3, 8):
+                abi_flags += 'm'
+
             if 'linux' in self.platform:
                 self.moduleSuffixes += [
-                    ('.cpython-{0}{1}m-x86_64-linux-gnu.so'.format(*sys.version_info), 'rb', 3),
-                    ('.cpython-{0}{1}m-i686-linux-gnu.so'.format(*sys.version_info), 'rb', 3),
+                    ('.cpython-{0}{1}-x86_64-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
+                    ('.cpython-{0}{1}-i686-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
             elif 'win' in self.platform:
+                # ABI flags are not appended on Windows.
                 self.moduleSuffixes += [
-                    ('.cp{0}{1}-win_amd64.pyd'.format(*sys.version_info), 'rb', 3),
-                    ('.cp{0}{1}-win32.pyd'.format(*sys.version_info), 'rb', 3),
+                    ('.cp{0}-win_amd64.pyd'.format(abi_version), 'rb', 3),
+                    ('.cp{0}-win32.pyd'.format(abi_version), 'rb', 3),
                     ('.pyd', 'rb', 3),
                 ]
             elif 'mac' in self.platform:
                 self.moduleSuffixes += [
-                    ('.cpython-{0}{1}m-darwin.so'.format(*sys.version_info), 'rb', 3),
+                    ('.cpython-{0}{1}-darwin.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
             else: # FreeBSD et al.
                 self.moduleSuffixes += [
-                    ('.cpython-{0}{1}m.so'.format(*sys.version_info), 'rb', 3),
-                    ('.abi{0}.so'.format(*sys.version_info), 'rb', 3),
+                    ('.cpython-{0}{1}.so'.format(abi_version, abi_flags), 'rb', 3),
+                    ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
 

+ 56 - 0
direct/src/dist/commands.py

@@ -14,12 +14,14 @@ import struct
 import imp
 import string
 import time
+import tempfile
 
 import setuptools
 import distutils.log
 
 from . import FreezeTool
 from . import pefile
+from direct.p3d.DeploymentTools import Icon
 import panda3d.core as p3d
 
 
@@ -224,6 +226,7 @@ class build_apps(setuptools.Command):
         self.exclude_patterns = []
         self.include_modules = {}
         self.exclude_modules = {}
+        self.icons = {}
         self.platforms = [
             'manylinux1_x86_64',
             'macosx_10_6_x86_64',
@@ -298,6 +301,7 @@ class build_apps(setuptools.Command):
             key: _parse_list(value)
             for key, value in _parse_dict(self.exclude_modules).items()
         }
+        self.icons = _parse_dict(self.icons)
         self.platforms = _parse_list(self.platforms)
         self.plugins = _parse_list(self.plugins)
         self.extra_prc_files = _parse_list(self.extra_prc_files)
@@ -357,6 +361,18 @@ class build_apps(setuptools.Command):
         tmp.update(self.package_data_dirs)
         self.package_data_dirs = tmp
 
+        self.icon_objects = {}
+        for app, iconpaths in self.icons.items():
+            if not isinstance(iconpaths, list) and not isinstance(iconpaths, tuple):
+                iconpaths = (iconpaths,)
+
+            iconobj = Icon()
+            for iconpath in iconpaths:
+                iconobj.addImage(iconpath)
+
+            iconobj.generateMissingImages()
+            self.icon_objects[app] = iconobj
+
     def run(self):
         self.announce('Building platforms: {0}'.format(','.join(self.platforms)), distutils.log.INFO)
 
@@ -433,6 +449,22 @@ class build_apps(setuptools.Command):
 
         return wheelpaths
 
+    def update_pe_resources(self, appname, runtime):
+        """Update resources (e.g., icons) in windows PE file"""
+
+        icon = self.icon_objects.get(
+            appname,
+            self.icon_objects.get('*', None),
+        )
+
+        if icon is not None:
+            pef = pefile.PEFile()
+            pef.open(runtime, 'r+')
+            pef.add_icon(icon)
+            pef.add_resource_section()
+            pef.write_changes()
+            pef.close()
+
     def bundle_macos_app(self, builddir):
         """Bundle built runtime into a .app for macOS"""
 
@@ -474,6 +506,15 @@ class build_apps(setuptools.Command):
             'CFBundleSignature': '', #TODO
             'CFBundleExecutable': self.macos_main_app,
         }
+
+        icon = self.icon_objects.get(
+            self.macos_main_app,
+            self.icon_objects.get('*', None)
+        )
+        if icon is not None:
+            plist['CFBundleIconFile'] = 'iconfile'
+            icon.makeICNS(os.path.join(resdir, 'iconfile.icns'))
+
         with open(os.path.join(contentsdir, 'Info.plist'), 'wb') as f:
             if hasattr(plistlib, 'dump'):
                 plistlib.dump(plist, f)
@@ -618,6 +659,18 @@ class build_apps(setuptools.Command):
                 stub_path = os.path.join(os.path.dirname(dtool_path), '..', 'bin', stub_name)
                 stub_file = open(stub_path, 'rb')
 
+            # Do we need an icon?  On Windows, we need to add this to the stub
+            # before we add the blob.
+            if 'win' in platform:
+                temp_file = tempfile.NamedTemporaryFile(suffix='-icon.exe', delete=False)
+                temp_file.write(stub_file.read())
+                stub_file.close()
+                temp_file.close()
+                self.update_pe_resources(appname, temp_file.name)
+                stub_file = open(temp_file.name, 'rb')
+            else:
+                temp_file = None
+
             freezer.generateRuntimeFromStub(target_path, stub_file, use_console, {
                 'prc_data': prcexport if self.embed_prc_data else None,
                 'default_prc_dir': self.default_prc_dir,
@@ -633,6 +686,9 @@ class build_apps(setuptools.Command):
             }, self.log_append)
             stub_file.close()
 
+            if temp_file:
+                os.unlink(temp_file.name)
+
             # Copy the dependencies.
             search_path = [builddir]
             if use_wheels:

+ 4 - 3
direct/src/distributed/cConnectionRepository.cxx

@@ -26,6 +26,7 @@
 
 #ifdef HAVE_PYTHON
 #include "py_panda.h"
+#include "dcClass_ext.h"
 #endif
 
 using std::endl;
@@ -736,7 +737,7 @@ handle_update_field() {
       // get into trouble if it tried to delete the object from the doId2do
       // map.
       Py_INCREF(distobj);
-      dclass->receive_update(distobj, _di);
+      invoke_extension(dclass).receive_update(distobj, _di);
       Py_DECREF(distobj);
 
       if (PyErr_Occurred()) {
@@ -820,7 +821,7 @@ handle_update_field_owner() {
         // make a copy of the datagram iterator so that we can use the main
         // iterator for the non-owner update
         DatagramIterator _odi(_di);
-        dclass->receive_update(distobjOV, _odi);
+        invoke_extension(dclass).receive_update(distobjOV, _odi);
         Py_DECREF(distobjOV);
 
         if (PyErr_Occurred()) {
@@ -861,7 +862,7 @@ handle_update_field_owner() {
         // get into trouble if it tried to delete the object from the doId2do
         // map.
         Py_INCREF(distobj);
-        dclass->receive_update(distobj, _di);
+        invoke_extension(dclass).receive_update(distobj, _di);
         Py_DECREF(distobj);
 
         if (PyErr_Occurred()) {

+ 1 - 1
direct/src/distributed/cConnectionRepository.h

@@ -53,7 +53,7 @@ class SocketStream;
  * the C++ layer, while server messages that are not understood by the C++
  * layer are returned up to the Python layer for processing.
  */
-class EXPCL_DIRECT_DISTRIBUTED CConnectionRepository {
+class CConnectionRepository {
 PUBLISHED:
   explicit CConnectionRepository(bool has_owner_view = false,
                                  bool threaded_net = false);

+ 4 - 0
direct/src/distributed/cDistributedSmoothNodeBase.cxx

@@ -18,6 +18,10 @@
 #include "dcmsgtypes.h"
 #include "config_distributed.h"
 
+#ifdef HAVE_PYTHON
+#include "py_panda.h"
+#endif
+
 static const PN_stdfloat smooth_node_epsilon = 0.01;
 static const double network_time_precision = 100.0;  // Matches ClockDelta.py
 

+ 1 - 2
direct/src/distributed/cDistributedSmoothNodeBase.h

@@ -18,7 +18,6 @@
 #include "nodePath.h"
 #include "dcbase.h"
 #include "dcPacker.h"
-#include "dcPython.h"  // to pick up Python.h
 #include "clockObject.h"
 
 class DCClass;
@@ -28,7 +27,7 @@ class CConnectionRepository;
  * This class defines some basic methods of DistributedSmoothNodeBase which
  * have been moved into C++ as a performance optimization.
  */
-class EXPCL_DIRECT_DISTRIBUTED CDistributedSmoothNodeBase {
+class CDistributedSmoothNodeBase {
 PUBLISHED:
   CDistributedSmoothNodeBase();
   ~CDistributedSmoothNodeBase();

+ 4 - 4
direct/src/distributed/config_distributed.h

@@ -23,10 +23,10 @@
 
 NotifyCategoryDecl(distributed, EXPCL_DIRECT_DISTRIBUTED, EXPTP_DIRECT_DISTRIBUTED);
 
-extern ConfigVariableInt game_server_timeout_ms;
-extern ConfigVariableDouble min_lag;
-extern ConfigVariableDouble max_lag;
-extern ConfigVariableBool handle_datagrams_internally;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableInt game_server_timeout_ms;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableDouble min_lag;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableDouble max_lag;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableBool handle_datagrams_internally;
 
 extern EXPCL_DIRECT_DISTRIBUTED void init_libdistributed();
 

+ 2 - 1
direct/src/gui/DirectDialog.py

@@ -3,6 +3,7 @@
 __all__ = ['findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog', 'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog']
 
 from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
 from . import DirectGuiGlobals as DGG
 from .DirectFrame import *
 from .DirectButton import *
@@ -207,7 +208,7 @@ class DirectDialog(DirectFrame):
             image = None
         # Get size of text/geom without image (for state 0)
         if image:
-            image.reparentTo(hidden)
+            image.reparentTo(ShowBaseGlobal.hidden)
         bounds = self.stateNodePath[0].getTightBounds()
         if image:
             image.reparentTo(self.stateNodePath[0])

+ 9 - 7
direct/src/gui/DirectEntry.py

@@ -4,6 +4,7 @@ text entered using the keyboard."""
 __all__ = ['DirectEntry']
 
 from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
 from . import DirectGuiGlobals as DGG
 from .DirectFrame import *
 from .OnscreenText import OnscreenText
@@ -94,7 +95,7 @@ class DirectEntry(DirectFrame):
         self.onscreenText = self.createcomponent(
             'text', (), None,
             OnscreenText,
-            (), parent = hidden,
+            (), parent = ShowBaseGlobal.hidden,
             # Pass in empty text to avoid extra work, since its really
             # The PGEntry which will use the TextNode to generate geometry
             text = '',
@@ -215,11 +216,11 @@ class DirectEntry(DirectFrame):
         self._autoCapitalize()
 
     def _autoCapitalize(self):
-        name = self.get().decode('utf-8')
+        name = self.guiItem.getWtext()
         # capitalize each word, allowing for things like McMutton
-        capName = ''
+        capName = u''
         # track each individual word to detect prefixes like Mc
-        wordSoFar = ''
+        wordSoFar = u''
         # track whether the previous character was part of a word or not
         wasNonWordChar = True
         for i, character in enumerate(name):
@@ -228,9 +229,9 @@ class DirectEntry(DirectFrame):
             #   This assumes that string.lower and string.upper will return different
             #   values for all unicode letters.
             # - Don't count apostrophes as a break between words
-            if character.lower() == character.upper() and character != "'":
+            if character.lower() == character.upper() and character != u"'":
                 # we are between words
-                wordSoFar = ''
+                wordSoFar = u''
                 wasNonWordChar = True
             else:
                 capitalize = False
@@ -254,7 +255,8 @@ class DirectEntry(DirectFrame):
                 wordSoFar += character
                 wasNonWordChar = False
             capName += character
-        self.enterText(capName.encode('utf-8'))
+        self.guiItem.setWtext(capName)
+        self.guiItem.setCursorPosition(self.guiItem.getNumCharacters())
 
     def focusOutCommandFunc(self):
         if self['focusOutCommand']:

+ 25 - 3
direct/src/gui/DirectEntryScroll.py

@@ -29,19 +29,41 @@ class DirectEntryScroll(DirectFrame):
            # frameSize = (-0.006, 3.2, -0.015, 0.036),
         # if you need to scale the entry scale it's parent instead
 
-        self.entry = entry
         self.canvas = NodePath(self.guiItem.getCanvasNode())
-        self.entry.reparentTo(self.canvas)
         self.canvas.setPos(0,0,0)
 
-        self.entry.bind(DGG.CURSORMOVE,self.cursorMove)
+        self.entry = None
+        if entry is not None:
+            self.entry = entry
+            self.entry.reparentTo(self.canvas)
+            self.entry.bind(DGG.CURSORMOVE, self.cursorMove)
 
         self.canvas.node().setBounds(OmniBoundingVolume())
         self.canvas.node().setFinal(1)
         self.resetCanvas()
 
+    def setEntry(self, entry):
+        """
+        Sets a DirectEntry element for this scroll frame. A DirectEntryScroll
+        can only hold one entry at a time, so make sure to not call this
+        function twice or call clearEntry before to make sure no entry
+        is already set.
+        """
+        assert self.entry is None, "An entry was already set for this DirectEntryScroll element"
+        self.entry = entry
+        self.entry.reparentTo(self.canvas)
 
+        self.entry.bind(DGG.CURSORMOVE, self.cursorMove)
 
+    def clearEntry(self):
+        """
+        detaches and unbinds the entry from the scroll frame and its
+        events. You'll be responsible for destroying it.
+        """
+        if self.entry is None: return
+        self.entry.unbind(DGG.CURSORMOVE)
+        self.entry.detachNode()
+        self.entry = None
 
     def cursorMove(self, cursorX, cursorY):
         cursorX = self.entry.guiItem.getCursorX() * self.entry['text_scale'][0]

+ 3 - 3
direct/src/gui/DirectGuiBase.py

@@ -634,7 +634,7 @@ class DirectGuiBase(DirectObject.DirectObject):
         """
         # Need to tack on gui item specific id
         gEvent = event + self.guiId
-        if ShowBase.config.GetBool('debug-directgui-msgs', False):
+        if ShowBaseGlobal.config.GetBool('debug-directgui-msgs', False):
             from direct.showbase.PythonUtil import StackTrace
             print(gEvent)
             print(StackTrace())
@@ -663,7 +663,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
     # Determine the default initial state for inactive (or
     # unclickable) components.  If we are in edit mode, these are
     # actually clickable by default.
-    guiEdit = ShowBase.config.GetBool('direct-gui-edit', False)
+    guiEdit = ShowBaseGlobal.config.GetBool('direct-gui-edit', False)
     if guiEdit:
         inactiveInitState = DGG.NORMAL
     else:
@@ -729,7 +729,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
             guiObjectCollector.addLevel(1)
             guiObjectCollector.flushLevel()
             # track gui items by guiId for tracking down leaks
-            if ShowBase.config.GetBool('track-gui-items', False):
+            if ShowBaseGlobal.config.GetBool('track-gui-items', False):
                 if not hasattr(ShowBase, 'guiItems'):
                     ShowBase.guiItems = {}
                 if self.guiId in ShowBase.guiItems:

+ 4 - 3
direct/src/gui/DirectOptionMenu.py

@@ -27,7 +27,7 @@ class DirectOptionMenu(DirectButton):
             # Amount of padding to place around popup button indicator
             ('popupMarkerBorder', (.1, .1), None),
             # The initial position of the popup marker
-            ('popupMarker_pos', (0, 0, 0), None),
+            ('popupMarker_pos', None, None),
             # Background color to use to highlight popup menu items
             ('highlightColor', (.5, .5, .5, 1), None),
             # Extra scale to use on highlight popup menu items
@@ -44,8 +44,6 @@ class DirectOptionMenu(DirectButton):
         DirectButton.__init__(self, parent)
         # Record any user specified frame size
         self.initFrameSize = self['frameSize']
-        # Record any user specified popup marker position
-        self.initPopupMarkerPos = self['popupMarker_pos']
         # Create a small rectangular marker to distinguish this button
         # as a popup menu button
         self.popupMarker = self.createcomponent(
@@ -54,6 +52,8 @@ class DirectOptionMenu(DirectButton):
             frameSize = (-0.5, 0.5, -0.2, 0.2),
             scale = 0.4,
             relief = DGG.RAISED)
+        # Record any user specified popup marker position
+        self.initPopupMarkerPos = self['popupMarker_pos']
         # This needs to popup the menu too
         self.popupMarker.bind(DGG.B1PRESS, self.showPopupMenu)
         # Check if item is highlighted on release and select it if it is
@@ -77,6 +77,7 @@ class DirectOptionMenu(DirectButton):
             state = 'normal')
         # Make sure this is on top of all the other widgets
         self.cancelFrame.setBin('gui-popup', 0)
+        self.cancelFrame.node().setBounds(OmniBoundingVolume())
         self.cancelFrame.bind(DGG.B1PRESS, self.hidePopupMenu)
         # Default action on press is to show popup menu
         self.bind(DGG.B1PRESS, self.showPopupMenu)

+ 6 - 1
direct/src/gui/DirectScrolledFrame.py

@@ -33,7 +33,7 @@ class DirectScrolledFrame(DirectFrame):
             ('canvasSize',     (-1, 1, -1, 1),        self.setCanvasSize),
             ('manageScrollBars', 1,                self.setManageScrollBars),
             ('autoHideScrollBars', 1,              self.setAutoHideScrollBars),
-            ('scrollBarWidth', 0.08,               None),
+            ('scrollBarWidth', 0.08,               self.setScrollBarWidth),
             ('borderWidth',    (0.01, 0.01),       self.setBorderWidth),
             )
 
@@ -72,6 +72,11 @@ class DirectScrolledFrame(DirectFrame):
         # Call option initialization functions
         self.initialiseoptions(DirectScrolledFrame)
 
+    def setScrollBarWidth(self):
+        w = self['scrollBarWidth']
+        self.verticalScroll["frameSize"] = (-w / 2.0, w / 2.0, -1, 1)
+        self.horizontalScroll["frameSize"] = (-1, 1, -w / 2.0, w / 2.0)
+
     def setCanvasSize(self):
         f = self['canvasSize']
         self.guiItem.setVirtualFrame(f[0], f[1], f[2], f[3])

+ 5 - 4
direct/src/gui/DirectScrolledList.py

@@ -3,6 +3,7 @@
 __all__ = ['DirectScrolledListItem', 'DirectScrolledList']
 
 from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
 from . import DirectGuiGlobals as DGG
 from direct.directnotify import DirectNotifyGlobal
 from direct.task.Task import Task
@@ -369,7 +370,7 @@ class DirectScrolledList(DirectFrame):
                 del self.currentSelected
             self["items"].remove(item)
             if type(item) != type(''):
-                item.reparentTo(hidden)
+                item.reparentTo(ShowBaseGlobal.hidden)
             self.refresh()
             return 1
         else:
@@ -387,7 +388,7 @@ class DirectScrolledList(DirectFrame):
                 item.destroy()
             self["items"].remove(item)
             if type(item) != type(''):
-                item.reparentTo(hidden)
+                item.reparentTo(ShowBaseGlobal.hidden)
             self.refresh()
             return 1
         else:
@@ -410,7 +411,7 @@ class DirectScrolledList(DirectFrame):
             self["items"].remove(item)
             if type(item) != type(''):
                 #RAU possible leak here, let's try to do the right thing
-                #item.reparentTo(hidden)
+                #item.reparentTo(ShowBaseGlobal.hidden)
                 item.removeNode()
             retval = 1
 
@@ -435,7 +436,7 @@ class DirectScrolledList(DirectFrame):
             self["items"].remove(item)
             if type(item) != type(''):
                 #RAU possible leak here, let's try to do the right thing
-                #item.reparentTo(hidden)
+                #item.reparentTo(ShowBaseGlobal.hidden)
                 item.removeNode()
             retval = 1
         if (refresh):

+ 1 - 19
direct/src/interval/SoundInterval.py

@@ -27,8 +27,7 @@ class SoundInterval(Interval.Interval):
     # than explicitly restarting the sound every time around. This
     # prevents a skip in the sound at every repetition (the gap in
     # the sound is caused by the delay between the end of the sound
-    # and the next taskMgr cycle). There still seems to be a skip
-    # in Miles when looping MP3s. =(
+    # and the next taskMgr cycle).
     # RAU 03/01/07 add listenerNode in case we don't want to
     # use base.camera as the listener, node must not be None
     def __init__(self, sound, loop = 0, duration = 0.0, name = None,
@@ -62,23 +61,6 @@ class SoundInterval(Interval.Interval):
             #if (duration == 0):
             #    self.notify.warning('zero length duration!')
 
-            # MPG - hack for Miles bug
-            #duration += 1.5
-
-            # DCR - hack for Miles bug - adding 1.5 seconds caused
-            # problems for MG_neg_buzzer.wav
-
-            # DCR - what this is all about: Miles is under-reporting the
-            # length of MP3 files, and they're getting cut off too early.
-            # This is a temporary hack. We could:
-            # - hack Miles to fix its MP3 length calculation
-            # - complain louder about this to RAD
-            # - precompute MP3 durations and store them in a table
-
-            # drose - ok, I've put in a lower-level workaround in the
-            # MilesAudioManager.  This is no longer necessary up here,
-            # where it pollutes SoundInterval for everyone.
-            #duration += min(duration * 2.4, 1.5)
 
         # Generate unique name if necessary
         if (name == None):

+ 199 - 62
direct/src/p3d/DeploymentTools.py

@@ -10,7 +10,7 @@ from direct.directnotify.DirectNotifyGlobal import *
 from direct.showbase.AppRunnerGlobal import appRunner
 from panda3d.core import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile
 from panda3d.core import TiXmlDocument, TiXmlDeclaration, TiXmlElement, readXmlStream
-from panda3d.core import PNMImage, PNMFileTypeRegistry
+from panda3d.core import PNMImage, PNMFileTypeRegistry, StringStream
 from direct.stdpy.file import *
 from direct.p3d.HostInfo import HostInfo
 # This is important for some reason
@@ -332,6 +332,135 @@ class Icon:
 
         return True
 
+    def generateMissingImages(self):
+        """ Generates image sizes that should be present but aren't by scaling
+        from the next higher size. """
+
+        for required_size in (256, 128, 48, 32, 16):
+            if required_size in self.images:
+                continue
+
+            sizes = sorted(self.images.keys())
+            if required_size * 2 in sizes:
+                from_size = required_size * 2
+            else:
+                for from_size in sizes:
+                    if from_size > required_size:
+                        break
+
+            if from_size > required_size:
+                Icon.notify.warning("Generating %dx%d icon by scaling down %dx%d image" % (required_size, required_size, from_size, from_size))
+
+                image = PNMImage(required_size, required_size)
+                if self.images[from_size].hasAlpha():
+                    image.addAlpha()
+                image.quickFilterFrom(self.images[from_size])
+                self.images[required_size] = image
+            else:
+                Icon.notify.warning("Cannot generate %dx%d icon; no higher resolution image available" % (required_size, required_size))
+
+    def _write_bitmap(self, fp, image, size, bpp):
+        """ Writes the bitmap header and data of an .ico file. """
+
+        fp.write(struct.pack('<IiiHHIIiiII', 40, size, size * 2, 1, bpp, 0, 0, 0, 0, 0, 0))
+
+        # XOR mask
+        if bpp == 24:
+            # Align rows to 4-byte boundary
+            rowalign = '\0' * (-(size * 3) & 3)
+            for y in xrange(size):
+                for x in xrange(size):
+                    r, g, b = image.getXel(x, size - y - 1)
+                    fp.write(struct.pack('<BBB', int(b * 255), int(g * 255), int(r * 255)))
+                fp.write(rowalign)
+
+        elif bpp == 32:
+            for y in xrange(size):
+                for x in xrange(size):
+                    r, g, b, a = image.getXelA(x, size - y - 1)
+                    fp.write(struct.pack('<BBBB', int(b * 255), int(g * 255), int(r * 255), int(a * 255)))
+
+        elif bpp == 8:
+            # We'll have to generate a palette of 256 colors.
+            hist = PNMImage.Histogram()
+            if image.hasAlpha():
+                # Make a copy without alpha channel.
+                image2 = PNMImage(image)
+                image2.premultiplyAlpha()
+                image2.removeAlpha()
+            else:
+                image2 = image
+            image2.make_histogram(hist)
+            colors = list(hist.get_pixels())
+            if len(colors) > 256:
+                # Palette too large; remove infrequent colors.
+                colors.sort(key=hist.get_count, reverse=True)
+
+                # Find the closest color on the palette matching each color
+                # that didn't fit.  This is certainly not the best palette
+                # generation code, but it'll do for now.
+                closest_indices = []
+                for color in colors[256:]:
+                    closest_index = 0
+                    closest_diff = 1025
+                    for i, closest_color in enumerate(colors[:256]):
+                        diff = abs(color.get_red() - closest_color.get_red()) \
+                             + abs(color.get_green() - closest_color.get_green()) \
+                             + abs(color.get_blue() - closest_color.get_blue())
+                        if diff < closest_diff:
+                            closest_index = i
+                            closest_diff = diff
+                    assert closest_diff < 100
+                    closest_indices.append(closest_index)
+
+            # Write the palette.
+            i = 0
+            while i < 256 and i < len(colors):
+                r, g, b, a = colors[i]
+                fp.write(struct.pack('<BBBB', b, g, r, 0))
+                i += 1
+            if i < 256:
+                # Fill the rest with zeroes.
+                fp.write(b'\x00' * (4 * (256 - i)))
+
+            # Write indices.  Align rows to 4-byte boundary.
+            rowalign = b'\0' * (-size & 3)
+            for y in xrange(size):
+                for x in xrange(size):
+                    pixel = image2.get_pixel(x, size - y - 1)
+                    index = colors.index(pixel)
+                    if index >= 256:
+                        # Find closest pixel instead.
+                        index = closest_indices[index - 256]
+                    fp.write(struct.pack('<B', index))
+                fp.write(rowalign)
+        else:
+            raise ValueError("Invalid bpp %d" % (bpp))
+
+        # Create an AND mask, aligned to 4-byte boundary
+        if image.hasAlpha() and bpp <= 8:
+            rowalign = b'\0' * (-((size + 7) >> 3) & 3)
+            for y in xrange(size):
+                mask = 0
+                num_bits = 7
+                for x in xrange(size):
+                    a = image.get_alpha_val(x, size - y - 1)
+                    if a <= 1:
+                        mask |= (1 << num_bits)
+                    num_bits -= 1
+                    if num_bits < 0:
+                        fp.write(struct.pack('<B', mask))
+                        mask = 0
+                        num_bits = 7
+                if num_bits < 7:
+                    fp.write(struct.pack('<B', mask))
+                fp.write(rowalign)
+        else:
+            andsize = (size + 7) >> 3
+            if andsize % 4 != 0:
+                andsize += 4 - (andsize % 4)
+            fp.write(b'\x00' * (andsize * size))
+
     def makeICO(self, fn):
         """ Writes the images to a Windows ICO file.  Returns True on success. """
 
@@ -339,57 +468,71 @@ class Icon:
             fn = Filename.fromOsSpecific(fn)
         fn.setBinary()
 
+        # ICO files only support resolutions up to 256x256.
         count = 0
         for size in self.images.keys():
+            if size < 256:
+                count += 1
             if size <= 256:
                 count += 1
+        dataoffs = 6 + count * 16
 
         ico = open(fn, 'wb')
         ico.write(struct.pack('<HHH', 0, 1, count))
 
-        # Write the directory
+        # Write 8-bpp image headers for sizes under 256x256.
         for size, image in self.images.items():
-            if size == 256:
+            if size >= 256:
+                continue
+            ico.write(struct.pack('<BB', size, size))
+
+            # Calculate row sizes
+            xorsize = size
+            if xorsize % 4 != 0:
+                xorsize += 4 - (xorsize % 4)
+            andsize = (size + 7) >> 3
+            if andsize % 4 != 0:
+                andsize += 4 - (andsize % 4)
+            datasize = 40 + 256 * 4 + (xorsize + andsize) * size
+
+            ico.write(struct.pack('<BBHHII', 0, 0, 1, 8, datasize, dataoffs))
+            dataoffs += datasize
+
+        # Write 24/32-bpp image headers.
+        for size, image in self.images.items():
+            if size > 256:
+                continue
+            elif size == 256:
                 ico.write('\0\0')
             else:
                 ico.write(struct.pack('<BB', size, size))
-            bpp = 32 if image.hasAlpha() else 24
-            ico.write(struct.pack('<BBHHII', 0, 0, 1, bpp, 0, 0))
 
-        # Now write the actual icons
-        ptr = 14
-        for size, image in self.images.items():
-            loc = ico.tell()
-            bpp = 32 if image.hasAlpha() else 24
-            ico.write(struct.pack('<IiiHHIIiiII', 40, size, size * 2, 1, bpp, 0, 0, 0, 0, 0, 0))
-
-            # XOR mask
-            if bpp == 24:
-                # Align rows to 4-byte boundary
-                rowalign = '\0' * (-(size * 3) & 3)
-                for y in xrange(size):
-                    for x in xrange(size):
-                        r, g, b = image.getXel(x, size - y - 1)
-                        ico.write(struct.pack('<BBB', int(b * 255), int(g * 255), int(r * 255)))
-                    ico.write(rowalign)
+            # Calculate the size so we can write the offset within the file.
+            if image.hasAlpha():
+                bpp = 32
+                xorsize = size * 4
             else:
-                for y in xrange(size):
-                    for x in xrange(size):
-                        r, g, b, a = image.getXelA(x, size - y - 1)
-                        ico.write(struct.pack('<BBBB', int(b * 255), int(g * 255), int(r * 255), int(a * 255)))
+                bpp = 24
+                xorsize = size * 3 + (-(size * 3) & 3)
+            andsize = (size + 7) >> 3
+            if andsize % 4 != 0:
+                andsize += 4 - (andsize % 4)
+            datasize = 40 + (xorsize + andsize) * size
+
+            ico.write(struct.pack('<BBHHII', 0, 0, 1, bpp, datasize, dataoffs))
+            dataoffs += datasize
 
-            # Empty AND mask, aligned to 4-byte boundary
-            #TODO: perhaps we should convert alpha into an AND mask
-            # to support older versions of Windows that don't support alpha.
-            ico.write('\0' * (size * (size / 8 + (-((size / 8) * 3) & 3))))
+        # Now write the actual icon bitmap data.
+        for size, image in self.images.items():
+            if size < 256:
+                self._write_bitmap(ico, image, size, 8)
 
-            # Go back to write the location
-            dataend = ico.tell()
-            ico.seek(ptr)
-            ico.write(struct.pack('<II', dataend - loc, loc))
-            ico.seek(dataend)
-            ptr += 16
+        for size, image in self.images.items():
+            if size <= 256:
+                bpp = 32 if image.hasAlpha() else 24
+                self._write_bitmap(ico, image, size, bpp)
 
+        assert ico.tell() == dataoffs
         ico.close()
 
         return True
@@ -401,32 +544,35 @@ class Icon:
             fn = Filename.fromOsSpecific(fn)
         fn.setBinary()
 
-        vfs = VirtualFileSystem.getGlobalPtr()
-        stream = vfs.openWriteFile(fn, False, True)
-        icns = open(stream, 'wb')
+        icns = open(fn, 'wb')
         icns.write(b'icns\0\0\0\0')
 
-        icon_types = {16: 'is32', 32: 'il32', 48: 'ih32', 128: 'it32'}
-        mask_types = {16: 's8mk', 32: 'l8mk', 48: 'h8mk', 128: 't8mk'}
-        png_types = {256: 'ic08', 512: 'ic09'}
+        icon_types = {16: b'is32', 32: b'il32', 48: b'ih32', 128: b'it32'}
+        mask_types = {16: b's8mk', 32: b'l8mk', 48: b'h8mk', 128: b't8mk'}
+        png_types = {256: b'ic08', 512: b'ic09', 1024: b'ic10'}
 
         pngtype = PNMFileTypeRegistry.getGlobalPtr().getTypeFromExtension("png")
 
-        for size, image in self.images.items():
-            if size in png_types:
-                if pngtype is None:
-                    continue
-                icns.write(png_types[size])
-                icns.write(b'\0\0\0\0')
-                start = icns.tell()
-
+        for size, image in sorted(self.images.items(), key=lambda item:item[0]):
+            if size in png_types and pngtype is not None:
+                stream = StringStream()
                 image.write(stream, "", pngtype)
-                pngsize = icns.tell() - start
-                icns.seek(start - 4)
-                icns.write(struct.pack('>I', pngsize + 8))
-                icns.seek(start + pngsize)
+                pngdata = stream.data
+
+                icns.write(png_types[size])
+                icns.write(struct.pack('>I', len(pngdata)))
+                icns.write(pngdata)
 
             elif size in icon_types:
+                # If it has an alpha channel, we write out a mask too.
+                if image.hasAlpha():
+                    icns.write(mask_types[size])
+                    icns.write(struct.pack('>I', size * size + 8))
+
+                    for y in xrange(size):
+                        for x in xrange(size):
+                            icns.write(struct.pack('<B', int(image.getAlpha(x, y) * 255)))
+
                 icns.write(icon_types[size])
                 icns.write(struct.pack('>I', size * size * 4 + 8))
 
@@ -435,15 +581,6 @@ class Icon:
                         r, g, b = image.getXel(x, y)
                         icns.write(struct.pack('>BBBB', 0, int(r * 255), int(g * 255), int(b * 255)))
 
-                if not image.hasAlpha():
-                    continue
-                icns.write(mask_types[size])
-                icns.write(struct.pack('>I', size * size + 8))
-
-                for y in xrange(size):
-                    for x in xrange(size):
-                        icns.write(struct.pack('<B', int(image.getAlpha(x, y) * 255)))
-
         length = icns.tell()
         icns.seek(4)
         icns.write(struct.pack('>I', length))

+ 18 - 0
direct/src/showbase/BufferViewer.py

@@ -442,3 +442,21 @@ class BufferViewer(DirectObject):
                     cards[index] = placer
 
         return Task.cont
+
+    # Snake-case aliases, for people who prefer these.
+    advance_card = advanceCard
+    analyze_texture_set = analyzeTextureSet
+    is_enabled = isEnabled
+    is_valid_texture_set = isValidTextureSet
+    maintain_readout = maintainReadout
+    make_frame = makeFrame
+    refresh_readout = refreshReadout
+    select_card = selectCard
+    set_card_size = setCardSize
+    set_exclude = setExclude
+    set_include = setInclude
+    set_layout = setLayout
+    set_position = setPosition
+    set_render_parent = setRenderParent
+    set_sort = setSort
+    toggle_enable = toggleEnable

+ 17 - 1
direct/src/showbase/Loader.py

@@ -147,12 +147,28 @@ class Loader(DirectObject):
         Loader.loaderIndex += 1
         self.accept(self.hook, self.__gotAsyncObject)
 
+        if ConfigVariableBool('loader-support-entry-points', True):
+            self._loadPythonFileTypes()
+
     def destroy(self):
         self.ignore(self.hook)
         self.loader.stopThreads()
         del self.base
         del self.loader
 
+    def _loadPythonFileTypes(self):
+        import importlib
+        try:
+            pkg_resources = importlib.import_module('pkg_resources')
+        except ImportError:
+            pkg_resources = None
+
+        if pkg_resources:
+            registry = LoaderFileTypeRegistry.getGlobalPtr()
+
+            for entry_point in pkg_resources.iter_entry_points('panda3d.loaders'):
+                registry.register_deferred_type(entry_point)
+
     # model loading funcs
     def loadModel(self, modelPath, loaderOptions = None, noCache = None,
                   allowInstance = False, okMissing = None,
@@ -214,7 +230,7 @@ class Loader(DirectObject):
 
         """
 
-        assert Loader.notify.debug("Loading model: %s" % (modelPath))
+        assert Loader.notify.debug("Loading model: %s" % (modelPath,))
         if loaderOptions is None:
             loaderOptions = LoaderOptions()
         else:

+ 5 - 0
direct/src/showbase/PythonUtil.py

@@ -1130,6 +1130,10 @@ def weightedChoice(choiceList, rng=random.random, sum=None):
     """given a list of (weight, item) pairs, chooses an item based on the
     weights. rng must return 0..1. if you happen to have the sum of the
     weights, pass it in 'sum'."""
+    # Throw an IndexError if we got an empty list.
+    if not choiceList:
+        raise IndexError('Cannot choose from an empty sequence')
+
     # TODO: add support for dicts
     if sum is None:
         sum = 0.
@@ -1138,6 +1142,7 @@ def weightedChoice(choiceList, rng=random.random, sum=None):
 
     rand = rng()
     accum = rand * sum
+    item = None
     for weight, item in choiceList:
         accum -= weight
         if accum <= 0.:

+ 4 - 1
direct/src/showbase/ShowBase.py

@@ -207,7 +207,10 @@ class ShowBase(DirectObject.DirectObject):
             # Has the clusterSyncFlag been set via a config variable
             self.clusterSyncFlag = self.config.GetBool('cluster-sync', 0)
 
-        self.hidden = NodePath('hidden')
+        # We've already created aspect2d in ShowBaseGlobal, for the
+        # benefit of creating DirectGui elements before ShowBase.
+        from . import ShowBaseGlobal
+        self.hidden = ShowBaseGlobal.hidden
 
         ## The global graphics engine, ie. GraphicsEngine.getGlobalPtr()
         self.graphicsEngine = GraphicsEngine.getGlobalPtr()

+ 1 - 0
direct/src/showbase/ShowBaseGlobal.py

@@ -25,6 +25,7 @@ pandaSystem = PandaSystem.getGlobalPtr()
 
 # This is defined here so GUI elements can be instantiated before ShowBase.
 aspect2d = NodePath(PGTop("aspect2d"))
+hidden = NodePath("hidden")
 
 # Set direct notify categories now that we have config
 directNotify.setDconfigLevels()

+ 2 - 1
direct/src/showbase/Transitions.py

@@ -5,6 +5,7 @@ a particular color."""
 __all__ = ['Transitions']
 
 from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
 from direct.gui.DirectGui import DirectFrame
 from direct.gui import DirectGuiGlobals as DGG
 from direct.interval.LerpInterval import LerpColorScaleInterval, LerpColorInterval, LerpScaleInterval, LerpPosInterval
@@ -73,7 +74,7 @@ class Transitions:
             # so that it will also obscure mouse events for objects
             # positioned behind it.
             self.fade = DirectFrame(
-                parent = hidden,
+                parent = ShowBaseGlobal.hidden,
                 guiId = 'fade',
                 relief = None,
                 image = self.fadeModel,

+ 2 - 1
direct/src/tkpanels/MopathRecorder.py

@@ -4,6 +4,7 @@ __all__ = ['MopathRecorder']
 
 # Import Tkinter, Pmw, and the dial code from this directory tree.
 from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.TkGlobal import *
 from direct.tkwidgets.AppShell import *
@@ -852,7 +853,7 @@ class MopathRecorder(AppShell, DirectObject):
         if self.getVariable('Style', 'Marker').get():
             self.playbackMarker.reparentTo(self.recorderNodePath)
         else:
-            self.playbackMarker.reparentTo(hidden)
+            self.playbackMarker.reparentTo(ShowBaseGlobal.hidden)
 
     def setNumSegs(self, value):
         self.numSegs = int(value)

+ 25 - 15
direct/src/tkpanels/ParticlePanel.py

@@ -214,23 +214,23 @@ class ParticlePanel(AppShell):
             ('System', 'Pool Size',
              'Max number of simultaneous particles',
              self.setSystemPoolSize,
-             1.0, 1.0),
+             1.0, 2000000, 1.0),
             ('System', 'Birth Rate',
              'Seconds between particle births',
              self.setSystemBirthRate,
-             0.0, None),
+             0.0, None, None),
             ('System', 'Litter Size',
              'Number of particle created at each birth',
              self.setSystemLitterSize,
-             1.0, 1.0),
+             1.0, 0x7fffffff, 1.0),
             ('System', 'Litter Spread',
              'Variation in litter size',
              self.setSystemLitterSpread,
-             0.0, 1.0),
+             0.0, 0x7fffffff, 1.0),
             ('System', 'Lifespan',
              'Age in seconds at which the system (vs. particles) should die',
              self.setSystemLifespan,
-             0.0, None)
+             0.0, None, None)
             )
         self.createFloaters(systemPage, systemFloaterDefs)
 
@@ -269,27 +269,27 @@ class ParticlePanel(AppShell):
             ('Factory', 'Life Span',
              'Average particle lifespan in seconds',
              self.setFactoryLifeSpan,
-             0.0, None),
+             0.0, None, None),
             ('Factory', 'Life Span Spread',
              'Variation in lifespan',
              self.setFactoryLifeSpanSpread,
-             0.0, None),
+             0.0, None, None),
             ('Factory', 'Mass',
              'Average particle mass',
              self.setFactoryParticleMass,
-             0.001, None),
+             0.001, None, None),
             ('Factory', 'Mass Spread',
              'Variation in particle mass',
              self.setFactoryParticleMassSpread,
-             0.0, None),
+             0.0, None, None),
             ('Factory', 'Terminal Velocity',
              'Cap on average particle velocity',
              self.setFactoryTerminalVelocity,
-             0.0, None),
+             0.0, None, None),
             ('Factory', 'Terminal Vel. Spread',
              'Variation in terminal velocity',
              self.setFactoryTerminalVelocitySpread,
-             0.0, None),
+             0.0, None, None),
         )
         self.createFloaters(factoryPage, factoryWidgets)
 
@@ -966,19 +966,29 @@ class ParticlePanel(AppShell):
 
     def createFloaters(self, parent, widgetDefinitions):
         widgets = []
-        for category, label, balloonHelp, command, min, resolution in widgetDefinitions:
+        for category, label, balloonHelp, command, min, max, resolution in widgetDefinitions:
             widgets.append(
                 self.createFloater(parent, category, label, balloonHelp,
-                                   command, min, resolution)
+                                   command, min, max, resolution)
                 )
         return widgets
 
     def createFloater(self, parent, category, text, balloonHelp,
-                      command = None, min = 0.0, resolution = None,
-                      numDigits = 3, **kw):
+                      command = None, min = 0.0, max = None, resolution = None,
+                      numDigits = None, **kw):
         kw['text'] = text
         kw['min'] = min
+        if max is not None:
+            kw['max'] = max
         kw['resolution'] = resolution
+        if numDigits is None:
+            # If this is apparently an integer setting, show no decimals.
+            if resolution is not None and int(resolution) == resolution and \
+                (min is None or int(min) == min) and \
+                (max is None or int(max) == max):
+                numDigits = 0
+            else:
+                numDigits = 3
         kw['numDigits'] = numDigits
         widget = Floater.Floater(parent, **kw)
         # Do this after the widget so command isn't called on creation

+ 0 - 108
dmodels/src/level_editor/donaldsDockColors.txt

@@ -1,108 +0,0 @@
-corniceColor: Vec4(0.11, 0.80, 0.22, 1.0)
-corniceColor: Vec4(0.21, 0.73, 0.31, 1.0)
-corniceColor: Vec4(0.22, 0.96, 0.27, 1.0)
-corniceColor: Vec4(0.57, 0.39, 0.34, 1.0)
-corniceColor: Vec4(0.59, 1.00, 0.92, 1.0)
-corniceColor: Vec4(0.66, 1.00, 0.86, 1.0)
-corniceColor: Vec4(0.73, 0.51, 0.51, 1.0)
-corniceColor: Vec4(0.75, 0.45, 0.45, 1.0)
-corniceColor: Vec4(0.76, 0.57, 0.29, 1.0)
-corniceColor: Vec4(0.76, 0.73, 0.64, 1.0)
-corniceColor: Vec4(0.82, 0.91, 0.44, 1.0)
-corniceColor: Vec4(0.90, 0.56, 0.34, 1.0)
-corniceColor: Vec4(0.91, 0.54, 0.44, 1.0)
-corniceColor: Vec4(0.91, 0.63, 0.44, 1.0)
-corniceColor: Vec4(0.94, 0.35, 0.35, 1.0)
-corniceColor: Vec4(0.94, 0.72, 0.66, 1.0)
-corniceColor: Vec4(1.00, 0.42, 0.27, 1.0)
-corniceColor: Vec4(1.00, 0.50, 0.38, 1.0)
-corniceColor: Vec4(1.00, 0.69, 0.49, 1.0)
-corniceColor: Vec4(1.00, 0.71, 0.27, 1.0)
-corniceColor: Vec4(1.00, 0.84, 0.59, 1.0)
-corniceColor: Vec4(1.00, 0.88, 0.80, 1.0)
-corniceColor: Vec4(1.00, 0.92, 0.59, 1.0)
-corniceColor: Vec4(1.00, 1.00, 0.59, 1.0)
-
-doorColor: Vec4(0.45, 0.53, 0.45, 1.0)
-doorColor: Vec4(0.52, 0.61, 0.52, 1.0)
-doorColor: Vec4(0.60, 0.67, 0.54, 1.0)
-doorColor: Vec4(0.63, 0.48, 0.34, 1.0)
-doorColor: Vec4(0.79, 0.47, 0.47, 1.0)
-doorColor: Vec4(0.86, 0.48, 0.23, 1.0)
-doorColor: Vec4(0.88, 0.43, 0.43, 1.0)
-doorColor: Vec4(0.91, 0.34, 0.34, 1.0)
-doorColor: Vec4(0.94, 0.70, 0.49, 1.0)
-doorColor: Vec4(0.97, 0.55, 0.40, 1.0)
-doorColor: Vec4(1.00, 0.59, 0.59, 1.0)
-
-wallColor: Vec4(0.17, 0.44, 0.28, 1.0)
-wallColor: Vec4(0.17, 0.45, 0.23, 1.0)
-wallColor: Vec4(0.36, 0.45, 0.22, 1.0)
-wallColor: Vec4(0.38, 0.31, 0.19, 1.0)
-wallColor: Vec4(0.38, 0.68, 0.46, 1.0)
-wallColor: Vec4(0.42, 0.16, 0.16, 1.0)
-wallColor: Vec4(0.42, 0.25, 0.25, 1.0)
-wallColor: Vec4(0.44, 0.67, 0.45, 1.0)
-wallColor: Vec4(0.45, 0.31, 0.17, 1.0)
-wallColor: Vec4(0.48, 0.93, 0.74, 1.0)
-wallColor: Vec4(0.52, 0.96, 0.79, 1.0)
-wallColor: Vec4(0.57, 0.70, 0.35, 1.0)
-wallColor: Vec4(0.63, 0.47, 0.24, 1.0)
-wallColor: Vec4(0.63, 0.60, 0.40, 1.0)
-wallColor: Vec4(0.64, 0.24, 0.32, 1.0)
-wallColor: Vec4(0.71, 0.49, 0.35, 1.0)
-wallColor: Vec4(0.75, 0.45, 0.45, 1.0)
-wallColor: Vec4(0.81, 0.48, 0.48, 1.0)
-wallColor: Vec4(0.87, 0.61, 0.61, 1.0)
-wallColor: Vec4(0.87, 0.69, 0.42, 1.0)
-wallColor: Vec4(0.91, 0.54, 0.54, 1.0)
-wallColor: Vec4(0.92, 0.63, 0.42, 1.0)
-wallColor: Vec4(0.93, 0.15, 0.15, 1.0)
-wallColor: Vec4(1.00, 0.00, 0.00, 1.0)
-
-windowColor: Vec4(0.00, 0.53, 0.32, 1.0)
-windowColor: Vec4(0.00, 0.61, 0.36, 1.0)
-windowColor: Vec4(0.07, 0.47, 0.31, 1.0)
-windowColor: Vec4(0.08, 0.47, 0.31, 1.0)
-windowColor: Vec4(0.17, 0.64, 0.45, 1.0)
-windowColor: Vec4(0.50, 0.38, 0.22, 1.0)
-windowColor: Vec4(0.64, 0.93, 0.45, 1.0)
-windowColor: Vec4(0.69, 0.44, 0.15, 1.0)
-windowColor: Vec4(0.73, 0.46, 0.37, 1.0)
-windowColor: Vec4(0.73, 0.63, 0.37, 1.0)
-windowColor: Vec4(0.73, 0.63, 0.45, 1.0)
-windowColor: Vec4(0.74, 0.55, 0.32, 1.0)
-windowColor: Vec4(0.78, 0.49, 0.29, 1.0)
-windowColor: Vec4(0.79, 0.47, 0.47, 1.0)
-windowColor: Vec4(0.82, 0.82, 0.40, 1.0)
-windowColor: Vec4(0.87, 0.55, 0.33, 1.0)
-windowColor: Vec4(0.87, 0.65, 0.33, 1.0)
-windowColor: Vec4(0.88, 0.43, 0.43, 1.0)
-windowColor: Vec4(0.91, 0.34, 0.45, 1.0)
-windowColor: Vec4(1.00, 0.50, 0.38, 1.0)
-windowColor: Vec4(1.00, 0.63, 0.38, 1.0)
-
-propColor: Vec4(0.17, 0.44, 0.28, 1.0)
-propColor: Vec4(0.17, 0.45, 0.23, 1.0)
-propColor: Vec4(0.36, 0.45, 0.22, 1.0)
-propColor: Vec4(0.38, 0.31, 0.19, 1.0)
-propColor: Vec4(0.38, 0.68, 0.46, 1.0)
-propColor: Vec4(0.42, 0.16, 0.16, 1.0)
-propColor: Vec4(0.42, 0.25, 0.25, 1.0)
-propColor: Vec4(0.44, 0.67, 0.45, 1.0)
-propColor: Vec4(0.45, 0.31, 0.17, 1.0)
-propColor: Vec4(0.48, 0.93, 0.74, 1.0)
-propColor: Vec4(0.52, 0.96, 0.79, 1.0)
-propColor: Vec4(0.57, 0.70, 0.35, 1.0)
-propColor: Vec4(0.63, 0.47, 0.24, 1.0)
-propColor: Vec4(0.63, 0.60, 0.40, 1.0)
-propColor: Vec4(0.64, 0.24, 0.32, 1.0)
-propColor: Vec4(0.71, 0.49, 0.35, 1.0)
-propColor: Vec4(0.75, 0.45, 0.45, 1.0)
-propColor: Vec4(0.81, 0.48, 0.48, 1.0)
-propColor: Vec4(0.87, 0.61, 0.61, 1.0)
-propColor: Vec4(0.87, 0.69, 0.42, 1.0)
-propColor: Vec4(0.91, 0.54, 0.54, 1.0)
-propColor: Vec4(0.92, 0.63, 0.42, 1.0)
-propColor: Vec4(0.93, 0.15, 0.15, 1.0)
-propColor: Vec4(1.00, 0.00, 0.00, 1.0)

+ 0 - 64
dmodels/src/level_editor/donaldsDockStyles.txt

@@ -1,64 +0,0 @@
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.417323, 0.15711, 0.15711, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.874016, 0.654655, 0.329041, 1.0)
-corniceTexture: cornice_marble_ur
-corniceColor: Vec4(0.76378, 0.572086, 0.287541, 1.0)
-
-wallTexture: wall_sm_wood_ur
-wallColor: Vec4(0.874016, 0.610097, 0.610097, 1.0)
-windowTexture: window_sm_shuttered_ur
-windowColor: Vec4(0.874016, 0.548402, 0.329041, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_sm_wood_ur
-wallColor: Vec4(0.913386, 0.540868, 0.540868, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.0778138, 0.472441, 0.314961, 1.0)
-corniceTexture: cornice_horizontal_ur
-corniceColor: Vec4(1.0, 0.501961, 0.376471, 1.0)
-
-wallTexture: wall_sm_wood_ur
-wallColor: Vec4(0.913386, 0.540868, 0.540868, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.0778138, 0.472441, 0.314961, 1.0)
-corniceTexture: cornice_shingles_ur
-corniceColor: Vec4(0.732283, 0.511163, 0.511163, 1.0)
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.384314, 0.305635, 0.187618, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.779528, 0.489115, 0.293469, 1.0)
-corniceTexture: cornice_dental_ur
-corniceColor: Vec4(0.574803, 0.38771, 0.340374, 1.0)
-
-wallTexture: wall_bricks_dr
-wallColor: Vec4(0.629921, 0.471823, 0.237147, 1.0)
-windowTexture: window_sm_shuttered_ur
-windowColor: Vec4(1.0, 0.627451, 0.376471, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_board_ur
-wallColor: Vec4(0.929134, 0.153034, 0.153034, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.0, 0.532747, 0.317894, 1.0)
-corniceTexture: cornice_shingles_ur
-corniceColor: Vec4(0.944882, 0.715146, 0.659565, 1.0)
-
-wallTexture: wall_lg_brick_ur
-wallColor: Vec4(0.166003, 0.440945, 0.276671, 1.0)
-windowTexture: window_md_curtains_ur
-windowColor: Vec4(0.17258, 0.637795, 0.450208, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_board_ur
-wallColor: Vec4(0.929134, 0.153034, 0.153034, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.0, 0.532747, 0.317894, 1.0)
-corniceTexture: None
-corniceColor: None
-
-

BIN
dmodels/src/level_editor/donalds_dock_layout.flt


+ 0 - 61
dmodels/src/level_editor/minniesMelodyLandColors.txt

@@ -1,61 +0,0 @@
-corniceColor: Vec4(0.43, 0.32, 0.85, 1.0)
-corniceColor: Vec4(0.67, 0.40, 0.99, 1.0)
-corniceColor: Vec4(0.68, 0.22, 0.80, 1.0)
-corniceColor: Vec4(0.80, 0.22, 0.68, 1.0)
-corniceColor: Vec4(0.81, 0.30, 0.51, 1.0)
-corniceColor: Vec4(1.00, 1.00, 0.58, 1.0)
-
-doorColor: Vec4(0.42, 0.42, 0.85, 1.0)
-doorColor: Vec4(0.45, 0.22, 0.60, 1.0)
-doorColor: Vec4(0.49, 0.79, 1.00, 1.0)
-doorColor: Vec4(0.56, 0.80, 0.65, 1.0)
-doorColor: Vec4(0.59, 0.49, 1.00, 1.0)
-doorColor: Vec4(0.64, 0.45, 0.92, 1.0)
-doorColor: Vec4(0.65, 0.40, 0.82, 1.0)
-doorColor: Vec4(0.69, 0.33, 0.69, 1.0)
-doorColor: Vec4(0.80, 0.56, 0.65, 1.0)
-doorColor: Vec4(0.87, 0.80, 0.47, 1.0)
-doorColor: Vec4(0.97, 0.87, 0.47, 1.0)
-doorColor: Vec4(1.00, 0.42, 0.85, 1.0)
-doorColor: Vec4(1.00, 0.79, 0.49, 1.0)
-
-wallColor: Vec4(0.49, 0.69, 1.00, 1.0)
-wallColor: Vec4(0.59, 0.75, 1.00, 1.0)
-wallColor: Vec4(0.70, 0.76, 1.00, 1.0)
-wallColor: Vec4(0.80, 0.47, 0.80, 1.0)
-wallColor: Vec4(0.80, 0.80, 0.47, 1.0)
-wallColor: Vec4(0.91, 0.91, 0.54, 1.0)
-wallColor: Vec4(0.96, 0.96, 0.47, 1.0)
-wallColor: Vec4(0.96, 0.96, 0.73, 1.0)
-wallColor: Vec4(0.97, 0.36, 0.36, 1.0)
-wallColor: Vec4(0.97, 0.47, 0.67, 1.0)
-wallColor: Vec4(1.00, 0.70, 0.70, 1.0)
-wallColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-windowColor: Vec4(0.42, 0.42, 0.85, 1.0)
-windowColor: Vec4(0.45, 0.22, 0.60, 1.0)
-windowColor: Vec4(0.49, 0.79, 1.00, 1.0)
-windowColor: Vec4(0.56, 0.80, 0.65, 1.0)
-windowColor: Vec4(0.59, 0.49, 1.00, 1.0)
-windowColor: Vec4(0.64, 0.45, 0.92, 1.0)
-windowColor: Vec4(0.65, 0.40, 0.82, 1.0)
-windowColor: Vec4(0.69, 0.33, 0.69, 1.0)
-windowColor: Vec4(0.80, 0.45, 0.71, 1.0)
-windowColor: Vec4(0.80, 0.56, 0.65, 1.0)
-windowColor: Vec4(0.97, 0.87, 0.47, 1.0)
-windowColor: Vec4(1.00, 0.79, 0.49, 1.0)
-
-propColor: Vec4(0.49, 0.69, 1.00, 1.0)
-propColor: Vec4(0.59, 0.75, 1.00, 1.0)
-propColor: Vec4(0.70, 0.76, 1.00, 1.0)
-propColor: Vec4(0.80, 0.47, 0.80, 1.0)
-propColor: Vec4(0.80, 0.80, 0.47, 1.0)
-propColor: Vec4(0.91, 0.91, 0.54, 1.0)
-propColor: Vec4(0.96, 0.96, 0.47, 1.0)
-propColor: Vec4(0.96, 0.96, 0.73, 1.0)
-propColor: Vec4(0.97, 0.36, 0.36, 1.0)
-propColor: Vec4(0.97, 0.47, 0.67, 1.0)
-propColor: Vec4(1.00, 0.70, 0.70, 1.0)
-propColor: Vec4(1.00, 1.00, 1.00, 1.0)
-propColor: Vec4(0.92, 0.92, 0.92, 1.0)
-wallColor: Vec4(0.97, 0.58, 0.55, 1.0)

+ 0 - 79
dmodels/src/level_editor/minniesMelodyLandStyles.txt

@@ -1,79 +0,0 @@
-wallTexture: wall_md_bricks_ur
-wallColor: Vec4(0.59, 0.75, 1.00, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.45, 0.22, 0.60, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.91, 0.91, 0.54, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.65, 0.40, 0.82, 1.0)
-corniceTexture: cornice_curved_ur
-corniceColor: Vec4(0.68, 0.22, 0.80, 1.0)
-
-wallTexture: wall_sm_brick_pink_ur
-wallColor: Vec4(1.00, 1.00, 1.00, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.49, 0.79, 1.00, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_lg_rock_ur
-wallColor: Vec4(0.80, 0.47, 0.80, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.80, 0.56, 0.65, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_lg_rock_ur
-wallColor: Vec4(0.80, 0.80, 0.47, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.56, 0.80, 0.65, 1.0)
-corniceTexture: cornice_marble_ur
-corniceColor: Vec4(0.80, 0.22, 0.68, 1.0)
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.49, 0.69, 1.00, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.59, 0.49, 1.00, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_bricks_ur
-wallColor: Vec4(1.00, 0.70, 0.70, 1.0)
-windowTexture: window_md_curved_ur
-windowColor: Vec4(1.00, 0.79, 0.49, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.96, 0.96, 0.47, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.69, 0.33, 0.69, 1.0)
-corniceTexture: cornice_brick_ur
-corniceColor: Vec4(0.81, 0.30, 0.51, 1.0)
-
-wallTexture: wall_md_bricks_ur
-wallColor: Vec4(0.70, 0.76, 1.00, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.42, 0.42, 0.85, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_lg_brick_ur
-wallColor: Vec4(0.97, 0.47, 0.67, 1.0)
-windowTexture: window_md_curtains_ur
-windowColor: Vec4(0.64, 0.45, 0.92, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.97, 0.36, 0.36, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.97, 0.87, 0.47, 1.0)
-corniceTexture: cornice_curved_ur
-corniceColor: Vec4(0.43, 0.32, 0.85, 1.0)
-
-
-

BIN
dmodels/src/level_editor/minnies_melody_land_layout.flt


+ 0 - 65
dmodels/src/level_editor/theBurrrghColors.txt

@@ -1,65 +0,0 @@
-corniceColor: Vec4(0.00, 0.34, 0.58, 1.0)
-corniceColor: Vec4(0.34, 0.90, 0.87, 1.0)
-corniceColor: Vec4(0.41, 0.58, 0.85, 1.0)
-corniceColor: Vec4(0.42, 0.98, 0.74, 1.0)
-corniceColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-doorColor: Vec4(0.00, 0.87, 0.69, 1.0)
-doorColor: Vec4(0.22, 0.83, 0.83, 1.0)
-doorColor: Vec4(0.23, 0.71, 0.84, 1.0)
-doorColor: Vec4(0.23, 0.85, 0.60, 1.0)
-doorColor: Vec4(0.24, 0.76, 0.89, 1.0)
-doorColor: Vec4(0.27, 1.00, 0.85, 1.0)
-doorColor: Vec4(0.38, 0.63, 1.00, 1.0)
-doorColor: Vec4(0.49, 0.69, 1.00, 1.0)
-doorColor: Vec4(0.49, 0.89, 1.00, 1.0)
-doorColor: Vec4(0.50, 0.64, 0.85, 1.0)
-doorColor: Vec4(0.59, 1.00, 0.92, 1.0)
-doorColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-wallColor: Vec4(0.00, 0.87, 0.69, 1.0)
-wallColor: Vec4(0.22, 0.83, 0.83, 1.0)
-wallColor: Vec4(0.23, 0.85, 0.60, 1.0)
-wallColor: Vec4(0.24, 0.76, 0.89, 1.0)
-wallColor: Vec4(0.25, 0.64, 0.91, 1.0)
-wallColor: Vec4(0.27, 1.00, 0.85, 1.0)
-wallColor: Vec4(0.38, 0.63, 1.00, 1.0)
-wallColor: Vec4(0.49, 0.69, 1.00, 1.0)
-wallColor: Vec4(0.49, 0.89, 1.00, 1.0)
-wallColor: Vec4(0.50, 0.64, 0.85, 1.0)
-wallColor: Vec4(0.59, 1.00, 0.92, 1.0)
-wallColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-windowColor: Vec4(0.00, 0.69, 0.69, 1.0)
-windowColor: Vec4(0.00, 0.87, 0.69, 1.0)
-windowColor: Vec4(0.14, 0.42, 0.85, 1.0)
-windowColor: Vec4(0.16, 0.67, 1.00, 1.0)
-windowColor: Vec4(0.20, 0.53, 0.46, 1.0)
-windowColor: Vec4(0.22, 0.83, 0.83, 1.0)
-windowColor: Vec4(0.23, 0.71, 0.84, 1.0)
-windowColor: Vec4(0.23, 0.85, 0.60, 1.0)
-windowColor: Vec4(0.24, 0.76, 0.89, 1.0)
-windowColor: Vec4(0.27, 1.00, 0.85, 1.0)
-windowColor: Vec4(0.38, 0.63, 1.00, 1.0)
-windowColor: Vec4(0.38, 0.87, 1.00, 1.0)
-windowColor: Vec4(0.49, 0.69, 1.00, 1.0)
-windowColor: Vec4(0.49, 0.89, 1.00, 1.0)
-windowColor: Vec4(0.49, 1.00, 1.00, 1.0)
-windowColor: Vec4(0.50, 0.64, 0.85, 1.0)
-windowColor: Vec4(0.59, 0.85, 0.69, 1.0)
-windowColor: Vec4(0.59, 0.92, 0.61, 1.0)
-windowColor: Vec4(0.59, 1.00, 0.92, 1.0)
-windowColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-propColor: Vec4(0.00, 0.87, 0.69, 1.0)
-propColor: Vec4(0.22, 0.83, 0.83, 1.0)
-propColor: Vec4(0.23, 0.85, 0.60, 1.0)
-propColor: Vec4(0.24, 0.76, 0.89, 1.0)
-propColor: Vec4(0.25, 0.64, 0.91, 1.0)
-propColor: Vec4(0.27, 1.00, 0.85, 1.0)
-propColor: Vec4(0.38, 0.63, 1.00, 1.0)
-propColor: Vec4(0.49, 0.69, 1.00, 1.0)
-propColor: Vec4(0.49, 0.89, 1.00, 1.0)
-propColor: Vec4(0.50, 0.64, 0.85, 1.0)
-propColor: Vec4(0.59, 1.00, 0.92, 1.0)
-propColor: Vec4(1.00, 1.00, 1.00, 1.0)

+ 0 - 90
dmodels/src/level_editor/theBurrrghStyles.txt

@@ -1,90 +0,0 @@
-wallTexture: wall_lg_brick_ur
-wallColor: Vec4(0.25, 0.64, 0.91, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.23, 0.71, 0.84, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.00, 0.87, 0.69, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.23, 0.71, 0.84, 1.0)
-corniceTexture: cornice_curved_ur
-corniceColor: Vec4(0.00, 0.34, 0.58, 1.0)
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.49, 0.69, 1.00, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.20, 0.53, 0.46, 1.0)
-corniceTexture: cornice_ice_ur
-corniceColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-wallTexture: wall_sm_brick_blue_ur
-wallColor: Vec4(1.00, 1.00, 1.00, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.49, 1.00, 1.00, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.27, 1.00, 0.85, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.49, 1.00, 1.00, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.49, 0.89, 1.00, 1.0)
-windowTexture: window_sm_pointed_ur
-windowColor: Vec4(0.00, 0.69, 0.69, 1.0)
-corniceTexture: cornice_ice_ur
-corniceColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-wallTexture: wall_md_bricks_ur
-wallColor: Vec4(0.50, 0.64, 0.85, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.14, 0.42, 0.85, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.38, 0.63, 1.00, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.14, 0.42, 0.85, 1.0)
-corniceTexture: cornice_dental_ur
-corniceColor: Vec4(0.41, 0.58, 0.85, 1.0)
-
-wallTexture: wall_bricks_ur
-wallColor: Vec4(0.23, 0.85, 0.60, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.59, 0.85, 0.69, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_bricks_ur
-wallColor: Vec4(0.59, 1.00, 0.92, 1.0)
-windowTexture: window_md_curved_ur
-windowColor: Vec4(0.16, 0.67, 1.00, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.22, 0.83, 0.83, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.38, 0.87, 1.00, 1.0)
-corniceTexture: cornice_ice_ur
-corniceColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-wallTexture: wall_sm_cement_blue_ur
-wallColor: Vec4(1.00, 1.00, 1.00, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.59, 0.92, 0.61, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_bricks_ur
-wallColor: Vec4(0.24, 0.76, 0.89, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.59, 0.92, 0.61, 1.0)
-corniceTexture: None
-corniceColor: None

BIN
dmodels/src/level_editor/the_burrrgh_layout.flt


+ 0 - 106
dmodels/src/level_editor/toontownCentralColors.txt

@@ -1,106 +0,0 @@
-corniceColor: Vec4(0.11, 0.80, 0.22, 1.0)
-corniceColor: Vec4(0.21, 0.73, 0.31, 1.0)
-corniceColor: Vec4(0.22, 0.96, 0.27, 1.0)
-corniceColor: Vec4(0.27, 0.77, 0.42, 1.0)
-corniceColor: Vec4(0.32, 0.54, 0.36, 1.0)
-corniceColor: Vec4(0.44, 0.91, 0.63, 1.0)
-corniceColor: Vec4(0.76, 0.73, 0.64, 1.0)
-corniceColor: Vec4(0.79, 0.36, 0.19, 1.0)
-corniceColor: Vec4(0.82, 0.91, 0.44, 1.0)
-corniceColor: Vec4(0.90, 0.56, 0.34, 1.0)
-corniceColor: Vec4(0.91, 0.54, 0.44, 1.0)
-corniceColor: Vec4(0.91, 0.63, 0.44, 1.0)
-corniceColor: Vec4(0.94, 0.35, 0.35, 1.0)
-corniceColor: Vec4(0.94, 0.71, 0.66, 1.0)
-corniceColor: Vec4(0.98, 0.58, 0.42, 1.0)
-corniceColor: Vec4(1.00, 0.42, 0.27, 1.0)
-corniceColor: Vec4(1.00, 0.56, 0.21, 1.0)
-corniceColor: Vec4(1.00, 0.69, 0.49, 1.0)
-corniceColor: Vec4(1.00, 0.71, 0.27, 1.0)
-corniceColor: Vec4(1.00, 0.75, 0.38, 1.0)
-corniceColor: Vec4(1.00, 0.84, 0.59, 1.0)
-corniceColor: Vec4(1.00, 0.88, 0.80, 1.0)
-corniceColor: Vec4(1.00, 0.92, 0.59, 1.0)
-corniceColor: Vec4(1.00, 1.00, 0.59, 1.0)
-
-doorColor: Vec4(0.75, 0.79, 0.55, 1.0)
-doorColor: Vec4(0.81, 0.61, 0.48, 1.0)
-doorColor: Vec4(1.00, 0.59, 0.59, 1.0)
-doorColor: Vec4(1.00, 0.63, 0.38, 1.0)
-doorColor: Vec4(1.00, 0.87, 0.38, 1.0)
-doorColor: Vec4(1.00, 0.88, 0.56, 1.0)
-doorColor: Vec4(1.00, 0.88, 0.56, 1.0)
-
-wallColor: Vec4(0.11, 0.80, 0.22, 1.0)
-wallColor: Vec4(0.21, 0.73, 0.31, 1.0)
-wallColor: Vec4(0.22, 0.96, 0.27, 1.0)
-wallColor: Vec4(0.24, 0.79, 0.32, 1.0)
-wallColor: Vec4(0.25, 0.65, 0.29, 1.0)
-wallColor: Vec4(0.49, 0.96, 0.42, 1.0)
-wallColor: Vec4(0.76, 0.73, 0.64, 1.0)
-wallColor: Vec4(0.82, 0.91, 0.44, 1.0)
-wallColor: Vec4(0.90, 0.56, 0.34, 1.0)
-wallColor: Vec4(0.91, 0.54, 0.44, 1.0)
-wallColor: Vec4(0.91, 0.63, 0.44, 1.0)
-wallColor: Vec4(0.94, 0.35, 0.35, 1.0)
-wallColor: Vec4(0.95, 0.89, 0.17, 1.0)
-wallColor: Vec4(0.99, 0.67, 0.25, 1.0)
-wallColor: Vec4(0.99, 0.69, 0.49, 1.0)
-wallColor: Vec4(0.99, 0.89, 0.49, 1.0)
-wallColor: Vec4(1.00, 0.42, 0.27, 1.0)
-wallColor: Vec4(1.00, 0.67, 0.59, 1.0)
-wallColor: Vec4(1.00, 0.69, 0.49, 1.0)
-wallColor: Vec4(1.00, 0.71, 0.27, 1.0)
-wallColor: Vec4(1.00, 0.84, 0.59, 1.0)
-wallColor: Vec4(1.00, 0.88, 0.80, 1.0)
-wallColor: Vec4(1.00, 0.90, 0.33, 1.0)
-wallColor: Vec4(1.00, 0.92, 0.59, 1.0)
-wallColor: Vec4(1.00, 1.00, 0.59, 1.0)
-
-windowColor: Vec4(0.10, 0.59, 0.39, 1.0)
-windowColor: Vec4(0.11, 0.67, 0.33, 1.0)
-windowColor: Vec4(0.13, 0.78, 0.52, 1.0)
-windowColor: Vec4(0.14, 0.53, 0.30, 1.0)
-windowColor: Vec4(0.27, 1.00, 0.42, 1.0)
-windowColor: Vec4(0.31, 0.63, 0.37, 1.0)
-windowColor: Vec4(0.32, 0.54, 0.36, 1.0)
-windowColor: Vec4(0.32, 0.67, 0.60, 1.0)
-windowColor: Vec4(0.40, 0.61, 0.67, 1.0)
-windowColor: Vec4(0.59, 1.00, 0.49, 1.0)
-windowColor: Vec4(0.88, 0.44, 0.15, 1.0)
-windowColor: Vec4(0.91, 0.63, 0.44, 1.0)
-windowColor: Vec4(0.95, 0.89, 0.17, 1.0)
-windowColor: Vec4(0.99, 0.99, 0.49, 1.0)
-windowColor: Vec4(1.00, 0.27, 0.27, 1.0)
-windowColor: Vec4(1.00, 0.50, 0.38, 1.0)
-windowColor: Vec4(1.00, 0.59, 0.59, 1.0)
-windowColor: Vec4(1.00, 0.63, 0.30, 1.0)
-windowColor: Vec4(1.00, 0.63, 0.38, 1.0)
-windowColor: Vec4(1.00, 0.87, 0.38, 1.0)
-windowColor: Vec4(1.00, 0.92, 0.59, 1.0)
-
-propColor: Vec4(0.11, 0.80, 0.22, 1.0)
-propColor: Vec4(0.21, 0.73, 0.31, 1.0)
-propColor: Vec4(0.22, 0.96, 0.27, 1.0)
-propColor: Vec4(0.24, 0.79, 0.32, 1.0)
-propColor: Vec4(0.25, 0.65, 0.29, 1.0)
-propColor: Vec4(0.49, 0.96, 0.42, 1.0)
-propColor: Vec4(0.76, 0.73, 0.64, 1.0)
-propColor: Vec4(0.82, 0.91, 0.44, 1.0)
-propColor: Vec4(0.90, 0.56, 0.34, 1.0)
-propColor: Vec4(0.91, 0.54, 0.44, 1.0)
-propColor: Vec4(0.91, 0.63, 0.44, 1.0)
-propColor: Vec4(0.94, 0.35, 0.35, 1.0)
-propColor: Vec4(0.95, 0.89, 0.17, 1.0)
-propColor: Vec4(0.99, 0.67, 0.25, 1.0)
-propColor: Vec4(0.99, 0.69, 0.49, 1.0)
-propColor: Vec4(0.99, 0.89, 0.49, 1.0)
-propColor: Vec4(1.00, 0.42, 0.27, 1.0)
-propColor: Vec4(1.00, 0.67, 0.59, 1.0)
-propColor: Vec4(1.00, 0.69, 0.49, 1.0)
-propColor: Vec4(1.00, 0.71, 0.27, 1.0)
-propColor: Vec4(1.00, 0.84, 0.59, 1.0)
-propColor: Vec4(1.00, 0.88, 0.80, 1.0)
-propColor: Vec4(1.00, 0.90, 0.33, 1.0)
-propColor: Vec4(1.00, 0.92, 0.59, 1.0)
-propColor: Vec4(1.00, 1.00, 0.59, 1.0)

+ 0 - 84
dmodels/src/level_editor/toontownCentralStyles.txt

@@ -1,84 +0,0 @@
-wallTexture: wall_md_pillars_ur
-wallColor: Vec4(1.0, 0.917, 0.592, 1.0)
-windowTexture: window_sm_pointed_ur
-windowColor: Vec4(0.396,  0.611,  0.666,  1.0)
-corniceTexture: cornice_stone_ur
-corniceColor: Vec4(1.0, 1.0, 0.592,  1.0)
-
-wallTexture: wall_md_pillars_ur
-wallColor: Vec4(1.0, 1.0, 0.592157, 1.0)
-windowTexture: window_sm_pointed_ur
-windowColor: Vec4(0.142751, 0.527559, 0.295847, 1.0)
-corniceTexture: cornice_stone_ur
-corniceColor: Vec4(1.0, 1.0, 0.592157, 1.0)
-        
-wallTexture: wall_lg_brick_ur
-wallColor: Vec4(1.0, 0.415686, 0.270588, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.306315, 0.627451, 0.370542, 1.0)
-corniceTexture: None
-corniceColor: None
-        
-wallTexture: wall_sm_cement_ur
-wallColor: Vec4(1.0, 0.882353, 0.803922, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.0972673, 0.590551, 0.393701, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_dental_ur
-wallColor: Vec4(0.996078, 0.894118, 0.486275, 1.0)
-windowTexture: window_md_curved_ur
-windowColor: Vec4(1.0, 0.87451, 0.376471, 1.0)
-corniceTexture: None
-corniceColor: None
-        
-wallTexture: wall_md_pillars_ur
-wallColor: Vec4(0.996078, 0.690196, 0.486275, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.31706, 0.535433, 0.361155, 1.0)
-corniceTexture: None
-corniceColor: None
-        
-wallTexture: wall_md_pillars_ur
-wallColor: Vec4(0.996078, 0.690196, 0.486275, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.31706, 0.535433, 0.361155, 1.0)
-corniceTexture: cornice_brick_ur
-corniceColor: Vec4(0.31706, 0.535433, 0.361155, 1.0)
-
-wallTexture: wall_lg_brick_ur
-wallColor: Vec4(0.996078, 0.690196, 0.486275, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.996078, 0.996078, 0.486275, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(1.0, 0.415686, 0.270588, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(1.0, 0.87451, 0.376471, 1.0)
-corniceTexture: cornice_marble_ur
-corniceColor: Vec4(1.0, 0.74902, 0.376471, 1.0)
-
-wallTexture: wall_sm_brick_ur
-wallColor: Vec4(1.0, 0.67451, 0.592157, 1.0)
-windowTexture: window_sm_pointed_ur
-windowColor: Vec4(0.88189, 0.439216, 0.145252, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(1.0, 0.705882, 0.270588, 1.0)
-windowTexture: window_sm_pointed_ur
-windowColor: Vec4(0.110236, 0.669291, 0.333333, 1.0)
-corniceTexture: cornice_stone_ur
-corniceColor: Vec4(0.944882, 0.711441, 0.559518, 1.0)
-
-wallTexture: wall_md_dental_ur
-wallColor: Vec4(0.909804, 0.630415, 0.444156, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(1.0, 0.270588, 0.270588, 1.0)
-corniceTexture: None
-corniceColor: None
-

BIN
dmodels/src/level_editor/toontown_central_layout.flt


+ 43 - 0
doc/ReleaseNotes

@@ -1,3 +1,46 @@
+------------------------  RELEASE 1.10.4.1  ---------------------
+
+This release fixes only one critical regression: calling destroy()
+on a DirectGUI item would cause an exception.
+
+------------------------  RELEASE 1.10.4  -----------------------
+
+This release fixes a regression with DirectScrolledList in 1.10.3,
+fixes various other bugs, and introduces a few minor features.
+
+* Fix exception trying to create DirectScrolledList
+* Fix flickering in DirectScrolledFrame and other scissor issues (#681)
+* Experimental support for Python 3.8
+* Support adding icons to deployed applications
+* Support non-affine (eg. projective) transforms in calc_tight_bounds
+* Allow setting notify-output after initial import
+* Fix macOS issue locating Panda3D using Python 2.7.13+ from python.org
+* Support for Maya 2019
+* On Windows, pip is now installed by the installer (#690)
+* Fix Actor.makeSubpart on models with pre-bound animations (#647)
+* Properly interrupt task manager if first task chain raises error (#692)
+* Fix return value of encrypt_string in Python 3 (#684)
+* Support writing loader plug-ins in Python
+* Fix reading multiple p3d_TextureMatrix[] values from GLSL shaders
+* Fix shader error flag not being set if GLSL compilation failed (#622)
+* Add NodePath.replace_texture() convenience method
+* Fix deadlock when building with SIMPLE_THREADS=1 (#704)
+* Fix DirectOptionMenu cancelFrame not working inside scrolled frame (#658)
+* Fix assertion when calling analyze() on geometry with strip cut index
+* Implement fallback in GL renderer when F_sluminance is not supported (#693)
+* Set reasonable limits for sliders in ParticlePanel
+* Fix for DirectEntry autoCapitalize feature on Python 3 (#628)
+* Fix various DirectGUI items not working before ShowBase is instantiated
+* Work around an MSVC compiler bug in the release build
+* PythonUtil.weightedChoice now raises IndexError on empty list
+* Support changing DirectScrollBar width after initialiation (#699)
+* Workaround for Bullet deadlock when adding shape to a scaled body (#689)
+* Support setting DirectEntryScroll entry after initialization (#702)
+* Fix some missing imports in directtools (#698)
+* Fix undefined behavior issue when using musl-libc
+* Update Eigen in Windows thirdparty packages to 3.3.7
+* Update metadata of pip wheels
+
 ------------------------  RELEASE 1.10.3  -----------------------
 
 This is another bugfix release that addresses a variety of issues

+ 15 - 11
dtool/src/cppparser/cppIdentifier.cxx

@@ -254,19 +254,23 @@ get_scope(CPPScope *current_scope, CPPScope *global_scope,
   }
 
   while (i + 1 < (int)_names.size() && scope != nullptr) {
-    CPPScope *next_scope = scope->find_scope(_names[i].get_name(), global_scope);
+    // Check for an explicitly specialized scope first.
+    CPPScope *next_scope = scope->find_scope(_names[i].get_name_with_templ(), global_scope);
     if (next_scope == nullptr) {
-      if (error_sink != nullptr) {
-        error_sink->error("Symbol " + _names[i].get_name() +
-                          " is not a known scope in " +
-                          scope->get_fully_scoped_name(),
-                          _loc);
+      next_scope = scope->find_scope(_names[i].get_name(), global_scope);
+      if (next_scope == nullptr) {
+        if (error_sink != nullptr) {
+          error_sink->error("Symbol " + _names[i].get_name() +
+                            " is not a known scope in " +
+                            scope->get_fully_scoped_name(),
+                            _loc);
+        }
+        return nullptr;
+      }
+      if (_names[i].has_templ()) {
+        next_scope = next_scope->instantiate(_names[i].get_templ(),
+                                             current_scope, global_scope);
       }
-      return nullptr;
-    }
-    if (_names[i].has_templ()) {
-      next_scope = next_scope->instantiate(_names[i].get_templ(),
-                                           current_scope, global_scope);
     }
     scope = next_scope;
     i++;

+ 0 - 47
dtool/src/dtoolbase/dllbase.txt

@@ -1,47 +0,0 @@
-; This file is used by the MSVC linker to specify DLL base addresses to
-; prevent runtime relocation of DLLs by the windows loader.  This speeds
-; loading.  This file was generated using $PANDATOOL/src/scripts/basedlls.pl
-; which uses rebase.exe from the MS platform SDK.  It may additionally be
-; hand-hacked to simplify addresses for popular dlls to aid debugging.
-
-; release dlls
-
-libpanda         0x0000000060000000 0x004c0000
-libwindisplay    0x00000000604c0000 0x00010000
-libpandadx8      0x0000000061000000 0x00090000
-libpandadx7      0x0000000061000000 0x00040000
-libpandagl       0x0000000061000000 0x00030000
-libpandaexpress  0x00000000605d0000 0x00150000
-libpandaphysics  0x0000000060720000 0x00060000
-libpandafx       0x0000000060780000 0x00020000
-libdtool         0x00000000607a0000 0x00020000
-libdtoolconfig   0x00000000607c0000 0x00040000
-libpystub        0x0000000060800000 0x00010000
-libdirect        0x0000000060810000 0x00050000
-libtoontown      0x0000000060860000 0x000b0000
-; TODO put in a base for Pirates Opt 4 DLL
-libpirates       0x0000000060860000 0x000b0000
-libmiles_audio   0x0000000060910000 0x00020000
-libpandaegg      0x0000000060930000 0x00130000
-libframework     0x0000000060a60000 0x00020000
-
-; debug dlls
-
-libpanda_d       0x0000000060000000 0x007e0000
-libwindisplay_d  0x00000000607e0000 0x00020000
-libpandadx8_d    0x0000000060800000 0x000d0000
-libpandadx7_d    0x00000000608d0000 0x00060000
-libpandagl_d     0x0000000060930000 0x00040000
-libpandaexpress_d 0x0000000060970000 0x00220000
-libpandaphysics_d 0x0000000060b90000 0x000a0000
-libpandafx_d     0x0000000060c30000 0x00030000
-libdtool_d       0x0000000060c60000 0x00030000
-libdtoolconfig_d 0x0000000060c90000 0x00070000
-libpystub_d      0x0000000060d00000 0x00010000
-libdirect_d      0x0000000060d10000 0x00080000
-libtoontown_d    0x0000000060d90000 0x00130000
-; TODO put in a base for Pirates
-libpirates_d    0x0000000060d90000 0x00130000
-libmiles_audio_d 0x0000000060ec0000 0x00030000
-libpandaegg_d    0x0000000060ef0000 0x00210000
-libframework_d   0x0000000061100000 0x00030000

+ 0 - 12
dtool/src/dtoolbase/dtoolsymbols.h

@@ -125,16 +125,4 @@
   #define EXPTP_MISC IMPORT_TEMPL
 #endif /* BUILDING_MISC */
 
-
-/* These two are always defined empty, because pystub is statically
-   built.  But we leave the symbol around in case we change our minds
-   to make pystub once again be a dynamic library. */
-#if __GNUC__ >= 4
-/* In GCC, though, we still need to mark the symbols as visible. */
-#define EXPCL_PYSTUB __attribute__((visibility("default")))
-#else
-#define EXPCL_PYSTUB
-#endif
-#define EXPTP_PYSTUB
-
 #endif

+ 6 - 6
dtool/src/dtoolutil/iostream_ext.cxx

@@ -24,12 +24,12 @@ extern struct Dtool_PyTypedObject Dtool_std_istream;
  * If the given size is -1, all bytes are read from the stream.
  */
 PyObject *Extension<istream>::
-read(int size) {
+read(Py_ssize_t size) {
   if (size < 0) {
     return readall();
   }
 
-  char *buffer;
+  char *buffer = nullptr;
   std::streamsize read_bytes = 0;
 
   if (size > 0) {
@@ -62,7 +62,7 @@ read(int size) {
  * will always be greater than 0 until EOF is reached.
  */
 PyObject *Extension<istream>::
-read1(int size) {
+read1(Py_ssize_t size) {
   std::streambuf *buf = _this->rdbuf();
   nassertr(buf != nullptr, nullptr);
 
@@ -171,7 +171,7 @@ readinto(PyObject *b) {
  * Returns empty string when the end of file is reached.
  */
 PyObject *Extension<istream>::
-readline(int size) {
+readline(Py_ssize_t size) {
   std::streambuf *buf = _this->rdbuf();
   nassertr(buf != nullptr, nullptr);
 
@@ -207,7 +207,7 @@ readline(int size) {
  * for readline().
  */
 PyObject *Extension<istream>::
-readlines(int hint) {
+readlines(Py_ssize_t hint) {
   PyObject *lst = PyList_New(0);
   if (lst == nullptr) {
     return nullptr;
@@ -223,7 +223,7 @@ readlines(int hint) {
       py_line = readline(-1);
     }
   } else {
-    size_t totchars = 0;
+    Py_ssize_t totchars = 0;
     while (Py_SIZE(py_line) > 0) {
       totchars += Py_SIZE(py_line);
       PyList_Append(lst, py_line);

+ 4 - 4
dtool/src/dtoolutil/iostream_ext.h

@@ -31,13 +31,13 @@
 template<>
 class Extension<istream> : public ExtensionBase<istream> {
 public:
-  PyObject *read(int size=-1);
-  PyObject *read1(int size=-1);
+  PyObject *read(Py_ssize_t size=-1);
+  PyObject *read1(Py_ssize_t size=-1);
   PyObject *readall();
   std::streamsize readinto(PyObject *b);
 
-  PyObject *readline(int size=-1);
-  PyObject *readlines(int hint=-1);
+  PyObject *readline(Py_ssize_t size=-1);
+  PyObject *readlines(Py_ssize_t hint=-1);
   PyObject *__iter__(PyObject *self);
 };
 

+ 10 - 1
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -2878,8 +2878,13 @@ write_module_class(ostream &out, Object *obj) {
 
   // destructor tp_dealloc;
   out << "    &Dtool_FreeInstance_" << ClassName << ",\n";
-  // printfunc tp_print;
+
+  out << "#if PY_VERSION_HEX >= 0x03080000\n";
+  out << "    0, // tp_vectorcall_offset\n";
+  out << "#else\n";
   write_function_slot(out, 4, slots, "tp_print");
+  out << "#endif\n";
+
   // getattrfunc tp_getattr;
   write_function_slot(out, 4, slots, "tp_getattr");
   // setattrfunc tp_setattr;
@@ -3070,6 +3075,10 @@ write_module_class(ostream &out, Object *obj) {
   out << "#if PY_VERSION_HEX >= 0x03040000\n";
   out << "    nullptr, // tp_finalize\n";
   out << "#endif\n";
+  // vectorcallfunc tp_vectorcall
+  out << "#if PY_VERSION_HEX >= 0x03080000\n";
+  out << "    nullptr, // tp_vectorcall\n";
+  out << "#endif\n";
   out << "  },\n";
 
   // It's tempting to initialize the type handle here, but this causes static

+ 6 - 3
dtool/src/interrogate/interrogate.cxx

@@ -574,6 +574,8 @@ main(int argc, char **argv) {
     the_output_include = &output_include;
   }
 
+  int status = 0;
+
   // Now output all of the wrapper functions.
   if (!output_code_filename.empty())
   {
@@ -595,6 +597,7 @@ main(int argc, char **argv) {
 
     if (output_code.fail()) {
       nout << "Unable to write to " << output_code_filename << "\n";
+      status = -1;
     } else {
       builder.write_code(output_code,the_output_include, def);
     }
@@ -609,13 +612,13 @@ main(int argc, char **argv) {
     pofstream output_data;
     output_data_filename.open_write(output_data);
 
-    if (output_data.fail())
-    {
+    if (output_data.fail()) {
       nout << "Unable to write to " << output_data_filename << "\n";
+      status = -1;
     } else {
       InterrogateDatabase::get_ptr()->write(output_data, def);
     }
   }
 
-  return (0);
+  return status;
 }

+ 8 - 1
dtool/src/interrogatedb/dtool_super_base.cxx

@@ -80,7 +80,7 @@ Dtool_PyTypedObject *Dtool_GetSuperBase() {
       sizeof(Dtool_PyInstDef),
       0, // tp_itemsize
       &Dtool_FreeInstance_DTOOL_SUPER_BASE,
-      nullptr, // tp_print
+      0, // tp_vectorcall_offset
       nullptr, // tp_getattr
       nullptr, // tp_setattr
 #if PY_MAJOR_VERSION >= 3
@@ -129,6 +129,13 @@ Dtool_PyTypedObject *Dtool_GetSuperBase() {
       nullptr, // tp_subclasses
       nullptr, // tp_weaklist
       nullptr, // tp_del
+      0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+      nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+      nullptr, // tp_vectorcall
+#endif
     },
     TypeHandle::none(),
     Dtool_PyModuleClassInit_DTOOL_SUPER_BASE,

+ 1 - 1
dtool/src/interrogatedb/py_compat.h

@@ -139,7 +139,7 @@ typedef long Py_hash_t;
 
 /* Python 3.6 */
 
-#ifndef _PyObject_CallNoArg
+#if PY_VERSION_HEX < 0x03080000 && !defined(_PyObject_CallNoArg)
 INLINE PyObject *_PyObject_CallNoArg(PyObject *func) {
   static PyTupleObject empty_tuple = {PyVarObject_HEAD_INIT(nullptr, 0)};
 #ifdef Py_TRACE_REFS

+ 73 - 10
dtool/src/interrogatedb/py_wrappers.cxx

@@ -541,7 +541,7 @@ static PyObject *Dtool_MappingWrapper_keys(PyObject *self, PyObject *) {
     sizeof(Dtool_SequenceWrapper),
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
@@ -582,6 +582,13 @@ static PyObject *Dtool_MappingWrapper_keys(PyObject *self, PyObject *) {
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
 
   static bool registered = false;
@@ -675,7 +682,7 @@ static PyObject *Dtool_MappingWrapper_values(PyObject *self, PyObject *) {
     sizeof(Dtool_MappingWrapper),
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
@@ -716,6 +723,13 @@ static PyObject *Dtool_MappingWrapper_values(PyObject *self, PyObject *) {
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
 
   static bool registered = false;
@@ -817,7 +831,7 @@ static PyObject *Dtool_MappingWrapper_items(PyObject *self, PyObject *) {
     sizeof(Dtool_MappingWrapper),
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
@@ -858,6 +872,13 @@ static PyObject *Dtool_MappingWrapper_items(PyObject *self, PyObject *) {
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
 
   static bool registered = false;
@@ -1192,7 +1213,7 @@ Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name
     sizeof(Dtool_SequenceWrapper),
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
@@ -1233,6 +1254,13 @@ Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
 
   static bool registered = false;
@@ -1296,7 +1324,7 @@ Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, co
     sizeof(Dtool_MutableSequenceWrapper),
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
@@ -1337,6 +1365,13 @@ Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, co
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
 
   static bool registered = false;
@@ -1404,7 +1439,7 @@ Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name)
     sizeof(Dtool_MappingWrapper),
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
@@ -1445,6 +1480,13 @@ Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name)
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
 
   static bool registered = false;
@@ -1517,7 +1559,7 @@ Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char
     sizeof(Dtool_MappingWrapper),
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
@@ -1558,6 +1600,13 @@ Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
 
   static bool registered = false;
@@ -1594,7 +1643,7 @@ Dtool_NewGenerator(PyObject *self, iternextfunc gen_next) {
     sizeof(Dtool_GeneratorWrapper),
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
@@ -1635,6 +1684,13 @@ Dtool_NewGenerator(PyObject *self, iternextfunc gen_next) {
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
 
   if (PyType_Ready(&wrapper_type) < 0) {
@@ -1663,7 +1719,7 @@ Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
     sizeof(PyGetSetDescrObject),
     0, // tp_itemsize
     (destructor)Dtool_StaticProperty_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_reserved
@@ -1696,7 +1752,7 @@ Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
     nullptr, // tp_init
     nullptr, // tp_alloc
     nullptr, // tp_new
-    nullptr, // tp_del
+    nullptr, // tp_free
     nullptr, // tp_is_gc
     nullptr, // tp_bases
     nullptr, // tp_mro
@@ -1704,6 +1760,13 @@ Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
 
   if (PyType_Ready(&wrapper_type) < 0) {

+ 4 - 4
dtool/src/parser-inc/iostream

@@ -50,13 +50,13 @@ namespace std {
   __published:
     istream(const istream&) = delete;
 
-    __extension PyObject *read(int size=-1);
-    __extension PyObject *read1(int size=-1);
+    __extension PyObject *read(Py_ssize_t size=-1);
+    __extension PyObject *read1(Py_ssize_t size=-1);
     __extension PyObject *readall();
     __extension std::streamsize readinto(PyObject *b);
 
-    __extension PyObject *readline(int size=-1);
-    __extension PyObject *readlines(int hint=-1);
+    __extension PyObject *readline(Py_ssize_t size=-1);
+    __extension PyObject *readlines(Py_ssize_t hint=-1);
     __extension PyObject *__iter__(PyObject *self);
 
     int get();

+ 5 - 1
dtool/src/prc/encryptStream.cxx

@@ -13,6 +13,8 @@
 
 #include "encryptStream.h"
 
+#ifdef HAVE_OPENSSL
+
 /**
  * Must be called immediately after open_read().  Decrypts the given number of
  * bytes and checks that they match.  The amount of header bytes are added to
@@ -25,10 +27,12 @@ read_magic(const char *magic, size_t size) {
   char *this_magic = (char *)alloca(size);
   read(this_magic, size);
 
-  if (!fail() && gcount() == size && memcmp(this_magic, magic, size) == 0) {
+  if (!fail() && (size_t)gcount() == size && memcmp(this_magic, magic, size) == 0) {
     _buf.set_magic_length(size);
     return true;
   } else {
     return false;
   }
 }
+
+#endif

+ 16 - 11
dtool/src/prc/notify.cxx

@@ -21,6 +21,10 @@
 
 #include <ctype.h>
 
+#ifdef PHAVE_ATOMIC
+#include <atomic>
+#endif
+
 #ifdef BUILD_IPHONE
 #include <fcntl.h>
 #endif
@@ -422,28 +426,29 @@ string_severity(const string &str) {
  */
 void Notify::
 config_initialized() {
-  static bool already_initialized = false;
-  if (already_initialized) {
-    nout << "Notify::config_initialized() called more than once.\n";
-    return;
-  }
-  already_initialized = true;
+  // We allow this to be called more than once to allow the user to specify a
+  // notify-output even after the initial import of Panda3D modules.  However,
+  // it cannot be changed after the first time it is set.
 
   if (_ostream_ptr == &cerr) {
-    ConfigVariableFilename notify_output
+    static ConfigVariableFilename notify_output
       ("notify-output", "",
        "The filename to which to write all the output of notify");
 
-    if (!notify_output.empty()) {
-      if (notify_output == "stdout") {
+    // We use this to ensure that only one thread can initialize the output.
+    static std::atomic_flag initialized = ATOMIC_FLAG_INIT;
+
+    std::string value = notify_output.get_value();
+    if (!value.empty() && !initialized.test_and_set()) {
+      if (value == "stdout") {
         cout.setf(std::ios::unitbuf);
         set_ostream_ptr(&cout, false);
 
-      } else if (notify_output == "stderr") {
+      } else if (value == "stderr") {
         set_ostream_ptr(&cerr, false);
 
       } else {
-        Filename filename = notify_output;
+        Filename filename = value;
         filename.set_text();
 #ifdef BUILD_IPHONE
         // On the iPhone, route everything through cerr, and then send cerr to

+ 6 - 0
dtool/src/prc/notifyCategory.cxx

@@ -181,10 +181,16 @@ update_severity_cache() {
     } else {
       // Unless, of course, we're the root.
       _severity_cache = NS_info;
+
+      // Take this opportunity to have Notify check whether the notify-output
+      // variable changed.
+      Notify::ptr()->config_initialized();
     }
   } else {
     _severity_cache = _severity;
+    Notify::ptr()->config_initialized();
   }
+
   mark_cache_valid(_local_modified);
 }
 

+ 0 - 492
dtool/src/pystub/pystub.cxx

@@ -1,492 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file pystub.cxx
- * @author drose
- * @date 2000-08-09
- */
-
-#include "pystub.h"
-
-extern "C" {
-  EXPCL_PYSTUB int PyArg_Parse(...);
-  EXPCL_PYSTUB int PyArg_ParseTuple(...);
-  EXPCL_PYSTUB int PyArg_ParseTupleAndKeywords(...);
-  EXPCL_PYSTUB int PyArg_UnpackTuple(...);
-  EXPCL_PYSTUB int PyBool_FromLong(...);
-  EXPCL_PYSTUB int PyBuffer_Release(...);
-  EXPCL_PYSTUB int PyBytes_AsString(...);
-  EXPCL_PYSTUB int PyBytes_AsStringAndSize(...);
-  EXPCL_PYSTUB int PyBytes_FromStringAndSize(...);
-  EXPCL_PYSTUB int PyBytes_Size(...);
-  EXPCL_PYSTUB int PyCFunction_New(...);
-  EXPCL_PYSTUB int PyCFunction_NewEx(...);
-  EXPCL_PYSTUB int PyCallable_Check(...);
-  EXPCL_PYSTUB int PyCapsule_GetPointer(...);
-  EXPCL_PYSTUB int PyCapsule_New(...);
-  EXPCL_PYSTUB int PyDict_DelItem(...);
-  EXPCL_PYSTUB int PyDict_DelItemString(...);
-  EXPCL_PYSTUB int PyDict_GetItem(...);
-  EXPCL_PYSTUB int PyDict_GetItemString(...);
-  EXPCL_PYSTUB int PyDict_New(...);
-  EXPCL_PYSTUB int PyDict_Next(...);
-  EXPCL_PYSTUB int PyDict_SetItem(...);
-  EXPCL_PYSTUB int PyDict_SetItemString(...);
-  EXPCL_PYSTUB int PyDict_Size(...);
-  EXPCL_PYSTUB int PyErr_Clear(...);
-  EXPCL_PYSTUB int PyErr_ExceptionMatches(...);
-  EXPCL_PYSTUB int PyErr_Fetch(...);
-  EXPCL_PYSTUB int PyErr_Format(...);
-  EXPCL_PYSTUB int PyErr_NoMemory(...);
-  EXPCL_PYSTUB int PyErr_Occurred(...);
-  EXPCL_PYSTUB int PyErr_Print(...);
-  EXPCL_PYSTUB int PyErr_Restore(...);
-  EXPCL_PYSTUB int PyErr_SetString(...);
-  EXPCL_PYSTUB int PyErr_Warn(...);
-  EXPCL_PYSTUB int PyErr_WarnEx(...);
-  EXPCL_PYSTUB int PyEval_CallFunction(...);
-  EXPCL_PYSTUB int PyEval_CallObjectWithKeywords(...);
-  EXPCL_PYSTUB int PyEval_RestoreThread(...);
-  EXPCL_PYSTUB int PyEval_SaveThread(...);
-  EXPCL_PYSTUB int PyFloat_AsDouble(...);
-  EXPCL_PYSTUB int PyFloat_FromDouble(...);
-  EXPCL_PYSTUB int PyGen_Check(...);
-  EXPCL_PYSTUB int PyGILState_Ensure(...);
-  EXPCL_PYSTUB int PyGILState_Release(...);
-  EXPCL_PYSTUB int PyImport_GetModuleDict(...);
-  EXPCL_PYSTUB int PyImport_ImportModule(...);
-  EXPCL_PYSTUB int PyInt_AsLong(...);
-  EXPCL_PYSTUB int PyInt_AsSsize_t(...);
-  EXPCL_PYSTUB int PyInt_FromLong(...);
-  EXPCL_PYSTUB int PyInt_FromSize_t(...);
-  EXPCL_PYSTUB int PyIter_Next(...);
-  EXPCL_PYSTUB int PyList_Append(...);
-  EXPCL_PYSTUB int PyList_AsTuple(...);
-  EXPCL_PYSTUB int PyList_GetItem(...);
-  EXPCL_PYSTUB int PyList_New(...);
-  EXPCL_PYSTUB int PyList_SetItem(...);
-  EXPCL_PYSTUB int PyLong_AsLong(...);
-  EXPCL_PYSTUB int PyLong_AsLongLong(...);
-  EXPCL_PYSTUB int PyLong_AsSsize_t(...);
-  EXPCL_PYSTUB int PyLong_AsUnsignedLong(...);
-  EXPCL_PYSTUB int PyLong_AsUnsignedLongLong(...);
-  EXPCL_PYSTUB int PyLong_FromLong(...);
-  EXPCL_PYSTUB int PyLong_FromLongLong(...);
-  EXPCL_PYSTUB int PyLong_FromSize_t(...);
-  EXPCL_PYSTUB int PyLong_FromUnsignedLong(...);
-  EXPCL_PYSTUB int PyLong_FromUnsignedLongLong(...);
-  EXPCL_PYSTUB int PyMapping_GetItemString(...);
-  EXPCL_PYSTUB int PyMem_Free(...);
-  EXPCL_PYSTUB int PyMemoryView_FromObject(...);
-  EXPCL_PYSTUB int PyModule_AddIntConstant(...);
-  EXPCL_PYSTUB int PyModule_AddObject(...);
-  EXPCL_PYSTUB int PyModule_AddStringConstant(...);
-  EXPCL_PYSTUB int PyModule_Create2(...);
-  EXPCL_PYSTUB int PyModule_Create2TraceRefs(...);
-  EXPCL_PYSTUB int PyModule_GetDict(...);
-  EXPCL_PYSTUB int PyNumber_AsSsize_t(...);
-  EXPCL_PYSTUB int PyNumber_Check(...);
-  EXPCL_PYSTUB int PyNumber_Float(...);
-  EXPCL_PYSTUB int PyNumber_Int(...);
-  EXPCL_PYSTUB int PyNumber_Long(...);
-  EXPCL_PYSTUB int PyObject_ASCII(...);
-  EXPCL_PYSTUB int PyObject_Call(...);
-  EXPCL_PYSTUB int PyObject_CallFunction(...);
-  EXPCL_PYSTUB int PyObject_CallFunctionObjArgs(...);
-  EXPCL_PYSTUB int PyObject_CallMethod(...);
-  EXPCL_PYSTUB int PyObject_CallMethodObjArgs(...);
-  EXPCL_PYSTUB int PyObject_CallObject(...);
-  EXPCL_PYSTUB int PyObject_Cmp(...);
-  EXPCL_PYSTUB int PyObject_Compare(...);
-  EXPCL_PYSTUB int PyObject_Free(...);
-  EXPCL_PYSTUB int PyObject_GC_Del(...);
-  EXPCL_PYSTUB int PyObject_GenericGetAttr(...);
-  EXPCL_PYSTUB int PyObject_GenericSetAttr(...);
-  EXPCL_PYSTUB int PyObject_GetAttrString(...);
-  EXPCL_PYSTUB int PyObject_GetBuffer(...);
-  EXPCL_PYSTUB int PyObject_GetIter(...);
-  EXPCL_PYSTUB int PyObject_HasAttrString(...);
-  EXPCL_PYSTUB int PyObject_IsInstance(...);
-  EXPCL_PYSTUB int PyObject_IsTrue(...);
-  EXPCL_PYSTUB int PyObject_Malloc(...);
-  EXPCL_PYSTUB int PyObject_Repr(...);
-  EXPCL_PYSTUB int PyObject_RichCompareBool(...);
-  EXPCL_PYSTUB int PyObject_SelfIter(...);
-  EXPCL_PYSTUB int PyObject_SetAttr(...);
-  EXPCL_PYSTUB int PyObject_SetAttrString(...);
-  EXPCL_PYSTUB int PyObject_Str(...);
-  EXPCL_PYSTUB int PySeqIter_New(...);
-  EXPCL_PYSTUB int PySequence_Check(...);
-  EXPCL_PYSTUB int PySequence_Fast(...);
-  EXPCL_PYSTUB int PySequence_GetItem(...);
-  EXPCL_PYSTUB int PySequence_Size(...);
-  EXPCL_PYSTUB int PySequence_Tuple(...);
-  EXPCL_PYSTUB int PyString_AsString(...);
-  EXPCL_PYSTUB int PyString_AsStringAndSize(...);
-  EXPCL_PYSTUB int PyString_FromFormat(...);
-  EXPCL_PYSTUB int PyString_FromString(...);
-  EXPCL_PYSTUB int PyString_FromStringAndSize(...);
-  EXPCL_PYSTUB int PyString_InternFromString(...);
-  EXPCL_PYSTUB int PyString_InternInPlace(...);
-  EXPCL_PYSTUB int PyString_Size(...);
-  EXPCL_PYSTUB int PySys_GetObject(...);
-  EXPCL_PYSTUB int PySys_SetObject(...);
-  EXPCL_PYSTUB int PyThreadState_Clear(...);
-  EXPCL_PYSTUB int PyThreadState_Delete(...);
-  EXPCL_PYSTUB int PyThreadState_Get(...);
-  EXPCL_PYSTUB int PyThreadState_New(...);
-  EXPCL_PYSTUB int PyThreadState_Swap(...);
-  EXPCL_PYSTUB int PyTuple_GetItem(...);
-  EXPCL_PYSTUB int PyTuple_New(...);
-  EXPCL_PYSTUB int PyTuple_Pack(...);
-  EXPCL_PYSTUB int PyTuple_Size(...);
-  EXPCL_PYSTUB int PyType_GenericAlloc(...);
-  EXPCL_PYSTUB int PyType_IsSubtype(...);
-  EXPCL_PYSTUB int PyType_Ready(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_AsWideChar(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_AsWideCharString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_CompareWithASCIIString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_FromFormat(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_FromString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_FromStringAndSize(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_FromWideChar(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_GetSize(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_AsWideChar(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_AsWideCharString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_CompareWithASCIIString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_FromFormat(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_FromString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_FromStringAndSize(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_FromWideChar(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_GetSize(...);
-  EXPCL_PYSTUB int PyUnicode_AsUTF8(...);
-  EXPCL_PYSTUB int PyUnicode_AsUTF8AndSize(...);
-  EXPCL_PYSTUB int PyUnicode_AsWideChar(...);
-  EXPCL_PYSTUB int PyUnicode_AsWideCharString(...);
-  EXPCL_PYSTUB int PyUnicode_CompareWithASCIIString(...);
-  EXPCL_PYSTUB int PyUnicode_FromFormat(...);
-  EXPCL_PYSTUB int PyUnicode_FromString(...);
-  EXPCL_PYSTUB int PyUnicode_FromStringAndSize(...);
-  EXPCL_PYSTUB int PyUnicode_FromWideChar(...);
-  EXPCL_PYSTUB int PyUnicode_GetSize(...);
-  EXPCL_PYSTUB int PyUnicode_InternFromString(...);
-  EXPCL_PYSTUB int PyUnicode_InternInPlace(...);
-  EXPCL_PYSTUB int Py_BuildValue(...);
-  EXPCL_PYSTUB int Py_GetVersion(...);
-  EXPCL_PYSTUB int Py_InitModule4(...);
-  EXPCL_PYSTUB int Py_InitModule4_64(...);
-  EXPCL_PYSTUB int Py_InitModule4TraceRefs(...);
-  EXPCL_PYSTUB int Py_InitModule4TraceRefs_64(...);
-  EXPCL_PYSTUB int _PyArg_ParseTuple_SizeT(...);
-  EXPCL_PYSTUB int _PyArg_ParseTupleAndKeywords_SizeT(...);
-  EXPCL_PYSTUB int _PyArg_Parse_SizeT(...);
-  EXPCL_PYSTUB int _PyErr_BadInternalCall(...);
-  EXPCL_PYSTUB int _PyLong_AsByteArray(...);
-  EXPCL_PYSTUB int _PyLong_Sign(...);
-  EXPCL_PYSTUB int _PyObject_CallFunction_SizeT(...);
-  EXPCL_PYSTUB int _PyObject_CallMethod_SizeT(...);
-  EXPCL_PYSTUB int _PyObject_DebugFree(...);
-  EXPCL_PYSTUB int _PyObject_Del(...);
-  EXPCL_PYSTUB int _PyObject_FastCallDict(...);
-  EXPCL_PYSTUB int _PyUnicode_AsString(...);
-  EXPCL_PYSTUB int _PyUnicode_AsStringAndSize(...);
-  EXPCL_PYSTUB int _PyUnicode_EqualToASCIIString(...);
-  EXPCL_PYSTUB int _Py_AddToAllObjects(...);
-  EXPCL_PYSTUB int _Py_BuildValue_SizeT(...);
-  EXPCL_PYSTUB int _Py_Dealloc(...);
-  EXPCL_PYSTUB int _Py_ForgetReference(...);
-  EXPCL_PYSTUB int _Py_NegativeRefcount(...);
-  EXPCL_PYSTUB int _Py_NewReference(...);
-  EXPCL_PYSTUB int _Py_PrintReferenceAddresses(...);
-  EXPCL_PYSTUB int _Py_PrintReferences(...);
-  EXPCL_PYSTUB int _Py_RefTotal(...);
-
-  EXPCL_PYSTUB void Py_Initialize();
-  EXPCL_PYSTUB int Py_IsInitialized();
-  EXPCL_PYSTUB void PyEval_InitThreads();
-
-  EXPCL_PYSTUB extern void *PyExc_AssertionError;
-  EXPCL_PYSTUB extern void *PyExc_AttributeError;
-  EXPCL_PYSTUB extern void *PyExc_BufferError;
-  EXPCL_PYSTUB extern void *PyExc_ConnectionError;
-  EXPCL_PYSTUB extern void *PyExc_Exception;
-  EXPCL_PYSTUB extern void *PyExc_FutureWarning;
-  EXPCL_PYSTUB extern void *PyExc_ImportError;
-  EXPCL_PYSTUB extern void *PyExc_IndexError;
-  EXPCL_PYSTUB extern void *PyExc_KeyError;
-  EXPCL_PYSTUB extern void *PyExc_NameError;
-  EXPCL_PYSTUB extern void *PyExc_OSError;
-  EXPCL_PYSTUB extern void *PyExc_OverflowError;
-  EXPCL_PYSTUB extern void *PyExc_RuntimeError;
-  EXPCL_PYSTUB extern void *PyExc_StandardError;
-  EXPCL_PYSTUB extern void *PyExc_StopIteration;
-  EXPCL_PYSTUB extern void *PyExc_SystemExit;
-  EXPCL_PYSTUB extern void *PyExc_TypeError;
-  EXPCL_PYSTUB extern void *PyExc_ValueError;
-  EXPCL_PYSTUB extern void *PyDict_Type;
-  EXPCL_PYSTUB extern void *PyFloat_Type;
-  EXPCL_PYSTUB extern void *PyGen_Type;
-  EXPCL_PYSTUB extern void *PyInt_Type;
-  EXPCL_PYSTUB extern void *PyList_Type;
-  EXPCL_PYSTUB extern void *PyLong_Type;
-  EXPCL_PYSTUB extern void *PyObject_Type;
-  EXPCL_PYSTUB extern void *PyString_Type;
-  EXPCL_PYSTUB extern void *PyTuple_Type;
-  EXPCL_PYSTUB extern void *PyType_Type;
-  EXPCL_PYSTUB extern void *PyUnicode_Type;
-  EXPCL_PYSTUB extern void *_PyThreadState_Current;
-  EXPCL_PYSTUB extern void *_Py_FalseStruct;
-  EXPCL_PYSTUB extern void *_Py_NoneStruct;
-  EXPCL_PYSTUB extern void *_Py_NotImplementedStruct;
-  EXPCL_PYSTUB extern void *_Py_TrueStruct;
-  EXPCL_PYSTUB extern void *_Py_ZeroStruct;
-};
-
-
-int PyArg_Parse(...) { return 0; };
-int PyArg_ParseTuple(...) { return 0; }
-int PyArg_ParseTupleAndKeywords(...) { return 0; }
-int PyArg_UnpackTuple(...) { return 0; };
-int PyBool_FromLong(...) { return 0; }
-int PyBuffer_Release(...) { return 0; }
-int PyBytes_AsString(...) { return 0; }
-int PyBytes_AsStringAndSize(...) { return 0; }
-int PyBytes_FromStringAndSize(...) { return 0; }
-int PyBytes_Size(...) { return 0; }
-int PyCFunction_New(...) { return 0; };
-int PyCFunction_NewEx(...) { return 0; };
-int PyCallable_Check(...) { return 0; }
-int PyCapsule_GetPointer(...) { return 0; }
-int PyCapsule_New(...) { return 0; }
-int PyDict_DelItem(...) { return 0; }
-int PyDict_DelItemString(...) { return 0; }
-int PyDict_GetItem(...) { return 0; }
-int PyDict_GetItemString(...) { return 0; }
-int PyDict_New(...) { return 0; };
-int PyDict_Next(...) { return 0; };
-int PyDict_SetItem(...) { return 0; };
-int PyDict_SetItemString(...) { return 0; };
-int PyDict_Size(...){ return 0; }
-int PyErr_Clear(...) { return 0; };
-int PyErr_ExceptionMatches(...) { return 0; };
-int PyErr_Fetch(...) { return 0; }
-int PyErr_Format(...) { return 0; };
-int PyErr_NoMemory(...) { return 0; }
-int PyErr_Occurred(...) { return 0; }
-int PyErr_Print(...) { return 0; }
-int PyErr_Restore(...) { return 0; }
-int PyErr_SetString(...) { return 0; }
-int PyErr_Warn(...) { return 0; }
-int PyErr_WarnEx(...) { return 0; }
-int PyEval_CallFunction(...) { return 0; }
-int PyEval_CallObjectWithKeywords(...) { return 0; }
-int PyEval_InitThreads(...) { return 0; }
-int PyEval_RestoreThread(...) { return 0; }
-int PyEval_SaveThread(...) { return 0; }
-int PyFloat_AsDouble(...) { return 0; }
-int PyFloat_FromDouble(...) { return 0; }
-int PyGen_Check(...) { return 0; }
-int PyGILState_Ensure(...) { return 0; }
-int PyGILState_Release(...) { return 0; }
-int PyImport_GetModuleDict(...) { return 0; }
-int PyImport_ImportModule(...) { return 0; }
-int PyInt_AsLong(...) { return 0; }
-int PyInt_AsSsize_t(...) { return 0; }
-int PyInt_FromLong(...) { return 0; }
-int PyInt_FromSize_t(...) { return 0; }
-int PyIter_Next(...) { return 0; }
-int PyList_Append(...) { return 0; }
-int PyList_AsTuple(...) { return 0; }
-int PyList_GetItem(...) { return 0; }
-int PyList_New(...) { return 0; }
-int PyList_SetItem(...) { return 0; }
-int PyLong_AsLong(...) { return 0; }
-int PyLong_AsLongLong(...) { return 0; }
-int PyLong_AsSsize_t(...) { return 0; }
-int PyLong_AsUnsignedLong(...) { return 0; }
-int PyLong_AsUnsignedLongLong(...) { return 0; }
-int PyLong_FromLong(...) { return 0; }
-int PyLong_FromLongLong(...) { return 0; }
-int PyLong_FromSize_t(...) { return 0; }
-int PyLong_FromUnsignedLong(...) { return 0; }
-int PyLong_FromUnsignedLongLong(...) { return 0; }
-int PyMapping_GetItemString(...) { return 0; }
-int PyMem_Free(...) { return 0; }
-int PyMemoryView_FromObject(...) { return 0; }
-int PyModule_AddIntConstant(...) { return 0; };
-int PyModule_AddObject(...) { return 0; };
-int PyModule_AddStringConstant(...) { return 0; };
-int PyModule_Create2(...) { return 0; };
-int PyModule_Create2TraceRefs(...) { return 0; };
-int PyModule_GetDict(...) { return 0; };
-int PyNumber_AsSsize_t(...) { return 0; }
-int PyNumber_Check(...) { return 0; }
-int PyNumber_Float(...) { return 0; }
-int PyNumber_Int(...) { return 0; }
-int PyNumber_Long(...) { return 0; }
-int PyObject_ASCII(...) { return 0; }
-int PyObject_Call(...) { return 0; }
-int PyObject_CallFunction(...) { return 0; }
-int PyObject_CallFunctionObjArgs(...) { return 0; }
-int PyObject_CallMethod(...) { return 0; }
-int PyObject_CallMethodObjArgs(...) { return 0; }
-int PyObject_CallObject(...) { return 0; }
-int PyObject_Cmp(...) { return 0; }
-int PyObject_Compare(...) { return 0; }
-int PyObject_Free(...) { return 0; }
-int PyObject_GC_Del(...) { return 0; }
-int PyObject_GenericGetAttr(...) { return 0; };
-int PyObject_GenericSetAttr(...) { return 0; };
-int PyObject_GetAttrString(...) { return 0; }
-int PyObject_GetBuffer(...) { return 0; }
-int PyObject_GetIter(...) { return 0; }
-int PyObject_HasAttrString(...) { return 0; }
-int PyObject_IsInstance(...) { return 0; }
-int PyObject_IsTrue(...) { return 0; }
-int PyObject_Malloc(...) { return 0; }
-int PyObject_Repr(...) { return 0; }
-int PyObject_RichCompareBool(...) { return 0; }
-int PyObject_SelfIter(...) { return 0; }
-int PyObject_SetAttr(...) { return 0; }
-int PyObject_SetAttrString(...) { return 0; }
-int PyObject_Str(...) { return 0; }
-int PySeqIter_New(...) { return 0; }
-int PySequence_Check(...) { return 0; }
-int PySequence_Fast(...) { return 0; }
-int PySequence_GetItem(...) { return 0; }
-int PySequence_Size(...) { return 0; }
-int PySequence_Tuple(...) { return 0; }
-int PyString_AsString(...) { return 0; }
-int PyString_AsStringAndSize(...) { return 0; }
-int PyString_FromFormat(...) { return 0; }
-int PyString_FromString(...) { return 0; }
-int PyString_FromStringAndSize(...) { return 0; }
-int PyString_InternFromString(...) { return 0; }
-int PyString_InternInPlace(...) { return 0; }
-int PySys_GetObject(...) { return 0; }
-int PySys_SetObject(...) { return 0; }
-int PyThreadState_Clear(...) { return 0; }
-int PyThreadState_Delete(...) { return 0; }
-int PyThreadState_Get(...) { return 0; }
-int PyThreadState_New(...) { return 0; }
-int PyThreadState_Swap(...) { return 0; }
-int PyTuple_GetItem(...) { return 0; }
-int PyTuple_New(...) { return 0; }
-int PyTuple_Pack(...) { return 0; }
-int PyTuple_Size(...) { return 0; };
-int PyType_GenericAlloc(...) { return 0; };
-int PyType_IsSubtype(...) { return 0; }
-int PyType_Ready(...) { return 0; };
-int PyUnicodeUCS2_AsWideChar(...) { return 0; }
-int PyUnicodeUCS2_AsWideCharString(...) { return 0; }
-int PyUnicodeUCS2_CompareWithASCIIString(...) { return 0; }
-int PyUnicodeUCS2_FromFormat(...) { return 0; }
-int PyUnicodeUCS2_FromString(...) { return 0; }
-int PyUnicodeUCS2_FromStringAndSize(...) { return 0; }
-int PyUnicodeUCS2_FromWideChar(...) { return 0; }
-int PyUnicodeUCS2_GetSize(...) { return 0; }
-int PyUnicodeUCS4_AsWideChar(...) { return 0; }
-int PyUnicodeUCS4_AsWideCharString(...) { return 0; }
-int PyUnicodeUCS4_CompareWithASCIIString(...) { return 0; }
-int PyUnicodeUCS4_FromFormat(...) { return 0; }
-int PyUnicodeUCS4_FromString(...) { return 0; }
-int PyUnicodeUCS4_FromStringAndSize(...) { return 0; }
-int PyUnicodeUCS4_FromWideChar(...) { return 0; }
-int PyUnicodeUCS4_GetSize(...) { return 0; }
-int PyUnicode_AsUTF8(...) { return 0; }
-int PyUnicode_AsUTF8AndSize(...) { return 0; }
-int PyUnicode_AsWideChar(...) { return 0; }
-int PyUnicode_AsWideCharString(...) { return 0; }
-int PyUnicode_CompareWithASCIIString(...) { return 0; }
-int PyUnicode_FromFormat(...) { return 0; }
-int PyUnicode_FromString(...) { return 0; }
-int PyUnicode_FromStringAndSize(...) { return 0; }
-int PyUnicode_FromWideChar(...) { return 0; }
-int PyUnicode_GetSize(...) { return 0; }
-int PyUnicode_InternFromString(...) { return 0; }
-int PyUnicode_InternInPlace(...) { return 0; }
-int Py_GetVersion(...) { return 0; }
-int Py_BuildValue(...) { return 0; }
-int Py_InitModule4(...) { return 0; }
-int Py_InitModule4_64(...) { return 0; }
-int Py_InitModule4TraceRefs(...) { return 0; };
-int Py_InitModule4TraceRefs_64(...) { return 0; };
-int _PyArg_ParseTuple_SizeT(...) { return 0; };
-int _PyArg_ParseTupleAndKeywords_SizeT(...) { return 0; };
-int _PyArg_Parse_SizeT(...) { return 0; };
-int _PyErr_BadInternalCall(...) { return 0; };
-int _PyLong_AsByteArray(...) { return 0; };
-int _PyLong_Sign(...) { return 0; };
-int _PyObject_CallFunction_SizeT(...) { return 0; };
-int _PyObject_CallMethod_SizeT(...) { return 0; };
-int _PyObject_DebugFree(...) { return 0; };
-int _PyObject_Del(...) { return 0; };
-int _PyObject_FastCallDict(...) { return 0; };
-int _PyUnicode_AsString(...) { return 0; };
-int _PyUnicode_AsStringAndSize(...) { return 0; };
-int _PyUnicode_EqualToASCIIString(...) { return 0; };
-int _Py_AddToAllObjects(...) { return 0; };
-int _Py_BuildValue_SizeT(...) { return 0; };
-int _Py_Dealloc(...) { return 0; };
-int _Py_ForgetReference(...) { return 0; };
-int _Py_NegativeRefcount(...) { return 0; };
-int _Py_NewReference(...) { return 0; };
-int _Py_PrintReferenceAddresses(...) { return 0; };
-int _Py_PrintReferences(...) { return 0; };
-int _Py_RefTotal(...) { return 0; };
-
-// We actually might call this one.
-void Py_Initialize() {
-}
-int Py_IsInitialized() {
-  return 0;
-}
-void PyEval_InitThreads() {
-}
-
-
-void *PyExc_AssertionError = nullptr;
-void *PyExc_AttributeError = nullptr;
-void *PyExc_BufferError = nullptr;
-void *PyExc_ConnectionError = nullptr;
-void *PyExc_Exception = nullptr;
-void *PyExc_FutureWarning = nullptr;
-void *PyExc_ImportError = nullptr;
-void *PyExc_IndexError = nullptr;
-void *PyExc_KeyError = nullptr;
-void *PyExc_NameError = nullptr;
-void *PyExc_OSError = nullptr;
-void *PyExc_OverflowError = nullptr;
-void *PyExc_RuntimeError = nullptr;
-void *PyExc_StandardError = nullptr;
-void *PyExc_StopIteration = nullptr;
-void *PyExc_SystemExit = nullptr;
-void *PyExc_TypeError = nullptr;
-void *PyExc_ValueError = nullptr;
-void *PyDict_Type = nullptr;
-void *PyFloat_Type = nullptr;
-void *PyGen_Type = nullptr;
-void *PyInt_Type = nullptr;
-void *PyList_Type = nullptr;
-void *PyLong_Type = nullptr;
-void *PyObject_Type = nullptr;
-void *PyString_Type = nullptr;
-void *PyTuple_Type = nullptr;
-void *PyType_Type = nullptr;
-void *PyUnicode_Type = nullptr;
-void *_PyThreadState_Current = nullptr;
-void *_Py_FalseStruct = nullptr;
-void *_Py_NoneStruct = nullptr;
-void *_Py_NotImplementedStruct = nullptr;
-void *_Py_TrueStruct = nullptr;
-void *_Py_ZeroStruct = nullptr;
-
-
-void
-pystub() {
-}

+ 0 - 38
dtool/src/pystub/pystub.h

@@ -1,38 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file pystub.h
- * @author drose
- * @date 2000-08-08
- */
-
-#ifndef PYSTUB_H
-#define PYSTUB_H
-
-#include "dtoolbase.h"
-
-// The sole purpose of this header file is to allow a program other than
-// Python to load in a module that includes Python wrappers.
-
-// We need this if we build the Panda libraries with Python wrappers, but want
-// to run a standalone program with those libraries.
-
-// This header file just stubs out the Python functions that these wrappers
-// will call.  You should include this header file in exactly one .C file in
-// your project, preferably in the .C file that defines main(), and then link
-// with -lpystub.  Do not include this header file in a .C or .h file that
-// will become part of an .so that might eventually link with Python.
-
-
-// You might need to call this function in main() or somewhere to force the
-// .so to be linked in--some OS'es try to be smart about not pulling in shared
-// libraries whose symbols aren't referenced anywhere.
-EXPCL_PYSTUB void pystub();
-
-
-#endif

+ 2 - 2
makepanda/getversion.py

@@ -4,13 +4,13 @@
 # and returns it on the command-line.  This is useful for the
 # automated scripts that build the Panda3D releases.
 
-from makepandacore import ParsePandaVersion, ParsePluginVersion
+from makepandacore import ParsePandaVersion, ParsePluginVersion, GetMetadataValue
 import sys
 
 if '--runtime' in sys.argv:
     version = ParsePluginVersion("dtool/PandaVersion.pp")
 else:
-    version = ParsePandaVersion("dtool/PandaVersion.pp")
+    version = GetMetadataValue('version')
 
 version = version.strip()
 sys.stdout.write(version)

+ 27 - 0
makepanda/installer.nsi

@@ -84,6 +84,7 @@ LangString DESC_SecTools ${LANG_ENGLISH} "Useful tools and model converters to h
 LangString DESC_SecGroupPython ${LANG_ENGLISH} "Contains modules that provide Python support for Panda3D."
 LangString DESC_SecPyShared ${LANG_ENGLISH} "Contains the common Python code used by the Panda3D Python bindings."
 LangString DESC_SecPython ${LANG_ENGLISH} "Contains a ${REGVIEW}-bit copy of Python ${INCLUDE_PYVER} preconfigured to make use of Panda3D."
+LangString DESC_SecEnsurePip ${LANG_ENGLISH} "Installs the pip package manager into the included Python installation."
 LangString DESC_SecHeadersLibs ${LANG_ENGLISH} "Headers and libraries needed for C++ development with Panda3D."
 LangString DESC_SecSamples ${LANG_ENGLISH} "The sample programs demonstrate how to make Python applications with Panda3D."
 LangString DESC_SecMaxPlugins ${LANG_ENGLISH} "Plug-ins for Autodesk 3ds Max (${REGVIEW}-bit) that can be used to export models to Panda3D."
@@ -477,6 +478,19 @@ Section "Python ${INCLUDE_PYVER}" SecPython
     SkipRegPath:
 
 SectionEnd
+
+Section "Install pip" SecEnsurePip
+    SectionIn 1 2 3
+
+    SetDetailsPrint both
+    DetailPrint "Installing the pip package manager..."
+    SetDetailsPrint listonly
+
+    SetOutPath $INSTDIR
+    nsExec::ExecToLog '"$INSTDIR\python\python.exe" -m ensurepip --default-pip'
+    Pop $0
+    DetailPrint "Command returned exit status $0"
+SectionEnd
 !endif
 
 !macro MaybeEnablePyBindingSection PYVER
@@ -537,6 +551,18 @@ Function .onSelChange
         IntOp $R0 $R0 | ${SF_SELECTED}
         SectionSetFlags ${SecPyShared} $R0
     ${EndIf}
+
+    !ifdef INCLUDE_PYVER
+        ${If} ${SectionIsSelected} ${SecPython}
+            !insertmacro SectionFlagIsSet ${SecEnsurePip} ${SF_RO} 0 SkipSelectEnsurePip
+            !insertmacro SelectSection ${SecEnsurePip}
+            SkipSelectEnsurePip:
+            !insertmacro ClearSectionFlag ${SecEnsurePip} ${SF_RO}
+        ${Else}
+            !insertmacro UnselectSection ${SecEnsurePip}
+            !insertmacro SetSectionFlag ${SecEnsurePip} ${SF_RO}
+        ${EndIf}
+    !endif
 FunctionEnd
 
 !ifdef INCLUDE_PYVER
@@ -917,6 +943,7 @@ SectionEnd
   !endif
   !ifdef INCLUDE_PYVER
     !insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython)
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecEnsurePip} $(DESC_SecEnsurePip)
   !endif
   !insertmacro MUI_DESCRIPTION_TEXT ${SecHeadersLibs} $(DESC_SecHeadersLibs)
   !ifdef HAVE_SAMPLES

+ 1 - 1
makepanda/installpanda.py

@@ -226,7 +226,7 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir(
         oscmd("echo '"+libdir+"/panda3d'>    "+destdir+"/etc/ld.so.conf.d/panda3d.conf")
 
     for base in os.listdir(outputdir+"/lib"):
-        if (not base.endswith(".a")) or base == "libp3pystub.a":
+        if not base.endswith(".a"):
             # We really need to specify -R in order not to follow symlinks on non-GNU
             oscmd("cp -R -P "+outputdir+"/lib/"+base+" "+destdir+libdir+"/panda3d/"+base)
 

+ 5 - 7
makepanda/makepackage.py

@@ -562,9 +562,9 @@ def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs):
         oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver))
         WriteFile("dstroot/pybindings%s/Library/Python/%s/site-packages/Panda3D.pth" % (pyver, pyver), "/Developer/Panda3D")
 
-        # Evidently not all Python 3.7 installations read the above path, so do this for now
-        # See GitHub #502
-        if pyver == "3.7":
+        # Somewhere in Python 2.7.13 and 3.7, the above path was removed from
+        # sys.path of the python.org distribution.  See bpo-28440 and GH #502.
+        if pyver not in ("3.0", "3.1", "3.2", "3.3", "3.4", "3.5", "3.6"):
             dir = "dstroot/pybindings%s/Library/Frameworks/Python.framework/Versions/%s/lib/python%s/site-packages" % (pyver, pyver, pyver)
             oscmd("mkdir -p %s" % (dir))
             WriteFile("%s/Panda3D.pth" % (dir), "/Developer/Panda3D")
@@ -584,8 +584,6 @@ def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs):
 
     oscmd("mkdir -p dstroot/headers/Developer/Panda3D/lib")
     oscmd("cp -R %s/include               dstroot/headers/Developer/Panda3D/include" % outputdir)
-    if os.path.isfile(outputdir + "/lib/libp3pystub.a"):
-        oscmd("cp -R -P %s/lib/libp3pystub.a dstroot/headers/Developer/Panda3D/lib/" % outputdir)
 
     if os.path.isdir("samples"):
         oscmd("mkdir -p dstroot/samples/Developer/Examples/Panda3D")
@@ -1039,7 +1037,7 @@ def MakeInstaller(version, **kwargs):
 
 
 if __name__ == "__main__":
-    version = ParsePandaVersion("dtool/PandaVersion.pp")
+    version = GetMetadataValue('version')
 
     parser = OptionParser()
     parser.add_option('', '--version', dest='version', help='Panda3D version number (default: %s)' % (version), default=version)
@@ -1071,7 +1069,7 @@ if __name__ == "__main__":
             PkgDisable(pkg)
 
     # Parse the version.
-    match = re.match(r'^\d+\.\d+\.\d+', options.version)
+    match = re.match(r'^\d+\.\d+(\.\d+)+', options.version)
     if not match:
         exit("version requires three digits")
 

+ 31 - 56
makepanda/makepanda.py

@@ -222,7 +222,7 @@ def parseopts(args):
             elif (option=="--arch"): target_arch = value.strip()
             elif (option=="--nocolor"): DisableColors()
             elif (option=="--version"):
-                match = re.match(r'^\d+\.\d+\.\d+', value)
+                match = re.match(r'^\d+\.\d+(\.\d+)+', value)
                 if not match:
                     usage("version requires three digits")
                 WHLVERSION = value
@@ -924,6 +924,10 @@ if (COMPILER=="GCC"):
         else:
             PkgDisable("OPENCV")
 
+        if GetTarget() == "darwin" and not PkgSkip("OPENAL"):
+            LibName("OPENAL", "-framework AudioToolbox")
+            LibName("OPENAL", "-framework CoreAudio")
+
         if not PkgSkip("ASSIMP") and \
             os.path.isfile(GetThirdpartyDir() + "assimp/lib/libassimp.a"):
             # Also pick up IrrXML, which is needed when linking statically.
@@ -1716,12 +1720,6 @@ def CompileLink(dll, obj, opts):
                     else:
                         cmd += ' /NOD:{}.lib'.format(pythonv)
 
-                # Yes, we know we are importing "locally defined symbols".
-                for x in obj:
-                    if x.endswith('libp3pystub.lib'):
-                        cmd += ' /ignore:4049,4217'
-                        break
-
             # Set the subsystem.  Specify that we want to target Windows XP.
             subsystem = GetValueOption(opts, "SUBSYSTEM:") or "CONSOLE"
             cmd += " /SUBSYSTEM:" + subsystem
@@ -2680,7 +2678,7 @@ PANDAVERSION_H="""
 #define PANDA_SEQUENCE_VERSION $VERSION3
 #define PANDA_VERSION $NVERSION
 #define PANDA_NUMERIC_VERSION $NVERSION
-#define PANDA_VERSION_STR "$VERSION1.$VERSION2.$VERSION3"
+#define PANDA_VERSION_STR "$VERSION"
 #define PANDA_ABI_VERSION_STR "$VERSION1.$VERSION2"
 #define PANDA_DISTRIBUTOR "$DISTRIBUTOR"
 #define PANDA_PACKAGE_VERSION_STR "$RTDIST_VERSION"
@@ -2799,6 +2797,7 @@ def CreatePandaVersionFiles():
     pandaversion_h = pandaversion_h.replace("$VERSION1",str(version1))
     pandaversion_h = pandaversion_h.replace("$VERSION2",str(version2))
     pandaversion_h = pandaversion_h.replace("$VERSION3",str(version3))
+    pandaversion_h = pandaversion_h.replace("$VERSION",VERSION)
     pandaversion_h = pandaversion_h.replace("$NVERSION",str(nversion))
     pandaversion_h = pandaversion_h.replace("$DISTRIBUTOR",DISTRIBUTOR)
     pandaversion_h = pandaversion_h.replace("$RTDIST_VERSION",RTDIST_VERSION)
@@ -2899,7 +2898,9 @@ if '__file__' in locals():
 
     bindir = os.path.join(os.path.dirname(__file__), '..', 'bin')
     if os.path.isdir(bindir):
-        if not os.environ.get('PATH'):
+        if hasattr(os, 'add_dll_directory'):
+            os.add_dll_directory(bindir)
+        elif not os.environ.get('PATH'):
             os.environ['PATH'] = bindir
         else:
             os.environ['PATH'] = bindir + os.pathsep + os.environ['PATH']
@@ -3206,9 +3207,8 @@ if tp_dir is not None:
 
 # Copy over the MSVC runtime.
 if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK:
-    vsver = "%s%s" % SDK["VISUALSTUDIO_VERSION"]
     vcver = "%s%s" % (SDK["MSVC_VERSION"][0], 0)        # ignore minor version.
-    crtname = "Microsoft.VC%s.CRT" % (vsver)
+    crtname = "Microsoft.VC%s.CRT" % (vcver)
     if ("VCTOOLSVERSION" in SDK):
         dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "Redist", "MSVC", SDK["VCTOOLSVERSION"], "onecore", GetTargetArch(), crtname)
     else:
@@ -3272,7 +3272,6 @@ CopyAllHeaders('dtool/src/prc', skip=["prc_parameters.h"])
 CopyAllHeaders('dtool/src/dconfig')
 CopyAllHeaders('dtool/src/interrogatedb')
 CopyAllHeaders('dtool/metalibs/dtoolconfig')
-CopyAllHeaders('dtool/src/pystub')
 CopyAllHeaders('dtool/src/interrogate')
 CopyAllHeaders('dtool/src/test_interrogate')
 CopyAllHeaders('panda/src/putil')
@@ -3577,17 +3576,6 @@ PyTargetAdd('interrogatedb.pyd', input='libp3dtool.dll')
 PyTargetAdd('interrogatedb.pyd', input='libp3dtoolconfig.dll')
 PyTargetAdd('interrogatedb.pyd', input='libp3interrogatedb.dll')
 
-#
-# DIRECTORY: dtool/src/pystub/
-#
-
-if not RUNTIME and not RTDIST:
-  OPTS=['DIR:dtool/src/pystub']
-  TargetAdd('p3pystub_pystub.obj', opts=OPTS, input='pystub.cxx')
-  TargetAdd('libp3pystub.lib', input='p3pystub_pystub.obj')
-  #TargetAdd('libp3pystub.lib', input='libp3dtool.dll')
-  TargetAdd('libp3pystub.lib', opts=['ADVAPI'])
-
 #
 # DIRECTORY: dtool/src/interrogate/
 #
@@ -3734,7 +3722,6 @@ OPTS=['DIR:panda/src/downloader', 'OPENSSL', 'ZLIB']
 IGATEFILES=GetDirectoryContents('panda/src/downloader', ["*.h", "*_composite*.cxx"])
 TargetAdd('libp3downloader.in', opts=OPTS, input=IGATEFILES)
 TargetAdd('libp3downloader.in', opts=['IMOD:panda3d.core', 'ILIB:libp3downloader', 'SRCDIR:panda/src/downloader'])
-PyTargetAdd('p3downloader_stringStream_ext.obj', opts=OPTS, input='stringStream_ext.cxx')
 
 #
 # DIRECTORY: panda/metalibs/pandaexpress/
@@ -4357,7 +4344,6 @@ if (not RUNTIME):
   PyTargetAdd('core.pyd', input='p3prc_ext_composite.obj')
 
   PyTargetAdd('core.pyd', input='libp3downloader_igate.obj')
-  PyTargetAdd('core.pyd', input='p3downloader_stringStream_ext.obj')
   PyTargetAdd('core.pyd', input='p3express_ext_composite.obj')
   PyTargetAdd('core.pyd', input='libp3express_igate.obj')
 
@@ -5252,11 +5238,11 @@ if (PkgSkip("DIRECT")==0):
 if (PkgSkip("DIRECT")==0):
   OPTS=['DIR:direct/src/dcparser', 'BUILDING:DIRECT_DCPARSER', 'WITHINPANDA', 'BISONPREFIX_dcyy']
   CreateFile(GetOutputDir()+"/include/dcParser.h")
-  PyTargetAdd('p3dcparser_dcParser.obj', opts=OPTS, input='dcParser.yxx')
+  TargetAdd('p3dcparser_dcParser.obj', opts=OPTS, input='dcParser.yxx')
   #TargetAdd('dcParser.h', input='p3dcparser_dcParser.obj', opts=['DEPENDENCYONLY'])
-  PyTargetAdd('p3dcparser_dcLexer.obj', opts=OPTS, input='dcLexer.lxx')
-  PyTargetAdd('p3dcparser_composite1.obj', opts=OPTS, input='p3dcparser_composite1.cxx')
-  PyTargetAdd('p3dcparser_composite2.obj', opts=OPTS, input='p3dcparser_composite2.cxx')
+  TargetAdd('p3dcparser_dcLexer.obj', opts=OPTS, input='dcLexer.lxx')
+  TargetAdd('p3dcparser_composite1.obj', opts=OPTS, input='p3dcparser_composite1.cxx')
+  TargetAdd('p3dcparser_composite2.obj', opts=OPTS, input='p3dcparser_composite2.cxx')
 
   OPTS=['DIR:direct/src/dcparser', 'WITHINPANDA']
   IGATEFILES=GetDirectoryContents('direct/src/dcparser', ["*.h", "*_composite*.cxx"])
@@ -5264,6 +5250,7 @@ if (PkgSkip("DIRECT")==0):
   if "dcmsgtypes.h" in IGATEFILES: IGATEFILES.remove('dcmsgtypes.h')
   TargetAdd('libp3dcparser.in', opts=OPTS, input=IGATEFILES)
   TargetAdd('libp3dcparser.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3dcparser', 'SRCDIR:direct/src/dcparser'])
+  PyTargetAdd('p3dcparser_ext_composite.obj', opts=OPTS, input='p3dcparser_ext_composite.cxx')
 
 #
 # DIRECTORY: direct/src/deadrec/
@@ -5285,13 +5272,13 @@ if (PkgSkip("DIRECT")==0):
 if (PkgSkip("DIRECT")==0):
   OPTS=['DIR:direct/src/distributed', 'DIR:direct/src/dcparser', 'WITHINPANDA', 'BUILDING:DIRECT', 'OPENSSL']
   TargetAdd('p3distributed_config_distributed.obj', opts=OPTS, input='config_distributed.cxx')
-  PyTargetAdd('p3distributed_cConnectionRepository.obj', opts=OPTS, input='cConnectionRepository.cxx')
-  PyTargetAdd('p3distributed_cDistributedSmoothNodeBase.obj', opts=OPTS, input='cDistributedSmoothNodeBase.cxx')
 
   OPTS=['DIR:direct/src/distributed', 'WITHINPANDA', 'OPENSSL']
   IGATEFILES=GetDirectoryContents('direct/src/distributed', ["*.h", "*.cxx"])
   TargetAdd('libp3distributed.in', opts=OPTS, input=IGATEFILES)
   TargetAdd('libp3distributed.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3distributed', 'SRCDIR:direct/src/distributed'])
+  PyTargetAdd('p3distributed_cConnectionRepository.obj', opts=OPTS, input='cConnectionRepository.cxx')
+  PyTargetAdd('p3distributed_cDistributedSmoothNodeBase.obj', opts=OPTS, input='cDistributedSmoothNodeBase.cxx')
 
 #
 # DIRECTORY: direct/src/interval/
@@ -5341,10 +5328,15 @@ if (PkgSkip("DIRECT")==0):
 
 if (PkgSkip("DIRECT")==0):
   TargetAdd('libp3direct.dll', input='p3directbase_directbase.obj')
+  TargetAdd('libp3direct.dll', input='p3dcparser_composite1.obj')
+  TargetAdd('libp3direct.dll', input='p3dcparser_composite2.obj')
+  TargetAdd('libp3direct.dll', input='p3dcparser_dcParser.obj')
+  TargetAdd('libp3direct.dll', input='p3dcparser_dcLexer.obj')
   TargetAdd('libp3direct.dll', input='p3showbase_showBase.obj')
   if GetTarget() == 'darwin':
     TargetAdd('libp3direct.dll', input='p3showbase_showBase_assist.obj')
   TargetAdd('libp3direct.dll', input='p3deadrec_composite1.obj')
+  TargetAdd('libp3direct.dll', input='p3distributed_config_distributed.obj')
   TargetAdd('libp3direct.dll', input='p3interval_composite1.obj')
   TargetAdd('libp3direct.dll', input='p3motiontrail_config_motiontrail.obj')
   TargetAdd('libp3direct.dll', input='p3motiontrail_cMotionTrail.obj')
@@ -5369,11 +5361,7 @@ if (PkgSkip("DIRECT")==0):
   # These are part of direct.pyd, not libp3direct.dll, because they rely on
   # the Python libraries.  If a C++ user needs these modules, we can move them
   # back and filter out the Python-specific code.
-  PyTargetAdd('direct.pyd', input='p3dcparser_composite1.obj')
-  PyTargetAdd('direct.pyd', input='p3dcparser_composite2.obj')
-  PyTargetAdd('direct.pyd', input='p3dcparser_dcParser.obj')
-  PyTargetAdd('direct.pyd', input='p3dcparser_dcLexer.obj')
-  PyTargetAdd('direct.pyd', input='p3distributed_config_distributed.obj')
+  PyTargetAdd('direct.pyd', input='p3dcparser_ext_composite.obj')
   PyTargetAdd('direct.pyd', input='p3distributed_cConnectionRepository.obj')
   PyTargetAdd('direct.pyd', input='p3distributed_cDistributedSmoothNodeBase.obj')
 
@@ -5387,18 +5375,13 @@ if (PkgSkip("DIRECT")==0):
 # DIRECTORY: direct/src/dcparse/
 #
 
-if (PkgSkip("PYTHON")==0 and PkgSkip("DIRECT")==0 and not RTDIST and not RUNTIME):
+if (PkgSkip("DIRECT")==0 and not RTDIST and not RUNTIME):
   OPTS=['DIR:direct/src/dcparse', 'DIR:direct/src/dcparser', 'WITHINPANDA', 'ADVAPI']
-  PyTargetAdd('dcparse_dcparse.obj', opts=OPTS, input='dcparse.cxx')
-  PyTargetAdd('p3dcparse.exe', input='p3dcparser_composite1.obj')
-  PyTargetAdd('p3dcparse.exe', input='p3dcparser_composite2.obj')
-  PyTargetAdd('p3dcparse.exe', input='p3dcparser_dcParser.obj')
-  PyTargetAdd('p3dcparse.exe', input='p3dcparser_dcLexer.obj')
-  PyTargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj')
-  PyTargetAdd('p3dcparse.exe', input='libp3direct.dll')
-  PyTargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS)
-  PyTargetAdd('p3dcparse.exe', input='libp3pystub.lib')
-  PyTargetAdd('p3dcparse.exe', opts=['ADVAPI'])
+  TargetAdd('dcparse_dcparse.obj', opts=OPTS, input='dcparse.cxx')
+  TargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj')
+  TargetAdd('p3dcparse.exe', input='libp3direct.dll')
+  TargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS)
+  TargetAdd('p3dcparse.exe', opts=['ADVAPI'])
 
 #
 # DIRECTORY: direct/src/plugin/
@@ -5602,7 +5585,6 @@ if (RUNTIME):
   TargetAdd('panda3d.exe', input='libpandaexpress.dll')
   TargetAdd('panda3d.exe', input='libp3dtoolconfig.dll')
   TargetAdd('panda3d.exe', input='libp3dtool.dll')
-  #TargetAdd('panda3d.exe', input='libp3pystub.lib')
   TargetAdd('panda3d.exe', input='libp3tinyxml.ilb')
   TargetAdd('panda3d.exe', opts=['NOICON', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'WINSHELL', 'ADVAPI', 'WINSOCK2', 'WINOLE', 'CARBON'])
 
@@ -5616,7 +5598,6 @@ if (RUNTIME):
     TargetAdd('Panda3D.app', input='libpandaexpress.dll')
     TargetAdd('Panda3D.app', input='libp3dtoolconfig.dll')
     TargetAdd('Panda3D.app', input='libp3dtool.dll')
-    #TargetAdd('Panda3D.app', input='libp3pystub.lib')
     TargetAdd('Panda3D.app', input='libp3tinyxml.ilb')
     TargetAdd('Panda3D.app', input='panda3d_mac.plist', ipath=OPTS)
     TargetAdd('Panda3D.app', input='models/plugin_images/panda3d.icns')
@@ -5631,18 +5612,16 @@ if (RUNTIME):
     TargetAdd('panda3dw.exe', input='libpandaexpress.dll')
     TargetAdd('panda3dw.exe', input='libp3dtoolconfig.dll')
     TargetAdd('panda3dw.exe', input='libp3dtool.dll')
-    #TargetAdd('panda3dw.exe', input='libp3pystub.lib')
     TargetAdd('panda3dw.exe', input='libp3tinyxml.ilb')
     TargetAdd('panda3dw.exe', opts=['SUBSYSTEM:WINDOWS', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'WINSHELL', 'ADVAPI', 'WINSOCK2', 'WINOLE', 'CARBON'])
 
 if (RTDIST):
-  OPTS=['BUILDING:P3D_PLUGIN', 'DIR:direct/src/plugin_standalone', 'DIR:direct/src/plugin', 'DIR:dtool/src/dtoolbase', 'DIR:dtool/src/dtoolutil', 'DIR:dtool/src/pystub', 'DIR:dtool/src/prc', 'DIR:dtool/src/dconfig', 'DIR:panda/src/express', 'DIR:panda/src/downloader', 'RUNTIME', 'P3DEMBED', 'OPENSSL', 'ZLIB']
+  OPTS=['BUILDING:P3D_PLUGIN', 'DIR:direct/src/plugin_standalone', 'DIR:direct/src/plugin', 'DIR:dtool/src/dtoolbase', 'DIR:dtool/src/dtoolutil', 'DIR:dtool/src/prc', 'DIR:dtool/src/dconfig', 'DIR:panda/src/express', 'DIR:panda/src/downloader', 'RUNTIME', 'P3DEMBED', 'OPENSSL', 'ZLIB']
   # This is arguably a big fat ugly hack, but doing it otherwise would complicate the build process considerably.
   DefSymbol("P3DEMBED", "LINK_ALL_STATIC", "")
   TargetAdd('plugin_standalone_panda3dBase.obj', opts=OPTS, input='panda3dBase.cxx')
   TargetAdd('plugin_standalone_p3dEmbedMain.obj', opts=OPTS, input='p3dEmbedMain.cxx')
   TargetAdd('plugin_standalone_p3dEmbed.obj', opts=OPTS, input='p3dEmbed.cxx')
-  #TargetAdd('plugin_standalone_pystub.obj', opts=OPTS, input='pystub.cxx')
   TargetAdd('plugin_standalone_dtoolbase_composite1.obj', opts=OPTS, input='p3dtoolbase_composite1.cxx')
   TargetAdd('plugin_standalone_dtoolbase_composite2.obj', opts=OPTS, input='p3dtoolbase_composite2.cxx')
   TargetAdd('plugin_standalone_lookup3.obj', opts=OPTS, input='lookup3.c')
@@ -5660,7 +5639,6 @@ if (RTDIST):
   TargetAdd('p3dembed.exe', input='plugin_standalone_panda3dBase.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_p3dEmbedMain.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_p3dEmbed.obj')
-  #TargetAdd('p3dembed.exe', input='plugin_standalone_pystub.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolbase_composite1.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolbase_composite2.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_lookup3.obj')
@@ -5690,7 +5668,6 @@ if (RTDIST):
     TargetAdd('p3dembedw.exe', input='plugin_standalone_panda3dBase.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_p3dEmbedWinMain.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_p3dEmbed.obj')
-    #TargetAdd('p3dembedw.exe', input='plugin_standalone_pystub.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_dtoolbase_composite1.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_dtoolbase_composite2.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_lookup3.obj')
@@ -6445,8 +6422,6 @@ for VER in MAYAVERSIONS:
     TargetAdd('mayaeggimport'+VNUM+'.mll', input='mayaeggimport'+VNUM+'_mayaeggimport.obj')
     TargetAdd('mayaeggimport'+VNUM+'.mll', input='libpandaegg.dll')
     TargetAdd('mayaeggimport'+VNUM+'.mll', input=COMMON_PANDA_LIBS)
-    #if GetTarget() == 'windows':
-    #  TargetAdd('mayaeggimport'+VNUM+'.mll', input='libp3pystub.lib')
     TargetAdd('mayaeggimport'+VNUM+'.mll', opts=['ADVAPI', VER])
 
     TargetAdd('mayaloader'+VNUM+'_config_mayaloader.obj', opts=OPTS, input='config_mayaloader.cxx')

+ 0 - 4
makepanda/makepanda.vcproj

@@ -361,10 +361,6 @@
 				<File RelativePath="..\dtool\src\dtoolbase\typedObject.h"></File>
 				<File RelativePath="..\dtool\src\dtoolbase\mutexWin32Impl.cxx"></File>
 			</Filter>
-			<Filter Name="pystub">
-				<File RelativePath="..\dtool\src\pystub\pystub.h"></File>
-				<File RelativePath="..\dtool\src\pystub\pystub.cxx"></File>
-			</Filter>
 			<Filter Name="dtoolutil">
 				<File RelativePath="..\dtool\src\dtoolutil\pandaFileStreamBuf.h"></File>
 				<File RelativePath="..\dtool\src\dtoolutil\vector_src.h"></File>

+ 5 - 1
makepanda/makepandacore.py

@@ -105,6 +105,7 @@ MAYAVERSIONINFO = [("MAYA6",   "6.0"),
                    ("MAYA20165","2016.5"),
                    ("MAYA2017","2017"),
                    ("MAYA2018","2018"),
+                   ("MAYA2019","2019"),
 ]
 
 MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"),
@@ -2788,7 +2789,7 @@ def SetupVisualStudioEnviron():
 
     # Targeting the 7.1 SDK (which is the only way to have Windows XP support)
     # with Visual Studio 2015 requires use of the Universal CRT.
-    if winsdk_ver == '7.1' and SDK["VISUALSTUDIO_VERSION"] >= (14,0):
+    if winsdk_ver in ('7.1', '7.1A') and SDK["VISUALSTUDIO_VERSION"] >= (14,0):
         win_kit = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
 
         # Fallback in case we can't read the registry.
@@ -3392,6 +3393,9 @@ def GetPythonABI():
 
     soabi = 'cpython-%d%d' % (sys.version_info[:2])
 
+    if sys.version_info >= (3, 8):
+        return soabi
+
     debug_flag = sysconfig.get_config_var('Py_DEBUG')
     if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag:
         soabi += 'd'

+ 14 - 12
makepanda/makewheel.py

@@ -1,10 +1,5 @@
 """
 Generates a wheel (.whl) file from the output of makepanda.
-
-Since the wheel requires special linking, this will only work if compiled with
-the `--wheel` parameter.
-
-Please keep this file work with Panda3D 1.9 until that reaches EOL.
 """
 from __future__ import print_function, unicode_literals
 from distutils.util import get_platform
@@ -20,7 +15,7 @@ import tempfile
 import subprocess
 from distutils.sysconfig import get_config_var
 from optparse import OptionParser
-from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose, GetMetadataValue
+from makepandacore import ColorText, LocateBinary, GetExtensionSuffix, SetVerbose, GetVerbose, GetMetadataValue
 from base64 import urlsafe_b64encode
 
 
@@ -34,6 +29,9 @@ def get_abi_tag():
 
     soabi = 'cp%d%d' % (sys.version_info[:2])
 
+    if sys.version_info >= (3, 8):
+        return soabi
+
     debug_flag = get_config_var('Py_DEBUG')
     if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag:
         soabi += 'd'
@@ -118,6 +116,8 @@ Root-Is-Purelib: false
 Tag: {0}-{1}-{2}
 """
 
+PROJECT_URLS = dict([line.split('=', 1) for line in GetMetadataValue('project_urls').strip().splitlines()])
+
 METADATA = {
     "license": GetMetadataValue('license'),
     "name": GetMetadataValue('name'),
@@ -126,9 +126,7 @@ METADATA = {
     "summary": GetMetadataValue('description'),
     "extensions": {
         "python.details": {
-            "project_urls": {
-                "Home": GetMetadataValue('url'),
-            },
+            "project_urls": dict(PROJECT_URLS, Home=GetMetadataValue('url')),
             "document_names": {
                 "license": "LICENSE.txt"
             },
@@ -173,15 +171,18 @@ questions.
 PANDA3D_TOOLS_INIT = """import os, sys
 import panda3d
 
+dir = os.path.dirname(panda3d.__file__)
+del panda3d
+
 if sys.platform in ('win32', 'cygwin'):
     path_var = 'PATH'
+    if hasattr(os, 'add_dll_directory'):
+        os.add_dll_directory(dir)
 elif sys.platform == 'darwin':
     path_var = 'DYLD_LIBRARY_PATH'
 else:
     path_var = 'LD_LIBRARY_PATH'
 
-dir = os.path.dirname(panda3d.__file__)
-del panda3d
 if not os.environ.get(path_var):
     os.environ[path_var] = dir
 else:
@@ -565,6 +566,7 @@ def makewheel(version, output_dir, platform=None):
         "Summary: {summary}\n" \
         "License: {license}\n".format(**METADATA),
         "Home-page: {0}\n".format(homepage),
+    ] + ["Project-URL: {0}, {1}\n".format(*url) for url in PROJECT_URLS.items()] + [
         "Author: {0}\n".format(author),
         "Author-email: {0}\n".format(email),
         "Platform: {0}\n".format(platform),
@@ -728,7 +730,7 @@ if __debug__:
 
 
 if __name__ == "__main__":
-    version = ParsePandaVersion("dtool/PandaVersion.pp")
+    version = GetMetadataValue('version')
 
     parser = OptionParser()
     parser.add_option('', '--version', dest = 'version', help = 'Panda3D version number (default: %s)' % (version), default = version)

+ 8 - 3
makepanda/test_wheel.py

@@ -19,9 +19,6 @@ def test_wheel(wheel, verbose=False):
     print("Setting up virtual environment in {0}".format(envdir))
     sys.stdout.flush()
 
-    # Make sure pip is up-to-date first.
-    subprocess.call([sys.executable, "-B", "-m", "pip", "install", "-U", "pip"])
-
     # Create a virtualenv.
     if sys.version_info >= (3, 0):
         subprocess.call([sys.executable, "-B", "-m", "venv", "--clear", envdir])
@@ -39,6 +36,14 @@ def test_wheel(wheel, verbose=False):
         shutil.rmtree(envdir)
         sys.exit(1)
 
+    # Temp hack to patch issue pypa/pip#6885 in pip 19.2.2 and Python 3.8.
+    if sys.platform == "win32" and "-cp38-cp38-" in wheel and os.path.isdir(os.path.join(envdir, "Lib", "site-packages", "pip-19.2.2.dist-info")):
+        pep425tags = os.path.join(envdir, "Lib", "site-packages", "pip", "_internal", "pep425tags.py")
+        if os.path.isfile(pep425tags):
+            data = open(pep425tags, "r").read()
+            data = data.replace(" m = 'm'\n", " m = ''\n")
+            open(pep425tags, "w").write(data)
+
     # Install pytest into the environment, as well as our wheel.
     if subprocess.call([python, "-m", "pip", "install", "pytest", wheel]) != 0:
         shutil.rmtree(envdir)

+ 0 - 8
panda/src/audio/audioManager.cxx

@@ -325,11 +325,3 @@ void AudioManager::
 write(std::ostream &out) const {
   out << (*this) << "\n";
 }
-
-/**
- * For use only with Miles.
- */
-void AudioManager::
-set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2, LVecBase3 *speaker3, LVecBase3 *speaker4, LVecBase3 *speaker5, LVecBase3 *speaker6, LVecBase3 *speaker7, LVecBase3 *speaker8, LVecBase3 *speaker9) {
-  // intentionally blank
-}

+ 0 - 3
panda/src/audio/audioManager.h

@@ -178,9 +178,6 @@ PUBLISHED:
   virtual void output(std::ostream &out) const;
   virtual void write(std::ostream &out) const;
 
-  // set_speaker_configuration is a Miles only method.
-  virtual void set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2=nullptr, LVecBase3 *speaker3=nullptr, LVecBase3 *speaker4=nullptr, LVecBase3 *speaker5=nullptr, LVecBase3 *speaker6=nullptr, LVecBase3 *speaker7=nullptr, LVecBase3 *speaker8=nullptr, LVecBase3 *speaker9=nullptr);
-
 public:
   static void register_AudioManager_creator(Create_AudioManager_proc* proc);
 

+ 0 - 17
panda/src/audio/audioSound.cxx

@@ -83,23 +83,6 @@ set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat cente
     // intentionally blank
 }
 
-/**
- * For use only with Miles.
- */
-PN_stdfloat AudioSound::
-get_speaker_level(int index) {
-  // intentionally blank
-  return 0.0;
-}
-
-/**
- * For use only with Miles.
- */
-void AudioSound::
-set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2, PN_stdfloat level3, PN_stdfloat level4, PN_stdfloat level5, PN_stdfloat level6, PN_stdfloat level7, PN_stdfloat level8, PN_stdfloat level9) {
-  // intentionally blank
-}
-
 /**
  * Configure the local DSP filter chain.
  *

+ 1 - 8
panda/src/audio/audioSound.h

@@ -104,17 +104,10 @@ PUBLISHED:
   virtual void set_3d_max_distance(PN_stdfloat dist);
   virtual PN_stdfloat get_3d_max_distance() const;
 
-  // *_speaker_mix and *_speaker_level(s) serve the same purpose.
-  // *_speaker_mix is for use with FMOD. *_speaker_level(s) is for use with
-  // Miles.  Both interfaces exist because of a significant difference in the
-  // two APIs.  Hopefully the difference can be reconciled into a single
-  // interface at some point.
+  // *_speaker_mix is for use with FMOD.
   virtual PN_stdfloat get_speaker_mix(int speaker);
   virtual void set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat center, PN_stdfloat sub, PN_stdfloat backleft, PN_stdfloat backright, PN_stdfloat sideleft, PN_stdfloat  sideright);
 
-  virtual PN_stdfloat get_speaker_level(int index);
-  virtual void set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2=-1.0f, PN_stdfloat level3=-1.0f, PN_stdfloat level4=-1.0f, PN_stdfloat level5=-1.0f, PN_stdfloat level6=-1.0f, PN_stdfloat level7=-1.0f, PN_stdfloat level8=-1.0f, PN_stdfloat level9=-1.0f);
-
   virtual int get_priority();
   virtual void set_priority(int priority);
 

+ 0 - 23
panda/src/audio/config_audio.cxx

@@ -100,11 +100,6 @@ ConfigVariableEnum<FmodSpeakerMode> fmod_speaker_mode
           "Options: raw, mono, stereo, quad, surround, 5.1 and 7.1. "));
 
 
-// Config variables for Miles:
-
-ConfigVariableBool audio_software_midi
-("audio-software-midi", true);
-
 ConfigVariableFilename audio_dls_file
 ("audio-dls-file", Filename(),
  PRC_DESC("Specifies a DLS file that defines an instrument set to load "
@@ -113,24 +108,6 @@ ConfigVariableFilename audio_dls_file
           "one is available; the likely success of this depends on the "
           "operating system."));
 
-ConfigVariableBool audio_play_midi
-("audio-play-midi", true);
-
-ConfigVariableBool audio_play_wave
-("audio-play-wave", true);
-
-ConfigVariableBool audio_play_mp3
-("audio-play-mp3", true);
-
-ConfigVariableInt audio_output_rate
-("audio-output-rate", 22050);
-
-ConfigVariableInt audio_output_bits
-("audio-output-bits", 16);
-
-ConfigVariableInt audio_output_channels
-("audio-output-channels", 2);
-
 ConfigureFn(config_audio) {
   FilterProperties::init_type();
   AudioLoadRequest::init_type();

+ 1 - 13
panda/src/audio/config_audio.h

@@ -58,6 +58,7 @@ EXPCL_PANDA_AUDIO std::istream &operator >> (std::istream &in, FmodSpeakerMode &
 extern EXPCL_PANDA_AUDIO ConfigVariableInt fmod_number_of_sound_channels;
 extern EXPCL_PANDA_AUDIO ConfigVariableBool fmod_use_surround_sound;
 extern EXPCL_PANDA_AUDIO ConfigVariableEnum<FmodSpeakerMode> fmod_speaker_mode;
+extern EXPCL_PANDA_AUDIO ConfigVariableFilename audio_dls_file;
 
 // Config vars for OpenAL:
 
@@ -67,19 +68,6 @@ extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_drop_off_factor;
 extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_buffering_seconds;
 extern EXPCL_PANDA_AUDIO ConfigVariableInt    audio_preload_threshold;
 
-// Config vars for Miles:
-
-extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_software_midi;
-extern EXPCL_PANDA_AUDIO ConfigVariableFilename audio_dls_file;
-extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_play_midi;
-extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_play_wave;
-extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_play_mp3;
-extern EXPCL_PANDA_AUDIO ConfigVariableInt audio_output_rate;
-extern EXPCL_PANDA_AUDIO ConfigVariableInt audio_output_bits;
-extern EXPCL_PANDA_AUDIO ConfigVariableInt audio_output_channels;
-
-
-
 #ifdef NOTIFY_DEBUG //[
   // Non-release build:
   #define audio_debug(msg) \

+ 1 - 1
panda/src/audio/nullAudioManager.h

@@ -21,7 +21,7 @@
 class EXPCL_PANDA_AUDIO NullAudioManager : public AudioManager {
   // All of these methods are stubbed out to some degree.  If you're looking
   // for a starting place for a new AudioManager, please consider looking at
-  // the milesAudioManager.
+  // the openalAudioManager.
 
 public:
   NullAudioManager();

+ 1 - 1
panda/src/audio/nullAudioSound.h

@@ -23,7 +23,7 @@
 class EXPCL_PANDA_AUDIO NullAudioSound : public AudioSound {
   // All of these methods are stubbed out to some degree.  If you're looking
   // for a starting place for a new AudioManager, please consider looking at
-  // the milesAudioManager.
+  // the openalAudioManager.
 
 public:
   ~NullAudioSound();

+ 0 - 102
panda/src/audiotraits/config_milesAudio.cxx

@@ -1,102 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file config_milesAudio.cxx
- * @author skyler
- */
-
-#include "pandabase.h"
-#ifdef HAVE_RAD_MSS //[
-
-#include "config_milesAudio.h"
-#include "milesAudioManager.h"
-#include "milesAudioSound.h"
-#include "milesAudioSample.h"
-#include "milesAudioSequence.h"
-#include "milesAudioStream.h"
-#include "pandaSystem.h"
-#include "dconfig.h"
-
-#if !defined(CPPPARSER) && !defined(LINK_ALL_STATIC) && !defined(BUILDING_MILES_AUDIO)
-  #error Buildsystem error: BUILDING_MILES_AUDIO not defined
-#endif
-
-ConfigureDef(config_milesAudio);
-NotifyCategoryDef(milesAudio, ":audio");
-
-ConfigureFn(config_milesAudio) {
-  init_libMilesAudio();
-}
-
-ConfigVariableBool miles_audio_force_midi_reset
-("audio-force-midi-reset", true);
-
-ConfigVariableInt miles_audio_expand_mp3_threshold
-("miles-audio-expand-mp3-threshold", 16384,
- PRC_DESC("This enables a Miles workaround in which small MP3 files are "
-          "expanded in-memory at load time into WAV format, which can "
-          "work around problems with Miles being unable to correctly "
-          "report the length of, or seek within, a variable bit-rate encoded "
-          "MP3 file.  Any MP3 file whose length in bytes is less than "
-          "this value will be expanded.  This only applies to files "
-          "within the miles-audio-preload-threshold."));
-
-ConfigVariableInt miles_audio_preload_threshold
-("miles-audio-preload-threshold", -1,
- PRC_DESC("This should be no smaller "
-          "than miles-audio-expand-mp3-threshold.  Files that are smaller "
-          "than this number of bytes will be preloaded and kept "
-          "resident in memory, while files that are this size or larger "
-          "will be streamed from disk.  Set this to -1 to preload "
-          "every file."));
-
-ConfigVariableBool miles_audio_panda_threads
-("miles-audio-panda-threads", true,
- PRC_DESC("Set this true to service Miles background audio via Panda's "
-          "threading interface, instead of Miles' built-in threading "
-          "interface.  This gives Panda more control over the threading, "
-          "and ensures better lock protection within Panda.  This has "
-          "no meaning unless Panda is compiled with thread support."));
-
-/**
- * Initializes the library.  This must be called at least once before any of
- * the functions or classes in this library can be used.  Normally it will be
- * called by the static initializers and need not be called explicitly, but
- * special cases exist.
- */
-void
-init_libMilesAudio() {
-  static bool initialized = false;
-  if (initialized) {
-    return;
-  }
-  initialized = true;
-  MilesAudioManager::init_type();
-  MilesAudioSound::init_type();
-  MilesAudioSample::init_type();
-  MilesAudioSequence::init_type();
-  MilesAudioStream::init_type();
-
-  PandaSystem *ps = PandaSystem::get_global_ptr();
-  ps->add_system("Miles");
-  ps->add_system("audio");
-  ps->set_system_tag("audio", "implementation", "Miles");
-}
-
-/**
- * This function is called when the dynamic library is loaded; it should
- * return the Create_AudioManager function appropriate to create a
- * MilesAudioManager.
- */
-Create_AudioManager_proc *
-get_audio_manager_func_miles_audio() {
-  init_libMilesAudio();
-  return &Create_MilesAudioManager;
-}
-
-#endif //]

+ 0 - 37
panda/src/audiotraits/config_milesAudio.h

@@ -1,37 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file config_milesAudio.h
- * @author skyler
- */
-
-#ifndef CONFIG_MILESAUDIO_H
-#define CONFIG_MILESAUDIO_H
-
-#include "pandabase.h"
-
-#ifdef HAVE_RAD_MSS //[
-#include "notifyCategoryProxy.h"
-#include "configVariableBool.h"
-#include "configVariableInt.h"
-#include "dconfig.h"
-#include "audioManager.h"
-
-ConfigureDecl(config_milesAudio, EXPCL_MILES_AUDIO, EXPTP_MILES_AUDIO);
-NotifyCategoryDecl(milesAudio, EXPCL_MILES_AUDIO, EXPTP_MILES_AUDIO);
-
-extern ConfigVariableBool miles_audio_force_midi_reset;
-extern ConfigVariableInt miles_audio_expand_mp3_threshold;
-extern ConfigVariableInt miles_audio_preload_threshold;
-extern ConfigVariableBool miles_audio_panda_threads;
-
-extern EXPCL_MILES_AUDIO void init_libMilesAudio();
-extern "C" EXPCL_MILES_AUDIO Create_AudioManager_proc *get_audio_manager_func_miles_audio();
-#endif //]
-
-#endif // CONFIG_MILESAUDIO_H

+ 0 - 39
panda/src/audiotraits/globalMilesManager.I

@@ -1,39 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file globalMilesManager.I
- * @author drose
- * @date 2007-07-30
- */
-
-/**
- * Returns true if the Miles Sound System is open (and active), false if it is
- * not.
- */
-INLINE bool GlobalMilesManager::
-is_open() const {
-  return _is_open;
-}
-
-/**
- * Returns the number of sample handles that have been allocated.
- */
-INLINE int GlobalMilesManager::
-get_num_samples() const {
-  LightMutexHolder holder(_samples_lock);
-  return _samples.size();
-}
-
-/**
- * Returns the number of sequence handles that have been allocated.
- */
-INLINE int GlobalMilesManager::
-get_num_sequences() const {
-  LightMutexHolder holder(_sequences_lock);
-  return _sequences.size();
-}

+ 0 - 446
panda/src/audiotraits/globalMilesManager.cxx

@@ -1,446 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file globalMilesManager.cxx
- * @author drose
- * @date 2007-07-26
- */
-
-#include "globalMilesManager.h"
-
-#ifdef HAVE_RAD_MSS //[
-
-#include "lightMutexHolder.h"
-#include "milesAudioManager.h"
-#include "milesAudioSample.h"
-#include "milesAudioSequence.h"
-
-#ifdef WIN32
-// For midiOutReset()
-#include <windows.h>
-#include <mmsystem.h>
-#endif
-
-using std::istream;
-using std::string;
-
-GlobalMilesManager *GlobalMilesManager::_global_ptr;
-
-/**
- *
- */
-GlobalMilesManager::
-GlobalMilesManager() :
-  _managers_lock("GlobalMilesManager::_managers_lock"),
-  _samples_lock("GlobalMilesManager::_samples_lock"),
-  _sequences_lock("GlobalMilesManager::_sequences_lock")
-{
-  _digital_driver = 0;
-  _midi_driver = 0;
-  _dls_device = 0;
-  _dls_file = 0;
-  _is_open = false;
-}
-
-/**
- * Records a new MilesAudioManager in the world.  This will open the Miles API
- * when the first audio manager is added.
- */
-void GlobalMilesManager::
-add_manager(MilesAudioManager *manager) {
-  LightMutexHolder holder(_managers_lock);
-  _managers.insert(manager);
-  if (!_is_open) {
-    open_api();
-  }
-}
-
-/**
- * Records that a MilesAudioManager is destructing.  This will clsoe the Miles
- * API when the last audio manager is removed.
- */
-void GlobalMilesManager::
-remove_manager(MilesAudioManager *manager) {
-  LightMutexHolder holder(_managers_lock);
-  _managers.erase(manager);
-  if (_managers.empty() && _is_open) {
-    close_api();
-  }
-}
-
-/**
- * Calls cleanup() on all MilesAudioManagers, to cause a clean shutdown.
- */
-void GlobalMilesManager::
-cleanup() {
-  LightMutexHolder holder(_managers_lock);
-  Managers::iterator mi;
-  for (mi = _managers.begin(); mi != _managers.end(); ++mi) {
-    (*mi)->cleanup();
-  }
-}
-
-/**
- * Gets a sample handle from the global pool for the digital output device, to
- * be used with the indicated AudioSound.
- *
- * If successful, sets the sample handle and the index (which should later be
- * used to release the sample) and returns true.  If unsuccessful (because
- * there are no more available handles), returns false.
- *
- * This is a very limited resource; you should only get a sample just before
- * playing a sound.
- */
-bool GlobalMilesManager::
-get_sample(HSAMPLE &sample, size_t &index, MilesAudioSample *sound) {
-  LightMutexHolder holder(_samples_lock);
-
-  for (size_t i = 0; i < _samples.size(); ++i) {
-    SampleData &smp = _samples[i];
-    if (AIL_sample_status(smp._sample) == SMP_DONE) {
-      if (smp._sound != nullptr) {
-        // Tell the last sound that was using this sample that it's done now.
-        smp._sound->internal_stop();
-      }
-      smp._sound = sound;
-      sample = smp._sample;
-      index = i;
-      return true;
-    }
-  }
-
-  // No more already-allocated samples; get a new one from the system.
-  sample = AIL_allocate_sample_handle(_digital_driver);
-  if (sample == 0) {
-    return false;
-  }
-
-  AIL_init_sample(sample, DIG_F_STEREO_16, 0);
-  index = _samples.size();
-
-  SampleData smp;
-  smp._sound = sound;
-  smp._sample = sample;
-  _samples.push_back(smp);
-  return true;
-}
-
-/**
- * Indicates that the indicated AudioSound no longer needs this sample.
- */
-void GlobalMilesManager::
-release_sample(size_t index, MilesAudioSample *sound) {
-  LightMutexHolder holder(_samples_lock);
-  nassertv(index < _samples.size());
-
-  SampleData &smp = _samples[index];
-  if (smp._sound == sound) {
-    smp._sound = nullptr;
-  }
-}
-
-/**
- * Gets a sequence handle from the global pool for the digital output device,
- * to be used with the indicated AudioSound.
- *
- * If successful, sets the sequence handle and the index (which should later
- * be used to release the sequence) and returns true.  If unsuccessful
- * (because there are no more available handles), returns false.
- *
- * This is a very limited resource; you should only get a sequence just before
- * playing a sound.
- */
-bool GlobalMilesManager::
-get_sequence(HSEQUENCE &sequence, size_t &index, MilesAudioSequence *sound) {
-  LightMutexHolder holder(_sequences_lock);
-
-  for (size_t i = 0; i < _sequences.size(); ++i) {
-    SequenceData &seq = _sequences[i];
-    if (AIL_sequence_status(seq._sequence) == SEQ_DONE) {
-      if (seq._sound != nullptr) {
-        // Tell the last sound that was using this sequence that it's done
-        // now.
-        seq._sound->internal_stop();
-      }
-      seq._sound = sound;
-      sequence = seq._sequence;
-      index = i;
-      return true;
-    }
-  }
-
-  // No more already-allocated sequences; get a new one from the system.
-  sequence = AIL_allocate_sequence_handle(_midi_driver);
-  if (sequence == 0) {
-    return false;
-  }
-
-  index = _sequences.size();
-
-  SequenceData seq;
-  seq._sound = sound;
-  seq._sequence = sequence;
-  _sequences.push_back(seq);
-  return true;
-}
-
-/**
- * Indicates that the indicated AudioSound no longer needs this sequence.
- */
-void GlobalMilesManager::
-release_sequence(size_t index, MilesAudioSequence *sound) {
-  LightMutexHolder holder(_sequences_lock);
-  nassertv(index < _sequences.size());
-
-  SequenceData &seq = _sequences[index];
-  if (seq._sound == sound) {
-    seq._sound = nullptr;
-  }
-}
-
-/**
- * Sometimes Miles seems to leave midi notes hanging, even after stop is
- * called, so call this method to perform an explicit reset using winMM.dll
- * calls, just to ensure silence.
- */
-void GlobalMilesManager::
-force_midi_reset() {
-  if (!miles_audio_force_midi_reset) {
-    audio_debug("MilesAudioManager::skipping force_midi_reset");
-    return;
-  }
-  audio_debug("MilesAudioManager::force_midi_reset");
-
-#ifdef WIN32
-  if ((_midi_driver!=nullptr) && (_midi_driver->deviceid != MIDI_nullptr_DRIVER) && (_midi_driver->hMidiOut != nullptr)) {
-    audio_debug("MilesAudioManager::calling midiOutReset");
-    midiOutReset(_midi_driver->hMidiOut);
-  }
-#endif
-}
-
-/**
- * Returns the pointer to the one GlobalMilesManager object.
- */
-GlobalMilesManager *GlobalMilesManager::
-get_global_ptr() {
-  if (_global_ptr == nullptr) {
-    _global_ptr = new GlobalMilesManager;
-  }
-  return _global_ptr;
-}
-
-/**
- * Called internally to initialize the Miles API.
- */
-void GlobalMilesManager::
-open_api() {
-  audio_debug("GlobalMilesManager::open_api()")
-  nassertv(!_is_open);
-
-  bool use_digital = (audio_play_wave || audio_play_mp3);
-  if (audio_play_midi && audio_software_midi) {
-    use_digital = true;
-  }
-
-#ifdef IS_OSX
-  audio_software_midi = true;
-#endif
-
-  audio_debug("  use_digital="<<use_digital);
-  audio_debug("  audio_play_midi="<<audio_play_midi);
-  audio_debug("  audio_software_midi="<<audio_software_midi);
-  audio_debug("  audio_output_rate="<<audio_output_rate);
-  audio_debug("  audio_output_bits="<<audio_output_bits);
-  audio_debug("  audio_output_channels="<<audio_output_channels);
-  audio_debug("  audio_software_midi="<<audio_software_midi);
-
-#if !defined(NDEBUG) && defined(AIL_MSS_version) //[
-  char version[8];
-  AIL_MSS_version(version, 8);
-  audio_debug("  Mss32.dll Version: "<<version);
-#endif //]
-
-  if (!AIL_startup()) {
-    milesAudio_cat.warning()
-      << "Miles Sound System already initialized!\n";
-  }
-
-  AIL_set_file_callbacks(open_callback, close_callback,
-                         seek_callback, read_callback);
-
-  if (use_digital) {
-    _digital_driver =
-      AIL_open_digital_driver(audio_output_rate, audio_output_bits,
-                              audio_output_channels, 0);
-  }
-
-  if (audio_play_midi) {
-    if (audio_software_midi) {
-      _midi_driver = AIL_open_XMIDI_driver(AIL_OPEN_XMIDI_NULL_DRIVER);
-
-      // Load the downloadable sounds file:
-      _dls_device = AIL_DLS_open(_midi_driver, _digital_driver, nullptr, 0,
-                                 audio_output_rate, audio_output_bits,
-                                 audio_output_channels);
-
-      Filename dls_pathname = AudioManager::get_dls_pathname();
-
-      VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-      vfs->resolve_filename(dls_pathname, get_model_path());
-
-      _dls_data.clear();
-      PT(VirtualFile) file = vfs->get_file(dls_pathname);
-      if (file == nullptr) {
-        milesAudio_cat.warning()
-          << "DLS file does not exist: " << dls_pathname << "\n";
-
-      } else if (!file->read_file(_dls_data, true)) {
-        milesAudio_cat.warning()
-          << "Could not read DLS file: " << dls_pathname << "\n";
-
-      } else if (_dls_data.empty()) {
-        milesAudio_cat.warning()
-          << "DLS file is empty: " << dls_pathname << "\n";
-
-      } else {
-        _dls_file = AIL_DLS_load_memory(_dls_device, &_dls_data[0], 0);
-      }
-
-      if (_dls_file == 0) {
-        audio_error("  Could not get DLS file, switching to hardware MIDI.");
-        AIL_DLS_close(_dls_device, 0);
-        _dls_device = 0;
-        AIL_close_XMIDI_driver(_midi_driver);
-        _midi_driver = AIL_open_XMIDI_driver(0);
-
-      } else {
-        audio_info("  using Miles software midi");
-      }
-    } else {
-      _midi_driver = AIL_open_XMIDI_driver(0);
-      audio_info("  using Miles hardware midi");
-    }
-  }
-
-  _is_open = true;
-}
-
-/**
- * Called internally to shut down the Miles API.
- */
-void GlobalMilesManager::
-close_api() {
-  audio_debug("GlobalMilesManager::close_api()")
-  nassertv(_is_open);
-
-  Samples::iterator si;
-  for (si = _samples.begin(); si != _samples.end(); ++si) {
-    SampleData &smp = (*si);
-    AIL_release_sample_handle(smp._sample);
-  }
-  _samples.clear();
-
-  Sequences::iterator qi;
-  for (qi = _sequences.begin(); qi != _sequences.end(); ++qi) {
-    SequenceData &smp = (*qi);
-    AIL_release_sequence_handle(smp._sequence);
-  }
-  _sequences.clear();
-
-  if (_dls_file != 0) {
-    AIL_DLS_unload(_dls_device, _dls_file);
-    _dls_file = 0;
-  }
-
-  if (_dls_device != 0) {
-    AIL_DLS_close(_dls_device, 0);
-    _dls_device = 0;
-  }
-
-  if (_midi_driver != 0) {
-    AIL_close_XMIDI_driver(_midi_driver);
-    _midi_driver = 0;
-  }
-
-  if (_digital_driver != 0) {
-    AIL_close_digital_driver(_digital_driver);
-    _digital_driver = 0;
-  }
-
-  AIL_shutdown();
-
-  _is_open = false;
-}
-
-/**
- * This callback function is given to Miles to handle file I/O via the Panda
- * VFS.  It's only used to implemented streaming audio files, since in all
- * other cases we open files directly.
- */
-U32 AILCALLBACK GlobalMilesManager::
-open_callback(char const *filename, UINTa *file_handle) {
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  istream *strm = vfs->open_read_file(Filename::binary_filename(string(filename)), true);
-  if (strm == nullptr) {
-    // Failure.
-    return 0;
-  }
-  // Success.
-  (*file_handle) = (UINTa)strm;
-  return 1;
-}
-
-/**
- * This callback function is given to Miles to handle file I/O via the Panda
- * VFS.
- */
-void AILCALLBACK GlobalMilesManager::
-close_callback(UINTa file_handle) {
-  istream *strm = (istream *)file_handle;
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  vfs->close_read_file(strm);
-}
-
-/**
- * This callback function is given to Miles to handle file I/O via the Panda
- * VFS.
- */
-S32 AILCALLBACK GlobalMilesManager::
-seek_callback(UINTa file_handle, S32 offset, U32 type) {
-  istream *strm = (istream *)file_handle;
-  strm->clear();
-  switch (type) {
-  case AIL_FILE_SEEK_BEGIN:
-    strm->seekg(offset, std::ios::beg);
-    break;
-
-  case AIL_FILE_SEEK_CURRENT:
-    strm->seekg(offset, std::ios::cur);
-    break;
-
-  case AIL_FILE_SEEK_END:
-    strm->seekg(offset, std::ios::end);
-    break;
-  }
-
-  return strm->tellg();
-}
-
-/**
- * This callback function is given to Miles to handle file I/O via the Panda
- * VFS.
- */
-U32 AILCALLBACK GlobalMilesManager::
-read_callback(UINTa file_handle, void *buffer, U32 bytes) {
-  istream *strm = (istream *)file_handle;
-  strm->read((char *)buffer, bytes);
-  return strm->gcount();
-}
-
-#endif //]

+ 0 - 117
panda/src/audiotraits/globalMilesManager.h

@@ -1,117 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file globalMilesManager.h
- * @author drose
- * @date 2007-07-26
- */
-
-#ifndef GLOBALMILESMANAGER_H
-#define GLOBALMILESMANAGER_H
-
-#include "pandabase.h"
-#ifdef HAVE_RAD_MSS //[
-
-#include "pset.h"
-#include "lightMutex.h"
-#include "lightMutexHolder.h"
-
-#include <mss.h>
-
-#ifndef UINTa
-#define UINTa U32
-#endif
-
-#ifndef SINTa
-#define SINTa S32
-#endif
-
-class MilesAudioSample;
-class MilesAudioSequence;
-
-/**
- * This is a wrapper around the parts of the Miles API that should only be
- * created once.  This represents the global data common to all
- * MilesAudioManagers.
- */
-class EXPCL_MILES_AUDIO GlobalMilesManager {
-private:
-  GlobalMilesManager();
-
-public:
-  void add_manager(MilesAudioManager *manager);
-  void remove_manager(MilesAudioManager *manager);
-  void cleanup();
-  INLINE bool is_open() const;
-
-  bool get_sample(HSAMPLE &sample, size_t &index, MilesAudioSample *sound);
-  void release_sample(size_t index, MilesAudioSample *sound);
-  INLINE int get_num_samples() const;
-
-  bool get_sequence(HSEQUENCE &sequence, size_t &index, MilesAudioSequence *sound);
-  void release_sequence(size_t index, MilesAudioSequence *sound);
-  INLINE int get_num_sequences() const;
-
-  void force_midi_reset();
-
-  static GlobalMilesManager *get_global_ptr();
-
-public:
-  HDIGDRIVER _digital_driver;
-  HMDIDRIVER _midi_driver;
-
-  // For software MIDI:
-  HDLSDEVICE _dls_device;
-  HDLSFILEID _dls_file;
-  vector_uchar _dls_data;
-
-private:
-  void open_api();
-  void close_api();
-
-  static U32 AILCALLBACK open_callback(char const *filename, UINTa *file_handle);
-  static void AILCALLBACK close_callback(UINTa file_handle);
-  static S32 AILCALLBACK seek_callback(UINTa file_handle, S32 offset, U32 type);
-  static U32 AILCALLBACK read_callback(UINTa file_handle, void *buffer, U32 bytes);
-
-
-private:
-  bool _is_open;
-
-  typedef pset<MilesAudioManager *> Managers;
-  Managers _managers;
-  LightMutex _managers_lock;
-
-  class SampleData {
-  public:
-    HSAMPLE _sample;
-    MilesAudioSample *_sound;
-  };
-
-  typedef pvector<SampleData> Samples;
-  Samples _samples;
-  LightMutex _samples_lock;
-
-  class SequenceData {
-  public:
-    HSEQUENCE _sequence;
-    MilesAudioSequence *_sound;
-  };
-
-  typedef pvector<SequenceData> Sequences;
-  Sequences _sequences;
-  LightMutex _sequences_lock;
-
-  static GlobalMilesManager *_global_ptr;
-};
-
-#include "globalMilesManager.I"
-
-#endif //]
-
-#endif

+ 0 - 1121
panda/src/audiotraits/milesAudioManager.cxx

@@ -1,1121 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file milesAudioManager.cxx
- * @author skyler
- * @date 2001-06-06
- * Prior system by: cary
- */
-
-#include "milesAudioManager.h"
-
-#ifdef HAVE_RAD_MSS //[
-
-#include "milesAudioSound.h"
-#include "milesAudioSample.h"
-#include "milesAudioStream.h"
-#include "globalMilesManager.h"
-#include "config_audio.h"
-#include "config_putil.h"
-#include "config_express.h"
-#include "virtualFileSystem.h"
-#include "nullAudioSound.h"
-#include "string_utils.h"
-#include "mutexHolder.h"
-#include "lightReMutexHolder.h"
-
-#include <algorithm>
-
-using std::string;
-
-
-TypeHandle MilesAudioManager::_type_handle;
-
-AudioManager *Create_MilesAudioManager() {
-  audio_debug("Create_MilesAudioManager()");
-  return new MilesAudioManager();
-}
-
-/**
- * Create an audio manager.  This may open the Miles sound system if there
- * were no other MilesAudioManager instances.  Subsequent managers may use the
- * same Miles resources.
- */
-MilesAudioManager::
-MilesAudioManager() :
-  _lock("MilesAudioManager::_lock"),
-  _streams_lock("MilesAudioManager::_streams_lock"),
-  _streams_cvar(_streams_lock)
-{
-  audio_debug("MilesAudioManager::MilesAudioManager(), this = "
-              << (void *)this);
-  GlobalMilesManager::get_global_ptr()->add_manager(this);
-  audio_debug("  audio_active="<<audio_active);
-  audio_debug("  audio_volume="<<audio_volume);
-  _cleanup_required = true;
-  _active = audio_active;
-  _volume = audio_volume;
-  _play_rate = 1.0f;
-  _cache_limit = audio_cache_limit;
-  _concurrent_sound_limit = 0;
-  _is_valid = true;
-  _hasMidiSounds = false;
-  _sounds_finished = false;
-
-  // We used to hang a call to a force-shutdown function on atexit(), so that
-  // any running sounds (particularly MIDI sounds) would be silenced on exit,
-  // especially a sudden exit triggered by a Python exception.  But that
-  // causes problems because Miles itself also hangs a force-shutdown function
-  // on atexit(), and you can't call AIL_cleanup() twice--that results in a
-  // crash.
-
-  // Nowadays, we provide the AudioManager::shutdown() method instead, which
-  // allows the app to force all sounds to stop cleanly before we get to the
-  // atexit() stack.  In Python, we guarantee that this method will be called
-  // in the sys.exitfunc chain.
-}
-
-/**
- * Clean up this audio manager and possibly release the Miles resources that
- * are reserved by the application (the later happens if this is the last
- * active manager).
- */
-MilesAudioManager::
-~MilesAudioManager() {
-  audio_debug("MilesAudioManager::~MilesAudioManager(), this = "
-              << (void *)this);
-  cleanup();
-  GlobalMilesManager::get_global_ptr()->remove_manager(this);
-
-  audio_debug("MilesAudioManager::~MilesAudioManager() finished");
-}
-
-/**
- * Call this at exit time to shut down the audio system.  This will invalidate
- * all currently-active AudioManagers and AudioSounds in the system.  If you
- * change your mind and want to play sounds again, you will have to recreate
- * all of these objects.
- */
-void MilesAudioManager::
-shutdown() {
-  audio_debug("shutdown() started");
-  GlobalMilesManager::get_global_ptr()->cleanup();
-  audio_debug("shutdown() finished");
-}
-
-/**
- * This is mostly for debugging, but it it could be used to detect errors in a
- * release build if you don't mind the cpu cost.
- */
-bool MilesAudioManager::
-is_valid() {
-  LightReMutexHolder holder(_lock);
-  return do_is_valid();
-}
-
-/**
- *
- */
-PT(AudioSound) MilesAudioManager::
-get_sound(const Filename &file_name, bool, int) {
-  LightReMutexHolder holder(_lock);
-  audio_debug("MilesAudioManager::get_sound(file_name=\""<<file_name<<"\")");
-
-  if (!do_is_valid()) {
-     audio_debug("invalid MilesAudioManager returning NullSound");
-     return get_null_sound();
-  }
-
-  Filename path = file_name;
-
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  vfs->resolve_filename(path, get_model_path());
-  audio_debug("Reading "<<path);
-  audio_debug("  resolved file_name is '"<<path<<"'");
-
-  PT(SoundData) sd;
-  // Get the sound, either from the cache or from a loader:
-  SoundMap::const_iterator si=_sounds.find(path);
-  if (si != _sounds.end()) {
-    // ...found the sound in the cache.
-    sd = (*si).second;
-    audio_debug("  sound found in pool 0x" << (void*)sd);
-
-  } else {
-    // ...the sound was not found in the cachepool.
-    sd = load(path);
-    if (sd != nullptr) {
-      while (_sounds.size() >= (unsigned int)_cache_limit) {
-        uncache_a_sound();
-      }
-      // Put it in the pool: The following is roughly like: _sounds[path] =
-      // sd; But, it gives us an iterator into the map.
-      std::pair<SoundMap::const_iterator, bool> ib
-          = _sounds.insert(SoundMap::value_type(path, sd));
-      if (!ib.second) {
-        // The insert failed.
-        audio_debug("  failed map insert of "<<path);
-        nassertr(do_is_valid(), nullptr);
-        return get_null_sound();
-      }
-      // Set si, so that we can get a reference to the path for the
-      // MilesAudioSound.
-      si=ib.first;
-    }
-  }
-  // Create an AudioSound from the sound:
-  PT(AudioSound) audioSound;
-
-  if (sd != nullptr) {
-    most_recently_used((*si).first);
-    if (sd->_file_type == AILFILETYPE_MIDI ||
-        sd->_file_type == AILFILETYPE_XMIDI ||
-        sd->_file_type == AILFILETYPE_XMIDI_DLS ||
-        sd->_file_type == AILFILETYPE_XMIDI_MLS) {
-      // MIDI file.
-      audioSound = new MilesAudioSequence(this, sd, file_name);
-
-    } else if (!sd->_raw_data.empty()) {
-      // WAV or MP3 file preloaded into memory.
-      audioSound = new MilesAudioSample(this, sd, file_name);
-
-    } else {
-      // WAV or MP3 file streamed from disk.
-      audioSound = new MilesAudioStream(this, file_name, path);
-    }
-
-    audioSound->set_active(_active);
-
-    bool inserted = _sounds_on_loan.insert((MilesAudioSound *)audioSound.p()).second;
-    nassertr(inserted, audioSound);
-
-    _hasMidiSounds |= (file_name.find(".mid")!=string::npos);
-  } else {
-    // Couldn't load the file; just return a NullAudioSound.
-    audioSound = new NullAudioSound;
-  }
-
-  audio_debug("  returning 0x" << (void*)audioSound);
-  nassertr(do_is_valid(), nullptr);
-  return audioSound;
-}
-
-/**
- *
- */
-PT(AudioSound) MilesAudioManager::
-get_sound(MovieAudio *sound, bool, int) {
-  nassert_raise("Miles audio manager does not support MovieAudio sources.");
-  return nullptr;
-}
-
-/**
- *
- */
-void MilesAudioManager::
-uncache_sound(const Filename &file_name) {
-  audio_debug("MilesAudioManager::uncache_sound(file_name=\""
-      <<file_name<<"\")");
-  LightReMutexHolder holder(_lock);
-  nassertv(do_is_valid());
-  Filename path = file_name;
-
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  vfs->resolve_filename(path, get_model_path());
-
-  audio_debug("  path=\""<<path<<"\"");
-  SoundMap::iterator i = _sounds.find(path);
-  if (i != _sounds.end()) {
-    nassertv(_lru.size() > 0);
-    LRU::iterator lru_i = find(_lru.begin(), _lru.end(), &(i->first));
-    nassertv(lru_i != _lru.end());
-    _lru.erase(lru_i);
-    _sounds.erase(i);
-  }
-  nassertv(do_is_valid());
-}
-
-/**
- * Clear out the sound cache.
- */
-void MilesAudioManager::
-clear_cache() {
-  audio_debug("MilesAudioManager::clear_cache()");
-  LightReMutexHolder holder(_lock);
-  do_clear_cache();
-}
-
-/**
- * Set the number of sounds that the cache can hold.
- */
-void MilesAudioManager::
-set_cache_limit(unsigned int count) {
-  LightReMutexHolder holder(_lock);
-
-  audio_debug("MilesAudioManager::set_cache_limit(count="<<count<<")");
-  nassertv(do_is_valid());
-  while (_lru.size() > count) {
-    uncache_a_sound();
-  }
-  _cache_limit=count;
-  nassertv(do_is_valid());
-}
-
-/**
- *
- */
-unsigned int MilesAudioManager::
-get_cache_limit() const {
-  return _cache_limit;
-}
-
-/**
- * set the overall volume
- */
-void MilesAudioManager::
-set_volume(PN_stdfloat volume) {
-  audio_debug("MilesAudioManager::set_volume(volume="<<volume<<")");
-  LightReMutexHolder holder(_lock);
-  if (_volume != volume) {
-    _volume = volume;
-    // Tell our AudioSounds to adjust:
-    AudioSet::iterator i = _sounds_on_loan.begin();
-    for (; i!=_sounds_on_loan.end(); ++i) {
-      (*i)->set_volume((*i)->get_volume());
-    }
-  }
-}
-
-/**
- * get the overall volume
- */
-PN_stdfloat MilesAudioManager::
-get_volume() const {
-  return _volume;
-}
-
-/**
- * set the overall play rate
- */
-void MilesAudioManager::
-set_play_rate(PN_stdfloat play_rate) {
-  audio_debug("MilesAudioManager::set_play_rate(play_rate="<<play_rate<<")");
-  LightReMutexHolder holder(_lock);
-  if (_play_rate != play_rate) {
-    _play_rate = play_rate;
-    // Tell our AudioSounds to adjust:
-    AudioSet::iterator i = _sounds_on_loan.begin();
-    for (; i != _sounds_on_loan.end(); ++i) {
-      (*i)->set_play_rate((*i)->get_play_rate());
-    }
-  }
-}
-
-/**
- * get the overall speed/pitch/play rate
- */
-PN_stdfloat MilesAudioManager::
-get_play_rate() const {
-  return _play_rate;
-}
-
-/**
- * turn on/off
- */
-void MilesAudioManager::
-set_active(bool active) {
-  audio_debug("MilesAudioManager::set_active(flag="<<active<<")");
-  LightReMutexHolder holder(_lock);
-  if (_active != active) {
-    _active=active;
-    // Tell our AudioSounds to adjust:
-    AudioSet::iterator i = _sounds_on_loan.begin();
-    for (; i != _sounds_on_loan.end(); ++i) {
-      (*i)->set_active(_active);
-    }
-
-    if ((!_active) && _hasMidiSounds) {
-      GlobalMilesManager::get_global_ptr()->force_midi_reset();
-    }
-  }
-}
-
-/**
- *
- */
-bool MilesAudioManager::
-get_active() const {
-  return _active;
-}
-
-/**
- *
- */
-void MilesAudioManager::
-set_concurrent_sound_limit(unsigned int limit) {
-  LightReMutexHolder holder(_lock);
-  _concurrent_sound_limit = limit;
-  do_reduce_sounds_playing_to(_concurrent_sound_limit);
-}
-
-/**
- *
- */
-unsigned int MilesAudioManager::
-get_concurrent_sound_limit() const {
-  return _concurrent_sound_limit;
-}
-
-/**
- *
- */
-void MilesAudioManager::
-reduce_sounds_playing_to(unsigned int count) {
-  LightReMutexHolder holder(_lock);
-  do_reduce_sounds_playing_to(count);
-}
-
-/**
- * Stop playback on all sounds managed by this manager.
- */
-void MilesAudioManager::
-stop_all_sounds() {
-  audio_debug("MilesAudioManager::stop_all_sounds()");
-  reduce_sounds_playing_to(0);
-}
-
-/**
- * Set spatial attributes of the listener for 3D sounds.  Note that Y and Z
- * are switched to translate into Miles's coordinate system.
- */
-void MilesAudioManager::audio_3d_set_listener_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz, PN_stdfloat fx, PN_stdfloat fy, PN_stdfloat fz, PN_stdfloat ux, PN_stdfloat uy, PN_stdfloat uz) {
-  audio_debug("MilesAudioManager::audio_3d_set_listener_attributes()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  AIL_set_listener_3D_position(mgr->_digital_driver, px, pz, py);
-  AIL_set_listener_3D_velocity_vector(mgr->_digital_driver, vx, vz, vy);
-  AIL_set_listener_3D_orientation(mgr->_digital_driver, fx, fz, fy, ux, uz, uy);
-}
-
-/**
- * Get spatial attributes of the listener for 3D sounds.  Note that Y and Z
- * are switched to translate from Miles's coordinate system.
- */
-void MilesAudioManager::audio_3d_get_listener_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz, PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz) {
-  audio_debug("MilesAudioManager::audio_3d_get_listener_attributes()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  float lpx, lpy, lpz, lvx, lvy, lvz, lfx, lfy, lfz, lux, luy, luz;
-  AIL_listener_3D_position(mgr->_digital_driver, &lpx, &lpz, &lpy);
-  AIL_listener_3D_velocity(mgr->_digital_driver, &lvx, &lvz, &lvy);
-  AIL_listener_3D_orientation(mgr->_digital_driver, &lfx, &lfz, &lfy, &lux, &luz, &luy);
-
-  *px = lpx;
-  *py = lpy;
-  *pz = lpz;
-  *vx = lvx;
-  *vy = lvy;
-  *vz = lvz;
-  *fx = lfx;
-  *fy = lfy;
-  *fz = lfz;
-  *ux = lux;
-  *uy = luy;
-  *uz = luz;
-}
-
-/**
- * Set factor to allow user to easily work in a different scale.  1.0
- * represents meters.
- */
-void MilesAudioManager::audio_3d_set_distance_factor(PN_stdfloat factor) {
-  audio_debug("MilesAudioManager::audio_3d_set_distance_factor( factor= " << factor << ")");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  AIL_set_3D_distance_factor(mgr->_digital_driver, factor);
-}
-
-/**
- * Get factor controlling working units.
- */
-PN_stdfloat MilesAudioManager::audio_3d_get_distance_factor() const {
-  audio_debug("MilesAudioManager::audio_3d_get_distance_factor()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  return AIL_3D_distance_factor(mgr->_digital_driver);
-}
-
-/**
- * Exaggerates or diminishes the Doppler effect.  Defaults to 1.0
- */
-void MilesAudioManager::audio_3d_set_doppler_factor(PN_stdfloat factor) {
-  audio_debug("MilesAudioManager::audio_3d_set_doppler_factor(factor="<<factor<<")");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  AIL_set_3D_doppler_factor(mgr->_digital_driver, factor);
-}
-
-/**
- * Get the factor controlling the Doppler effect.
- */
-PN_stdfloat MilesAudioManager::audio_3d_get_doppler_factor() const {
-  audio_debug("MilesAudioManager::audio_3d_get_doppler_factor()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  return AIL_3D_doppler_factor(mgr->_digital_driver);
-}
-
-/**
- * Control the effect distance has on audability.  Defaults to 1.0
- */
-void MilesAudioManager::audio_3d_set_drop_off_factor(PN_stdfloat factor) {
-  audio_debug("MilesAudioManager::audio_3d_set_drop_off_factor("<<factor<<")");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  AIL_set_3D_rolloff_factor(mgr->_digital_driver, factor);
-}
-
-/**
- * Get the factor controlling how quickly sound falls off with distance.
- */
-PN_stdfloat MilesAudioManager::audio_3d_get_drop_off_factor() const {
-  audio_debug("MilesAudioManager::audio_3d_get_drop_off_factor()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  return AIL_3D_rolloff_factor(mgr->_digital_driver);
-}
-
-/**
- * Works similarly to MilesAudioSound::set_speaker_levels, but specifies the
- * 3D positions of the speakers in space.
- *
- * Once a NULL value is found for a speaker position, no more speaker
- * positions will be used.
- *
- * Note that Y and Z are switched to translate from Miles's coordinate system.
- */
-void MilesAudioManager::
-set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2, LVecBase3 *speaker3, LVecBase3 *speaker4, LVecBase3 *speaker5, LVecBase3 *speaker6, LVecBase3 *speaker7, LVecBase3 *speaker8, LVecBase3 *speaker9) {
-  audio_debug("MilesAudioManager::set_speaker_configuration()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-
-  MSSVECTOR3D speakers[9];
-
-  if(speaker1 != nullptr) {
-    speakers[0].x = speaker1->get_x();
-    speakers[0].y = speaker1->get_z();
-    speakers[0].z = speaker1->get_y();
-  }
-  if(speaker2 != nullptr) {
-    speakers[1].x = speaker2->get_x();
-    speakers[1].y = speaker2->get_z();
-    speakers[1].z = speaker2->get_y();
-  }
-  if(speaker3 != nullptr) {
-    speakers[2].x = speaker3->get_x();
-    speakers[2].y = speaker3->get_z();
-    speakers[2].z = speaker3->get_y();
-  }
-  if(speaker4 != nullptr) {
-    speakers[3].x = speaker4->get_x();
-    speakers[3].y = speaker4->get_z();
-    speakers[3].z = speaker4->get_y();
-  }
-  if(speaker5 != nullptr) {
-    speakers[4].x = speaker5->get_x();
-    speakers[4].y = speaker5->get_z();
-    speakers[4].z = speaker5->get_y();
-  }
-  if(speaker6 != nullptr) {
-    speakers[5].x = speaker6->get_x();
-    speakers[5].y = speaker6->get_z();
-    speakers[5].z = speaker6->get_y();
-  }
-  if(speaker7 != nullptr) {
-    speakers[6].x = speaker7->get_x();
-    speakers[6].y = speaker7->get_z();
-    speakers[6].z = speaker7->get_y();
-  }
-  if(speaker8 != nullptr) {
-    speakers[7].x = speaker8->get_x();
-    speakers[7].y = speaker8->get_z();
-    speakers[7].z = speaker8->get_y();
-  }
-  if(speaker9 != nullptr) {
-    speakers[8].x = speaker9->get_x();
-    speakers[8].y = speaker9->get_z();
-    speakers[8].z = speaker9->get_y();
-  }
-
-  if(speaker1 == nullptr) {
-    audio_error("No valid speaker positions specified in MilesAudioManager::set_speaker_configuration().");
-  } else if(speaker2 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 1, 1.0);
-  } else if(speaker3 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 2, 1.0);
-  } else if(speaker4 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 3, 1.0);
-  } else if(speaker5 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 4, 1.0);
-  } else if(speaker6 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 5, 1.0);
-  } else if(speaker7 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 6, 1.0);
-  } else if(speaker8 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 7, 1.0);
-  } else if(speaker9 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 8, 1.0);
-  } else {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 9, 1.0);
-  }
-}
-
-/**
- * Must be called every frame.  Failure to call this every frame could cause
- * problems for some audio managers.
- */
-void MilesAudioManager::
-update() {
-  {
-    MutexHolder holder(_streams_lock);
-    if (_stream_thread.is_null() && !_streams.empty()) {
-      // If we don't have a sub-thread, we have to service the streams in the
-      // main thread.
-      do_service_streams();
-    }
-  }
-
-  if (_sounds_finished) {
-    _sounds_finished = false;
-
-    // If the _sounds_finished flag was set, we should scan our list of
-    // playing sounds and see if any of them have finished recently.  We don't
-    // do this in the finished callback, because that might have been called
-    // in a sub-thread (and we may not have threading supported--and mutex
-    // protection--compiled in).
-
-    SoundsPlaying::iterator si = _sounds_playing.begin();
-    while (si != _sounds_playing.end()) {
-      MilesAudioSound *sound = (*si);
-      ++si;
-
-      if (sound->status() == AudioSound::READY) {
-        sound->stop();
-      }
-    }
-  }
-}
-
-/**
- *
- */
-void MilesAudioManager::
-release_sound(MilesAudioSound *audioSound) {
-  audio_debug("MilesAudioManager::release_sound(audioSound=\""
-              <<audioSound->get_name()<<"\"), this = " << (void *)this);
-  LightReMutexHolder holder(_lock);
-  AudioSet::iterator ai = _sounds_on_loan.find(audioSound);
-  if (ai != _sounds_on_loan.end()) {
-    _sounds_on_loan.erase(ai);
-  }
-
-  audio_debug("MilesAudioManager::release_sound() finished");
-}
-
-/**
- * Shuts down the audio manager and releases any resources associated with it.
- * Also cleans up all AudioSounds created via the manager.
- */
-void MilesAudioManager::
-cleanup() {
-  audio_debug("MilesAudioManager::cleanup(), this = " << (void *)this
-              << ", _cleanup_required = " << _cleanup_required);
-  LightReMutexHolder holder(_lock);
-  if (!_cleanup_required) {
-    return;
-  }
-
-  // Be sure to cleanup associated sounds before cleaning up the manager:
-  AudioSet orig_sounds;
-  orig_sounds.swap(_sounds_on_loan);
-  AudioSet::iterator ai;
-  for (ai = orig_sounds.begin(); ai != orig_sounds.end(); ++ai) {
-    (*ai)->cleanup();
-  }
-
-  do_clear_cache();
-
-  // Now stop the thread, if it has been started.
-  if (!_stream_thread.is_null()) {
-    milesAudio_cat.info()
-      << "Stopping audio streaming thread.\n";
-    PT(StreamThread) old_thread;
-    {
-      MutexHolder holder(_streams_lock);
-      nassertv(!_stream_thread.is_null());
-      _stream_thread->_keep_running = false;
-      _streams_cvar.notify();
-      old_thread = _stream_thread;
-      _stream_thread.clear();
-    }
-    old_thread->join();
-  }
-
-  _cleanup_required = false;
-  audio_debug("MilesAudioManager::cleanup() finished");
-}
-
-/**
- *
- */
-void MilesAudioManager::
-output(std::ostream &out) const {
-  LightReMutexHolder holder(_lock);
-  out << get_type() << ": " << _sounds_playing.size()
-      << " / " << _sounds_on_loan.size() << " sounds playing / total";
-}
-
-/**
- *
- */
-void MilesAudioManager::
-write(std::ostream &out) const {
-  LightReMutexHolder holder(_lock);
-
-  out << (*this) << "\n";
-  AudioSet::const_iterator ai;
-  for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) {
-    MilesAudioSound *sound = (*ai);
-    out << "  " << *sound << "\n";
-  }
-
-  size_t total_preload = 0;
-  size_t num_preloaded = 0;
-  SoundMap::const_iterator si;
-  for (si = _sounds.begin(); si != _sounds.end(); ++si) {
-    if (!(*si).second->_raw_data.empty()) {
-      ++num_preloaded;
-      total_preload += (*si).second->_raw_data.size();
-    }
-  }
-  out << num_preloaded << " of " << _sounds.size() << " sounds preloaded, size used is " << (total_preload + 1023) / 1024 << "K\n";
-
-  {
-    MutexHolder holder(_streams_lock);
-    out << _streams.size() << " streams opened.\n";
-    if (!_stream_thread.is_null()) {
-      out << "(Audio streaming thread has been started.)\n";
-    }
-  }
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-
-  int num_samples = mgr->get_num_samples();
-  out << num_samples << " sample handles allocated globally.\n";
-
-  int num_sequences = mgr->get_num_sequences();
-  out << num_sequences << " sequence handles allocated globally.\n";
-}
-
-
-/**
- * Implementation of is_valid().  Assumes the lock is already held.
- */
-bool MilesAudioManager::
-do_is_valid() {
-  bool check = true;
-  if (_sounds.size() != _lru.size()) {
-    audio_debug("-- Error _sounds.size() != _lru.size() --");
-    check = false;
-
-  } else {
-    LRU::const_iterator i = _lru.begin();
-    for (; i != _lru.end(); ++i) {
-      SoundMap::const_iterator smi = _sounds.find(**i);
-      if (smi == _sounds.end()) {
-        audio_debug("-- "<<**i<<" in _lru and not in _sounds --");
-        check = false;
-        break;
-      }
-    }
-  }
-  return _is_valid && check;
-}
-
-/**
- * Assumes the lock is already held.
- */
-void MilesAudioManager::
-do_reduce_sounds_playing_to(unsigned int count) {
-  int limit = _sounds_playing.size() - count;
-  while (limit-- > 0) {
-    SoundsPlaying::iterator sound = _sounds_playing.begin();
-    assert(sound != _sounds_playing.end());
-    (**sound).stop();
-  }
-}
-
-/**
- * Assumes the lock is already held.
- */
-void MilesAudioManager::
-do_clear_cache() {
-  if (_is_valid) { nassertv(do_is_valid()); }
-  _sounds.clear();
-  _lru.clear();
-  if (_is_valid) { nassertv(do_is_valid()); }
-}
-
-/**
- * Adds the indicated stream to the list of streams to be serviced by a Panda
- * sub-thread.  This is in lieu of Miles' auto-service-stream mechanism.
- */
-void MilesAudioManager::
-start_service_stream(HSTREAM stream) {
-  MutexHolder holder(_streams_lock);
-  nassertv(find(_streams.begin(), _streams.end(), stream) == _streams.end());
-  _streams.push_back(stream);
-  _streams_cvar.notify();
-
-  if (_stream_thread.is_null() && Thread::is_threading_supported()) {
-    milesAudio_cat.info()
-      << "Starting audio streaming thread.\n";
-    _stream_thread = new StreamThread(this);
-    _stream_thread->start(TP_low, true);
-  }
-}
-
-/**
- * Removes the indicated stream from the list of streams to be serviced by a
- * Panda sub-thread.
- */
-void MilesAudioManager::
-stop_service_stream(HSTREAM stream) {
-  MutexHolder holder(_streams_lock);
-  Streams::iterator si = find(_streams.begin(), _streams.end(), stream);
-  if (si != _streams.end()) {
-    _streams.erase(si);
-  }
-}
-
-
-/**
- * Assumes the lock is already held.
- */
-void MilesAudioManager::
-most_recently_used(const string &path) {
-  audio_debug("MilesAudioManager::most_recently_used(path=\""
-      <<path<<"\")");
-  LRU::iterator i=find(_lru.begin(), _lru.end(), &path);
-  if (i != _lru.end()) {
-    _lru.erase(i);
-  }
-  // At this point, path should not exist in the _lru:
-  assert(find(_lru.begin(), _lru.end(), &path) == _lru.end());
-  _lru.push_back(&path);
-  nassertv(do_is_valid());
-}
-
-/**
- * Assumes the lock is already held.
- */
-void MilesAudioManager::
-uncache_a_sound() {
-  audio_debug("MilesAudioManager::uncache_a_sound()");
-  nassertv(do_is_valid());
-  // uncache least recently used:
-  assert(_lru.size()>0);
-  LRU::reference path=_lru.front();
-  SoundMap::iterator i = _sounds.find(*path);
-  assert(i != _sounds.end());
-  _lru.pop_front();
-
-  if (i != _sounds.end()) {
-    audio_debug("  uncaching \""<<i->first<<"\"");
-    _sounds.erase(i);
-  }
-  nassertv(do_is_valid());
-}
-
-/**
- * Inform the manager that a sound is about to play.
- */
-void MilesAudioManager::
-starting_sound(MilesAudioSound *audio) {
-  LightReMutexHolder holder(_lock);
-  if (_concurrent_sound_limit) {
-    do_reduce_sounds_playing_to(_concurrent_sound_limit);
-  }
-  _sounds_playing.insert(audio);
-}
-
-/**
- * Inform the manager that a sound is finished or someone called stop on the
- * sound (this should not be called if a sound is only paused).
- */
-void MilesAudioManager::
-stopping_sound(MilesAudioSound *audio) {
-  LightReMutexHolder holder(_lock);
-  _sounds_playing.erase(audio);
-  if (_hasMidiSounds && _sounds_playing.size() == 0) {
-    GlobalMilesManager::get_global_ptr()->force_midi_reset();
-  }
-}
-
-/**
- * Reads a sound file and allocates a SoundData pointer for it.  Returns NULL
- * if the sound file cannot be loaded.
- *
- * Assumes the lock is already held.
- */
-PT(MilesAudioManager::SoundData) MilesAudioManager::
-load(const Filename &file_name) {
-  PT(SoundData) sd = new SoundData;
-
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  PT(VirtualFile) file = vfs->get_file(file_name);
-  if (file == nullptr) {
-    milesAudio_cat.warning()
-      << "No such file: " << file_name << "\n";
-    return nullptr;
-  }
-
-  if (file->get_file_size() == 0) {
-    milesAudio_cat.warning()
-      << "File " << file_name << " is empty\n";
-    return nullptr;
-  }
-
-  sd->_basename = file_name.get_basename();
-
-  string extension = sd->_basename.get_extension();
-  if (extension == "pz" || extension == "gz") {
-    extension = Filename(sd->_basename.get_basename_wo_extension()).get_extension();
-  }
-
-  bool is_midi_file = (downcase(extension) == "mid");
-
-  if ((miles_audio_preload_threshold == -1 || file->get_file_size() < (std::streamsize)miles_audio_preload_threshold) ||
-      is_midi_file) {
-    // If the file is sufficiently small, we'll preload it into memory.  MIDI
-    // files cannot be streamed, so we always preload them, regardless of
-    // size.
-
-    if (!file->read_file(sd->_raw_data, true)) {
-      milesAudio_cat.warning()
-        << "Unable to read " << file_name << "\n";
-      return nullptr;
-    }
-
-    sd->_file_type =
-      AIL_file_type(&sd->_raw_data[0], sd->_raw_data.size());
-
-    if (sd->_file_type == AILFILETYPE_MIDI) {
-      // A standard MIDI file.  We have to convert this to XMIDI for Miles.
-      void *xmi;
-      U32 xmi_size;
-      if (AIL_MIDI_to_XMI(&sd->_raw_data[0], sd->_raw_data.size(),
-                          &xmi, &xmi_size, 0)) {
-        audio_debug("converted " << sd->_basename << " from standard MIDI ("
-                    << sd->_raw_data.size() << " bytes) to XMIDI ("
-                    << xmi_size << " bytes)");
-
-        // Copy the data to our own buffer and free the Miles-allocated data.
-        sd->_raw_data.clear();
-        sd->_raw_data.insert(sd->_raw_data.end(),
-                             (unsigned char *)xmi, (unsigned char *)xmi + xmi_size);
-        AIL_mem_free_lock(xmi);
-        sd->_file_type = AILFILETYPE_XMIDI;
-      } else {
-        milesAudio_cat.warning()
-          << "Could not convert " << sd->_basename << " to XMIDI.\n";
-      }
-    }
-
-    bool expand_to_wav = false;
-
-    if (sd->_file_type != AILFILETYPE_MPEG_L3_AUDIO) {
-      audio_debug(sd->_basename << " is not an mp3 file.");
-    } else if ((int)sd->_raw_data.size() >= miles_audio_expand_mp3_threshold) {
-      audio_debug(sd->_basename << " is too large to expand in-memory.");
-    } else {
-      audio_debug(sd->_basename << " will be expanded in-memory.");
-      expand_to_wav = true;
-    }
-
-    if (expand_to_wav) {
-      // Now convert the file to WAV format in-memory.  This is useful to work
-      // around seek and length problems associated with variable bit-rate MP3
-      // encoding.
-      void *wav_data;
-      U32 wav_data_size;
-      if (AIL_decompress_ASI(&sd->_raw_data[0], sd->_raw_data.size(),
-                             sd->_basename.c_str(), &wav_data, &wav_data_size,
-                             nullptr)) {
-        audio_debug("expanded " << sd->_basename << " from " << sd->_raw_data.size()
-                    << " bytes to " << wav_data_size << " bytes.");
-
-        if (wav_data_size != 0) {
-          // Now copy the memory into our own buffers, and free the Miles-
-          // allocated memory.
-          sd->_raw_data.clear();
-          sd->_raw_data.insert(sd->_raw_data.end(),
-                               (unsigned char *)wav_data, (unsigned char *)wav_data + wav_data_size);
-          sd->_file_type = AILFILETYPE_PCM_WAV;
-          sd->_basename.set_extension("wav");
-        }
-        AIL_mem_free_lock(wav_data);
-
-      } else {
-        audio_debug("unable to expand " << sd->_basename);
-      }
-    }
-
-  } else {
-    // If the file is large, we'll stream it from disk instead of preloading
-    // it.  This means we don't need to load any data at this point.
-  }
-
-  return sd;
-}
-
-/**
- * Called to service the streaming audio channels currently playing on the
- * audio manager.
- */
-void MilesAudioManager::
-thread_main(volatile bool &keep_running) {
-  MutexHolder holder(_streams_lock);
-
-  while (keep_running) {
-    if (_streams.empty()) {
-      // If there are no streams to service, block on the condition variable.
-      _streams_cvar.wait();
-    } else {
-      do_service_streams();
-    }
-
-    // Now yield to be polite to the main application.
-    _streams_lock.release();
-    Thread::force_yield();
-    _streams_lock.acquire();
-  }
-}
-
-/**
- * Internal function to service all the streams.  Assumes _streams_lock is
- * already held.
- */
-void MilesAudioManager::
-do_service_streams() {
-  size_t i = 0;
-  while (i < _streams.size()) {
-    HSTREAM stream = _streams[i];
-
-    // We must release the lock while we are servicing stream i.
-    _streams_lock.release();
-    AIL_service_stream(stream, 0);
-    Thread::consider_yield();
-    _streams_lock.acquire();
-
-    ++i;
-  }
-}
-
-/**
- *
- */
-MilesAudioManager::StreamThread::
-StreamThread(MilesAudioManager *mgr) :
-  Thread("StreamThread", "StreamThread"),
-  _mgr(mgr)
-{
-  _keep_running = true;
-}
-
-/**
- *
- */
-void MilesAudioManager::StreamThread::
-thread_main() {
-  _mgr->thread_main(_keep_running);
-}
-
-/**
- *
- */
-MilesAudioManager::SoundData::
-SoundData() :
-  _raw_data(MilesAudioManager::get_class_type()),
-  _has_length(false),
-  _length(0.0f)
-{
-}
-
-/**
- *
- */
-MilesAudioManager::SoundData::
-~SoundData() {
-}
-
-/**
- *
- */
-PN_stdfloat MilesAudioManager::SoundData::
-get_length() {
-  if (!_has_length) {
-    // Time to determine the length of the file.
-
-    if (_raw_data.empty()) {
-      _length = 0.0f;
-      _has_length = true;
-
-    } else if (_file_type == AILFILETYPE_MPEG_L3_AUDIO) {
-      // If it's an mp3 file, we have to calculate its length by walking
-      // through all of its frames.
-      audio_debug("Computing length of mp3 file " << _basename);
-
-      MP3_INFO info;
-      AIL_inspect_MP3(&info, &_raw_data[0], _raw_data.size());
-      _length = 0.0f;
-      while (AIL_enumerate_MP3_frames(&info)) {
-        _length += info.data_size * 8.0f / info.bit_rate;
-      }
-      _has_length = true;
-
-    } else if (_file_type == AILFILETYPE_PCM_WAV ||
-               _file_type == AILFILETYPE_ADPCM_WAV ||
-               _file_type == AILFILETYPE_OTHER_ASI_WAV) {
-      audio_debug("Getting length of wav file " << _basename);
-
-      AILSOUNDINFO info;
-      if (AIL_WAV_info(&_raw_data[0], &info)) {
-        _length = (PN_stdfloat)info.samples / (PN_stdfloat)info.rate;
-        audio_debug(info.samples << " samples at " << info.rate
-                    << "; length is " << _length << " seconds.");
-        _has_length = true;
-      }
-    }
-  }
-
-  nassertr(_has_length, 0.0f);
-  return _length;
-}
-
-/**
- * Records the sample length, as determined externally.
- */
-void MilesAudioManager::SoundData::
-set_length(PN_stdfloat length) {
-  _length = length;
-  _has_length = true;
-}
-
-#endif //]

+ 0 - 198
panda/src/audiotraits/milesAudioManager.h

@@ -1,198 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file milesAudioManager.h
- * @author skyler
- * @date 2001-06-06
- * Prior system by: cary
- */
-
-#ifndef __MILES_AUDIO_MANAGER_H__ //[
-#define __MILES_AUDIO_MANAGER_H__
-
-#include "pandabase.h"
-#ifdef HAVE_RAD_MSS //[
-
-#include "audioManager.h"
-#include "pset.h"
-#include "pmap.h"
-#include "pdeque.h"
-#include "pvector.h"
-#include "thread.h"
-#include "pmutex.h"
-#include "lightReMutex.h"
-#include "conditionVar.h"
-#include "vector_uchar.h"
-
-#include <mss.h>
-
-class MilesAudioSound;
-
-class EXPCL_MILES_AUDIO MilesAudioManager: public AudioManager {
-public:
-  // See AudioManager.h for documentation.
-
-  MilesAudioManager();
-  virtual ~MilesAudioManager();
-
-  virtual void shutdown();
-
-  virtual bool is_valid();
-
-  virtual PT(AudioSound) get_sound(const Filename &file_name, bool positional = false, int mode=SM_heuristic);
-  virtual PT(AudioSound) get_sound(MovieAudio *sound, bool positional = false, int mode=SM_heuristic);
-  virtual void uncache_sound(const Filename &file_name);
-  virtual void clear_cache();
-  virtual void set_cache_limit(unsigned int count);
-  virtual unsigned int get_cache_limit() const;
-
-  virtual void set_volume(PN_stdfloat volume);
-  virtual PN_stdfloat get_volume() const;
-
-  void set_play_rate(PN_stdfloat play_rate);
-  PN_stdfloat get_play_rate() const;
-
-  virtual void set_active(bool active);
-  virtual bool get_active() const;
-
-  virtual void set_concurrent_sound_limit(unsigned int limit = 0);
-  virtual unsigned int get_concurrent_sound_limit() const;
-
-  virtual void reduce_sounds_playing_to(unsigned int count);
-  virtual void stop_all_sounds();
-
-  virtual void update();
-
-  // Tell the manager that the sound dtor was called.
-  void release_sound(MilesAudioSound *audioSound);
-  void cleanup();
-
-  // 3D spatialized sound support.  Spatialized sound was originally added for
-  // FMOD, so there are parts of the interface in the Miles implementation
-  // that are a little more awkward than they would be otherwise.
-  virtual void audio_3d_set_listener_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat xy, PN_stdfloat xz, PN_stdfloat fx, PN_stdfloat fy, PN_stdfloat fz, PN_stdfloat ux, PN_stdfloat uy, PN_stdfloat uz);
-  virtual void audio_3d_get_listener_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz, PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz);
-  virtual void audio_3d_set_distance_factor(PN_stdfloat factor);
-  virtual PN_stdfloat audio_3d_get_distance_factor() const;
-  virtual void audio_3d_set_doppler_factor(PN_stdfloat factor);
-  virtual PN_stdfloat audio_3d_get_doppler_factor() const;
-  virtual void audio_3d_set_drop_off_factor(PN_stdfloat factor);
-  virtual PN_stdfloat audio_3d_get_drop_off_factor() const;
-  virtual void set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2=nullptr, LVecBase3 *speaker3=nullptr, LVecBase3 *speaker4=nullptr, LVecBase3 *speaker5=nullptr, LVecBase3 *speaker6=nullptr, LVecBase3 *speaker7=nullptr, LVecBase3 *speaker8=nullptr, LVecBase3 *speaker9=nullptr);
-
-  virtual void output(std::ostream &out) const;
-  virtual void write(std::ostream &out) const;
-
-private:
-  bool do_is_valid();
-  void do_reduce_sounds_playing_to(unsigned int count);
-  void do_clear_cache();
-
-  void start_service_stream(HSTREAM stream);
-  void stop_service_stream(HSTREAM stream);
-
-  void most_recently_used(const std::string &path);
-  void uncache_a_sound();
-
-  void starting_sound(MilesAudioSound *audio);
-  void stopping_sound(MilesAudioSound *audio);
-
-  class SoundData;
-  PT(SoundData) load(const Filename &file_name);
-
-  void thread_main(volatile bool &keep_running);
-  void do_service_streams();
-
-private:
-  class StreamThread : public Thread {
-  public:
-    StreamThread(MilesAudioManager *mgr);
-    virtual void thread_main();
-
-    MilesAudioManager *_mgr;
-    volatile bool _keep_running;
-  };
-
-  // The sound cache:
-  class SoundData : public ReferenceCount {
-  public:
-    SoundData();
-    ~SoundData();
-    PN_stdfloat get_length();
-    void set_length(PN_stdfloat length);
-
-    Filename _basename;
-    S32 _file_type;
-    vector_uchar _raw_data;
-    bool _has_length;
-    PN_stdfloat _length;  // in seconds.
-  };
-  typedef pmap<std::string, PT(SoundData) > SoundMap;
-  SoundMap _sounds;
-
-  typedef pset<MilesAudioSound *> AudioSet;
-  // The offspring of this manager:
-  AudioSet _sounds_on_loan;
-
-  typedef pset<MilesAudioSound *> SoundsPlaying;
-  // The sounds from this manager that are currently playing:
-  SoundsPlaying _sounds_playing;
-
-  // The Least Recently Used mechanism:
-  typedef pdeque<const std::string *> LRU;
-  LRU _lru;
-  // State:
-  PN_stdfloat _volume;
-  PN_stdfloat _play_rate;
-  bool _active;
-  int _cache_limit;
-  bool _cleanup_required;
-  unsigned int _concurrent_sound_limit;
-
-  bool _is_valid;
-  bool _hasMidiSounds;
-
-  // This mutex protects everything above.
-  LightReMutex _lock;
-  bool _sounds_finished;
-
-  typedef pvector<HSTREAM> Streams;
-  PT(StreamThread) _stream_thread;
-  Streams _streams;
-  Mutex _streams_lock;
-  ConditionVar _streams_cvar;
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    AudioManager::init_type();
-    register_type(_type_handle, "MilesAudioManager",
-                  AudioManager::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
-  static TypeHandle _type_handle;
-
-  friend class MilesAudioSound;
-  friend class MilesAudioSample;
-  friend class MilesAudioSequence;
-  friend class MilesAudioStream;
-};
-
-EXPCL_MILES_AUDIO AudioManager *Create_MilesAudioManager();
-
-
-#endif //]
-
-#endif //]

+ 0 - 13
panda/src/audiotraits/milesAudioSample.I

@@ -1,13 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file milesAudioSample.I
- * @author skyler
- * @date 2001-06-06
- * Prior system by: cary
- */

+ 0 - 530
panda/src/audiotraits/milesAudioSample.cxx

@@ -1,530 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file milesAudioSample.cxx
- * @author skyler
- * @date 2001-06-06
- */
-
-#include "milesAudioSample.h"
-
-#ifdef HAVE_RAD_MSS //[
-
-#include "milesAudioManager.h"
-
-
-TypeHandle MilesAudioSample::_type_handle;
-
-#undef miles_audio_debug
-
-#ifndef NDEBUG //[
-#define miles_audio_debug(x) \
-    audio_debug("MilesAudioSample \""<<get_name()<<"\" "<< x )
-#else //][
-#define miles_audio_debug(x) ((void)0)
-#endif //]
-
-/**
- * This constructor is called only by the MilesAudioManager.
- */
-MilesAudioSample::
-MilesAudioSample(MilesAudioManager *manager, MilesAudioManager::SoundData *sd,
-                 const std::string &file_name) :
-  MilesAudioSound(manager, file_name),
-  _sd(sd)
-{
-  nassertv(sd != nullptr);
-  audio_debug("MilesAudioSample(manager=0x"<<(void*)&manager
-              <<", sd=0x"<<(void*)sd<<", file_name="<<file_name<<")");
-
-  _sample = 0;
-  _sample_index = 0;
-  _original_playback_rate = 1.0f;
-}
-
-/**
- *
- */
-MilesAudioSample::
-~MilesAudioSample() {
-  miles_audio_debug("~MilesAudioSample()");
-  cleanup();
-  miles_audio_debug("~MilesAudioSample() done");
-}
-
-/**
- *
- */
-void MilesAudioSample::
-play() {
-  miles_audio_debug("play()");
-  if (_active) {
-    if (_sd->_raw_data.empty()) {
-      milesAudio_cat.warning()
-        << "Could not play " << _file_name << ": no data\n";
-    } else {
-      stop();
-      _manager->starting_sound(this);
-
-      nassertv(_sample == 0);
-
-      GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-      if (!mgr->get_sample(_sample, _sample_index, this)){
-        milesAudio_cat.warning()
-          << "Could not play " << _file_name << ": too many open samples\n";
-        _sample = 0;
-      } else {
-        AIL_set_named_sample_file(_sample, _sd->_basename.c_str(),
-                                  &_sd->_raw_data[0], _sd->_raw_data.size(),
-                                  0);
-        _original_playback_rate = AIL_sample_playback_rate(_sample);
-        AIL_set_sample_user_data(_sample, 0, (SINTa)this);
-        AIL_register_EOS_callback(_sample, finish_callback);
-
-        set_volume(_volume);
-        set_play_rate(_play_rate);
-        AIL_set_sample_loop_count(_sample, _loop_count);
-
-        if (_got_start_time) {
-          do_set_time(_start_time);
-          AIL_resume_sample(_sample);
-        } else {
-          AIL_start_sample(_sample);
-        }
-      }
-
-      _got_start_time = false;
-    }
-  } else {
-    // In case _loop_count gets set to forever (zero):
-    audio_debug("  paused "<<_file_name );
-    _paused = true;
-  }
-}
-
-/**
- *
- */
-void MilesAudioSample::
-stop() {
-  if (_manager == nullptr) {
-    return;
-  }
-
-  miles_audio_debug("stop()");
-  _manager->stopping_sound(this);
-  // The _paused flag should not be cleared here.  _paused is not like the
-  // Pause button on a cddvd player.  It is used as a flag to say that it was
-  // looping when it was set inactive.  There is no need to make this
-  // symmetrical with play().  set_active() is the 'owner' of _paused.  play()
-  // accesses _paused to help in the situation where someone calls play on an
-  // inactive sound().
-
-  // it fixes audio bug, I don't understand the reasoning of the above comment
-  _paused = false;
-
-  if (_sample != 0) {
-    AIL_end_sample(_sample);
-
-    GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-    mgr->release_sample(_sample_index, this);
-
-    _sample = 0;
-    _sample_index = 0;
-  }
-}
-
-/**
- *
- */
-PN_stdfloat MilesAudioSample::
-get_time() const {
-  if (_sample == 0) {
-    if (_got_start_time) {
-      return _start_time;
-    }
-    return 0.0f;
-  }
-
-  S32 current_ms;
-  AIL_sample_ms_position(_sample, nullptr, &current_ms);
-  PN_stdfloat time = PN_stdfloat(current_ms * 0.001f);
-
-  return time;
-}
-
-/**
- *
- */
-void MilesAudioSample::
-set_volume(PN_stdfloat volume) {
-  miles_audio_debug("set_volume(volume="<<volume<<")");
-
-  // Set the volume even if our volume is not changing, because the
-  // MilesAudioManager will call set_volume() when *its* volume changes.
-
-  // Set the volume:
-  _volume = volume;
-
-  if (_sample != 0) {
-    volume *= _manager->get_volume();
-
-    // Change to Miles volume, range 0 to 1.0:
-    F32 milesVolume = volume;
-    milesVolume = std::min(milesVolume, 1.0f);
-    milesVolume = std::max(milesVolume, 0.0f);
-
-    // Convert balance of -1.0..1.0 to 0-1.0:
-    F32 milesBalance = (F32)((_balance + 1.0f) * 0.5f);
-
-    AIL_set_sample_volume_pan(_sample, milesVolume, milesBalance);
-  }
-}
-
-/**
- *
- */
-void MilesAudioSample::
-set_balance(PN_stdfloat balance_right) {
-  miles_audio_debug("set_balance(balance_right="<<balance_right<<")");
-  _balance = balance_right;
-
-  // Call set_volume to effect the change:
-  set_volume(_volume);
-}
-
-/**
- *
- */
-void MilesAudioSample::
-set_play_rate(PN_stdfloat play_rate) {
-  miles_audio_debug("set_play_rate(play_rate="<<play_rate<<")");
-
-  // Set the play_rate:
-  _play_rate = play_rate;
-
-  if (_sample != 0) {
-    play_rate *= _manager->get_play_rate();
-
-    // wave and mp3 use sample rate (e.g.  44100)
-    S32 speed = (S32)(play_rate * (PN_stdfloat)_original_playback_rate);
-    AIL_set_sample_playback_rate(_sample, speed);
-    audio_debug("  play_rate for this wav or mp3 is now " << speed);
-  }
-}
-
-/**
- *
- */
-PN_stdfloat MilesAudioSample::
-length() const {
-  return _sd->get_length();
-}
-
-/**
- *
- */
-AudioSound::SoundStatus MilesAudioSample::
-status() const {
-  if (_sample == 0) {
-    return AudioSound::READY;
-  }
-  switch (AIL_sample_status(_sample)) {
-  case SMP_DONE:
-  case SMP_STOPPED:
-  case SMP_FREE:
-    return AudioSound::READY;
-
-  case SMP_PLAYING:
-  case SMP_PLAYINGBUTRELEASED:
-    return AudioSound::PLAYING;
-
-  default:
-    return AudioSound::BAD;
-  }
-}
-
-/**
- * Stops the sound from playing and releases any associated resources, in
- * preparation for releasing the sound or shutting down the sound system.
- */
-void MilesAudioSample::
-cleanup() {
-  stop();
-  set_active(false);
-  nassertv(_sample == 0);
-
-  if (_manager != nullptr) {
-    _manager->release_sound(this);
-    _manager = nullptr;
-  }
-}
-
-/**
- *
- */
-void MilesAudioSample::
-output(std::ostream &out) const {
-  out << get_type() << " " << get_name() << " " << status();
-  if (!_sd.is_null()) {
-    out << " " << (_sd->_raw_data.size() + 1023) / 1024 << "K";
-  }
-}
-
-/**
- * Set position and velocity of this sound.  Note that Y and Z are switched to
- * translate from Miles's coordinate system.
- */
-void MilesAudioSample::set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz) {
-  audio_debug("MilesAudioSample::set_3d_attributes()  Setting a sound's 3D Coordinates.");
-
-  if(_sample != 0) {
-    AIL_set_sample_3D_position(_sample, px, pz, py);
-    AIL_set_sample_3D_velocity_vector(_sample, vx, vz, vy);
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::set_3d_attributes().");
-  }
-}
-
-/**
- * Get position and velocity of this sound.
- */
-void MilesAudioSample::get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz) {
-  audio_debug("MilesAudioSample::get_3d_attributes().");
-
-  if(_sample != 0) {
-    float lpx, lpy, lpz, lvx, lvy, lvz;
-    AIL_sample_3D_position(_sample, &lpx, &lpz, &lpy);
-    AIL_sample_3D_velocity(_sample, &lvx, &lvz, &lvy);
-    *px = lpx;
-    *py = lpy;
-    *pz = lpz;
-    *vx = lvx;
-    *vy = lvy;
-    *vz = lvz;
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::get_3d_attributes().");
-  }
-}
-
-/**
- * Set the distance that this sound begins to fall off.  With Miles's default
- * falloff behavior, when the distance between the sound and the listener is
- * doubled, the volume is halved, and vice versa.
- */
-void MilesAudioSample::set_3d_min_distance(PN_stdfloat dist) {
-  audio_debug("MilesAudioSample::set_3d_min_distance() Setting the sound's 3D min distance ( min= " << dist << " ) ");
-
-  if(_sample != 0) {
-    // Implementation is awkward, since Miles gets and sets min and max
-    // distances in a single operation.
-    float max_dist;
-    int auto_3D_wet_atten;
-    AIL_sample_3D_distances(_sample, &max_dist, nullptr, &auto_3D_wet_atten);
-
-    AIL_set_sample_3D_distances(_sample, max_dist, dist, auto_3D_wet_atten);
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::set_3d_min_distance().");
-  }
-}
-
-/**
- * Get the distance that this sound begins to fall off.
- */
-PN_stdfloat MilesAudioSample::get_3d_min_distance() const {
-  audio_debug("MilesAudioSample::get_3d_min_distance() ");
-
-  if(_sample != 0) {
-    float min_dist;
-    AIL_sample_3D_distances(_sample, nullptr, &min_dist, nullptr);
-    return (PN_stdfloat)min_dist;
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::get_3d_min_distance().");
-    return -1.0;
-  }
-}
-
-/**
- * Set the distance at which this sound is clipped to silence.  Note that this
- * value does not affect the rate at which the sound falls off, but only the
- * distance at which it gets clipped.
- */
-void MilesAudioSample::set_3d_max_distance(PN_stdfloat dist) {
-  audio_debug("MilesAudioSample::set_3d_max_distance() Setting the sound's 3D max distance ( max= " << dist << " ) ");
-
-  if(_sample != 0) {
-    // Implementation is awkward, since Miles gets and sets min and max
-    // distances in a single operation.
-    float min_dist;
-    int auto_3D_wet_atten;
-    AIL_sample_3D_distances(_sample, nullptr, &min_dist, &auto_3D_wet_atten);
-
-    AIL_set_sample_3D_distances(_sample, dist, min_dist, auto_3D_wet_atten);
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::set_3d_max_distance().");
-  }
-}
-
-/**
- * Get the distance at which this sound is clipped to silence.
- */
-PN_stdfloat MilesAudioSample::get_3d_max_distance() const {
-  audio_debug("MilesAudioSample::get_3d_max_distance() ");
-
-  if(_sample != 0) {
-    float max_dist;
-    AIL_sample_3D_distances(_sample, &max_dist, nullptr, nullptr);
-    return (PN_stdfloat)max_dist;
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::get_3d_max_distance().");
-    return -1.0;
-  }
-}
-
-/**
- * Get the level of a particular logical channel (speaker). "index" specifies
- * which speaker in an array of all the logical channels currently in use to
- * retrieve the level of.
- *
- * For instance, in a standard 4.0 channel setup, speakers are setup as
- * [frontLeft, frontRight, backLeft, backRight]. Thus, get_speaker_level(2)
- * will retrieve the level of the backLeft speaker.
- *
- * The order in which speakers appear in the array for standard speaker setups
- * is defined to be:
- *
- * FRONT_LEFT FRONT_RIGHT FRONT_CENTER LOW_FREQUENCY (sub woofer) BACK_LEFT
- * BACK_RIGHT FRONT_LEFT_OF_CENTER FRONT_RIGHT_OF_CENTER BACK_CENTER SIDE_LEFT
- * SIDE_RIGHT TOP_CENTER TOP_FRONT_LEFT TOP_FRONT_CENTER TOP_FRONT_RIGHT
- * TOP_BACK_LEFT TOP_BACK_CENTER TOP_BACK_RIGHT
- *
- */
-PN_stdfloat MilesAudioSample::
-get_speaker_level(int index) {
-  audio_debug("MilesAudioSample::get_speaker_level(" << index << ")");
-
-  if(_sample != 0) {
-    int numLevels;
-    float *levels = AIL_sample_channel_levels(_sample, &numLevels);
-
-    if(index < numLevels) {
-      return (PN_stdfloat)levels[index];
-    } else {
-      audio_error("index out of range in MilesAudioSample::get_speaker_level.  numLevels: " << numLevels);
-      return -1.0;
-    }
-  } else {
-    audio_warning("Warning: MilesAudioSample::get_speaker_level only works for sounds that are currently playing");
-    return -1.0;
-  }
-}
-
-/**
- * Set the output levels on the logical channels (speakers) for this sound.
- * Values should be in the range 0.0 to 1.0. Levels for up to nine channels
- * may be specified.  As soon as a level is reached that falls outside the
- * range 0.0 to 1.0, the levels specified up to that point will be sent and
- * all other levels will be ignored.
- *
- * The user must know what the current speaker setup is in order to know which
- * level corresponds to which speaker.
- *
- * This method will have no effect if 3D attributes have been set for this
- * sound.
- *
- * The order in which speakers appear in the array for standard speaker setups
- * is defined to be:
- *
- * FRONT_LEFT FRONT_RIGHT FRONT_CENTER LOW_FREQUENCY (sub woofer) BACK_LEFT
- * BACK_RIGHT FRONT_LEFT_OF_CENTER FRONT_RIGHT_OF_CENTER BACK_CENTER SIDE_LEFT
- * SIDE_RIGHT TOP_CENTER TOP_FRONT_LEFT TOP_FRONT_CENTER TOP_FRONT_RIGHT
- * TOP_BACK_LEFT TOP_BACK_CENTER TOP_BACK_RIGHT
- *
- */
-void MilesAudioSample::
-set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2, PN_stdfloat level3, PN_stdfloat level4, PN_stdfloat level5, PN_stdfloat level6, PN_stdfloat level7, PN_stdfloat level8, PN_stdfloat level9) {
-  audio_debug("MilesAudioSample::set_speaker_levels()");
-
-  if(_sample != 0) {
-    float levels[9] = {level1, level2, level3, level4, level5, level6, level7, level8, level9};
-
-    if((level1 < 0.0) || (level1 > 1.0)) {
-      audio_error("No valid levels specified in MilesAudioSample::set_speaker_levels().");
-    } else if((level2 < 0.0) || (level2 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 1);
-    } else if((level3 < 0.0) || (level3 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 2);
-    } else if((level4 < 0.0) || (level4 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 3);
-    } else if((level5 < 0.0) || (level5 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 4);
-    } else if((level6 < 0.0) || (level6 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 5);
-    } else if((level7 < 0.0) || (level7 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 6);
-    } else if((level8 < 0.0) || (level8 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 7);
-    } else if((level9 < 0.0) || (level9 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 8);
-    } else {
-      AIL_set_sample_channel_levels(_sample, levels, 9);
-    }
-  } else {
-    audio_warning("Warning: MilesAudioSample::set_speaker_levels only works for sounds that are currently playing");
-  }
-}
-
-/**
- * Called by the GlobalMilesManager when it is detected that this particular
- * sound has already stopped, and its sample handle will be recycled.
- */
-void MilesAudioSample::
-internal_stop() {
-  _sample = 0;
-  _sample_index = 0;
-}
-
-/**
- * This callback is made by Miles (possibly in a sub-thread) when the sample
- * finishes.
- */
-void AILCALLBACK MilesAudioSample::
-finish_callback(HSAMPLE sample) {
-  MilesAudioSample *self = (MilesAudioSample *)AIL_sample_user_data(sample, 0);
-  if (milesAudio_cat.is_debug()) {
-    milesAudio_cat.debug()
-      << "finished " << *self << "\n";
-  }
-  if (self->_manager == nullptr) {
-    return;
-  }
-  self->_manager->_sounds_finished = true;
-}
-
-/**
- * Sets the start time of an already allocated sample.
- */
-void MilesAudioSample::
-do_set_time(PN_stdfloat time) {
-  miles_audio_debug("do_set_time(time="<<time<<")");
-  nassertv(_sample != 0);
-
-  // Ensure we don't inadvertently run off the end of the sound.
-  PN_stdfloat max_time = length();
-  if (time > max_time) {
-    milesAudio_cat.warning()
-      << "set_time(" << time << ") requested for sound of length "
-      << max_time << "\n";
-    time = max_time;
-  }
-
-  S32 time_ms = (S32)(1000.0f * time);
-  AIL_set_sample_ms_position(_sample, time_ms);
-}
-
-#endif //]

+ 0 - 103
panda/src/audiotraits/milesAudioSample.h

@@ -1,103 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file milesAudioSample.h
- * @author skyler
- * @date 2001-06-06
- * Prior system by: cary
- */
-
-#ifndef MILESAUDIOSAMPLE_H
-#define MILESAUDIOSAMPLE_H
-
-#include "pandabase.h"
-#ifdef HAVE_RAD_MSS //[
-
-#include "milesAudioSound.h"
-#include "milesAudioManager.h"
-
-#include <mss.h>
-
-/**
- * A sound file, such as a WAV or MP3 file, that is preloaded into memory and
- * played from memory.
- */
-class EXPCL_MILES_AUDIO MilesAudioSample : public MilesAudioSound {
-private:
-  MilesAudioSample(MilesAudioManager *manager,
-                   MilesAudioManager::SoundData *sd,
-                   const std::string &file_name);
-
-public:
-  virtual ~MilesAudioSample();
-
-  virtual void play();
-  virtual void stop();
-
-  virtual PN_stdfloat get_time() const;
-
-  virtual void set_volume(PN_stdfloat volume=1.0f);
-  virtual void set_balance(PN_stdfloat balance_right=0.0f);
-  virtual void set_play_rate(PN_stdfloat play_rate=1.0f);
-
-  virtual PN_stdfloat length() const;
-
-  virtual AudioSound::SoundStatus status() const;
-
-  virtual void cleanup();
-  virtual void output(std::ostream &out) const;
-
-  // 3D spatialized sound support.  Spatialized sound was originally added for
-  // FMOD, so there are parts of the interface in the Miles implementation
-  // that are a little more awkward than they would be otherwise.
-  void set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz);
-  void get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz);
-  void set_3d_min_distance(PN_stdfloat dist);
-  PN_stdfloat get_3d_min_distance() const;
-  void set_3d_max_distance(PN_stdfloat dist);
-  PN_stdfloat get_3d_max_distance() const;
-
-  virtual PN_stdfloat get_speaker_level(int index);
-  virtual void set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2=-1.0f, PN_stdfloat level3=-1.0f, PN_stdfloat level4=-1.0f, PN_stdfloat level5=-1.0f, PN_stdfloat level6=-1.0f, PN_stdfloat level7=-1.0f, PN_stdfloat level8=-1.0f, PN_stdfloat level9=-1.0f);
-
-private:
-  void internal_stop();
-  static void AILCALLBACK finish_callback(HSAMPLE sample);
-  void do_set_time(PN_stdfloat time);
-
-  PT(MilesAudioManager::SoundData) _sd;
-  HSAMPLE _sample;
-  size_t _sample_index;
-  S32 _original_playback_rate;
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    MilesAudioSound::init_type();
-    register_type(_type_handle, "MilesAudioSample",
-                  MilesAudioSound::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
-  static TypeHandle _type_handle;
-
-  friend class GlobalMilesManager;
-  friend class MilesAudioManager;
-};
-
-#include "milesAudioSample.I"
-
-#endif //]
-
-#endif  /* MILESAUDIOSAMPLE_H */

Some files were not shown because too many files changed in this diff