Browse Source

Merge branch 'master' into cmake

Sam Edwards 10 years ago
parent
commit
c902774cfd
100 changed files with 4484 additions and 3816 deletions
  1. 25 4
      README.md
  2. 0 1
      contrib/src/ai/p3ai_composite.cxx
  3. 6 4
      direct/src/actor/Actor.py
  4. 3 7
      direct/src/controls/GravityWalker.py
  5. 3 1
      direct/src/dcparser/dcPython.h
  6. 1 1
      direct/src/directbase/ppython.cxx
  7. 1 1
      direct/src/directnotify/Notifier.py
  8. 0 44
      direct/src/directscripts/cleancvstree
  9. 0 124
      direct/src/directscripts/make-panda3d-tgz.py
  10. 0 1
      direct/src/distributed/GridChild.py
  11. 0 97
      direct/src/extensions/CInterval-extensions.py
  12. 0 50
      direct/src/extensions/ConfigVariable-extensions.py
  13. 0 5
      direct/src/extensions/ConfigVariableBool-extensions.py
  14. 0 5
      direct/src/extensions/ConfigVariableDouble-extensions.py
  15. 0 5
      direct/src/extensions/ConfigVariableInt-extensions.py
  16. 0 18
      direct/src/extensions/ConfigVariableList-extensions.py
  17. 0 6
      direct/src/extensions/ConfigVariableString-extensions.py
  18. 0 31
      direct/src/extensions/HTTPChannel-extensions.py
  19. 0 16
      direct/src/extensions/Mat3-extensions.py
  20. 0 27
      direct/src/extensions/MouseWatcherRegion-extensions.py
  21. 0 20
      direct/src/extensions/Node-extensions.py
  22. 0 1093
      direct/src/extensions/NodePath-extensions.py
  23. 0 26
      direct/src/extensions/NurbsCurveEvaluator-extensions.py
  24. 0 36
      direct/src/extensions/NurbsSurfaceEvaluator-extensions.py
  25. 0 6
      direct/src/extensions/PandaSystem-extensions.py
  26. 0 3
      direct/src/extensions/Sources.pp
  27. 0 83
      direct/src/extensions/SpriteParticleRenderer-extensions.py
  28. 0 15
      direct/src/extensions/VBase3-extensions.py
  29. 0 15
      direct/src/extensions/VBase4-extensions.py
  30. 0 1
      direct/src/extensions/__init__.py
  31. 12 24
      direct/src/p3d/AppRunner.py
  32. 136 61
      direct/src/p3d/DeploymentTools.py
  33. 17 21
      direct/src/p3d/HostInfo.py
  34. 4 3
      direct/src/p3d/PackageInfo.py
  35. 148 63
      direct/src/p3d/Packager.py
  36. 1 1
      direct/src/p3d/coreapi.pdef
  37. 20 9
      direct/src/p3d/packp3d.py
  38. 12 4
      direct/src/p3d/panda3d.pdef
  39. 17 8
      direct/src/p3d/ppackage.py
  40. 3 3
      direct/src/p3d/thirdparty.pdef
  41. 2 2
      direct/src/particles/ParticleTest.py
  42. 17 6
      direct/src/plugin/load_plugin.cxx
  43. 4 2
      direct/src/plugin/load_plugin.h
  44. 4 3
      direct/src/plugin/p3dAuthSession.cxx
  45. 162 59
      direct/src/plugin/p3dCert.cxx
  46. 3 3
      direct/src/plugin/p3dCert.h
  47. 571 0
      direct/src/plugin/p3dCert_strings.cxx
  48. 45 0
      direct/src/plugin/p3dCert_strings.h
  49. 91 3
      direct/src/plugin/p3dInstance.cxx
  50. 12 0
      direct/src/plugin/p3dInstanceManager.I
  51. 23 6
      direct/src/plugin/p3dInstanceManager.cxx
  52. 4 1
      direct/src/plugin/p3dInstanceManager.h
  53. 98 70
      direct/src/plugin/p3dOsxSplashWindow.cxx
  54. 2 0
      direct/src/plugin/p3dOsxSplashWindow.h
  55. 4 4
      direct/src/plugin/p3dPythonMain.cxx
  56. 174 52
      direct/src/plugin/p3dPythonRun.cxx
  57. 8 3
      direct/src/plugin/p3dPythonRun.h
  58. 51 15
      direct/src/plugin/p3dSession.cxx
  59. 186 6
      direct/src/plugin/p3dSplashWindow.cxx
  60. 36 3
      direct/src/plugin/p3dSplashWindow.h
  61. 34 8
      direct/src/plugin/p3dWinSplashWindow.cxx
  62. 4 2
      direct/src/plugin/p3dWinSplashWindow.h
  63. 147 16
      direct/src/plugin/p3dX11SplashWindow.cxx
  64. 6 3
      direct/src/plugin/p3dX11SplashWindow.h
  65. 7 2
      direct/src/plugin/p3d_plugin.cxx
  66. 7 2
      direct/src/plugin/p3d_plugin.h
  67. 7 6
      direct/src/plugin/run_p3dpython.cxx
  68. 4 4
      direct/src/plugin/run_p3dpython.h
  69. 3 3
      direct/src/plugin_activex/PPInstance.cpp
  70. 2 2
      direct/src/plugin_npapi/ppInstance.cxx
  71. 23 6
      direct/src/plugin_standalone/p3dEmbed.cxx
  72. 185 135
      direct/src/plugin_standalone/panda3d.cxx
  73. 2 0
      direct/src/plugin_standalone/panda3d.h
  74. 5 5
      direct/src/plugin_standalone/panda3dBase.cxx
  75. 3 2
      direct/src/plugin_standalone/panda3dBase.h
  76. 1 1
      direct/src/showbase/Loader.py
  77. 1 1
      direct/src/showbase/PythonUtil.py
  78. 8 6
      direct/src/showbase/ShowBase.py
  79. 1 1
      direct/src/showbase/Transitions.py
  80. 60 49
      direct/src/showbase/VFSImporter.py
  81. 92 91
      direct/src/showutil/FreezeTool.py
  82. 5 1
      direct/src/showutil/Rope.py
  83. 32 17
      direct/src/showutil/pfreeze.py
  84. 213 203
      direct/src/stdpy/file.py
  85. 2 0
      direct/src/tkpanels/ParticlePanel.py
  86. 221 218
      dtool/metalibs/dtoolconfig/pydtool.cxx
  87. 541 495
      dtool/src/cppparser/cppBison.cxx.prebuilt
  88. 220 200
      dtool/src/cppparser/cppBison.h.prebuilt
  89. 274 108
      dtool/src/cppparser/cppBison.yxx
  90. 2 2
      dtool/src/cppparser/cppBisonDefs.h
  91. 3 1
      dtool/src/cppparser/cppEnumType.cxx
  92. 318 23
      dtool/src/cppparser/cppExpression.cxx
  93. 25 5
      dtool/src/cppparser/cppExpression.h
  94. 2 1
      dtool/src/cppparser/cppExtensionType.cxx
  95. 1 1
      dtool/src/cppparser/cppExtensionType.h
  96. 56 6
      dtool/src/cppparser/cppIdentifier.cxx
  97. 4 1
      dtool/src/cppparser/cppIdentifier.h
  98. 45 4
      dtool/src/cppparser/cppInstance.cxx
  99. 7 4
      dtool/src/cppparser/cppInstance.h
  100. 5 0
      dtool/src/cppparser/cppInstanceIdentifier.cxx

+ 25 - 4
README.md

@@ -81,14 +81,18 @@ sudo apt-get install build-essential pkg-config python-dev libpng-dev libjpeg-de
 ```
 ```
 
 
 Once Panda3D has built, you can either install the .deb or .rpm package that
 Once Panda3D has built, you can either install the .deb or .rpm package that
-it produced (if relevant to your platform, and you added --installer).  On
-other systems, you will need to use the installpanda script to install it onto
-your system.  Careful: it is not easy to uninstall Panda3D in this way!
+it produced, depending on which Linux distribution you are using.  For example,
+to install the package on Debian or Ubuntu, use this:
 
 
 ```bash
 ```bash
-python2.7 makepanda/installpanda.py --prefix=/usr/local
+sudo dpkg -i panda3d*.deb
 ```
 ```
 
 
+If you are not using a Linux distribution that supports .deb or .rpm packages, you
+may have to use the installpanda.py script instead, which will directly copy the
+files into the appropriate locations on your computer.  You may have to run the
+`ldconfig` tool in order to update your library cache after installing Panda3D.
+
 Mac OS X
 Mac OS X
 --------
 --------
 
 
@@ -110,3 +114,20 @@ by the release number, eg. 10.6 or 10.7.
 If the build was successful, makepanda will have generated a .dmg file in
 If the build was successful, makepanda will have generated a .dmg file in
 the source directory containing the installer.  Simply open it and run the
 the source directory containing the installer.  Simply open it and run the
 package file in order to install the SDK onto your system.
 package file in order to install the SDK onto your system.
+
+Reporting Issues
+================
+
+If you encounter any bugs when using Panda3D, please report them in the bug
+tracker.  This is hosted at:
+
+  https://bugs.launchpad.net/panda3d
+
+Make sure to first use the search function to see if the bug has already been
+reported.  When filling out a bug report, make sure that you include as much
+information as possible to help the developers track down the issue, such as
+your version of Panda3D, operating system, architecture, and any code and
+models that are necessary for the developers to reproduce the issue.
+
+If you're not sure whether you've encountered a bug, feel free to ask about
+it in the forums or the IRC channel first.

+ 0 - 1
contrib/src/ai/p3ai_composite.cxx

@@ -1 +0,0 @@
-#include "ai_composite1.cxx"

+ 6 - 4
direct/src/actor/Actor.py

@@ -23,6 +23,8 @@ class Actor(DirectObject, NodePath):
                                        LoaderOptions.LFConvertAnim)
                                        LoaderOptions.LFConvertAnim)
 
 
     validateSubparts = ConfigVariableBool('validate-subparts', True)
     validateSubparts = ConfigVariableBool('validate-subparts', True)
+    mergeLODBundles = ConfigVariableBool('merge-lod-bundles', True)
+    allowAsyncBind = ConfigVariableBool('allow-async-bind', True)
     
     
     class PartDef:
     class PartDef:
 
 
@@ -170,10 +172,10 @@ class Actor(DirectObject, NodePath):
         # ['common']; when it is false, __animControlDict has one key
         # ['common']; when it is false, __animControlDict has one key
         # per each LOD name.
         # per each LOD name.
         
         
-        if mergeLODBundles == None:
+        if mergeLODBundles is None:
             # If this isn't specified, it comes from the Config.prc
             # If this isn't specified, it comes from the Config.prc
             # file.
             # file.
-            self.mergeLODBundles = base.config.GetBool('merge-lod-bundles', True)
+            self.mergeLODBundles = Actor.mergeLODBundles.getValue()
         else:
         else:
             self.mergeLODBundles = mergeLODBundles
             self.mergeLODBundles = mergeLODBundles
 
 
@@ -181,8 +183,8 @@ class Actor(DirectObject, NodePath):
         # asynchronous animation binding.  This requires that you have
         # asynchronous animation binding.  This requires that you have
         # run "egg-optchar -preload" on your animation and models to
         # run "egg-optchar -preload" on your animation and models to
         # generate the appropriate AnimPreloadTable.
         # generate the appropriate AnimPreloadTable.
-        if allowAsyncBind == None:
-            self.allowAsyncBind = base.config.GetBool('allow-async-bind', True)
+        if allowAsyncBind is None:
+            self.allowAsyncBind = Actor.allowAsyncBind.getValue()
         else:
         else:
             self.allowAsyncBind = allowAsyncBind
             self.allowAsyncBind = allowAsyncBind
 
 

+ 3 - 7
direct/src/controls/GravityWalker.py

@@ -179,9 +179,9 @@ class GravityWalker(DirectObject.DirectObject):
         # a higher or lower value depending on whether you want an avatar
         # a higher or lower value depending on whether you want an avatar
         # that is outside of the world to step up to the floor when they
         # that is outside of the world to step up to the floor when they
         # get under valid floor:
         # get under valid floor:
-        cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
+        self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
         cRayNode = CollisionNode('GW.cRayNode')
         cRayNode = CollisionNode('GW.cRayNode')
-        cRayNode.addSolid(cRay)
+        cRayNode.addSolid(self.cRay)
         self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode)
         self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode)
         cRayNode.setFromCollideMask(bitmask)
         cRayNode.setFromCollideMask(bitmask)
         cRayNode.setIntoCollideMask(BitMask32.allOff())
         cRayNode.setIntoCollideMask(BitMask32.allOff())
@@ -697,8 +697,4 @@ class GravityWalker(DirectObject.DirectObject):
     # There are sometimes issues if the collision ray height is
     # There are sometimes issues if the collision ray height is
     # so tall that it collides with multiple levels of floors.
     # so tall that it collides with multiple levels of floors.
     def setCollisionRayHeight(self, height):
     def setCollisionRayHeight(self, height):
-        oldNode = self.avatarNodePath.getNode(0)
-        cRayNode = oldNode.getChild(2)
-        cRayNode.removeSolid(0)
-        cRay = CollisionRay(0.0, 0.0, height, 0.0, 0.0, -1.0)
-        cRayNode.addSolid(cRay)        
+        self.cRay.setOrigin(0.0, 0.0, height)

+ 3 - 1
direct/src/dcparser/dcPython.h

@@ -20,8 +20,10 @@
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
 
 
-#undef HAVE_LONG_LONG  // NSPR and Python both define this.
+#define PY_SSIZE_T_CLEAN 1
+
 #undef _POSIX_C_SOURCE
 #undef _POSIX_C_SOURCE
+#undef _XOPEN_SOURCE
 #include <Python.h>
 #include <Python.h>
 
 
 // Python 2.5 adds Py_ssize_t; earlier versions don't have it.
 // Python 2.5 adds Py_ssize_t; earlier versions don't have it.

+ 1 - 1
direct/src/directbase/ppython.cxx

@@ -36,7 +36,7 @@ int main(int argc, char *mb_argv[]) {
     size_t len = mbstowcs(NULL, mb_argv[i], 0);
     size_t len = mbstowcs(NULL, mb_argv[i], 0);
     argv[i] = new wchar_t[len + 1];
     argv[i] = new wchar_t[len + 1];
     mbstowcs(argv[i], mb_argv[i], len);
     mbstowcs(argv[i], mb_argv[i], len);
-    argv[i][len] = NULL;
+    argv[i][len] = 0;
   }
   }
   // Just for good measure
   // Just for good measure
   argv[argc] = NULL;
   argv[argc] = NULL;

+ 1 - 1
direct/src/directnotify/Notifier.py

@@ -235,7 +235,7 @@ class Notifier:
         Prints the string to output followed by a newline.
         Prints the string to output followed by a newline.
         """
         """
         if self.streamWriter:
         if self.streamWriter:
-            self.streamWriter.appendData(string + '\n')
+            self.streamWriter.write(string + '\n')
         else:
         else:
             print >> sys.stderr, string
             print >> sys.stderr, string
 
 

+ 0 - 44
direct/src/directscripts/cleancvstree

@@ -1,44 +0,0 @@
-##############################################################################
-#
-# cleancvstree
-#
-# Cleancvstree searches a CVS tree for files that are not in CVS, and
-# deletes them.  Be careful using it --- it's very aggressive.
-#
-##############################################################################
-
-import sys,os
-
-def cleanCvsTree(dir):
-    try:
-        sub = os.listdir(dir)
-    except:
-	print "Could not read directory: "+dir
-	return
-    valid = {}
-    try:
-	readentries = 0
-        cvsent = open(dir + "/CVS/Entries")
-        for line in cvsent:
-            words = line.split("/")
-            if (len(words) > 1):
-                valid[words[1]] = 1
-        cvsent.close()
-	readentries = 1
-    except:
-	print "Could not read "+dir+"/CVS/Entries"
-    if (readentries):
-	for file in sub:
-	    if (os.path.isfile(dir+"/"+file)):
-                if (valid.has_key(file)==0):
-		    os.unlink(dir+"/"+file)
-    for file in sub:
-	if (file != "CVS"):
-            if (os.path.isdir(dir+"/"+file)):
-                cleanCvsTree(dir+"/"+file)
-
-if (os.path.isdir(sys.argv[1])==0):
-    print "Not a directory: "+sys.argv[1]
-    os.exit(1)
-
-cleanCvsTree(sys.argv[1])

+ 0 - 124
direct/src/directscripts/make-panda3d-tgz.py

@@ -1,124 +0,0 @@
-#!/usr/bin/env python
-
-"""This script generates the panda3d-date.tar.gz tarball for a file
-release of panda3d onto the SourceForge download site.
-
-Options:
-
-    -d cvsroot
-        Specifies the CVSROOT string to use to tag and export the
-        tree.  The default is $SFROOT if it is defined, or $CVSROOT
-        otherwise.
-
-    -r tag
-        Specifies the tag to export from.  If this parameter is
-        specified, the tree is not tagged again; otherwise, the
-        current head of the CVS tree is tagged with the file version
-        name.
-
-    -m module
-        Specifies the module to check out and build.  The default is
-        panda3d.
-"""
-
-import sys
-import os
-import os.path
-import getopt
-import time
-import glob
-import shutil
-
-CVSROOT = os.getenv('SFROOT') or os.getenv('CVSROOT')
-ORIGTAG = ''
-MODULE  = 'panda3d'
-
-def usage(code, msg = ''):
-    print >> sys.stderr, __doc__
-    print >> sys.stderr, msg
-    sys.exit(code)
-
-try:
-    opts, args = getopt.getopt(sys.argv[1:], 'd:r:m:h')
-except getopt.error, msg:
-    usage(1, msg)
-
-for opt, arg in opts:
-    if opt == '-d':
-        CVSROOT = arg
-    elif opt == '-r':
-        ORIGTAG = arg
-    elif opt == '-m':
-        MODULE = arg
-    elif opt == '-h':
-        usage(0)
-
-if not CVSROOT:
-    usage(1, 'CVSROOT must have a value.')
-
-if not MODULE:
-    usage(1, 'MODULE must have a value.')
-
-basename = MODULE + '-' + time.strftime("%Y-%m-%d")
-tarfile = basename + '.tar.gz'
-zipfile = basename + '.zip'
-
-if os.path.exists(basename):
-    print basename, 'already exists in the local directory!'
-    sys.exit(1)
-
-if not ORIGTAG:
-    # If we weren't given a starting tag, make one.
-    tag = basename
-
-    print 'Tagging sources.'
-    cmd = 'cvs -f -d "%s" rtag -F -r HEAD "%s" "%s"' % (CVSROOT, tag, MODULE)
-    if os.system(cmd) != 0:
-        sys.exit(1)
-else:
-    # Otherwise, we were given a starting tag, so use it.
-    tag = ORIGTAG
-
-print 'Checking out "%s" as "%s".' % (MODULE, basename)
-cmd = 'cvs -z3 -f -d "%s" export -r "%s" -d "%s" "%s"' % (CVSROOT, tag,
-                                                          basename, MODULE)
-if os.system(cmd) != 0:
-    sys.exit(1)
-
-# Move the contents of the doc module into the root directory where people
-# will expect to see it.
-docdir = basename + os.sep + 'doc'
-if os.path.exists(docdir):
-    files = glob.glob(docdir + os.sep + '*')
-    for file in files:
-        shutil.copy(file, basename)
-        os.remove(file)
-    os.rmdir(docdir)
-
-# Generate the autoconf scripts for ppremake.
-if MODULE == 'ppremake':
-    ppremakedir = basename
-else:
-    ppremakedir = basename + os.sep + 'ppremake'
-if os.path.exists(ppremakedir):
-    cmd = 'cd "./%s" && aclocal && autoheader && automake --foreign -a && autoconf' % (ppremakedir)
-    if os.system(cmd) != 0:
-        sys.exit(1)
-
-# Generate the tarball.
-print 'Generating %s' % (tarfile)
-if os.path.exists(tarfile):
-    os.remove(tarfile)
-cmd = 'tar cf - "%s" | gzip -9 > "%s"' % (basename, tarfile)
-if os.system(cmd) != 0:
-    sys.exit(1)
-
-# Also generate a zip file.
-print 'Generating %s' % (zipfile)
-if os.path.exists(zipfile):
-    os.remove(zipfile)
-cmd = 'zip -9rq "%s" "%s"' % (zipfile, basename)
-if os.system(cmd) != 0:
-    sys.exit(1)
-
-shutil.rmtree(basename)

+ 0 - 1
direct/src/distributed/GridChild.py

@@ -1,6 +1,5 @@
 from direct.distributed.DistributedSmoothNodeBase import DistributedSmoothNodeBase
 from direct.distributed.DistributedSmoothNodeBase import DistributedSmoothNodeBase
 from direct.distributed.GridParent import GridParent
 from direct.distributed.GridParent import GridParent
-from pandac.PandaModules import EmbeddedValue
 
 
 class GridChild:
 class GridChild:
     """
     """

+ 0 - 97
direct/src/extensions/CInterval-extensions.py

@@ -1,97 +0,0 @@
-
-    """
-    CInterval-extensions module: contains methods to extend functionality
-    of the CInterval class
-    """
-
-    from direct.directnotify.DirectNotifyGlobal import directNotify
-    notify = directNotify.newCategory("Interval")
-
-    def setT(self, t):
-        # Overridden from the C++ function to call privPostEvent
-        # afterward.  We do this by renaming the C++ function in
-        # FFIRename.
-        self._priv__cSetT(t)
-        self.privPostEvent()
-
-    def play(self, t0 = 0.0, duration = None, scale = 1.0):
-        self.notify.error("using deprecated CInterval.play() interface")
-        if duration:  # None or 0 implies full length
-            self.start(t0, t0 + duration, scale)
-        else:
-            self.start(t0, -1, scale)
-
-    def stop(self):
-        self.notify.error("using deprecated CInterval.stop() interface")
-        self.finish()
-
-    def setFinalT(self):
-        self.notify.error("using deprecated CInterval.setFinalT() interface")
-        self.finish()
-
-    def privPostEvent(self):
-        # Call after calling any of the priv* methods to do any required
-        # Python finishing steps.
-        t = self.getT()
-        if hasattr(self, "setTHooks"):
-            for func in self.setTHooks:
-                func(t)
-
-    def popupControls(self, tl = None):
-        """
-        Popup control panel for interval.
-        """
-        from direct.showbase.TkGlobal import Toplevel, Frame, Button, LEFT, X, Pmw
-        import math
-        from direct.tkwidgets import EntryScale
-        if tl == None:
-            tl = Toplevel()
-            tl.title('Interval Controls')
-        outerFrame = Frame(tl)
-        def entryScaleCommand(t, s=self):
-            s.setT(t)
-            s.pause()
-        self.es = es = EntryScale.EntryScale(
-            outerFrame, text = self.getName(),
-            min = 0, max = math.floor(self.getDuration() * 100) / 100,
-            command = entryScaleCommand)
-        es.set(self.getT(), fCommand = 0)
-        es.pack(expand = 1, fill = X)
-        bf = Frame(outerFrame)
-        # Jump to start and end
-        def toStart(s=self, es=es):
-            s.clearToInitial()
-            s.setT(0)
-            es.set(0, fCommand = 0)
-        def toEnd(s=self):
-            s.setT(s.getDuration())
-            s.pause()
-        jumpToStart = Button(bf, text = '<<', command = toStart)
-        # Stop/play buttons
-        def doPlay(s=self, es=es):
-            s.resume(es.get())
-
-        stop = Button(bf, text = 'Stop',
-                      command = lambda s=self: s.pause())
-        play = Button(
-            bf, text = 'Play',
-            command = doPlay)
-        jumpToEnd = Button(bf, text = '>>', command = toEnd)
-        jumpToStart.pack(side = LEFT, expand = 1, fill = X)
-        play.pack(side = LEFT, expand = 1, fill = X)
-        stop.pack(side = LEFT, expand = 1, fill = X)
-        jumpToEnd.pack(side = LEFT, expand = 1, fill = X)
-        bf.pack(expand = 1, fill = X)
-        outerFrame.pack(expand = 1, fill = X)
-        # Add function to update slider during setT calls
-        def update(t, es=es):
-            es.set(t, fCommand = 0)
-        if not hasattr(self, "setTHooks"):
-            self.setTHooks = []
-        self.setTHooks.append(update)
-        self.setWantsTCallback(1)
-        # Clear out function on destroy
-        def onDestroy(e, s=self, u=update):
-            if u in s.setTHooks:
-                s.setTHooks.remove(u)
-        tl.bind('<Destroy>', onDestroy)

+ 0 - 50
direct/src/extensions/ConfigVariable-extensions.py

@@ -1,50 +0,0 @@
-
-    def __str__(self):
-        return self.getStringValue()
-
-    def __hash__(self):
-        raise AttributeError, "ConfigVariables are not immutable."
-
-    def ls(self):
-        from pandac.Notify import Notify
-        self.write(Notify.out())
-
-    def __int__(self):
-        return int(self.getValue())
-
-    def __long__(self):
-        return long(self.getValue())
-
-    def __float__(self):
-        return float(self.getValue())
-
-    def __nonzero__(self):
-        return bool(self.getValue())
-
-    def __oct__(self):
-        return oct(self.getValue())
-
-    def __hex__(self):
-        return hex(self.getValue())
-
-    def __cmp__(self, other):
-        return self.getValue().__cmp__(other)
-
-    def __neg__(self):
-        return -self.getValue()
-
-    def __coerce__(self, other):
-        return (self.getValue(), other)
-
-    def __len__(self):
-        return self.getNumWords()
-    
-    def __getitem__(self, n):
-        if n < 0 or n >= self.getNumWords():
-            raise IndexError
-        return self.getWord(n)
-
-    def __setitem__(self, n, value):
-        if n < 0 or n > self.getNumWords():
-            raise IndexError
-        self.setWord(n, value)

+ 0 - 5
direct/src/extensions/ConfigVariableBool-extensions.py

@@ -1,5 +0,0 @@
-    
-    def __getitem__(self, n):
-        if n < 0 or n >= self.getNumWords():
-            raise IndexError
-        return self.getWord(n)

+ 0 - 5
direct/src/extensions/ConfigVariableDouble-extensions.py

@@ -1,5 +0,0 @@
-    
-    def __getitem__(self, n):
-        if n < 0 or n >= self.getNumWords():
-            raise IndexError
-        return self.getWord(n)

+ 0 - 5
direct/src/extensions/ConfigVariableInt-extensions.py

@@ -1,5 +0,0 @@
-    
-    def __getitem__(self, n):
-        if n < 0 or n >= self.getNumWords():
-            raise IndexError
-        return self.getWord(n)

+ 0 - 18
direct/src/extensions/ConfigVariableList-extensions.py

@@ -1,18 +0,0 @@
-
-    def __hash__(self):
-        raise AttributeError, "ConfigVariables are not immutable."
-
-    def ls(self):
-        from pandac.Notify import Notify
-        self.write(Notify.out())
-
-    def __cmp__(self, other):
-        return list(self).__cmp__(list(other))
-
-    def __len__(self):
-        return self.getNumValues()
-    
-    def __getitem__(self, n):
-        if n < 0 or n >= self.getNumUniqueValues():
-            raise IndexError
-        return self.getUniqueValue(n)

+ 0 - 6
direct/src/extensions/ConfigVariableString-extensions.py

@@ -1,6 +0,0 @@
-
-    def __len__(self):
-        return self.getValue().__len__()
-    
-    def __getitem__(self, n):
-        return self.getValue().__getitem__(n)

+ 0 - 31
direct/src/extensions/HTTPChannel-extensions.py

@@ -1,31 +0,0 @@
-
-    """
-    HTTPChannel-extensions module: contains methods to extend functionality
-    of the HTTPChannel class
-    """
-
-    def spawnTask(self, name = None, callback = None, extraArgs = []):
-        """Spawns a task to service the download recently requested
-        via beginGetDocument(), etc., and/or downloadToFile() or
-        downloadToRam().  If a callback is specified, that function is
-        called when the download is complete, passing in the extraArgs
-        given.
-
-        Returns the newly-spawned task.
-        """
-        if not name:
-            name = str(self.getUrl())
-        from direct.task import Task
-        task = Task.Task(self.doTask)
-        task.callback = callback
-        task.callbackArgs = extraArgs
-        return taskMgr.add(task, name)    
-        
-    def doTask(self, task):
-        from direct.task import Task
-        if self.run():
-            return Task.cont
-        if task.callback:
-            task.callback(*task.callbackArgs)
-        return Task.done
-    

+ 0 - 16
direct/src/extensions/Mat3-extensions.py

@@ -1,16 +0,0 @@
-
-    """
-    Mat3-extensions module: contains methods to extend functionality
-    of the LMatrix3f class.
-    """
-
-    def __repr__(self):
-        return '%s(\n%s,\n%s,\n%s)' % (
-            self.__class__.__name__, self.getRow(0).pPrintValues(), self.getRow(1).pPrintValues(), self.getRow(2).pPrintValues())
-
-    def pPrintValues(self):
-        """
-        Pretty print
-        """
-        return "\n%s\n%s\n%s" % (
-            self.getRow(0).pPrintValues(), self.getRow(1).pPrintValues(), self.getRow(2).pPrintValues())

+ 0 - 27
direct/src/extensions/MouseWatcherRegion-extensions.py

@@ -1,27 +0,0 @@
-
-    """
-    MouseWatcherRegion-extensions module: contains methods to extend
-    functionality of the MouseWatcherRegion class
-    """
-
-    def setRelative(self, np, left, right, bottom, top):
-        """setRelation(NodePath np, float left, float right,
-                       float bottom, float top)
-
-        Sets the region to represnt the indicated rectangle, relative
-        to the given NodePath.  It is assumed that np represents some
-        node parented within the render2d hierarchy.
-
-        """
-        from pandac import Point3
-        
-        # Get the relative transform to the node.
-        mat = np.getMat(render2d)
-
-        # Use this matrix to transform the corners of the region.
-        ll = mat.xformPoint(Point3.Point3(left, 0, bottom))
-        ur = mat.xformPoint(Point3.Point3(right, 0, top))
-
-        # Set the frame to the transformed coordinates.
-        self.setFrame(ll[0], ur[0], ll[2], ur[2])
-        

+ 0 - 20
direct/src/extensions/Node-extensions.py

@@ -1,20 +0,0 @@
-
-    """
-    Node-extensions module: contains methods to extend functionality
-    of the Node class
-    """
-
-    def isHidden(self):
-        """Determine if a node is hidden.  Just pick the first parent,
-        since this is an ambiguous question for instanced nodes"""
-        import RenderRelation
-        import PruneTransition
-        rrClass = RenderRelation.RenderRelation.getClassType()
-        if self.getNumParents(rrClass) > 0:
-            arc = self.getParent(rrClass, 0)
-            if arc.hasTransition(PruneTransition.PruneTransition.getClassType()):
-                return 1
-            else:
-                return arc.getParent().isHidden()
-        else:
-            return 0

+ 0 - 1093
direct/src/extensions/NodePath-extensions.py

@@ -1,1093 +0,0 @@
-
-    """
-    NodePath-extensions module: contains methods to extend functionality
-    of the NodePath class
-    """
-
-    def id(self):
-        """Returns a unique id identifying the NodePath instance"""
-        return self.getKey()
-
-    def __hash__(self):
-        return self.getKey()
-
-    # For iterating over children
-    def getChildrenAsList(self):
-        """Converts a node path's child NodePathCollection into a list"""
-        return self.getChildren()
-
-    def printChildren(self):
-        """Prints out the children of the bottom node of a node path"""
-        for child in self.getChildren():
-            print child.getName()
-
-    def removeChildren(self):
-        """Deletes the children of the bottom node of a node path"""
-        for child in self.getChildren():
-            child.removeNode()
-
-    def toggleVis(self):
-        """Toggles visibility of a nodePath"""
-        if self.isHidden():
-            self.show()
-            return 1
-        else:
-            self.hide()
-            return 0
-
-    def showSiblings(self):
-        """Show all the siblings of a node path"""
-        for sib in self.getParent().getChildren():
-            if sib.node() != self.node():
-                sib.show()
-
-    def hideSiblings(self):
-        """Hide all the siblings of a node path"""
-        for sib in self.getParent().getChildren():
-            if sib.node() != self.node():
-                sib.hide()
-
-    def showAllDescendants(self):
-        """Show the node path and all its children"""
-        self.show()
-        for child in self.getChildren():
-            child.showAllDescendants()
-
-    def isolate(self):
-        """Show the node path and hide its siblings"""
-        self.showAllDescendants()
-        self.hideSiblings()
-
-    def remove(self):
-        """Remove a node path from the scene graph"""
-        # Send message in case anyone needs to do something
-        # before node is deleted
-        messenger.send('preRemoveNodePath', [self])
-        # Remove nodePath
-        self.removeNode()
-
-    def lsNames(self):
-        """Walk down a tree and print out the path"""
-        if self.isEmpty():
-            print "(empty)"
-        else:
-            type = self.node().getType().getName()
-            name = self.getName()
-            print type + "  " + name
-            self.lsNamesRecurse()
-
-    def lsNamesRecurse(self, indentString=' '):
-        """Walk down a tree and print out the path"""
-        for nodePath in self.getChildren():
-            type = nodePath.node().getType().getName()
-            name = nodePath.getName()
-            print indentString + type + "  " + name
-            nodePath.lsNamesRecurse(indentString + " ")
-
-    def reverseLsNames(self):
-        """Walk up a tree and print out the path to the root"""
-        ancestry = self.getAncestry()
-        indentString = ""
-        for nodePath in ancestry:
-            type = nodePath.node().getType().getName()
-            name = nodePath.getName()
-            print indentString + type + "  " + name
-            indentString = indentString + " "
-
-    def getAncestry(self):
-        """Get a list of a node path's ancestors"""
-        node = self.node()
-        if (self.hasParent()):
-            ancestry = self.getParent().getAncestry()
-            ancestry.append(self)
-            return ancestry
-        else:
-            return [self]
-
-    def getTightBounds(self):
-        from pandac import Point3
-        v1 = Point3.Point3(0)
-        v2 = Point3.Point3(0)
-        self.calcTightBounds(v1, v2)
-        return v1, v2
-
-    def pPrintString(self, other = None):
-        """
-        pretty print
-        """
-        if __debug__:
-            # Normally I would have put the if __debug__ around
-            # the entire funciton, the that doesn't seem to work
-            # with -extensions.  Maybe someone will look into
-            # this further.
-            if other:
-                pos = self.getPos(other)
-                hpr = self.getHpr(other)
-                scale = self.getScale(other)
-                shear = self.getShear(other)
-                otherString = "  'other': %s,\n" % (other.getName(),)
-            else:
-                pos = self.getPos()
-                hpr = self.getHpr()
-                scale = self.getScale()
-                shear = self.getShear()
-                otherString = '\n'
-            return (
-                "%s = {"%(self.getName()) +
-                otherString +
-                "  'Pos':   (%s),\n" % pos.pPrintValues() +
-                "  'Hpr':   (%s),\n" % hpr.pPrintValues() +
-                "  'Scale': (%s),\n" % scale.pPrintValues() +
-                "  'Shear': (%s),\n" % shear.pPrintValues() +
-                "}")
-
-    def printPos(self, other = None, sd = 2):
-        """ Pretty print a node path's pos """
-        formatString = '%0.' + '%d' % sd + 'f'
-        if other:
-            pos = self.getPos(other)
-            otherString = other.getName() + ', '
-        else:
-            pos = self.getPos()
-            otherString = ''
-        print (self.getName() + '.setPos(' + otherString +
-               formatString % pos[0] + ', ' +
-               formatString % pos[1] + ', ' +
-               formatString % pos[2] +
-               ')\n')
-
-    def printHpr(self, other = None, sd = 2):
-        """ Pretty print a node path's hpr """
-        formatString = '%0.' + '%d' % sd + 'f'
-        if other:
-            hpr = self.getHpr(other)
-            otherString = other.getName() + ', '
-        else:
-            hpr = self.getHpr()
-            otherString = ''
-        print (self.getName() + '.setHpr(' + otherString +
-               formatString % hpr[0] + ', ' +
-               formatString % hpr[1] + ', ' +
-               formatString % hpr[2] +
-               ')\n')
-
-    def printScale(self, other = None, sd = 2):
-        """ Pretty print a node path's scale """
-        formatString = '%0.' + '%d' % sd + 'f'
-        if other:
-            scale = self.getScale(other)
-            otherString = other.getName() + ', '
-        else:
-            scale = self.getScale()
-            otherString = ''
-        print (self.getName() + '.setScale(' + otherString +
-               formatString % scale[0] + ', ' +
-               formatString % scale[1] + ', ' +
-               formatString % scale[2] +
-               ')\n')
-
-    def printPosHpr(self, other = None, sd = 2):
-        """ Pretty print a node path's pos and, hpr """
-        formatString = '%0.' + '%d' % sd + 'f'
-        if other:
-            pos = self.getPos(other)
-            hpr = self.getHpr(other)
-            otherString = other.getName() + ', '
-        else:
-            pos = self.getPos()
-            hpr = self.getHpr()
-            otherString = ''
-        print (self.getName() + '.setPosHpr(' + otherString +
-               formatString % pos[0] + ', ' +
-               formatString % pos[1] + ', ' +
-               formatString % pos[2] + ', ' +
-               formatString % hpr[0] + ', ' +
-               formatString % hpr[1] + ', ' +
-               formatString % hpr[2] +
-               ')\n')
-
-    def printPosHprScale(self, other = None, sd = 2):
-        """ Pretty print a node path's pos, hpr, and scale """
-        formatString = '%0.' + '%d' % sd + 'f'
-        if other:
-            pos = self.getPos(other)
-            hpr = self.getHpr(other)
-            scale = self.getScale(other)
-            otherString = other.getName() + ', '
-        else:
-            pos = self.getPos()
-            hpr = self.getHpr()
-            scale = self.getScale()
-            otherString = ''
-        print (self.getName() + '.setPosHprScale(' + otherString +
-               formatString % pos[0] + ', ' +
-               formatString % pos[1] + ', ' +
-               formatString % pos[2] + ', ' +
-               formatString % hpr[0] + ', ' +
-               formatString % hpr[1] + ', ' +
-               formatString % hpr[2] + ', ' +
-               formatString % scale[0] + ', ' +
-               formatString % scale[1] + ', ' +
-               formatString % scale[2] +
-               ')\n')
-
-    def printTransform(self, other = None, sd = 2, fRecursive = 0):
-        from pandac.PandaModules import Vec3
-        fmtStr = '%%0.%df' % sd
-        name = self.getName()
-        if other == None:
-            transform = self.getTransform()
-        else:
-            transform = self.getTransform(other)
-        if transform.hasPos():
-            pos = transform.getPos()
-            if not pos.almostEqual(Vec3(0)):
-                outputString = '%s.setPos(%s, %s, %s)' % (name, fmtStr, fmtStr, fmtStr)
-                print outputString % (pos[0], pos[1], pos[2])
-        if transform.hasHpr():
-            hpr = transform.getHpr()
-            if not hpr.almostEqual(Vec3(0)):
-                outputString = '%s.setHpr(%s, %s, %s)' % (name, fmtStr, fmtStr, fmtStr)
-                print outputString % (hpr[0], hpr[1], hpr[2])
-        if transform.hasScale():
-            if transform.hasUniformScale():
-                scale = transform.getUniformScale()
-                if scale != 1.0:
-                    outputString = '%s.setScale(%s)' % (name, fmtStr)
-                    print outputString % scale
-            else:
-                scale = transform.getScale()
-                if not scale.almostEqual(Vec3(1)):
-                    outputString = '%s.setScale(%s, %s, %s)' % (name, fmtStr, fmtStr, fmtStr)
-                    print outputString % (scale[0], scale[1], scale[2])
-        if fRecursive:
-            for child in self.getChildren():
-                child.printTransform(other, sd, fRecursive)
-
-    def iPos(self, other = None):
-        """ Set node path's pos to 0, 0, 0 """
-        if other:
-            self.setPos(other, 0, 0, 0)
-        else:
-            self.setPos(0, 0, 0)
-
-    def iHpr(self, other = None):
-        """ Set node path's hpr to 0, 0, 0 """
-        if other:
-            self.setHpr(other, 0, 0, 0)
-        else:
-            self.setHpr(0, 0, 0)
-
-    def iScale(self, other = None):
-        """ SEt node path's scale to 1, 1, 1 """
-        if other:
-            self.setScale(other, 1, 1, 1)
-        else:
-            self.setScale(1, 1, 1)
-
-    def iPosHpr(self, other = None):
-        """ Set node path's pos and hpr to 0, 0, 0 """
-        if other:
-            self.setPosHpr(other, 0, 0, 0, 0, 0, 0)
-        else:
-            self.setPosHpr(0, 0, 0, 0, 0, 0)
-
-    def iPosHprScale(self, other = None):
-        """ Set node path's pos and hpr to 0, 0, 0 and scale to 1, 1, 1 """
-        if other:
-            self.setPosHprScale(other, 0, 0, 0, 0, 0, 0, 1, 1, 1)
-        else:
-            self.setPosHprScale(0, 0, 0, 0, 0, 0, 1, 1, 1)
-
-    # private methods
-
-    def __lerp(self, functorFunc, duration, blendType, taskName=None):
-        """
-        __lerp(self, functorFunc, float, string, string)
-        Basic lerp functionality used by other lerps.
-        Fire off a lerp. Make it a task if taskName given.
-        """
-        # functorFunc is a function which can be called to create a functor.
-        # functor creation is defered so initial state (sampled in functorFunc)
-        # will be appropriate for the time the lerp is spawned
-        from direct.task import Task
-        from direct.interval import LerpBlendHelpers
-        from direct.task.TaskManagerGlobal import taskMgr
-
-        # upon death remove the functorFunc
-        def lerpUponDeath(task):
-            # Try to break circular references
-            try:
-                del task.functorFunc
-            except:
-                pass
-            try:
-                del task.lerp
-            except:
-                pass
-
-        # make the task function
-        def lerpTaskFunc(task):
-            from pandac.Lerp import Lerp
-            from pandac.ClockObject import ClockObject
-            from direct.task.Task import Task, cont, done
-            if task.init == 1:
-                # make the lerp
-                functor = task.functorFunc()
-                task.lerp = Lerp(functor, task.duration, task.blendType)
-                task.init = 0
-            dt = globalClock.getDt()
-            task.lerp.setStepSize(dt)
-            task.lerp.step()
-            if (task.lerp.isDone()):
-                # Reset the init flag, in case the task gets re-used
-                task.init = 1
-                return(done)
-            else:
-                return(cont)
-
-        # make the lerp task
-        lerpTask = Task.Task(lerpTaskFunc)
-        lerpTask.init = 1
-        lerpTask.functorFunc = functorFunc
-        lerpTask.duration = duration
-        lerpTask.blendType = LerpBlendHelpers.getBlend(blendType)
-        lerpTask.setUponDeath(lerpUponDeath)
-
-        if (taskName == None):
-            # don't spawn a task, return one instead
-            return lerpTask
-        else:
-            # spawn the lerp task
-            taskMgr.add(lerpTask, taskName)
-            return lerpTask
-
-    def __autoLerp(self, functorFunc, time, blendType, taskName):
-        """_autoLerp(self, functor, float, string, string)
-        This lerp uses C++ to handle the stepping. Bonus is
-        its more efficient, trade-off is there is less control"""
-        from pandac import AutonomousLerp
-        from direct.interval import LerpBlendHelpers
-        # make a lerp that lives in C++ land
-        functor = functorFunc()
-        lerp = AutonomousLerp.AutonomousLerp(functor, time,
-                              LerpBlendHelpers.getBlend(blendType),
-                              base.eventHandler)
-        lerp.start()
-        return lerp
-
-
-    # user callable lerp methods
-    def lerpColor(self, *posArgs, **keyArgs):
-        """
-        determine which lerpColor* to call based on arguments
-        """
-        if (len(posArgs) == 2):
-            return apply(self.lerpColorVBase4, posArgs, keyArgs)
-        elif (len(posArgs) == 3):
-            return apply(self.lerpColorVBase4VBase4, posArgs, keyArgs)
-        elif (len(posArgs) == 5):
-            return apply(self.lerpColorRGBA, posArgs, keyArgs)
-        elif (len(posArgs) == 9):
-            return apply(self.lerpColorRGBARGBA, posArgs, keyArgs)
-        else:
-            # bad args
-            raise Exception("Error: NodePath.lerpColor: bad number of args")
-
-
-    def lerpColorRGBA(self, r, g, b, a, time,
-                      blendType="noBlend", auto=None, task=None):
-        """lerpColorRGBA(self, float, float, float, float, float,
-        string="noBlend", string=none, string=none)
-        """
-        def functorFunc(self = self, r = r, g = g, b = b, a = a):
-            from pandac import ColorLerpFunctor
-            # just end rgba values, use current color rgba values for start
-            startColor = self.getColor()
-            functor = ColorLerpFunctor.ColorLerpFunctor(
-                self,
-                startColor[0], startColor[1],
-                startColor[2], startColor[3],
-                r, g, b, a)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorRGBARGBA(self, sr, sg, sb, sa, er, eg, eb, ea, time,
-                          blendType="noBlend", auto=None, task=None):
-        """lerpColorRGBARGBA(self, float, float, float, float, float,
-        float, float, float, float, string="noBlend", string=none, string=none)
-        """
-        def functorFunc(self = self, sr = sr, sg = sg, sb = sb, sa = sa,
-                        er = er, eg = eg, eb = eb, ea = ea):
-            from pandac import ColorLerpFunctor
-            # start and end rgba values
-            functor = ColorLerpFunctor.ColorLerpFunctor(self, sr, sg, sb, sa,
-                                                        er, eg, eb, ea)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorVBase4(self, endColor, time,
-                        blendType="noBlend", auto=None, task=None):
-        """lerpColorVBase4(self, VBase4, float, string="noBlend", string=none,
-        string=none)
-        """
-        def functorFunc(self = self, endColor = endColor):
-            from pandac import ColorLerpFunctor
-            # just end vec4, use current color for start
-            startColor = self.getColor()
-            functor = ColorLerpFunctor.ColorLerpFunctor(
-                self, startColor, endColor)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorVBase4VBase4(self, startColor, endColor, time,
-                          blendType="noBlend", auto=None, task=None):
-        """lerpColorVBase4VBase4(self, VBase4, VBase4, float, string="noBlend",
-        string=none, string=none)
-        """
-        def functorFunc(self = self, startColor = startColor,
-                        endColor = endColor):
-            from pandac import ColorLerpFunctor
-            # start color and end vec
-            functor = ColorLerpFunctor.ColorLerpFunctor(
-                self, startColor, endColor)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-
-    # user callable lerp methods
-    def lerpColorScale(self, *posArgs, **keyArgs):
-        """lerpColorScale(self, *positionArgs, **keywordArgs)
-        determine which lerpColorScale* to call based on arguments
-        """
-        if (len(posArgs) == 2):
-            return apply(self.lerpColorScaleVBase4, posArgs, keyArgs)
-        elif (len(posArgs) == 3):
-            return apply(self.lerpColorScaleVBase4VBase4, posArgs, keyArgs)
-        elif (len(posArgs) == 5):
-            return apply(self.lerpColorScaleRGBA, posArgs, keyArgs)
-        elif (len(posArgs) == 9):
-            return apply(self.lerpColorScaleRGBARGBA, posArgs, keyArgs)
-        else:
-            # bad args
-            raise Exception("Error: NodePath.lerpColorScale: bad number of args")
-
-
-    def lerpColorScaleRGBA(self, r, g, b, a, time,
-                      blendType="noBlend", auto=None, task=None):
-        """lerpColorScaleRGBA(self, float, float, float, float, float,
-        string="noBlend", string=none, string=none)
-        """
-        def functorFunc(self = self, r = r, g = g, b = b, a = a):
-            from pandac import ColorScaleLerpFunctor
-            # just end rgba values, use current color rgba values for start
-            startColor = self.getColor()
-            functor = ColorScaleLerpFunctor.ColorScaleLerpFunctor(
-                self,
-                startColor[0], startColor[1],
-                startColor[2], startColor[3],
-                r, g, b, a)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorScaleRGBARGBA(self, sr, sg, sb, sa, er, eg, eb, ea, time,
-                          blendType="noBlend", auto=None, task=None):
-        """lerpColorScaleRGBARGBA(self, float, float, float, float, float,
-        float, float, float, float, string="noBlend", string=none, string=none)
-        """
-        def functorFunc(self = self, sr = sr, sg = sg, sb = sb, sa = sa,
-                        er = er, eg = eg, eb = eb, ea = ea):
-            from pandac import ColorScaleLerpFunctor
-            # start and end rgba values
-            functor = ColorScaleLerpFunctor.ColorScaleLerpFunctor(self, sr, sg, sb, sa,
-                                                        er, eg, eb, ea)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorScaleVBase4(self, endColor, time,
-                        blendType="noBlend", auto=None, task=None):
-        """lerpColorScaleVBase4(self, VBase4, float, string="noBlend", string=none,
-        string=none)
-        """
-        def functorFunc(self = self, endColor = endColor):
-            from pandac import ColorScaleLerpFunctor
-            # just end vec4, use current color for start
-            startColor = self.getColor()
-            functor = ColorScaleLerpFunctor.ColorScaleLerpFunctor(
-                self, startColor, endColor)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorScaleVBase4VBase4(self, startColor, endColor, time,
-                          blendType="noBlend", auto=None, task=None):
-        """lerpColorScaleVBase4VBase4(self, VBase4, VBase4, float, string="noBlend",
-        string=none, string=none)
-        """
-        def functorFunc(self = self, startColor = startColor,
-                        endColor = endColor):
-            from pandac import ColorScaleLerpFunctor
-            # start color and end vec
-            functor = ColorScaleLerpFunctor.ColorScaleLerpFunctor(
-                self, startColor, endColor)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-
-    def lerpHpr(self, *posArgs, **keyArgs):
-        """lerpHpr(self, *positionArgs, **keywordArgs)
-        Determine whether to call lerpHprHPR or lerpHprVBase3
-        based on first argument
-        """
-        # check to see if lerping with
-        # three floats or a VBase3
-        if (len(posArgs) == 4):
-            return apply(self.lerpHprHPR, posArgs, keyArgs)
-        elif(len(posArgs) == 2):
-            return apply(self.lerpHprVBase3, posArgs, keyArgs)
-        else:
-            # bad args
-            raise Exception("Error: NodePath.lerpHpr: bad number of args")
-
-    def lerpHprHPR(self, h, p, r, time, other=None,
-                   blendType="noBlend", auto=None, task=None, shortest=1):
-        """lerpHprHPR(self, float, float, float, float, string="noBlend",
-        string=none, string=none, NodePath=none)
-        Perform a hpr lerp with three floats as the end point
-        """
-        def functorFunc(self = self, h = h, p = p, r = r,
-                        other = other, shortest=shortest):
-            from pandac import HprLerpFunctor
-            # it's individual hpr components
-            if (other != None):
-                # lerp wrt other
-                startHpr = self.getHpr(other)
-                functor = HprLerpFunctor.HprLerpFunctor(
-                    self,
-                    startHpr[0], startHpr[1], startHpr[2],
-                    h, p, r, other)
-                if shortest:
-                    functor.takeShortest()
-            else:
-                startHpr = self.getHpr()
-                functor = HprLerpFunctor.HprLerpFunctor(
-                    self,
-                    startHpr[0], startHpr[1], startHpr[2],
-                    h, p, r)
-                if shortest:
-                    functor.takeShortest()
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpHprVBase3(self, hpr, time, other=None,
-                      blendType="noBlend", auto=None, task=None, shortest=1):
-        """lerpHprVBase3(self, VBase3, float, string="noBlend", string=none,
-        string=none, NodePath=None)
-        Perform a hpr lerp with a VBase3 as the end point
-        """
-        def functorFunc(self = self, hpr = hpr,
-                        other = other, shortest=shortest):
-            from pandac import HprLerpFunctor
-            # it's a vbase3 hpr
-            if (other != None):
-                # lerp wrt other
-                functor = HprLerpFunctor.HprLerpFunctor(
-                    self, (self.getHpr(other)), hpr, other)
-                if shortest:
-                    functor.takeShortest()
-            else:
-                functor = HprLerpFunctor.HprLerpFunctor(
-                    self, (self.getHpr()), hpr)
-                if shortest:
-                    functor.takeShortest()
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-    def lerpPos(self, *posArgs, **keyArgs):
-        """lerpPos(self, *positionArgs, **keywordArgs)
-        Determine whether to call lerpPosXYZ or lerpPosPoint3
-        based on the first argument
-        """
-        # check to see if lerping with three
-        # floats or a Point3
-        if (len(posArgs) == 4):
-            return apply(self.lerpPosXYZ, posArgs, keyArgs)
-        elif(len(posArgs) == 2):
-            return apply(self.lerpPosPoint3, posArgs, keyArgs)
-        else:
-            # bad number off args
-            raise Exception("Error: NodePath.lerpPos: bad number of args")
-
-    def lerpPosXYZ(self, x, y, z, time, other=None,
-                   blendType="noBlend", auto=None, task=None):
-        """lerpPosXYZ(self, float, float, float, float, string="noBlend",
-        string=None, NodePath=None)
-        Perform a pos lerp with three floats as the end point
-        """
-        def functorFunc(self = self, x = x, y = y, z = z, other = other):
-            from pandac import PosLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                startPos = self.getPos(other)
-                functor = PosLerpFunctor.PosLerpFunctor(self,
-                                         startPos[0], startPos[1], startPos[2],
-                                         x, y, z, other)
-            else:
-                startPos = self.getPos()
-                functor = PosLerpFunctor.PosLerpFunctor(self, startPos[0],
-                                         startPos[1], startPos[2], x, y, z)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return  self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpPosPoint3(self, pos, time, other=None,
-                      blendType="noBlend", auto=None, task=None):
-        """lerpPosPoint3(self, Point3, float, string="noBlend", string=None,
-        string=None, NodePath=None)
-        Perform a pos lerp with a Point3 as the end point
-        """
-        def functorFunc(self = self, pos = pos, other = other):
-            from pandac import PosLerpFunctor
-            if (other != None):
-                #lerp wrt other
-                functor = PosLerpFunctor.PosLerpFunctor(
-                    self, (self.getPos(other)), pos, other)
-            else:
-                functor = PosLerpFunctor.PosLerpFunctor(
-                    self, (self.getPos()), pos)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-    def lerpPosHpr(self, *posArgs, **keyArgs):
-        """lerpPosHpr(self, *positionArgs, **keywordArgs)
-        Determine whether to call lerpPosHprXYZHPR or lerpHprPoint3VBase3
-        based on first argument
-        """
-        # check to see if lerping with
-        # six floats or a Point3 and a VBase3
-        if (len(posArgs) == 7):
-            return apply(self.lerpPosHprXYZHPR, posArgs, keyArgs)
-        elif(len(posArgs) == 3):
-            return apply(self.lerpPosHprPoint3VBase3, posArgs, keyArgs)
-        else:
-            # bad number off args
-            raise Exception("Error: NodePath.lerpPosHpr: bad number of args")
-
-    def lerpPosHprPoint3VBase3(self, pos, hpr, time, other=None,
-                               blendType="noBlend", auto=None, task=None, shortest=1):
-        """lerpPosHprPoint3VBase3(self, Point3, VBase3, string="noBlend",
-        string=none, string=none, NodePath=None)
-        """
-        def functorFunc(self = self, pos = pos, hpr = hpr,
-                        other = other, shortest=shortest):
-            from pandac import PosHprLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                startPos = self.getPos(other)
-                startHpr = self.getHpr(other)
-                functor = PosHprLerpFunctor.PosHprLerpFunctor(
-                    self, startPos, pos,
-                    startHpr, hpr, other)
-                if shortest:
-                    functor.takeShortest()
-            else:
-                startPos = self.getPos()
-                startHpr = self.getHpr()
-                functor = PosHprLerpFunctor.PosHprLerpFunctor(
-                    self, startPos, pos,
-                    startHpr, hpr)
-                if shortest:
-                    functor.takeShortest()
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpPosHprXYZHPR(self, x, y, z, h, p, r, time, other=None,
-                         blendType="noBlend", auto=None, task=None, shortest=1):
-        """lerpPosHpr(self, float, string="noBlend", string=none,
-        string=none, NodePath=None)
-        """
-        def functorFunc(self = self, x = x, y = y, z = z,
-                        h = h, p = p, r = r, other = other, shortest=shortest):
-            from pandac import PosHprLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                startPos = self.getPos(other)
-                startHpr = self.getHpr(other)
-                functor = PosHprLerpFunctor.PosHprLerpFunctor(self,
-                                            startPos[0], startPos[1],
-                                            startPos[2], x, y, z,
-                                            startHpr[0], startHpr[1],
-                                            startHpr[2], h, p, r,
-                                            other)
-                if shortest:
-                    functor.takeShortest()
-            else:
-                startPos = self.getPos()
-                startHpr = self.getHpr()
-                functor = PosHprLerpFunctor.PosHprLerpFunctor(self,
-                                            startPos[0], startPos[1],
-                                            startPos[2], x, y, z,
-                                            startHpr[0], startHpr[1],
-                                            startHpr[2], h, p, r)
-                if shortest:
-                    functor.takeShortest()
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-    def lerpPosHprScale(self, pos, hpr, scale, time, other=None,
-                        blendType="noBlend", auto=None, task=None, shortest=1):
-        """lerpPosHpr(self, Point3, VBase3, float, float, string="noBlend",
-        string=none, string=none, NodePath=None)
-        Only one case, no need for extra args. Call the appropriate lerp
-        (auto, spawned, or blocking) based on how(if) a task name is given
-        """
-        def functorFunc(self = self, pos = pos, hpr = hpr,
-                        scale = scale, other = other, shortest=shortest):
-            from pandac import PosHprScaleLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                startPos = self.getPos(other)
-                startHpr = self.getHpr(other)
-                startScale = self.getScale(other)
-                functor = PosHprScaleLerpFunctor.PosHprScaleLerpFunctor(self,
-                                                 startPos, pos,
-                                                 startHpr, hpr,
-                                                 startScale, scale, other)
-                if shortest:
-                    functor.takeShortest()
-            else:
-                startPos = self.getPos()
-                startHpr = self.getHpr()
-                startScale = self.getScale()
-                functor = PosHprScaleLerpFunctor.PosHprScaleLerpFunctor(self,
-                                                 startPos, pos,
-                                                 startHpr, hpr,
-                                                 startScale, scale)
-                if shortest:
-                    functor.takeShortest()
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-    def lerpScale(self, *posArgs, **keyArgs):
-        """lerpSclae(self, *positionArgs, **keywordArgs)
-        Determine whether to call lerpScaleXYZ or lerpScaleaseV3
-        based on the first argument
-        """
-        # check to see if lerping with three
-        # floats or a Point3
-        if (len(posArgs) == 4):
-            return apply(self.lerpScaleXYZ, posArgs, keyArgs)
-        elif(len(posArgs) == 2):
-            return apply(self.lerpScaleVBase3, posArgs, keyArgs)
-        else:
-            # bad number off args
-            raise Exception("Error: NodePath.lerpScale: bad number of args")
-
-    def lerpScaleVBase3(self, scale, time, other=None,
-                        blendType="noBlend", auto=None, task=None):
-        """lerpPos(self, VBase3, float, string="noBlend", string=none,
-        string=none, NodePath=None)
-        """
-        def functorFunc(self = self, scale = scale, other = other):
-            from pandac import ScaleLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
-                                           (self.getScale(other)),
-                                           scale, other)
-            else:
-                functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
-                                           (self.getScale()), scale)
-
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpScaleXYZ(self, sx, sy, sz, time, other=None,
-                     blendType="noBlend", auto=None, task=None):
-        """lerpPos(self, float, float, float, float, string="noBlend",
-        string=none, string=none, NodePath=None)
-        """
-        def functorFunc(self = self, sx = sx, sy = sy, sz = sz, other = other):
-            from pandac import ScaleLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                startScale = self.getScale(other)
-                functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
-                                           startScale[0], startScale[1],
-                                           startScale[2], sx, sy, sz, other)
-            else:
-                startScale = self.getScale()
-                functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
-                                           startScale[0], startScale[1],
-                                           startScale[2], sx, sy, sz)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-
-
-    def place(self):
-        base.startDirect(fWantTk = 1)
-        from direct.tkpanels import Placer
-        return Placer.place(self)
-
-    def explore(self):
-        base.startDirect(fWantTk = 1)
-        from direct.tkwidgets import SceneGraphExplorer
-        return SceneGraphExplorer.explore(self)
-
-    def rgbPanel(self, cb = None):
-        base.startTk()
-        from direct.tkwidgets import Slider
-        return Slider.rgbPanel(self, cb)
-
-    def select(self):
-        base.startDirect(fWantTk = 0)
-        base.direct.select(self)
-
-    def deselect(self):
-        base.startDirect(fWantTk = 0)
-        base.direct.deselect(self)
-
-    def showCS(self, mask = None):
-        """
-        Shows the collision solids at or below this node.  If mask is
-        not None, it is a BitMask32 object (e.g. WallBitmask,
-        CameraBitmask) that indicates which particular collision
-        solids should be made visible; otherwise, all of them will be.
-        """
-        npc = self.findAllMatches('**/+CollisionNode')
-        for p in range(0, npc.getNumPaths()):
-            np = npc[p]
-            if (mask == None or (np.node().getIntoCollideMask() & mask).getWord()):
-                np.show()
-
-    def hideCS(self, mask = None):
-        """
-        Hides the collision solids at or below this node.  If mask is
-        not None, it is a BitMask32 object (e.g. WallBitmask,
-        CameraBitmask) that indicates which particular collision
-        solids should be hidden; otherwise, all of them will be.
-        """
-        npc = self.findAllMatches('**/+CollisionNode')
-        for p in range(0, npc.getNumPaths()):
-            np = npc[p]
-            if (mask == None or (np.node().getIntoCollideMask() & mask).getWord()):
-                np.hide()
-
-    def posInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosInterval(self, *args, **kw)
-
-    def hprInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpHprInterval(self, *args, **kw)
-
-    def quatInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpQuatInterval(self, *args, **kw)
-
-    def scaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpScaleInterval(self, *args, **kw)
-
-    def shearInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpShearInterval(self, *args, **kw)
-
-    def posHprInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosHprInterval(self, *args, **kw)
-
-    def posQuatInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosQuatInterval(self, *args, **kw)
-
-    def hprScaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpHprScaleInterval(self, *args, **kw)
-
-    def quatScaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpQuatScaleInterval(self, *args, **kw)
-
-    def posHprScaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosHprScaleInterval(self, *args, **kw)
-
-    def posQuatScaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosQuatScaleInterval(self, *args, **kw)
-
-    def posHprScaleShearInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosHprScaleShearInterval(self, *args, **kw)
-
-    def posQuatScaleShearInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosQuatScaleShearInterval(self, *args, **kw)
-
-    def colorInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpColorInterval(self, *args, **kw)
-
-    def colorScaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpColorScaleInterval(self, *args, **kw)
-
-    def attachCollisionSphere(self, name, cx, cy, cz, r, fromCollide, intoCollide):
-        from pandac import CollisionSphere
-        from pandac import CollisionNode
-        coll = CollisionSphere.CollisionSphere(cx, cy, cz, r)
-        collNode = CollisionNode.CollisionNode(name)
-        collNode.addSolid(coll)
-        collNode.setFromCollideMask(fromCollide)
-        collNode.setIntoCollideMask(intoCollide)
-        collNodePath = self.attachNewNode(collNode)
-        return collNodePath
-
-    def attachCollisionSegment(self, name, ax, ay, az, bx, by, bz, fromCollide, intoCollide):
-        from pandac import CollisionSegment
-        from pandac import CollisionNode
-        coll = CollisionSegment.CollisionSegment(ax, ay, az, bx, by, bz)
-        collNode = CollisionNode.CollisionNode(name)
-        collNode.addSolid(coll)
-        collNode.setFromCollideMask(fromCollide)
-        collNode.setIntoCollideMask(intoCollide)
-        collNodePath = self.attachNewNode(collNode)
-        return collNodePath
-
-    def attachCollisionRay(self, name, ox, oy, oz, dx, dy, dz, fromCollide, intoCollide):
-        from pandac import CollisionRay
-        from pandac import CollisionNode
-        coll = CollisionRay.CollisionRay(ox, oy, oz, dx, dy, dz)
-        collNode = CollisionNode.CollisionNode(name)
-        collNode.addSolid(coll)
-        collNode.setFromCollideMask(fromCollide)
-        collNode.setIntoCollideMask(intoCollide)
-        collNodePath = self.attachNewNode(collNode)
-        return collNodePath
-
-    def flattenMultitex(self, stateFrom = None, target = None,
-                        useGeom = 0, allowTexMat = 0, win = None):
-        from pandac import MultitexReducer
-        mr = MultitexReducer.MultitexReducer()
-        if target != None:
-            mr.setTarget(target)
-        mr.setUseGeom(useGeom)
-        mr.setAllowTexMat(allowTexMat)
-
-        if win == None:
-            win = base.win
-
-        if stateFrom == None:
-            mr.scan(self)
-        else:
-            mr.scan(self, stateFrom)
-        mr.flatten(win)

+ 0 - 26
direct/src/extensions/NurbsCurveEvaluator-extensions.py

@@ -1,26 +0,0 @@
-
-    """
-    NurbsCurveEvaluator-extensions module: contains methods to extend
-    functionality of the NurbsCurveEvaluator class
-    """
-
-    def getKnots(self):
-        """Returns the knot vector as a Python list of floats"""
-        knots = []
-        for i in range(self.getNumKnots()):
-            knots.append(self.getKnot(i))
-        return knots
-
-    def getVertices(self, relTo = None):
-        """Returns the vertices as a Python list of Vec4's, relative
-        to the indicated space if given."""
-        
-        verts = []
-        if relTo:
-            for i in range(self.getNumVertices()):
-                verts.append(self.getVertex(i, relTo))
-        else:
-            for i in range(self.getNumVertices()):
-                verts.append(self.getVertex(i))
-        return verts
-

+ 0 - 36
direct/src/extensions/NurbsSurfaceEvaluator-extensions.py

@@ -1,36 +0,0 @@
-
-    """
-    NurbsSurfaceEvaluator-extensions module: contains methods to extend
-    functionality of the NurbsSurfaceEvaluator class
-    """
-
-    def getUKnots(self):
-        """Returns the U knot vector as a Python list of floats"""
-        knots = []
-        for i in range(self.getNumUKnots()):
-            knots.append(self.getUKnot(i))
-        return knots
-
-    def getVKnots(self):
-        """Returns the V knot vector as a Python list of floats"""
-        knots = []
-        for i in range(self.getNumVKnots()):
-            knots.append(self.getVKnot(i))
-        return knots
-
-    def getVertices(self, relTo = None):
-        """Returns the vertices as a 2-d Python list of Vec4's, relative
-        to the indicated space if given."""
-
-        verts = []
-        for ui in range(self.getNumUVertices()):
-            v = []
-            if relTo:
-                for vi in range(self.getNumVVertices()):
-                    v.append(self.getVertex(ui, vi, relTo))
-            else:
-                for vi in range(self.getNumVVertices()):
-                    v.append(self.getVertex(ui, vi))
-            verts.append(v)
-            
-        return verts

+ 0 - 6
direct/src/extensions/PandaSystem-extensions.py

@@ -1,6 +0,0 @@
-
-    def getSystems(self):
-        l = []
-        for i in range(self.getNumSystems()):
-            l.append(self.getSystem(l))
-        return l

+ 0 - 3
direct/src/extensions/Sources.pp

@@ -1,3 +0,0 @@
-// For now, since we are not installing Python files, this file can
-// remain empty.
-

+ 0 - 83
direct/src/extensions/SpriteParticleRenderer-extensions.py

@@ -1,83 +0,0 @@
-
-    """
-    Contains methods to extend functionality
-    of the SpriteParticleRenderer class
-    """
-
-    # Initialize class variables for texture, source file and node for texture and
-    # node path textures to None.  These will be initialized to a hardcoded default
-    # or whatever the user specifies in his/her Configrc variable the first time they
-    # are accessed
-    # Will use instance copy of this in functions below
-    sourceTextureName = None
-    sourceFileName = None
-    sourceNodeName = None
-
-    def getSourceTextureName(self):
-        if self.sourceTextureName == None:
-            SpriteParticleRenderer.sourceTextureName = base.config.GetString(
-                'particle-sprite-texture', 'phase_3/maps/feyes.jpg')
-        # Return instance copy of class variable
-        return self.sourceTextureName
-
-    def setSourceTextureName(self, name):
-        # Set instance copy of class variable
-        self.sourceTextureName = name
-
-    def setTextureFromFile(self, fileName = None):
-        if fileName == None:
-            fileName = self.getSourceTextureName()
-        else:
-            self.setSourceTextureName(fileName)
-        t = loader.loadTexture(fileName)
-        if (t != None):
-            self.setTexture(t)
-        else:
-            print "Couldn't find rendererSpriteTexture file: %s" % fileName
-
-    def getSourceFileName(self):
-        if self.sourceFileName == None:
-            SpriteParticleRenderer.sourceFileName = base.config.GetString(
-                'particle-sprite-model', 'phase_3.5/models/props/suit-particles')
-        # Return instance copy of class variable
-        return self.sourceFileName
-
-    def setSourceFileName(self, name):
-        # Set instance copy of class variable
-        self.sourceFileName = name
-
-    def getSourceNodeName(self):
-        if self.sourceNodeName == None:
-            SpriteParticleRenderer.sourceNodeName = base.config.GetString(
-                'particle-sprite-node', '**/fire')
-        # Return instance copy of class variable
-        return self.sourceNodeName
-
-    def setSourceNodeName(self, name):
-        # Set instance copy of class variable
-        self.sourceNodeName = name
-
-    def setTextureFromNode(self, modelName = None, nodeName = None):
-        if modelName == None:
-            modelName = self.getSourceFileName()
-        else:
-            self.setSourceFileName(modelName)
-        if nodeName == None:
-            nodeName = self.getSourceNodeName()
-        else:
-            self.setSourceNodeName(nodeName)
-        # Load model and get texture
-        m = loader.loadModel(modelName)
-        if (m == None):
-            print "SpriteParticleRenderer: Couldn't find model: %s!" % modelName 
-            return None
-        nodeName = self.getSourceNodeName()
-        np = m.find(nodeName)
-        if np.isEmpty():
-            print "SpriteParticleRenderer: Couldn't find node: %s!" % nodeName
-            m.removeNode()
-            return None
-        self.setFromNode(np)
-        m.removeNode()
-        
-

+ 0 - 15
direct/src/extensions/VBase3-extensions.py

@@ -1,15 +0,0 @@
-
-    """
-    VBase3-extensions module: contains methods to extend functionality
-    of the VBase3 class
-    """
-
-    def __repr__(self):
-        return '%s(%s, %s, %s)' % (
-            self.__class__.__name__, self[0], self[1], self[2])
-
-    def pPrintValues(self):
-        """
-        Pretty print
-        """
-        return "% 10.4f, % 10.4f, % 10.4f" % (self[0], self[1], self[2])

+ 0 - 15
direct/src/extensions/VBase4-extensions.py

@@ -1,15 +0,0 @@
-
-    """
-    VBase4-extensions module: contains methods to extend functionality
-    of the VBase4 class
-    """
-
-    def __repr__(self):
-        return '%s(%s, %s, %s, %s)' % (
-            self.__class__.__name__, self[0], self[1], self[2], self[3])
-
-    def pPrintValues(self):
-        """
-        Pretty print
-        """
-        return '% 10.4f, % 10.4f, % 10.4f, % 10.4f' % (self[0], self[1], self[2], self[3])

+ 0 - 1
direct/src/extensions/__init__.py

@@ -1 +0,0 @@
-from PandaModules import *

+ 12 - 24
direct/src/p3d/AppRunner.py

@@ -13,22 +13,9 @@ __all__ = ["AppRunner", "dummyAppRunner", "ArgumentError"]
 
 
 import sys
 import sys
 import os
 import os
-import __builtin__
-
-if 'VFSImporter' in sys.modules:
-    # If we've already got a VFSImporter module defined at the
-    # toplevel, we must have come in here by way of the
-    # p3dPythonRun.cxx program, which starts out by importing a frozen
-    # VFSImporter.  Let's make sure we don't have two VFSImporter
-    # modules.
-    import VFSImporter
-    import direct.showbase
-    direct.showbase.VFSImporter = VFSImporter
-    sys.modules['direct.showbase.VFSImporter'] = VFSImporter
-else:
-    # Otherwise, we can import the VFSImporter normally.
-    from direct.showbase import VFSImporter
+import __builtin__ as builtins
 
 
+from direct.showbase import VFSImporter
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 from panda3d.core import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, WindowProperties, ExecutionEnvironment, PandaSystem, Notify, StreamWriter, ConfigVariableString, ConfigPageManager
 from panda3d.core import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, WindowProperties, ExecutionEnvironment, PandaSystem, Notify, StreamWriter, ConfigVariableString, ConfigPageManager
 from panda3d.direct import init_app_for_gui
 from panda3d.direct import init_app_for_gui
@@ -472,7 +459,7 @@ class AppRunner(DirectObject):
             file.unlink()
             file.unlink()
             return False
             return False
 
 
-        if not fileSpec.fullVerify(pathname = localPathname):
+        if not fileSpec.fullVerify(pathname = localPathname, notify = self.notify):
             # No good after download.
             # No good after download.
             self.notify.info("%s is still no good after downloading." % (url))
             self.notify.info("%s is still no good after downloading." % (url))
             return False
             return False
@@ -570,14 +557,14 @@ class AppRunner(DirectObject):
             for packageData in hostData.packages:
             for packageData in hostData.packages:
                 totalSize += packageData.totalSize
                 totalSize += packageData.totalSize
         self.notify.info("Total Panda3D disk space used: %s MB" % (
         self.notify.info("Total Panda3D disk space used: %s MB" % (
-            (totalSize + 524288) / 1048576))
+            (totalSize + 524288) // 1048576))
 
 
         if self.verifyContents == self.P3DVCNever:
         if self.verifyContents == self.P3DVCNever:
             # We're not allowed to delete anything anyway.
             # We're not allowed to delete anything anyway.
             return
             return
 
 
         self.notify.info("Configured max usage is: %s MB" % (
         self.notify.info("Configured max usage is: %s MB" % (
-            (self.maxDiskUsage + 524288) / 1048576))
+            (self.maxDiskUsage + 524288) // 1048576))
         if totalSize <= self.maxDiskUsage:
         if totalSize <= self.maxDiskUsage:
             # Still within budget; no need to clean up anything.
             # Still within budget; no need to clean up anything.
             return
             return
@@ -635,13 +622,13 @@ class AppRunner(DirectObject):
         try:
         try:
             taskMgr.run()
             taskMgr.run()
 
 
-        except SystemExit:
+        except SystemExit as err:
             # Presumably the window has already been shut down here, but shut
             # Presumably the window has already been shut down here, but shut
             # it down again for good measure.
             # it down again for good measure.
-            if hasattr(__builtin__, "base"):
+            if hasattr(builtins, "base"):
                 base.destroy()
                 base.destroy()
 
 
-            self.notify.info("Normal exit.")
+            self.notify.info("Normal exit with status %s." % repr(err.code))
             raise
             raise
 
 
         except:
         except:
@@ -697,9 +684,10 @@ class AppRunner(DirectObject):
             # Replace the builtin open and file symbols so user code will get
             # Replace the builtin open and file symbols so user code will get
             # our versions by default, which can open and read files out of
             # our versions by default, which can open and read files out of
             # the multifile.
             # the multifile.
-            __builtin__.file = file.file
-            __builtin__.open = file.open
-            __builtin__.execfile = file.execfile
+            builtins.open = file.open
+            if sys.version_info < (3, 0):
+                builtins.file = file.open
+                builtins.execfile = file.execfile
             os.listdir = file.listdir
             os.listdir = file.listdir
             os.walk = file.walk
             os.walk = file.walk
             os.path.join = file.join
             os.path.join = file.join

+ 136 - 61
direct/src/p3d/DeploymentTools.py

@@ -5,7 +5,7 @@ to build for as many platforms as possible. """
 __all__ = ["Standalone", "Installer"]
 __all__ = ["Standalone", "Installer"]
 
 
 import os, sys, subprocess, tarfile, shutil, time, zipfile, socket, getpass, struct
 import os, sys, subprocess, tarfile, shutil, time, zipfile, socket, getpass, struct
-from cStringIO import StringIO
+from io import BytesIO, TextIOWrapper
 from direct.directnotify.DirectNotifyGlobal import *
 from direct.directnotify.DirectNotifyGlobal import *
 from direct.showbase.AppRunnerGlobal import appRunner
 from direct.showbase.AppRunnerGlobal import appRunner
 from panda3d.core import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile
 from panda3d.core import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile
@@ -21,8 +21,8 @@ try:
 except ImportError:
 except ImportError:
     pwd = None
     pwd = None
 
 
-# Make sure this matches with the magic in p3dEmbed.cxx.
-P3DEMBED_MAGIC = "\xFF\x3D\x3D\x00"
+# Make sure this matches with the magic in p3dEmbedMain.cxx.
+P3DEMBED_MAGIC = 0xFF3D3D00
 
 
 # This filter function is used when creating
 # This filter function is used when creating
 # an archive that should be owned by root.
 # an archive that should be owned by root.
@@ -73,7 +73,7 @@ class Standalone:
 
 
         self.tempDir = Filename.temporary("", self.basename, "") + "/"
         self.tempDir = Filename.temporary("", self.basename, "") + "/"
         self.tempDir.makeDir()
         self.tempDir.makeDir()
-        self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = appRunner, hostDir = self.tempDir, asMirror = False)
+        self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = appRunner, hostDir = self.tempDir, asMirror = False, perPlatform = True)
 
 
         self.http = HTTPClient.getGlobalPtr()
         self.http = HTTPClient.getGlobalPtr()
         if not self.host.hasContentsFile:
         if not self.host.hasContentsFile:
@@ -99,6 +99,9 @@ class Standalone:
         if len(platforms) == 0:
         if len(platforms) == 0:
             Standalone.notify.warning("No platforms found to build for!")
             Standalone.notify.warning("No platforms found to build for!")
 
 
+        if 'win32' in platforms and 'win_i386' in platforms:
+            platforms.remove('win32')
+
         outputDir = Filename(outputDir + "/")
         outputDir = Filename(outputDir + "/")
         outputDir.makeDir()
         outputDir.makeDir()
         for platform in platforms:
         for platform in platforms:
@@ -158,10 +161,10 @@ class Standalone:
 
 
         # Find the magic size string and replace it with the real size,
         # Find the magic size string and replace it with the real size,
         # regardless of the endianness of the p3dembed executable.
         # regardless of the endianness of the p3dembed executable.
-        hex_size = hex(size)[2:].rjust(8, "0")
-        enc_size = "".join([chr(int(hex_size[i] + hex_size[i + 1], 16)) for i in range(0, len(hex_size), 2)])
-        p3dembed_data = p3dembed_data.replace(P3DEMBED_MAGIC, enc_size)
-        p3dembed_data = p3dembed_data.replace(P3DEMBED_MAGIC[::-1], enc_size[::-1])
+        p3dembed_data = p3dembed_data.replace(struct.pack('>I', P3DEMBED_MAGIC),
+                                              struct.pack('>I', size))
+        p3dembed_data = p3dembed_data.replace(struct.pack('<I', P3DEMBED_MAGIC),
+                                              struct.pack('<I', size))
 
 
         # Write the output file
         # Write the output file
         Standalone.notify.info("Creating %s..." % output)
         Standalone.notify.info("Creating %s..." % output)
@@ -170,12 +173,15 @@ class Standalone:
         ohandle.write(p3dembed_data)
         ohandle.write(p3dembed_data)
 
 
         # Write out the tokens. Set log_basename to the basename by default
         # Write out the tokens. Set log_basename to the basename by default
-        tokens = {"log_basename" : self.basename}
+        tokens = {"log_basename": self.basename}
         tokens.update(self.tokens)
         tokens.update(self.tokens)
         tokens.update(extraTokens)
         tokens.update(extraTokens)
-        for token in tokens.items():
-            ohandle.write("\0%s=%s" % token)
-        ohandle.write("\0\0")
+        for key, value in tokens.items():
+            ohandle.write(b"\0")
+            ohandle.write(key.encode('ascii'))
+            ohandle.write(b"=")
+            ohandle.write(value.encode())
+        ohandle.write(b"\0\0")
 
 
         # Buffer the p3d file to the output file. 1 MB buffer size.
         # Buffer the p3d file to the output file. 1 MB buffer size.
         phandle = open(self.p3dfile.toOsSpecific(), "rb")
         phandle = open(self.p3dfile.toOsSpecific(), "rb")
@@ -233,7 +239,7 @@ class PackageTree:
         if hostUrl in self.hosts:
         if hostUrl in self.hosts:
             return self.hosts[hostUrl]
             return self.hosts[hostUrl]
 
 
-        host = HostInfo(hostUrl, appRunner = appRunner, hostDir = self.hostDir, asMirror = False)
+        host = HostInfo(hostUrl, appRunner = appRunner, hostDir = self.hostDir, asMirror = False, perPlatform = True)
         if not host.hasContentsFile:
         if not host.hasContentsFile:
             if not host.readContentsFile():
             if not host.readContentsFile():
                 if not host.downloadContentsFile(self.http):
                 if not host.downloadContentsFile(self.http):
@@ -391,7 +397,7 @@ class Icon:
         vfs = VirtualFileSystem.getGlobalPtr()
         vfs = VirtualFileSystem.getGlobalPtr()
         stream = vfs.openWriteFile(fn, False, True)
         stream = vfs.openWriteFile(fn, False, True)
         icns = open(stream, 'wb')
         icns = open(stream, 'wb')
-        icns.write('icns\0\0\0\0')
+        icns.write(b'icns\0\0\0\0')
 
 
         icon_types = {16: 'is32', 32: 'il32', 48: 'ih32', 128: 'it32'}
         icon_types = {16: 'is32', 32: 'il32', 48: 'ih32', 128: 'it32'}
         mask_types = {16: 's8mk', 32: 'l8mk', 48: 'h8mk', 128: 't8mk'}
         mask_types = {16: 's8mk', 32: 'l8mk', 48: 'h8mk', 128: 't8mk'}
@@ -404,7 +410,7 @@ class Icon:
                 if pngtype is None:
                 if pngtype is None:
                     continue
                     continue
                 icns.write(png_types[size])
                 icns.write(png_types[size])
-                icns.write('\0\0\0\0')
+                icns.write(b'\0\0\0\0')
                 start = icns.tell()
                 start = icns.tell()
 
 
                 image.write(stream, "", pngtype)
                 image.write(stream, "", pngtype)
@@ -444,6 +450,7 @@ class Installer:
     notify = directNotify.newCategory("Installer")
     notify = directNotify.newCategory("Installer")
 
 
     def __init__(self, p3dfile, shortname, fullname, version, tokens = {}):
     def __init__(self, p3dfile, shortname, fullname, version, tokens = {}):
+        self.p3dFilename = p3dfile
         if not shortname:
         if not shortname:
             shortname = p3dfile.getBasenameWoExtension()
             shortname = p3dfile.getBasenameWoExtension()
         self.shortname = shortname
         self.shortname = shortname
@@ -477,22 +484,16 @@ class Installer:
         if not self.authoremail and ' ' not in uname:
         if not self.authoremail and ' ' not in uname:
             self.authoremail = "%s@%s" % (uname, socket.gethostname())
             self.authoremail = "%s@%s" % (uname, socket.gethostname())
 
 
-        self.standalone = Standalone(p3dfile, tokens)
-        self.tempDir = Filename.temporary("", self.shortname, "") + "/"
-        self.tempDir.makeDir()
-        self.__tempRoots = {}
-
         # Load the p3d file to read out the required packages
         # Load the p3d file to read out the required packages
         mf = Multifile()
         mf = Multifile()
-        if not mf.openRead(p3dfile):
+        if not mf.openRead(self.p3dFilename):
             Installer.notify.error("Not a Panda3D application: %s" % (p3dfile))
             Installer.notify.error("Not a Panda3D application: %s" % (p3dfile))
             return
             return
 
 
         # Now load the p3dInfo file.
         # Now load the p3dInfo file.
-        self.hostUrl = PandaSystem.getPackageHostUrl()
-        if not self.hostUrl:
-            self.hostUrl = self.standalone.host.hostUrl
+        self.hostUrl = None
         self.requires = []
         self.requires = []
+        self.extracts = []
         i = mf.findSubfile('p3d_info.xml')
         i = mf.findSubfile('p3d_info.xml')
         if i >= 0:
         if i >= 0:
             stream = mf.openReadSubfile(i)
             stream = mf.openReadSubfile(i)
@@ -511,14 +512,54 @@ class Installer:
                         p3dRequires.Attribute('host')))
                         p3dRequires.Attribute('host')))
                     p3dRequires = p3dRequires.NextSiblingElement('requires')
                     p3dRequires = p3dRequires.NextSiblingElement('requires')
 
 
+                p3dExtract = p3dPackage.FirstChildElement('extract')
+                while p3dExtract:
+                    filename = p3dExtract.Attribute('filename')
+                    self.extracts.append(filename)
+                    p3dExtract = p3dExtract.NextSiblingElement('extract')
+
                 if not self.fullname:
                 if not self.fullname:
                     p3dConfig = p3dPackage.FirstChildElement('config')
                     p3dConfig = p3dPackage.FirstChildElement('config')
                     if p3dConfig:
                     if p3dConfig:
                         self.fullname = p3dConfig.Attribute('display_name')
                         self.fullname = p3dConfig.Attribute('display_name')
+        else:
+            Installer.notify.warning("No p3d_info.xml was found in .p3d archive.")
+
+        mf.close()
+
+        if not self.hostUrl:
+            self.hostUrl = PandaSystem.getPackageHostUrl()
+            if not self.hostUrl:
+                self.hostUrl = self.standalone.host.hostUrl
+            Installer.notify.warning("No host URL was specified by .p3d archive.  Falling back to %s" % (self.hostUrl))
 
 
         if not self.fullname:
         if not self.fullname:
             self.fullname = self.shortname
             self.fullname = self.shortname
 
 
+        self.tempDir = Filename.temporary("", self.shortname, "") + "/"
+        self.tempDir.makeDir()
+        self.__tempRoots = {}
+
+        if self.extracts:
+            # Copy .p3d to a temporary file so we can remove the extracts.
+            p3dfile = Filename(self.tempDir, self.p3dFilename.getBasename())
+            shutil.copyfile(self.p3dFilename.toOsSpecific(), p3dfile.toOsSpecific())
+            mf = Multifile()
+            if not mf.openReadWrite(p3dfile):
+                Installer.notify.error("Failure to open %s for writing." % (p3dfile))
+
+            # We don't really need this silly thing when embedding, anyway.
+            mf.setHeaderPrefix("")
+
+            for fn in self.extracts:
+                if not mf.removeSubfile(fn):
+                    Installer.notify.error("Failure to remove %s from multifile." % (p3dfile))
+
+            mf.repack()
+            mf.close()
+
+        self.standalone = Standalone(p3dfile, tokens)
+
     def __del__(self):
     def __del__(self):
         try:
         try:
             appRunner.rmtree(self.tempDir)
             appRunner.rmtree(self.tempDir)
@@ -533,6 +574,22 @@ class Installer:
         if not self.includeRequires:
         if not self.includeRequires:
             return
             return
 
 
+        # Write out the extracts from the original .p3d.
+        if self.extracts:
+            mf = Multifile()
+            if not mf.openRead(self.p3dFilename):
+                Installer.notify.error("Failed to open .p3d archive: %s" % (filename))
+
+            for filename in self.extracts:
+                i = mf.findSubfile(filename)
+                if i < 0:
+                    Installer.notify.error("Cannot find extract in .p3d archive: %s" % (filename))
+                    continue
+
+                if not mf.extractSubfile(i, Filename(hostDir, filename)):
+                    Installer.notify.error("Failed to extract file from .p3d archive: %s" % (filename))
+            mf.close()
+
         pkgTree = PackageTree(platform, hostDir, self.hostUrl)
         pkgTree = PackageTree(platform, hostDir, self.hostUrl)
         pkgTree.installPackage("images", None, self.standalone.host.hostUrl)
         pkgTree.installPackage("images", None, self.standalone.host.hostUrl)
 
 
@@ -618,6 +675,9 @@ class Installer:
         if len(platforms) == 0:
         if len(platforms) == 0:
             Installer.notify.warning("No platforms found to build for!")
             Installer.notify.warning("No platforms found to build for!")
 
 
+        if 'win32' in platforms and 'win_i386' in platforms:
+            platforms.remove('win32')
+
         outputDir = Filename(outputDir + "/")
         outputDir = Filename(outputDir + "/")
         outputDir.makeDir()
         outputDir.makeDir()
         for platform in platforms:
         for platform in platforms:
@@ -660,7 +720,8 @@ class Installer:
 
 
         Filename(tempdir, "usr/bin/").makeDir()
         Filename(tempdir, "usr/bin/").makeDir()
         if self.includeRequires:
         if self.includeRequires:
-            extraTokens = {"host_dir" : "/usr/lib/" + self.shortname.lower()}
+            extraTokens = {"host_dir" : "/usr/lib/" + self.shortname.lower(),
+                           "start_dir" : "/usr/lib/" + self.shortname.lower()}
         else:
         else:
             extraTokens = {}
             extraTokens = {}
         self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform, extraTokens)
         self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform, extraTokens)
@@ -712,7 +773,7 @@ class Installer:
         print >>desktop, "Type=Application"
         print >>desktop, "Type=Application"
         desktop.close()
         desktop.close()
 
 
-        if self.includeRequires:
+        if self.includeRequires or self.extracts:
             hostDir = Filename(tempdir, "usr/lib/%s/" % self.shortname.lower())
             hostDir = Filename(tempdir, "usr/lib/%s/" % self.shortname.lower())
             hostDir.makeDir()
             hostDir.makeDir()
             self.installPackagesInto(hostDir, platform)
             self.installPackagesInto(hostDir, platform)
@@ -742,16 +803,18 @@ class Installer:
         tempdir, totsize = self.__buildTempLinux(platform)
         tempdir, totsize = self.__buildTempLinux(platform)
 
 
         # Create a control file in memory.
         # Create a control file in memory.
-        controlfile = StringIO()
-        print >>controlfile, "Package: %s" % self.shortname.lower()
-        print >>controlfile, "Version: %s" % self.version
-        print >>controlfile, "Maintainer: %s <%s>" % (self.authorname, self.authoremail)
-        print >>controlfile, "Section: games"
-        print >>controlfile, "Priority: optional"
-        print >>controlfile, "Architecture: %s" % arch
-        print >>controlfile, "Installed-Size: %d" % -(-totsize / 1024)
-        print >>controlfile, "Description: %s" % self.fullname
-        print >>controlfile, "Depends: libc6, libgcc1, libstdc++6, libx11-6"
+        controlfile = BytesIO()
+        cout = TextIOWrapper(controlfile, encoding='utf-8', newline='')
+        cout.write(u"Package: %s\n" % self.shortname.lower())
+        cout.write(u"Version: %s\n" % self.version)
+        cout.write(u"Maintainer: %s <%s>\n" % (self.authorname, self.authoremail))
+        cout.write(u"Section: games\n")
+        cout.write(u"Priority: optional\n")
+        cout.write(u"Architecture: %s\n" % arch)
+        cout.write(u"Installed-Size: %d\n" % -(-totsize // 1024))
+        cout.write(u"Description: %s\n" % self.fullname)
+        cout.write(u"Depends: libc6, libgcc1, libstdc++6, libx11-6\n")
+        cout.flush()
         controlinfo = TarInfoRoot("control")
         controlinfo = TarInfoRoot("control")
         controlinfo.mtime = modtime
         controlinfo.mtime = modtime
         controlinfo.size = controlfile.tell()
         controlinfo.size = controlfile.tell()
@@ -762,33 +825,43 @@ class Installer:
         if output.exists():
         if output.exists():
             output.unlink()
             output.unlink()
         debfile = open(output.toOsSpecific(), "wb")
         debfile = open(output.toOsSpecific(), "wb")
-        debfile.write("!<arch>\x0A")
-        debfile.write("debian-binary   %-12lu0     0     100644  %-10ld\x60\x0A" % (modtime, 4))
-        debfile.write("2.0\x0A")
-
-        # Write the control.tar.gz to the archive.
-        debfile.write("control.tar.gz  %-12lu0     0     100644  %-10ld\x60\x0A" % (modtime, 0))
+        debfile.write(b"!<arch>\x0A")
+        pad_mtime = str(modtime).encode().ljust(12, b' ')
+
+        # The first entry is a special file that marks it a .deb.
+        debfile.write(b"debian-binary   ")
+        debfile.write(pad_mtime)
+        debfile.write(b"0     0     100644  4         \x60\x0A")
+        debfile.write(b"2.0\x0A")
+
+        # Write the control.tar.gz to the archive.  We'll leave the
+        # size 0 for now, and go back and fill it in later.
+        debfile.write(b"control.tar.gz  ")
+        debfile.write(pad_mtime)
+        debfile.write(b"0     0     100644  0         \x60\x0A")
         ctaroffs = debfile.tell()
         ctaroffs = debfile.tell()
         ctarfile = tarfile.open("control.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
         ctarfile = tarfile.open("control.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
         ctarfile.addfile(controlinfo, controlfile)
         ctarfile.addfile(controlinfo, controlfile)
         ctarfile.close()
         ctarfile.close()
         ctarsize = debfile.tell() - ctaroffs
         ctarsize = debfile.tell() - ctaroffs
-        if (ctarsize & 1): debfile.write("\x0A")
+        if (ctarsize & 1): debfile.write(b"\x0A")
 
 
-        # Write the data.tar.gz to the archive.
-        debfile.write("data.tar.gz     %-12lu0     0     100644  %-10ld\x60\x0A" % (modtime, 0))
+        # Write the data.tar.gz to the archive.  Again, leave size 0.
+        debfile.write(b"data.tar.gz     ")
+        debfile.write(pad_mtime)
+        debfile.write(b"0     0     100644  0         \x60\x0A")
         dtaroffs = debfile.tell()
         dtaroffs = debfile.tell()
         dtarfile = tarfile.open("data.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
         dtarfile = tarfile.open("data.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
         dtarfile.add(Filename(tempdir, "usr").toOsSpecific(), "/usr")
         dtarfile.add(Filename(tempdir, "usr").toOsSpecific(), "/usr")
         dtarfile.close()
         dtarfile.close()
         dtarsize = debfile.tell() - dtaroffs
         dtarsize = debfile.tell() - dtaroffs
-        if (dtarsize & 1): debfile.write("\x0A")
+        if (dtarsize & 1): debfile.write(b"\x0A")
 
 
         # Write the correct sizes of the archives.
         # Write the correct sizes of the archives.
         debfile.seek(ctaroffs - 12)
         debfile.seek(ctaroffs - 12)
-        debfile.write("%-10ld" % ctarsize)
+        debfile.write(str(ctarsize).encode().ljust(10, b' '))
         debfile.seek(dtaroffs - 12)
         debfile.seek(dtaroffs - 12)
-        debfile.write("%-10ld" % dtarsize)
+        debfile.write(str(dtarsize).encode().ljust(10, b' '))
 
 
         debfile.close()
         debfile.close()
 
 
@@ -815,18 +888,20 @@ class Installer:
         tempdir, totsize = self.__buildTempLinux(platform)
         tempdir, totsize = self.__buildTempLinux(platform)
 
 
         # Create a pkginfo file in memory.
         # Create a pkginfo file in memory.
-        pkginfo = StringIO()
-        print >>pkginfo, "# Generated using pdeploy"
-        print >>pkginfo, "# %s" % time.ctime(modtime)
-        print >>pkginfo, "pkgname = %s" % self.shortname.lower()
-        print >>pkginfo, "pkgver = %s" % pkgver
-        print >>pkginfo, "pkgdesc = %s" % self.fullname
-        print >>pkginfo, "builddate = %s" % modtime
-        print >>pkginfo, "packager = %s <%s>" % (self.authorname, self.authoremail)
-        print >>pkginfo, "size = %d" % totsize
-        print >>pkginfo, "arch = %s" % arch
+        pkginfo = BytesIO()
+        pout = TextIOWrapper(pkginfo, encoding='utf-8', newline='')
+        pout.write(u"# Generated using pdeploy\n")
+        pout.write(u"# %s\n" % time.ctime(modtime))
+        pout.write(u"pkgname = %s\n" % self.shortname.lower())
+        pout.write(u"pkgver = %s\n" % pkgver)
+        pout.write(u"pkgdesc = %s\n" % self.fullname)
+        pout.write(u"builddate = %s\n" % modtime)
+        pout.write(u"packager = %s <%s>\n" % (self.authorname, self.authoremail))
+        pout.write(u"size = %d\n" % totsize)
+        pout.write(u"arch = %s\n" % arch)
         if self.licensename != "":
         if self.licensename != "":
-            print >>pkginfo, "license = %s" % self.licensename
+            pout.write(u"license = %s\n" % self.licensename)
+        pout.flush()
         pkginfoinfo = TarInfoRoot(".PKGINFO")
         pkginfoinfo = TarInfoRoot(".PKGINFO")
         pkginfoinfo.mtime = modtime
         pkginfoinfo.mtime = modtime
         pkginfoinfo.size = pkginfo.tell()
         pkginfoinfo.size = pkginfo.tell()
@@ -853,7 +928,7 @@ class Installer:
         exefile = Filename(output, "Contents/MacOS/" + self.shortname)
         exefile = Filename(output, "Contents/MacOS/" + self.shortname)
         exefile.makeDir()
         exefile.makeDir()
         if self.includeRequires:
         if self.includeRequires:
-            extraTokens = {"host_dir" : "../Resources"}
+            extraTokens = {"host_dir": "../Resources", "start_dir": "../Resources"}
         else:
         else:
             extraTokens = {}
             extraTokens = {}
         self.standalone.build(exefile, platform, extraTokens)
         self.standalone.build(exefile, platform, extraTokens)
@@ -1064,7 +1139,7 @@ class Installer:
         exefile = Filename(Filename.getTempDirectory(), self.shortname + ".exe")
         exefile = Filename(Filename.getTempDirectory(), self.shortname + ".exe")
         exefile.unlink()
         exefile.unlink()
         if self.includeRequires:
         if self.includeRequires:
-            extraTokens = {"host_dir" : "."}
+            extraTokens = {"host_dir": ".", "start_dir": "."}
         else:
         else:
             extraTokens = {}
             extraTokens = {}
         self.standalone.build(exefile, platform, extraTokens)
         self.standalone.build(exefile, platform, extraTokens)

+ 17 - 21
direct/src/p3d/HostInfo.py

@@ -161,14 +161,14 @@ class HostInfo:
                 self.notify.info("Downloading contents file %s" % (request))
                 self.notify.info("Downloading contents file %s" % (request))
                 statusCode = None
                 statusCode = None
                 statusString = ''
                 statusString = ''
-                for attempt in range(ConfigVariableInt('contents-xml-dl-attempts', 3)):
+                for attempt in range(int(ConfigVariableInt('contents-xml-dl-attempts', 3))):
                     if attempt > 0:
                     if attempt > 0:
                         self.notify.info("Retrying (%s)..."%(attempt,))
                         self.notify.info("Retrying (%s)..."%(attempt,))
                     rf = Ramfile()
                     rf = Ramfile()
                     channel = http.makeChannel(False)
                     channel = http.makeChannel(False)
                     channel.getDocument(request)
                     channel.getDocument(request)
                     if channel.downloadToRam(rf):
                     if channel.downloadToRam(rf):
-                        self.notify.warning("Successfully downloaded %s" % (url,))
+                        self.notify.info("Successfully downloaded %s" % (url,))
                         break
                         break
                     else:
                     else:
                         rf = None
                         rf = None
@@ -369,7 +369,7 @@ class HostInfo:
             assert self.hostDir
             assert self.hostDir
             self.__findHostXmlForHostDir(xcontents)
             self.__findHostXmlForHostDir(xcontents)
 
 
-        if not self.hostDir:
+        if self.rootDir and not self.hostDir:
             self.hostDir = self.__determineHostDir(None, self.hostUrl)
             self.hostDir = self.__determineHostDir(None, self.hostUrl)
 
 
         # Get the list of packages available for download and/or import.
         # Get the list of packages available for download and/or import.
@@ -403,7 +403,7 @@ class HostInfo:
         self.hasContentsFile = True
         self.hasContentsFile = True
 
 
         # Now save the contents.xml file into the standard location.
         # Now save the contents.xml file into the standard location.
-        if not self.appRunner or self.appRunner.verifyContents != self.appRunner.P3DVCNever:
+        if self.appRunner and self.appRunner.verifyContents != self.appRunner.P3DVCNever:
             assert self.hostDir
             assert self.hostDir
             filename = Filename(self.hostDir, 'contents.xml')
             filename = Filename(self.hostDir, 'contents.xml')
             filename.makeDir()
             filename.makeDir()
@@ -476,7 +476,7 @@ class HostInfo:
             self.descriptiveName = descriptiveName
             self.descriptiveName = descriptiveName
 
 
         hostDirBasename = xhost.Attribute('host_dir')
         hostDirBasename = xhost.Attribute('host_dir')
-        if not self.hostDir:
+        if self.rootDir and not self.hostDir:
             self.hostDir = self.__determineHostDir(hostDirBasename, self.hostUrl)
             self.hostDir = self.__determineHostDir(hostDirBasename, self.hostUrl)
 
 
         # Get the "download" URL, which is the source from which we
         # Get the "download" URL, which is the source from which we
@@ -513,17 +513,15 @@ class HostInfo:
         PackageInfo, returns it. """
         PackageInfo, returns it. """
 
 
         if not platform:
         if not platform:
-            # Ensure that we're on the same page with non-specified
-            # platforms.  We always use None, not empty string.
             platform = None
             platform = None
 
 
-        platforms = self.packages.setdefault((name, version), {})
-        package = platforms.get(platform, None)
+        platforms = self.packages.setdefault((name, version or ""), {})
+        package = platforms.get("", None)
         if not package:
         if not package:
             package = PackageInfo(self, name, version, platform = platform,
             package = PackageInfo(self, name, version, platform = platform,
                                   solo = solo, asMirror = self.asMirror,
                                   solo = solo, asMirror = self.asMirror,
                                   perPlatform = perPlatform)
                                   perPlatform = perPlatform)
-            platforms[platform] = package
+            platforms[platform or ""] = package
 
 
         return package
         return package
 
 
@@ -533,12 +531,12 @@ class HostInfo:
         platform, if one is provided by this host, or None if not. """
         platform, if one is provided by this host, or None if not. """
 
 
         assert self.hasContentsFile
         assert self.hasContentsFile
-        platforms = self.packages.get((name, version or None), {})
+        platforms = self.packages.get((name, version or ""), {})
 
 
-        if platform is not None:
+        if platform:
             # In this case, we are looking for a specific platform
             # In this case, we are looking for a specific platform
             # only.
             # only.
-            return platforms.get(platform or None, None)
+            return platforms.get(platform, None)
 
 
         # We are looking for one matching the current runtime
         # We are looking for one matching the current runtime
         # platform.  First, look for a package matching the current
         # platform.  First, look for a package matching the current
@@ -547,7 +545,7 @@ class HostInfo:
 
 
         # If not found, look for one matching no particular platform.
         # If not found, look for one matching no particular platform.
         if not package:
         if not package:
-            package = platforms.get(None, None)
+            package = platforms.get("", None)
 
 
         return package
         return package
 
 
@@ -563,7 +561,7 @@ class HostInfo:
             if name and pn != name:
             if name and pn != name:
                 continue
                 continue
 
 
-            if platform is None:
+            if not platform:
                 for p2 in platforms:
                 for p2 in platforms:
                     package = self.getPackage(pn, version, platform = p2)
                     package = self.getPackage(pn, version, platform = p2)
                     if package:
                     if package:
@@ -581,14 +579,12 @@ class HostInfo:
 
 
         result = []
         result = []
 
 
-        items = self.packages.items()
-        items.sort()
+        items = sorted(self.packages.items())
         for key, platforms in items:
         for key, platforms in items:
             if self.perPlatform or includeAllPlatforms:
             if self.perPlatform or includeAllPlatforms:
                 # If we maintain a different answer per platform,
                 # If we maintain a different answer per platform,
                 # return all of them.
                 # return all of them.
-                pitems = platforms.items()
-                pitems.sort()
+                pitems = sorted(platforms.items())
                 for pkey, package in pitems:
                 for pkey, package in pitems:
                     result.append(package)
                     result.append(package)
             else:
             else:
@@ -597,7 +593,7 @@ class HostInfo:
                 # current platform, or no particular platform.
                 # current platform, or no particular platform.
                 package = platforms.get(PandaSystem.getPlatform(), None)
                 package = platforms.get(PandaSystem.getPlatform(), None)
                 if not package:
                 if not package:
-                    package = platforms.get(None, None)
+                    package = platforms.get("", None)
 
 
                 if package:
                 if package:
                     result.append(package)
                     result.append(package)
@@ -701,7 +697,7 @@ class HostInfo:
 
 
             # If we successfully got a hostname, we don't really need the
             # If we successfully got a hostname, we don't really need the
             # full hash.  We'll keep half of it.
             # full hash.  We'll keep half of it.
-            keepHash = keepHash / 2;
+            keepHash = keepHash // 2
 
 
         md = HashVal()
         md = HashVal()
         md.hashString(hostUrl)
         md.hashString(hostUrl)

+ 4 - 3
direct/src/p3d/PackageInfo.py

@@ -277,7 +277,7 @@ class PackageInfo:
             # We've already got one.
             # We've already got one.
             yield self.stepComplete; return
             yield self.stepComplete; return
 
 
-        if self.host.appRunner and self.host.appRunner.verifyContents != self.host.appRunner.P3DVCNever:
+        if not self.host.appRunner or self.host.appRunner.verifyContents != self.host.appRunner.P3DVCNever:
             # We're allowed to download it.
             # We're allowed to download it.
             self.http = http
             self.http = http
 
 
@@ -523,7 +523,8 @@ class PackageInfo:
 
 
         # In case of unexpected failures on the internet, we will retry
         # In case of unexpected failures on the internet, we will retry
         # the full download instead of just giving up.
         # the full download instead of just giving up.
-        for retry in range(core.ConfigVariableInt('package-full-dl-retries', 1)):
+        retries = core.ConfigVariableInt('package-full-dl-retries', 1).getValue()
+        for retry in range(retries):
             self.installPlans.append(planB[:])
             self.installPlans.append(planB[:])
 
 
         pc.stop()
         pc.stop()
@@ -1151,7 +1152,7 @@ class PackageInfo:
         thisDir = ScanDirectoryNode(self.getPackageDir(), ignoreUsageXml = True)
         thisDir = ScanDirectoryNode(self.getPackageDir(), ignoreUsageXml = True)
         diskSpace = thisDir.getTotalSize()
         diskSpace = thisDir.getTotalSize()
         self.notify.info("Package %s uses %s MB" % (
         self.notify.info("Package %s uses %s MB" % (
-            self.packageName, (diskSpace + 524288) / 1048576))
+            self.packageName, (diskSpace + 524288) // 1048576))
         return diskSpace
         return diskSpace
 
 
     def markUsed(self):
     def markUsed(self):

+ 148 - 63
direct/src/p3d/Packager.py

@@ -16,6 +16,7 @@ import types
 import getpass
 import getpass
 import struct
 import struct
 import subprocess
 import subprocess
+import copy
 from direct.p3d.FileSpec import FileSpec
 from direct.p3d.FileSpec import FileSpec
 from direct.p3d.SeqValue import SeqValue
 from direct.p3d.SeqValue import SeqValue
 from direct.showbase import Loader
 from direct.showbase import Loader
@@ -185,7 +186,7 @@ class Packager:
         def getKey(self):
         def getKey(self):
             """ Returns a tuple used for sorting the PackageEntry
             """ Returns a tuple used for sorting the PackageEntry
             objects uniquely per package. """
             objects uniquely per package. """
-            return (self.packageName, self.platform, self.version)
+            return (self.packageName, self.platform or "", self.version or "")
 
 
         def fromFile(self, packageName, platform, version, solo, perPlatform,
         def fromFile(self, packageName, platform, version, solo, perPlatform,
                      installDir, descFilename, importDescFilename):
                      installDir, descFilename, importDescFilename):
@@ -299,8 +300,7 @@ class Packager:
                 xhost.InsertEndChild(xmirror)
                 xhost.InsertEndChild(xmirror)
 
 
             if packager:
             if packager:
-                altHosts = self.altHosts.items()
-                altHosts.sort()
+                altHosts = sorted(self.altHosts.items())
                 for keyword, alt in altHosts:
                 for keyword, alt in altHosts:
                     he = packager.hosts.get(alt, None)
                     he = packager.hosts.get(alt, None)
                     if he:
                     if he:
@@ -372,9 +372,15 @@ class Packager:
             self.requiredFilenames = []
             self.requiredFilenames = []
             self.requiredModules = []
             self.requiredModules = []
 
 
+            # A list of required packages that were missing.
+            self.missingPackages = []
+
             # This records the current list of modules we have added so
             # This records the current list of modules we have added so
             # far.
             # far.
             self.freezer = FreezeTool.Freezer(platform = self.packager.platform)
             self.freezer = FreezeTool.Freezer(platform = self.packager.platform)
+            
+            # Map of extensions to files to number (ignored by dir)
+            self.ignoredDirFiles = {}
 
 
         def close(self):
         def close(self):
             """ Writes out the contents of the current package.  Returns True
             """ Writes out the contents of the current package.  Returns True
@@ -385,6 +391,12 @@ class Packager:
                 message = 'Cannot generate packages without an installDir; use -i'
                 message = 'Cannot generate packages without an installDir; use -i'
                 raise PackagerError, message
                 raise PackagerError, message
 
 
+            if self.ignoredDirFiles:
+                exts = sorted(self.ignoredDirFiles.keys())
+                total = sum([x for x in self.ignoredDirFiles.values()])
+                self.notify.warning("excluded %s files not marked for inclusion: %s" \
+                                    % (total, ", ".join(["'" + ext + "'" for ext in exts])))
+                
             if not self.host:
             if not self.host:
                 self.host = self.packager.host
                 self.host = self.packager.host
 
 
@@ -454,7 +466,7 @@ class Packager:
             if self.p3dApplication:
             if self.p3dApplication:
                 allowPythonDev = self.configs.get('allow_python_dev', 0)
                 allowPythonDev = self.configs.get('allow_python_dev', 0)
                 if int(allowPythonDev):
                 if int(allowPythonDev):
-                    print "\n*** Generating %s.p3d with allow_python_dev enabled ***\n" % (self.packageName)
+                    print("\n*** Generating %s.p3d with allow_python_dev enabled ***\n" % (self.packageName))
 
 
             return result
             return result
 
 
@@ -485,6 +497,12 @@ class Packager:
             as a true package.  Either is implemented with a
             as a true package.  Either is implemented with a
             Multifile. """
             Multifile. """
 
 
+            if self.missingPackages:
+                missing = ', '.join([name for name, version in self.missingPackages])
+                self.notify.warning("Cannot build package %s due to missing dependencies: %s" % (self.packageName, missing))
+                self.cleanup()
+                return False
+
             self.multifile = Multifile()
             self.multifile = Multifile()
 
 
             # Write the multifile to a temporary filename until we
             # Write the multifile to a temporary filename until we
@@ -570,8 +588,7 @@ class Packager:
 
 
             # Add known module names.
             # Add known module names.
             self.moduleNames = {}
             self.moduleNames = {}
-            modules = self.freezer.modules.items()
-            modules.sort()
+            modules = sorted(self.freezer.modules.items())
             for newName, mdef in modules:
             for newName, mdef in modules:
                 if mdef.guess:
                 if mdef.guess:
                     # Not really a module.
                     # Not really a module.
@@ -702,7 +719,7 @@ class Packager:
             self.packageDesc = packageDir + self.packageDesc
             self.packageDesc = packageDir + self.packageDesc
             self.packageImportDesc = packageDir + self.packageImportDesc
             self.packageImportDesc = packageDir + self.packageImportDesc
 
 
-            print "Generating %s" % (self.packageFilename)
+            print("Generating %s" % (self.packageFilename))
 
 
             if self.p3dApplication:
             if self.p3dApplication:
                 self.packageFullpath = Filename(self.packager.p3dInstallDir, self.packageFilename)
                 self.packageFullpath = Filename(self.packager.p3dInstallDir, self.packageFilename)
@@ -820,6 +837,14 @@ class Packager:
             self.packager.contents[pe.getKey()] = pe
             self.packager.contents[pe.getKey()] = pe
             self.packager.contentsChanged = True
             self.packager.contentsChanged = True
 
 
+            # Hack for coreapi package, to preserve backward compatibility
+            # with old versions of the runtime, which still called the
+            # 32-bit Windows platform "win32".
+            if self.packageName == "coreapi" and self.platform == "win_i386":
+                pe2 = copy.copy(pe)
+                pe2.platform = "win32"
+                self.packager.contents[pe2.getKey()] = pe2
+
             self.cleanup()
             self.cleanup()
             return True
             return True
 
 
@@ -1232,13 +1257,13 @@ class Packager:
                 elf.close()
                 elf.close()
                 return None
                 return None
 
 
-            if not ident.startswith("\177ELF"):
+            if not ident.startswith(b"\177ELF"):
                 # No elf magic!  Beware of orcs.
                 # No elf magic!  Beware of orcs.
                 return None
                 return None
 
 
             # Make sure we read in the correct endianness and integer size
             # Make sure we read in the correct endianness and integer size
-            byteOrder = "<>"[ord(ident[5]) - 1]
-            elfClass = ord(ident[4]) - 1 # 0 = 32-bits, 1 = 64-bits
+            byteOrder = "<>"[ord(ident[5:6]) - 1]
+            elfClass = ord(ident[4:5]) - 1 # 0 = 32-bits, 1 = 64-bits
             headerStruct = byteOrder + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[elfClass]
             headerStruct = byteOrder + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[elfClass]
             sectionStruct = byteOrder + ("4xI8xIII8xI", "4xI16xQQI12xQ")[elfClass]
             sectionStruct = byteOrder + ("4xI8xIII8xI", "4xI16xQQI12xQ")[elfClass]
             dynamicStruct = byteOrder + ("iI", "qQ")[elfClass]
             dynamicStruct = byteOrder + ("iI", "qQ")[elfClass]
@@ -1272,17 +1297,17 @@ class Packager:
                 elf.seek(offset)
                 elf.seek(offset)
                 data = elf.read(entsize)
                 data = elf.read(entsize)
                 tag, val = struct.unpack_from(dynamicStruct, data)
                 tag, val = struct.unpack_from(dynamicStruct, data)
-                newSectionData = ""
+                newSectionData = b""
                 startReplace = None
                 startReplace = None
                 pad = 0
                 pad = 0
 
 
                 # Read tags until we find a NULL tag.
                 # Read tags until we find a NULL tag.
                 while tag != 0:
                 while tag != 0:
                     if tag == 1: # A NEEDED entry.  Read it from the string table.
                     if tag == 1: # A NEEDED entry.  Read it from the string table.
-                        filenames.append(stringTables[link][val : stringTables[link].find('\0', val)])
+                        filenames.append(stringTables[link][val : stringTables[link].find(b'\0', val)])
 
 
                     elif tag == 15 or tag == 29:
                     elif tag == 15 or tag == 29:
-                        rpath += stringTables[link][val : stringTables[link].find('\0', val)].split(':')
+                        rpath += stringTables[link][val : stringTables[link].find(b'\0', val)].split(b':')
                         # An RPATH or RUNPATH entry.
                         # An RPATH or RUNPATH entry.
                         if not startReplace:
                         if not startReplace:
                             startReplace = elf.tell() - entsize
                             startReplace = elf.tell() - entsize
@@ -1296,7 +1321,7 @@ class Packager:
                     tag, val = struct.unpack_from(dynamicStruct, data)
                     tag, val = struct.unpack_from(dynamicStruct, data)
 
 
                 if startReplace is not None:
                 if startReplace is not None:
-                    newSectionData += data + ("\0" * pad)
+                    newSectionData += data + (b"\0" * pad)
                     rewriteSections.append((startReplace, newSectionData))
                     rewriteSections.append((startReplace, newSectionData))
             elf.close()
             elf.close()
 
 
@@ -1329,7 +1354,7 @@ class Packager:
             for offset, data in rewriteSections:
             for offset, data in rewriteSections:
                 elf.seek(offset)
                 elf.seek(offset)
                 elf.write(data)
                 elf.write(data)
-            elf.write("\0" * pad)
+            elf.write(b"\0" * pad)
             elf.close()
             elf.close()
             return filenames
             return filenames
 
 
@@ -1468,6 +1493,10 @@ class Packager:
                     xhost = he.makeXml(packager = self.packager)
                     xhost = he.makeXml(packager = self.packager)
                     xpackage.InsertEndChild(xhost)
                     xpackage.InsertEndChild(xhost)
 
 
+            self.extracts.sort()
+            for name, xextract in self.extracts:
+                xpackage.InsertEndChild(xextract)
+
             doc.InsertEndChild(xpackage)
             doc.InsertEndChild(xpackage)
 
 
             # Write the xml file to a temporary file on disk, so we
             # Write the xml file to a temporary file on disk, so we
@@ -1813,7 +1842,10 @@ class Packager:
                         self.notify.warning(message)
                         self.notify.warning(message)
                     return
                     return
 
 
-            self.freezer.addModule(moduleName, filename = file.filename)
+            if file.text:
+                self.freezer.addModule(moduleName, filename = file.filename, text = file.text)
+            else:
+                self.freezer.addModule(moduleName, filename = file.filename)
 
 
         def addEggFile(self, file):
         def addEggFile(self, file):
             # Precompile egg files to bam's.
             # Precompile egg files to bam's.
@@ -2161,8 +2193,10 @@ class Packager:
                     ext = Filename(lowerName).getExtension()
                     ext = Filename(lowerName).getExtension()
                     if ext not in self.packager.nonuniqueExtensions:
                     if ext not in self.packager.nonuniqueExtensions:
                         self.skipFilenames[lowerName] = True
                         self.skipFilenames[lowerName] = True
+
                 for moduleName, mdef in package.moduleNames.items():
                 for moduleName, mdef in package.moduleNames.items():
-                    self.skipModules[moduleName] = mdef
+                    if not mdef.exclude:
+                        self.skipModules[moduleName] = mdef
 
 
     # Packager constructor
     # Packager constructor
     def __init__(self, platform = None):
     def __init__(self, platform = None):
@@ -2185,6 +2219,9 @@ class Packager:
         # ignoring any request to specify a particular download host,
         # ignoring any request to specify a particular download host,
         # e.g. for testing and development.
         # e.g. for testing and development.
         self.ignoreSetHost = False
         self.ignoreSetHost = False
+        
+        # Set this to true to verbosely log files ignored by dir().
+        self.verbosePrint = False
 
 
         # This will be appended to the basename of any .p3d package,
         # This will be appended to the basename of any .p3d package,
         # before the .p3d extension.
         # before the .p3d extension.
@@ -2333,7 +2370,7 @@ class Packager:
 
 
         # Binary files that are copied (and compressed) without
         # Binary files that are copied (and compressed) without
         # processing.
         # processing.
-        self.binaryExtensions = [ 'ttf', 'TTF', 'mid', 'ico' ]
+        self.binaryExtensions = [ 'ttf', 'TTF', 'mid', 'ico', 'cur' ]
 
 
         # Files that can have an existence in multiple different
         # Files that can have an existence in multiple different
         # packages simultaneously without conflict.
         # packages simultaneously without conflict.
@@ -2373,7 +2410,7 @@ class Packager:
                 }
                 }
 
 
         # Files that should be extracted to disk.
         # Files that should be extracted to disk.
-        self.extractExtensions = self.executableExtensions[:] + self.manifestExtensions[:] + [ 'ico' ]
+        self.extractExtensions = self.executableExtensions[:] + self.manifestExtensions[:] + [ 'ico', 'cur' ]
 
 
         # Files that indicate a platform dependency.
         # Files that indicate a platform dependency.
         self.platformSpecificExtensions = self.executableExtensions[:]
         self.platformSpecificExtensions = self.executableExtensions[:]
@@ -2390,6 +2427,14 @@ class Packager:
         # should be added exactly byte-for-byte as they are.
         # should be added exactly byte-for-byte as they are.
         self.unprocessedExtensions = []
         self.unprocessedExtensions = []
 
 
+        # Files for which warnings should be suppressed when they are
+        # not handled by dir()
+        self.suppressWarningForExtensions = ['', 'pyc', 'pyo', 
+                                             'p3d', 'pdef', 
+                                             'c', 'C', 'cxx', 'cpp', 'h', 'H',
+                                             'hpp', 'pp', 'I', 'pem', 'p12', 'crt',
+                                             'o', 'obj', 'a', 'lib', 'bc', 'll']
+         
         # System files that should never be packaged.  For
         # System files that should never be packaged.  For
         # case-insensitive filesystems (like Windows and OSX), put the
         # case-insensitive filesystems (like Windows and OSX), put the
         # lowercase filename here.  Case-sensitive filesystems should
         # lowercase filename here.  Case-sensitive filesystems should
@@ -2456,7 +2501,7 @@ class Packager:
         if not os.path.isfile('/sbin/ldconfig'):
         if not os.path.isfile('/sbin/ldconfig'):
             return False
             return False
 
 
-        handle = subprocess.Popen(['/sbin/ldconfig', '-p'], stdout=subprocess.PIPE)
+        handle = subprocess.Popen(['/sbin/ldconfig', '-p'], stdout=subprocess.PIPE, universal_newlines=True)
         out, err = handle.communicate()
         out, err = handle.communicate()
 
 
         if handle.returncode != 0:
         if handle.returncode != 0:
@@ -2617,12 +2662,20 @@ class Packager:
             if dirname.makeTrueCase():
             if dirname.makeTrueCase():
                 searchPath.appendDirectory(dirname)
                 searchPath.appendDirectory(dirname)
 
 
+    def _ensureExtensions(self):
+        self.knownExtensions = \
+            self.imageExtensions + \
+            self.modelExtensions + \
+            self.textExtensions + \
+            self.binaryExtensions + \
+            self.uncompressibleExtensions + \
+            self.unprocessedExtensions
 
 
     def setup(self):
     def setup(self):
         """ Call this method to initialize the class after filling in
         """ Call this method to initialize the class after filling in
         some of the values in the constructor. """
         some of the values in the constructor. """
 
 
-        self.knownExtensions = self.imageExtensions + self.modelExtensions + self.textExtensions + self.binaryExtensions + self.uncompressibleExtensions + self.unprocessedExtensions
+        self._ensureExtensions()
 
 
         self.currentPackage = None
         self.currentPackage = None
 
 
@@ -2710,11 +2763,15 @@ class Packager:
         # errors, and that the pdef file doesn't contain any really
         # errors, and that the pdef file doesn't contain any really
         # crazy Python code, all this will do is fill in the
         # crazy Python code, all this will do is fill in the
         # '__statements' list in the module scope.
         # '__statements' list in the module scope.
+        fn = packageDef.toOsSpecific()
+        f = open(fn)
+        code = compile(f.read(), fn, 'exec')
+        f.close()
 
 
         # It appears that having a separate globals and locals
         # It appears that having a separate globals and locals
         # dictionary causes problems with resolving symbols within a
         # dictionary causes problems with resolving symbols within a
         # class scope.  So, we just use one dictionary, the globals.
         # class scope.  So, we just use one dictionary, the globals.
-        execfile(packageDef.toOsSpecific(), globals)
+        exec(code, globals)
 
 
         packages = []
         packages = []
 
 
@@ -2979,10 +3036,10 @@ class Packager:
             # environment.
             # environment.
             return None
             return None
 
 
+        # Make sure we have a fresh version of the contents file.
         host = appRunner.getHost(hostUrl)
         host = appRunner.getHost(hostUrl)
-        if not host.readContentsFile():
-            if not host.downloadContentsFile(appRunner.http):
-                return None
+        if not host.downloadContentsFile(appRunner.http):
+            return None
 
 
         packageInfos = []
         packageInfos = []
         packageInfo = host.getPackage(packageName, version, platform = platform)
         packageInfo = host.getPackage(packageName, version, platform = platform)
@@ -3031,7 +3088,7 @@ class Packager:
         tuples = []
         tuples = []
         for package in packages:
         for package in packages:
             version = self.__makeVersionTuple(package.version)
             version = self.__makeVersionTuple(package.version)
-            tuples.append((version, file))
+            tuples.append((version, package))
         tuples.sort(reverse = True)
         tuples.sort(reverse = True)
 
 
         return [t[1] for t in tuples]
         return [t[1] for t in tuples]
@@ -3044,7 +3101,7 @@ class Packager:
         tuples = []
         tuples = []
         for package in packages:
         for package in packages:
             version = self.__makeVersionTuple(package.packageVersion)
             version = self.__makeVersionTuple(package.packageVersion)
-            tuples.append((version, file))
+            tuples.append((version, package))
         tuples.sort(reverse = True)
         tuples.sort(reverse = True)
 
 
         return [t[1] for t in tuples]
         return [t[1] for t in tuples]
@@ -3103,9 +3160,9 @@ class Packager:
         if panda1.version == panda2.version:
         if panda1.version == panda2.version:
             return True
             return True
 
 
-        print 'Rejecting package %s, version "%s": depends on %s, version "%s" instead of version "%s"' % (
+        print('Rejecting package %s, version "%s": depends on %s, version "%s" instead of version "%s"' % (
             package.packageName, package.version,
             package.packageName, package.version,
-            panda1.packageName, panda1.version, panda2.version)
+            panda1.packageName, panda1.version, panda2.version))
         return False
         return False
 
 
     def __findPackageInRequires(self, packageName, list):
     def __findPackageInRequires(self, packageName, list):
@@ -3188,7 +3245,9 @@ class Packager:
                                        requires = self.currentPackage.requires)
                                        requires = self.currentPackage.requires)
             if not package:
             if not package:
                 message = 'Unknown package %s, version "%s"' % (packageName, version)
                 message = 'Unknown package %s, version "%s"' % (packageName, version)
-                raise PackagerError, message
+                self.notify.warning(message)
+                self.currentPackage.missingPackages.append((packageName, pversion))
+                continue
 
 
             self.requirePackage(package)
             self.requirePackage(package)
 
 
@@ -3293,42 +3352,27 @@ class Packager:
         producing their own custom panda3d for download.  Should be
         producing their own custom panda3d for download.  Should be
         called before any other Python modules are named. """
         called before any other Python modules are named. """
 
 
-        # First, freeze just VFSImporter.py into its own
-        # _vfsimporter.pyd file.  This one is a special case, because
-        # we need this code in order to load python files from the
-        # Multifile, so this file can't itself be in the Multifile.
+        # This module and all its dependencies come frozen into p3dpython.
+        # We should mark them as having already been added so that we don't
+        # add them again to the Multifile.
+        self.do_module('direct.showbase.VFSImporter')
+        self.currentPackage.freezer.done(addStartupModules=True)
+        self.currentPackage.freezer.writeCode(None)
+        self.currentPackage.addExtensionModules()
+        self.currentPackage.freezer.reset()
 
 
-        # This requires a bit of care, because we only want to freeze
-        # VFSImporter.py, and not any other part of direct.  We do
-        # also want panda3d/__init__.py, though, since it would
-        # otherwise be part of the multifile.
-        self.do_excludeModule('direct')
-
-        # Import the actual VFSImporter module to get its filename on
-        # disk.
-        from direct.showbase import VFSImporter
-        filename = Filename.fromOsSpecific(VFSImporter.__file__)
-
-        self.do_module('VFSImporter', filename = filename)
-        self.do_freeze('_vfsimporter', compileToExe = False)
-
-        self.do_file('panda3d/core.pyd');
-
-        # Now that we're done freezing, explicitly add 'direct' to
-        # counteract the previous explicit excludeModule().
-        self.do_module('direct')
+        self.do_file('panda3d/_core.pyd', newDir='panda3d')
 
 
         # This is the key Python module that is imported at runtime to
         # This is the key Python module that is imported at runtime to
         # start an application running.
         # start an application running.
         self.do_module('direct.p3d.AppRunner')
         self.do_module('direct.p3d.AppRunner')
 
 
         # This is the main program that drives the runtime Python.  It
         # This is the main program that drives the runtime Python.  It
-        # is responsible for loading _vfsimporter.pyd, and then
-        # importing direct.p3d.AppRunner, to start an application
-        # running.  The program comes in two parts: an executable, and
-        # an associated dynamic library.  Note that the .exe and .dll
-        # extensions are automatically replaced with the appropriate
-        # platform-specific extensions.
+        # is responsible for importing direct.p3d.AppRunner to start an
+        # application running.  The program comes in two parts: an
+        # executable, and an associated dynamic library.  Note that the
+        # .exe and .dll extensions are automatically replaced with the
+        # appropriate platform-specific extensions.
 
 
         if self.platform.startswith('osx'):
         if self.platform.startswith('osx'):
             # On Mac, we package up a P3DPython.app bundle.  This
             # On Mac, we package up a P3DPython.app bundle.  This
@@ -3411,7 +3455,7 @@ class Packager:
                 freezer.addModule(moduleName, newName = newName)
                 freezer.addModule(moduleName, newName = newName)
             else:
             else:
                 freezer.modules[newName] = freezer.modules[moduleName]
                 freezer.modules[newName] = freezer.modules[moduleName]
-        freezer.done(compileToExe = compileToExe)
+        freezer.done(addStartupModules = compileToExe)
 
 
         dirname = ''
         dirname = ''
         basename = filename
         basename = filename
@@ -3604,6 +3648,43 @@ class Packager:
         filename = Filename(filename)
         filename = Filename(filename)
         self.currentPackage.excludeFile(filename)
         self.currentPackage.excludeFile(filename)
 
 
+
+    def do_includeExtensions(self, executableExtensions = None, extractExtensions = None, 
+                         imageExtensions = None, textExtensions = None, 
+                         uncompressibleExtensions = None, unprocessedExtensions = None,
+                         suppressWarningForExtensions = None):
+        """ Ensure that dir() will include files with the given extensions.
+        The extensions should not have '.' prefixes.
+        
+        All except 'suppressWarningForExtensions' allow the given kinds of files
+        to be packaged with their respective semantics (read the source).
+        
+        'suppressWarningForExtensions' lists extensions *expected* to be ignored, 
+        so no warnings will be emitted for them.
+        """
+        if executableExtensions:
+            self.executableExtensions += executableExtensions
+
+        if extractExtensions:
+            self.extractExtensions += extractExtensions
+
+        if imageExtensions:
+            self.imageExtensions += imageExtensions
+
+        if textExtensions:
+            self.textExtensions += textExtensions
+
+        if uncompressibleExtensions:
+            self.uncompressibleExtensions += uncompressibleExtensions
+
+        if unprocessedExtensions:
+            self.unprocessedExtensions += unprocessedExtensions
+
+        if suppressWarningForExtensions:
+            self.suppressWarningForExtensions += suppressWarningForExtensions
+
+        self._ensureExtensions()
+
     def do_dir(self, dirname, newDir = None, unprocessed = None):
     def do_dir(self, dirname, newDir = None, unprocessed = None):
 
 
         """ Adds the indicated directory hierarchy to the current
         """ Adds the indicated directory hierarchy to the current
@@ -3676,7 +3757,12 @@ class Packager:
                     filename.setBinary()
                     filename.setBinary()
                 self.currentPackage.addFile(filename, newName = newName,
                 self.currentPackage.addFile(filename, newName = newName,
                                             explicit = False, unprocessed = unprocessed)
                                             explicit = False, unprocessed = unprocessed)
-
+            elif not ext in self.suppressWarningForExtensions:
+                newCount = self.currentPackage.ignoredDirFiles.get(ext, 0) + 1
+                self.currentPackage.ignoredDirFiles[ext] = newCount
+                
+                if self.verbosePrint:
+                    self.notify.warning("ignoring file %s" % filename) 
 
 
     def readContentsFile(self):
     def readContentsFile(self):
         """ Reads the contents.xml file at the beginning of
         """ Reads the contents.xml file at the beginning of
@@ -3750,8 +3836,7 @@ class Packager:
                 xhost = he.makeXml(packager = self)
                 xhost = he.makeXml(packager = self)
                 xcontents.InsertEndChild(xhost)
                 xcontents.InsertEndChild(xhost)
 
 
-        contents = self.contents.items()
-        contents.sort()
+        contents = sorted(self.contents.items())
         for key, pe in contents:
         for key, pe in contents:
             xpackage = pe.makeXml()
             xpackage = pe.makeXml()
             xcontents.InsertEndChild(xpackage)
             xcontents.InsertEndChild(xpackage)

+ 1 - 1
direct/src/p3d/coreapi.pdef

@@ -66,7 +66,7 @@ class images(package):
             token = '%s_img' % (name)
             token = '%s_img' % (name)
             configDict[token] = basename
             configDict[token] = basename
         else:
         else:
-            print "Could not locate %s" % (filename)
+            print("Could not locate %s" % (filename))
 
 
     # Also make a few special cases.  We use the same default image
     # Also make a few special cases.  We use the same default image
     # for many of the states.
     # for many of the states.

+ 20 - 9
direct/src/p3d/packp3d.py

@@ -57,6 +57,14 @@ Options:
      instead of -e for files that are uncompressible by their nature
      instead of -e for files that are uncompressible by their nature
      (e.g. mpg files).  This option may be repeated as necessary.
      (e.g. mpg files).  This option may be repeated as necessary.
 
 
+  -x ext
+     Marks files with the given extensions of needing to be physically
+     extracted to disk before they can be loaded.  This is used for
+     file types that cannot be loaded via the virtual file system,
+     such as .ico files on Windows.
+     This option is currently only implemented when deploying the
+     application with pdeploy.
+
   -p python_lib_dir
   -p python_lib_dir
      Adds a directory to search for additional Python modules.  You
      Adds a directory to search for additional Python modules.  You
      can use this to add your system's Python path, to allow packp3d
      can use this to add your system's Python path, to allow packp3d
@@ -103,7 +111,7 @@ class ArgumentError(StandardError):
     pass
     pass
 
 
 def makePackedApp(args):
 def makePackedApp(args):
-    opts, args = getopt.getopt(args, 'o:d:m:S:e:n:p:c:r:s:Dh')
+    opts, args = getopt.getopt(args, 'o:d:m:S:e:n:x:p:c:r:s:Dh')
 
 
     packager = Packager.Packager()
     packager = Packager.Packager()
 
 
@@ -134,6 +142,8 @@ def makePackedApp(args):
             packager.binaryExtensions.append(value)
             packager.binaryExtensions.append(value)
         elif option == '-n':
         elif option == '-n':
             packager.uncompressibleExtensions.append(value)
             packager.uncompressibleExtensions.append(value)
+        elif option == '-x':
+            packager.extractExtensions.append(value)
         elif option == '-p':
         elif option == '-p':
             sys.path.append(value)
             sys.path.append(value)
         elif option == '-c':
         elif option == '-c':
@@ -154,10 +164,10 @@ def makePackedApp(args):
                 PandaSystem.getPackageHostUrl(),
                 PandaSystem.getPackageHostUrl(),
                 os.path.split(sys.argv[0])[1],
                 os.path.split(sys.argv[0])[1],
                 '%s.%s' % (sys.version_info[0], sys.version_info[1]))
                 '%s.%s' % (sys.version_info[0], sys.version_info[1]))
-            sys.exit(1)
+            sys.exit(0)
 
 
     if not appFilename:
     if not appFilename:
-        raise ArgumentError, "No target app specified.  Use:\n%s -o app.p3d" % (os.path.split(sys.argv[0])[1])
+        raise ArgumentError, "No target app specified.  Use:\n  %s -o app.p3d\nUse -h to get more usage information." % (os.path.split(sys.argv[0])[1])
 
 
     if args:
     if args:
         raise ArgumentError, "Extra arguments on command line."
         raise ArgumentError, "Extra arguments on command line."
@@ -170,19 +180,20 @@ def makePackedApp(args):
       appDir = Filename('.')
       appDir = Filename('.')
     appBase = appFilename.getBasenameWoExtension()
     appBase = appFilename.getBasenameWoExtension()
 
 
-    if not main:
+    if main:
+        main = Filename.fromOsSpecific(main)
+        main.makeAbsolute(root)
+    else:
         main = Filename(root, 'main.py')
         main = Filename(root, 'main.py')
-        if main.exists():
-            main = 'main.py'
-        else:
+        if not main.exists():
             main = glob.glob(os.path.join(root.toOsSpecific(), '*.py'))
             main = glob.glob(os.path.join(root.toOsSpecific(), '*.py'))
             if len(main) == 0:
             if len(main) == 0:
                 raise ArgumentError, 'No Python files in root directory.'
                 raise ArgumentError, 'No Python files in root directory.'
             elif len(main) > 1:
             elif len(main) > 1:
                 raise ArgumentError, 'Multiple Python files in root directory; specify the main application with -m "main".'
                 raise ArgumentError, 'Multiple Python files in root directory; specify the main application with -m "main".'
-            main = os.path.split(main[0])[1]
 
 
-    main = Filename.fromOsSpecific(main)
+            main = Filename.fromOsSpecific(os.path.split(main[0])[1])
+            main.makeAbsolute(root)
 
 
     packager.installDir = appDir
     packager.installDir = appDir
     packager.allowPythonDev = allowPythonDev
     packager.allowPythonDev = allowPythonDev

+ 12 - 4
direct/src/p3d/panda3d.pdef

@@ -61,7 +61,7 @@ class panda3d(package):
            'panda3d.physics')
            'panda3d.physics')
 
 
     # Include various standard Python encodings.  The rest is in morepy.
     # Include various standard Python encodings.  The rest is in morepy.
-    module('encodings', 'encodings.aliases', 'encodings.undefined,'
+    module('encodings', 'encodings.aliases', 'encodings.undefined',
            'encodings.utf_8', 'encodings.ascii', 'encodings.string_escape',
            'encodings.utf_8', 'encodings.ascii', 'encodings.string_escape',
            'encodings.mbcs', 'encodings.latin_1', 'io')
            'encodings.mbcs', 'encodings.latin_1', 'io')
 
 
@@ -195,7 +195,7 @@ class morepy(package):
            'termios', 'tty', 'pty', 'fcntl', 'pipes', 'posixfile',
            'termios', 'tty', 'pty', 'fcntl', 'pipes', 'posixfile',
            'resource', 'nis', 'syslog', 'commands', 'ic', 'MacOS',
            'resource', 'nis', 'syslog', 'commands', 'ic', 'MacOS',
            'macostools', 'findertools', 'EasyDialogs', 'FrameWork',
            'macostools', 'findertools', 'EasyDialogs', 'FrameWork',
-           'autoGIL', 'ColorPicker')
+           'autoGIL', 'ColorPicker', 'ast')
 
 
     # To add the multitude of standard Python string encodings.
     # To add the multitude of standard Python string encodings.
     module('encodings', 'encodings.*')
     module('encodings', 'encodings.*')
@@ -362,8 +362,16 @@ class rocket(package):
     config(display_name = "Panda3D libRocket support")
     config(display_name = "Panda3D libRocket support")
     require('panda3d')
     require('panda3d')
 
 
-    module('panda3d.rocket', required = True)
-    module('_rocketcore', '_rocketcontrols')
+    file('rocket.py', extract = True, text = """
+from _rocketcore import *
+try:
+    from _rocketcontrols import *
+except ImportError:
+    pass
+""")
+
+    module('panda3d.rocket', '_rocketcore', required = True)
+    module('_rocketcontrols')
     file('libp3rocket.dll', required = True)
     file('libp3rocket.dll', required = True)
 
 
 class vrpn(package):
 class vrpn(package):

+ 17 - 8
direct/src/p3d/ppackage.py

@@ -123,6 +123,10 @@ Options:
      as such.  This only applies to .p3d packages, not to other types
      as such.  This only applies to .p3d packages, not to other types
      of packages!
      of packages!
 
 
+  -v
+     Emit a warning for any file not recognized by the dir() command
+     (indicating there may be a need for addExtensions(...)).
+     
   -h
   -h
      Display this help
      Display this help
 """
 """
@@ -135,12 +139,12 @@ from direct.p3d import Packager
 from panda3d.core import *
 from panda3d.core import *
 
 
 def usage(code, msg = ''):
 def usage(code, msg = ''):
-    print >> sys.stderr, usageText % {
+    sys.stderr.write(usageText % {
         'version' : PandaSystem.getPackageVersionString(),
         'version' : PandaSystem.getPackageVersionString(),
         'host' : PandaSystem.getPackageHostUrl(),
         'host' : PandaSystem.getPackageHostUrl(),
         'prog' : os.path.split(sys.argv[0])[1]
         'prog' : os.path.split(sys.argv[0])[1]
-        }
-    print >> sys.stderr, msg
+        })
+    sys.stderr.write(msg + '\n')
     sys.exit(code)
     sys.exit(code)
 
 
 installDir = None
 installDir = None
@@ -151,12 +155,13 @@ allowPythonDev = False
 universalBinaries = False
 universalBinaries = False
 systemRoot = None
 systemRoot = None
 ignoreSetHost = False
 ignoreSetHost = False
+verbosePrint = False
 p3dSuffix = ''
 p3dSuffix = ''
 platforms = []
 platforms = []
 
 
 try:
 try:
-    opts, args = getopt.getopt(sys.argv[1:], 'i:ps:S:DuP:R:Ha:h')
-except getopt.error, msg:
+    opts, args = getopt.getopt(sys.argv[1:], 'i:ps:S:DuP:R:Ha:hv')
+except getopt.error as msg:
     usage(1, msg)
     usage(1, msg)
 
 
 for opt, arg in opts:
 for opt, arg in opts:
@@ -188,10 +193,13 @@ for opt, arg in opts:
     elif opt == '-a':
     elif opt == '-a':
         p3dSuffix = arg
         p3dSuffix = arg
 
 
+    elif opt == '-v':
+        verbosePrint = True
+        
     elif opt == '-h':
     elif opt == '-h':
         usage(0)
         usage(0)
     else:
     else:
-        print 'illegal option: ' + arg
+        print('illegal option: ' + arg)
         sys.exit(1)
         sys.exit(1)
 
 
 if not args:
 if not args:
@@ -212,7 +220,7 @@ else:
 
 
 if universalBinaries:
 if universalBinaries:
     if platforms:
     if platforms:
-        print '\nYou may not specify both -u and -P.\n'
+        print('\nYou may not specify both -u and -P.\n')
         sys.exit(1)
         sys.exit(1)
     if PandaSystem.getPlatform().startswith('osx_'):
     if PandaSystem.getPlatform().startswith('osx_'):
         platforms = ['osx_i386', 'osx_amd64']
         platforms = ['osx_i386', 'osx_amd64']
@@ -230,6 +238,7 @@ for platform in platforms:
     packager.allowPythonDev = allowPythonDev
     packager.allowPythonDev = allowPythonDev
     packager.systemRoot = systemRoot
     packager.systemRoot = systemRoot
     packager.ignoreSetHost = ignoreSetHost
     packager.ignoreSetHost = ignoreSetHost
+    packager.verbosePrint = verbosePrint
     packager.p3dSuffix = p3dSuffix
     packager.p3dSuffix = p3dSuffix
 
 
     try:
     try:
@@ -242,7 +251,7 @@ for platform in platforms:
     except Packager.PackagerError:
     except Packager.PackagerError:
         # Just print the error message and exit gracefully.
         # Just print the error message and exit gracefully.
         inst = sys.exc_info()[1]
         inst = sys.exc_info()[1]
-        print inst.args[0]
+        print(inst.args[0])
         sys.exit(1)
         sys.exit(1)
 
 
 # An explicit call to exit() is required to exit the program, when
 # An explicit call to exit() is required to exit the program, when

+ 3 - 3
direct/src/p3d/thirdparty.pdef

@@ -137,7 +137,7 @@ class pyopengl(package):
     module('OpenGL.GLU', 'OpenGL.GLUT', 'OpenGL.GLE', 'OpenGL.GLX')
     module('OpenGL.GLU', 'OpenGL.GLUT', 'OpenGL.GLE', 'OpenGL.GLX')
 
 
 class httplib2(package):
 class httplib2(package):
-    config(display_name = "httplib2")
+    config(display_name = "httplib2", platform_specific = False)
     require('panda3d')
     require('panda3d')
 
 
     module('httplib2', required = True)
     module('httplib2', required = True)
@@ -149,7 +149,7 @@ class box2d(package):
     module('Box2D', required = True)
     module('Box2D', required = True)
 
 
 class pyglet(package):
 class pyglet(package):
-    config(display_name = "pyglet")
-    require('panda3d')
+    config(display_name = "pyglet", platform_specific = False)
+    require('panda3d', 'morepy')
 
 
     module('pyglet', required = True)
     module('pyglet', required = True)

+ 2 - 2
direct/src/particles/ParticleTest.py

@@ -2,8 +2,8 @@
 if __name__ == "__main__":
 if __name__ == "__main__":
     from direct.directbase.TestStart import *
     from direct.directbase.TestStart import *
     
     
-    from pandac.LinearVectorForce import LinearVectorForce
-    from pandac.Vec3 import Vec3
+    from panda3d.physics import LinearVectorForce
+    from panda3d.core import Vec3
     import ParticleEffect
     import ParticleEffect
     from direct.tkpanels import ParticlePanel
     from direct.tkpanels import ParticlePanel
     import Particles
     import Particles

+ 17 - 6
direct/src/plugin/load_plugin.cxx

@@ -140,7 +140,7 @@ load_plugin(const string &p3d_plugin_filename,
             const string &log_directory, const string &log_basename,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             bool trusted_environment, bool console_environment,
             const string &root_dir, const string &host_dir,
             const string &root_dir, const string &host_dir,
-            ostream &logfile) {
+            const string &start_dir, ostream &logfile) {
   if (plugin_loaded) {
   if (plugin_loaded) {
     return true;
     return true;
   }
   }
@@ -259,7 +259,7 @@ load_plugin(const string &p3d_plugin_filename,
                    verify_contents, platform,
                    verify_contents, platform,
                    log_directory, log_basename,
                    log_directory, log_basename,
                    trusted_environment, console_environment,
                    trusted_environment, console_environment,
-                   root_dir, host_dir, logfile)) {
+                   root_dir, host_dir, start_dir, logfile)) {
     unload_dso();
     unload_dso();
     return false;
     return false;
   }
   }
@@ -283,7 +283,7 @@ init_plugin(const string &contents_filename, const string &host_url,
             const string &log_directory, const string &log_basename,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             bool trusted_environment, bool console_environment,
             const string &root_dir, const string &host_dir,
             const string &root_dir, const string &host_dir,
-            ostream &logfile) {
+            const string &start_dir, ostream &logfile) {
 
 
   // Ensure that all of the function pointers have been found.
   // Ensure that all of the function pointers have been found.
   if (P3D_initialize_ptr == NULL ||
   if (P3D_initialize_ptr == NULL ||
@@ -371,15 +371,26 @@ init_plugin(const string &contents_filename, const string &host_url,
     return false;
     return false;
   }
   }
 
 
-  if (!P3D_initialize_ptr(P3D_API_VERSION, contents_filename.c_str(),
+  // A bit of extra hand-hacked compatibility for using newer plug-ins
+  // with an older version of the core API.
+  int api_version = P3D_API_VERSION;
+  if (api_version == 17 && start_dir.empty()) {
+    api_version = 16;
+    if (host_dir.empty()) {
+      api_version = 15;
+    }
+  }
+
+  if (!P3D_initialize_ptr(api_version, contents_filename.c_str(),
                           host_url.c_str(), verify_contents, platform.c_str(),
                           host_url.c_str(), verify_contents, platform.c_str(),
                           log_directory.c_str(), log_basename.c_str(),
                           log_directory.c_str(), log_basename.c_str(),
                           trusted_environment, console_environment, 
                           trusted_environment, console_environment, 
-                          root_dir.c_str(), host_dir.c_str())) {
+                          root_dir.c_str(), host_dir.c_str(),
+                          start_dir.c_str())) {
     // Oops, failure to initialize.
     // Oops, failure to initialize.
     logfile
     logfile
       << "Failed to initialize plugin (passed API version " 
       << "Failed to initialize plugin (passed API version " 
-      << P3D_API_VERSION << ")\n";
+      << api_version << ")\n";
     return false;
     return false;
   }
   }
 
 

+ 4 - 2
direct/src/plugin/load_plugin.h

@@ -67,13 +67,15 @@ load_plugin(const string &p3d_plugin_filename,
             P3D_verify_contents verify_contents, const string &platform,
             P3D_verify_contents verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             bool trusted_environment, bool console_environment,
-            const string &root_dir, const string &host_dir, ostream &logfile);
+            const string &root_dir, const string &host_dir,
+            const string &start_dir, ostream &logfile);
 bool
 bool
 init_plugin(const string &contents_filename, const string &host_url, 
 init_plugin(const string &contents_filename, const string &host_url, 
             P3D_verify_contents verify_contents, const string &platform,
             P3D_verify_contents verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             bool trusted_environment, bool console_environment,
-            const string &root_dir, const string &host_dir, ostream &logfile);
+            const string &root_dir, const string &host_dir,
+            const string &start_dir, ostream &logfile);
 
 
 void unload_plugin(ostream &logfile);
 void unload_plugin(ostream &logfile);
 bool is_plugin_loaded();
 bool is_plugin_loaded();

+ 4 - 3
direct/src/plugin/p3dAuthSession.cxx

@@ -163,7 +163,7 @@ start_p3dcert() {
 
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
 
-  _start_dir = inst_mgr->get_root_dir() + "/start";
+  _start_dir = inst_mgr->get_start_dir();
 
 
   string root_dir = _inst->_p3dcert_package->get_package_dir();
   string root_dir = _inst->_p3dcert_package->get_package_dir();
   mkdir_complete(_start_dir, nout);
   mkdir_complete(_start_dir, nout);
@@ -183,7 +183,7 @@ start_p3dcert() {
   // These are the enviroment variables we forward from the current
   // These are the enviroment variables we forward from the current
   // environment, if they are set.
   // environment, if they are set.
   const wchar_t *keep[] = {
   const wchar_t *keep[] = {
-    L"TMP", L"TEMP", L"HOME", L"USER", 
+    L"TMP", L"TEMP", L"HOME", L"USER",
     L"SYSTEMROOT", L"USERPROFILE", L"COMSPEC",
     L"SYSTEMROOT", L"USERPROFILE", L"COMSPEC",
     NULL
     NULL
   };
   };
@@ -209,7 +209,8 @@ start_p3dcert() {
   // These are the enviroment variables we forward from the current
   // These are the enviroment variables we forward from the current
   // environment, if they are set.
   // environment, if they are set.
   const char *keep[] = {
   const char *keep[] = {
-    "TMP", "TEMP", "HOME", "USER", 
+    "TMP", "TEMP", "HOME", "USER",
+    "LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG",
 #ifdef HAVE_X11
 #ifdef HAVE_X11
     "DISPLAY", "XAUTHORITY",
     "DISPLAY", "XAUTHORITY",
 #endif
 #endif

+ 162 - 59
direct/src/plugin/p3dCert.cxx

@@ -13,6 +13,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "p3dCert.h"
 #include "p3dCert.h"
+#include "p3dCert_strings.h"
 #include "wstring_encode.h"
 #include "wstring_encode.h"
 #include "mkdir_complete.h"
 #include "mkdir_complete.h"
 
 
@@ -26,8 +27,9 @@
 #include <sys/stat.h>
 #include <sys/stat.h>
 #include <string.h>
 #include <string.h>
 #include <limits.h>
 #include <limits.h>
+#include <locale.h>
 
 
-#define BUTTON_WIDTH 120
+#define BUTTON_WIDTH 180 // fit the Russian text
 #define BUTTON_SPACE 10
 #define BUTTON_SPACE 10
 
 
 #include "ca_bundle_data_src.c"
 #include "ca_bundle_data_src.c"
@@ -36,63 +38,152 @@
 #define WIN32_LEAN_AND_MEAN
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <windows.h>
 #include <shellapi.h>
 #include <shellapi.h>
+#include <malloc.h>
 
 
 #define snprintf sprintf_s
 #define snprintf sprintf_s
 #endif
 #endif
 
 
-static const char
-self_signed_cert_text[] =
-  "This Panda3D application uses a self-signed certificate.  "
-  "This means the author's name can't be verified, and you have "
-  "no way of knowing for sure who wrote it.\n\n"
+#ifdef __APPLE__
+#include <CoreFoundation/CoreFoundation.h>
+#endif
 
 
-  "We recommend you click Cancel to avoid running this application.";
+static LanguageIndex li = LI_default;
+
+#if defined(_WIN32)
+static LanguageIndex detect_language() {
+  // This function was introduced in Windows Vista; it may not be available
+  // on older systems.
+  typedef BOOL (*GUPL)(DWORD, PULONG, PZZWSTR, PULONG);
+  GUPL pGetUserPreferredUILanguages = (GUPL)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
+                                                           TEXT("GetUserPreferredUILanguages"));
+  if (pGetUserPreferredUILanguages != NULL) {
+    ULONG num_langs = 0;
+    ULONG buffer_size = 0;
+    pGetUserPreferredUILanguages(8, &num_langs, NULL, &buffer_size);
+    PZZWSTR buffer = (PZZWSTR)_alloca(buffer_size);
+    if (pGetUserPreferredUILanguages(8, &num_langs, buffer, &buffer_size)) {
+      for (ULONG i = 0; i < num_langs; ++i) {
+        size_t len = wcslen(buffer);
+        if (len >= 2 && (buffer[2] == 0 || buffer[2] == L'-')) {
+          // It may be a two-letter code; match it in our list.
+          for (int j = 0; j < LI_COUNT; ++j) {
+            const char *lang_code = language_codes[j];
+            if (lang_code != NULL && lang_code[0] == buffer[0] &&
+                                     lang_code[1] == buffer[1]) {
+              return (LanguageIndex)j;
+            }
+          }
+        }
+        buffer += len + 1;
+      }
+    }
+  }
 
 
-static const char
-unknown_auth_cert_text[] =
-  "This Panda3D application has been signed, but we don't recognize "
-  "the authority that verifies the signature.  This means the author's "
-  "name can't be trusted, and you have no way of knowing "
-  "for sure who wrote it.\n\n"
+  // Fall back to the old Windows XP function.
+  LANGID lang = GetUserDefaultUILanguage() & 0x3ff;
+  if (lang == 0) {
+    return LI_default;
+  }
 
 
-  "We recommend you click Cancel to avoid running this application.";
+  for (int i = 0; i < LI_COUNT; ++i) {
+    if (language_ids[i] != 0 && language_ids[i] == lang) {
+      return (LanguageIndex)i;
+    }
+  }
+  return LI_default;
+}
 
 
-static const char
-verified_cert_text[] =
-  "This Panda3D application has been signed by %s. "
-  "If you trust %s, then click the Run button below "
-  "to run this application on your computer.  This will also "
-  "automatically approve this and any other applications signed by "
-  "%s in the future.\n\n"
+#elif defined(__APPLE__)
+static LanguageIndex detect_language() {
+  // Get and iterate through the list of preferred languages.
+  CFArrayRef langs = CFLocaleCopyPreferredLanguages();
+  CFIndex num_langs = CFArrayGetCount(langs);
 
 
-  "If you are unsure about this application, "
-  "you should click Cancel instead.";
+  for (long i = 0; i < num_langs; ++i) {
+    CFStringRef lang = (CFStringRef)CFArrayGetValueAtIndex(langs, i);
 
 
-static const char
-expired_cert_text[] =
-  "This Panda3D application has been signed by %s, "
-  "but the certificate has expired.\n\n"
+    CFIndex length = CFStringGetLength(lang);
+    if (length < 2) {
+      continue;
+    }
 
 
-  "You should check the current date set on your computer's clock "
-  "to make sure it is correct.\n\n"
+    CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
+    char *buffer = (char *)alloca(max_size);
+    if (!CFStringGetCString(lang, buffer, max_size, kCFStringEncodingUTF8)) {
+      continue;
+    }
 
 
-  "If your computer's date is correct, we recommend "
-  "you click Cancel to avoid running this application.";
+    if (isalnum(buffer[2])) {
+      // It's not a two-letter code.
+      continue;
+    }
 
 
-static const char
-generic_error_cert_text[] =
-  "This Panda3D application has been signed, but there is a problem "
-  "with the certificate (OpenSSL error code %d).\n\n"
+    // See if we support this language.
+    for (int j = 0; j < LI_COUNT; ++j) {
+      const char *lang_code = language_codes[j];
+      if (lang_code != NULL && strncasecmp(buffer, lang_code, 2) == 0) {
+        CFRelease(langs);
+        return (LanguageIndex)j;
+      }
+    }
+  }
 
 
-  "We recommend you click Cancel to avoid running this application.";
+  CFRelease(langs);
+  return LI_default;
+}
 
 
-static const char
-no_cert_text[] =
-  "This Panda3D application has not been signed.  This means you have "
-  "no way of knowing for sure who wrote it.\n\n"
+#else
+static LanguageIndex detect_language() {
+  // First consult the LANGUAGE variable, which is a GNU extension that can
+  // contain multiple languages in order of preference.
+  const char *lang = getenv("LANGUAGE");
+  while (lang != NULL && lang[0] != 0) {
+    size_t len;
+    const char *next = strchr(lang, ':');
+    if (next == NULL) {
+      len = strlen(lang);
+    } else {
+      len = (next - lang);
+      ++next;
+    }
 
 
-  "Click Cancel to avoid running this application.";
+    if (len >= 2 && !isalnum(lang[2])) {
+      // It may be a two-letter language code; match it in our list.
+      for (int i = 0; i < LI_COUNT; ++i) {
+        const char *lang_code = language_codes[i];
+        if (lang_code != NULL && strncasecmp(lang, lang_code, 2) == 0) {
+          return (LanguageIndex)i;
+        }
+      }
+    }
+
+    lang = next;
+  }
+
+  // Fall back to the C locale functions.
+  setlocale(LC_ALL, "");
+  lang = setlocale(LC_MESSAGES, NULL);
+
+  if (lang == NULL || lang[0] == 0 || strcmp(lang, "C") == 0) {
+    // Try the LANG variable.
+    lang = getenv("LANG");
+  }
+
+  if (lang == NULL || strlen(lang) < 2 || isalnum(lang[2])) {
+    // Couldn't extract a meaningful two-letter code.
+    return LI_default;
+  }
 
 
+  // It may be a two-letter language code; match it in our list.
+  for (int i = 0; i < LI_COUNT; ++i) {
+    const char *lang_code = language_codes[i];
+    if (lang_code != NULL && strncasecmp(lang, lang_code, 2) == 0) {
+      return (LanguageIndex)i;
+    }
+  }
+  return LI_default;
+}
+#endif
 
 
 #ifdef _WIN32
 #ifdef _WIN32
 int WINAPI
 int WINAPI
@@ -107,6 +198,8 @@ wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdS
     return 1;
     return 1;
   }
   }
 
 
+  li = detect_language();
+
   wstring cert_filename (argv[0]);
   wstring cert_filename (argv[0]);
   wstring cert_dir (argv[1]);
   wstring cert_dir (argv[1]);
   LocalFree(argv);
   LocalFree(argv);
@@ -126,6 +219,8 @@ int main(int argc, char **argv) {
     return 1;
     return 1;
   }
   }
 
 
+  li = detect_language();
+
   string cert_filename (argv[1]);
   string cert_filename (argv[1]);
   string cert_dir (argv[2]);
   string cert_dir (argv[2]);
 
 
@@ -148,7 +243,7 @@ AuthDialog(const wstring &cert_filename, const wstring &cert_dir) :
 AuthDialog::
 AuthDialog::
 AuthDialog(const string &cert_filename, const string &cert_dir) :
 AuthDialog(const string &cert_filename, const string &cert_dir) :
 #endif
 #endif
-  Fl_Window(435, 242, "New Panda3D Application"),
+  Fl_Window(435, 242, new_application_title[li]),
   _cert_dir(cert_dir)
   _cert_dir(cert_dir)
 {
 {
   _view_cert_dialog = NULL;
   _view_cert_dialog = NULL;
@@ -315,12 +410,20 @@ read_cert_file(const string &cert_filename) {
 #endif  // _WIN32
 #endif  // _WIN32
 
 
   if (fp == NULL) {
   if (fp == NULL) {
+#ifdef _WIN32
+    wcerr << L"Couldn't read " << cert_filename.c_str() << L"\n";
+#else
     cerr << "Couldn't read " << cert_filename.c_str() << "\n";
     cerr << "Couldn't read " << cert_filename.c_str() << "\n";
+#endif
     return;
     return;
   }
   }
   _cert = PEM_read_X509(fp, NULL, NULL, (void *)"");
   _cert = PEM_read_X509(fp, NULL, NULL, (void *)"");
   if (_cert == NULL) {
   if (_cert == NULL) {
+#ifdef _WIN32
+    wcerr << L"Could not read certificate in " << cert_filename.c_str() << L".\n";
+#else
     cerr << "Could not read certificate in " << cert_filename.c_str() << ".\n";
     cerr << "Could not read certificate in " << cert_filename.c_str() << ".\n";
+#endif
     fclose(fp);
     fclose(fp);
     return;
     return;
   }
   }
@@ -510,19 +613,19 @@ layout() {
   short bx = (w() - nbuttons * BUTTON_WIDTH - (nbuttons - 1) * BUTTON_SPACE) / 2;
   short bx = (w() - nbuttons * BUTTON_WIDTH - (nbuttons - 1) * BUTTON_SPACE) / 2;
 
 
   if (_verify_result == 0 && _cert != NULL) {
   if (_verify_result == 0 && _cert != NULL) {
-    Fl_Return_Button *run_button = new Fl_Return_Button(bx, next_y, BUTTON_WIDTH, 25, "Run");
+    Fl_Return_Button *run_button = new Fl_Return_Button(bx, next_y, BUTTON_WIDTH, 25, run_title[li]);
     run_button->callback(this->run_clicked, this);
     run_button->callback(this->run_clicked, this);
     bx += BUTTON_WIDTH + BUTTON_SPACE;
     bx += BUTTON_WIDTH + BUTTON_SPACE;
   }
   }
 
 
   if (_cert != NULL) {
   if (_cert != NULL) {
-    Fl_Button *view_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, "View Certificate");
+    Fl_Button *view_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, show_cert_title[li]);
     view_button->callback(this->view_cert_clicked, this);
     view_button->callback(this->view_cert_clicked, this);
     bx += BUTTON_WIDTH + BUTTON_SPACE;
     bx += BUTTON_WIDTH + BUTTON_SPACE;
   }
   }
 
 
   Fl_Button *cancel_button;
   Fl_Button *cancel_button;
-  cancel_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, "Cancel");
+  cancel_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, cancel_title[li]);
   cancel_button->callback(this->cancel_clicked, this);
   cancel_button->callback(this->cancel_clicked, this);
 
 
   next_y += 42;
   next_y += 42;
@@ -542,12 +645,12 @@ void AuthDialog::
 get_text(char *header, size_t hlen, char *text, size_t tlen) {
 get_text(char *header, size_t hlen, char *text, size_t tlen) {
   switch (_verify_result) {
   switch (_verify_result) {
   case -1:
   case -1:
-    strncpy(header, "No signature!", hlen);
-    strncpy(text, no_cert_text, tlen);
+    strncpy(header, no_cert_title[li], hlen);
+    strncpy(text, no_cert_text[li], tlen);
     break;
     break;
 
 
   case 0:
   case 0:
-    snprintf(text, tlen, verified_cert_text, _friendly_name.c_str(),
+    snprintf(text, tlen, verified_cert_text[li], _friendly_name.c_str(),
                         _friendly_name.c_str(), _friendly_name.c_str());
                         _friendly_name.c_str(), _friendly_name.c_str());
     break;
     break;
 
 
@@ -555,24 +658,24 @@ get_text(char *header, size_t hlen, char *text, size_t tlen) {
   case X509_V_ERR_CERT_HAS_EXPIRED:
   case X509_V_ERR_CERT_HAS_EXPIRED:
   case X509_V_ERR_CRL_NOT_YET_VALID:
   case X509_V_ERR_CRL_NOT_YET_VALID:
   case X509_V_ERR_CRL_HAS_EXPIRED:
   case X509_V_ERR_CRL_HAS_EXPIRED:
-    strncpy(header, "Expired signature!", hlen);
-    snprintf(text, tlen, expired_cert_text, _friendly_name.c_str());
+    strncpy(header, expired_cert_title[li], hlen);
+    snprintf(text, tlen, expired_cert_text[li], _friendly_name.c_str());
     break;
     break;
 
 
   case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
   case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-    strncpy(header, "Unverified signature!", hlen);
-    snprintf(text, tlen, unknown_auth_cert_text);
+    strncpy(header, unverified_cert_title[li], hlen);
+    strncpy(text, unknown_auth_cert_text[li], tlen);
     break;
     break;
 
 
   case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
   case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
   case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
   case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
-    strncpy(header, "Unverified signature!", hlen);
-    strncpy(text, self_signed_cert_text, tlen);
+    strncpy(header, unverified_cert_title[li], hlen);
+    strncpy(text, self_signed_cert_text[li], tlen);
     break;
     break;
 
 
   default:
   default:
-    strncpy(header, "Unverified signature!", hlen);
-    snprintf(text, tlen, generic_error_cert_text, _verify_result);
+    strncpy(header, unverified_cert_title[li], hlen);
+    snprintf(text, tlen, generic_error_cert_text[li], _verify_result);
   }
   }
 }
 }
 
 
@@ -583,7 +686,7 @@ get_text(char *header, size_t hlen, char *text, size_t tlen) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 ViewCertDialog::
 ViewCertDialog::
 ViewCertDialog(AuthDialog *auth_dialog, X509 *cert) :
 ViewCertDialog(AuthDialog *auth_dialog, X509 *cert) :
-  Fl_Window(600, 400, "View Certificate"),
+  Fl_Window(600, 400, show_cert_title[li]),
   _auth_dialog(auth_dialog),
   _auth_dialog(auth_dialog),
   _cert(cert)
   _cert(cert)
 {
 {
@@ -660,12 +763,12 @@ layout() {
 
 
   short bx = (w() - BUTTON_WIDTH * 2 - BUTTON_SPACE) / 2;
   short bx = (w() - BUTTON_WIDTH * 2 - BUTTON_SPACE) / 2;
 
 
-  Fl_Return_Button *run_button = new Fl_Return_Button(bx, 360, BUTTON_WIDTH, 25, "Run");
+  Fl_Return_Button *run_button = new Fl_Return_Button(bx, 360, BUTTON_WIDTH, 25, run_title[li]);
   run_button->callback(this->run_clicked, this);
   run_button->callback(this->run_clicked, this);
 
 
   bx += BUTTON_WIDTH + BUTTON_SPACE;
   bx += BUTTON_WIDTH + BUTTON_SPACE;
 
 
-  Fl_Button *cancel_button = new Fl_Button(bx, 360, BUTTON_WIDTH, 25, "Cancel");
+  Fl_Button *cancel_button = new Fl_Button(bx, 360, BUTTON_WIDTH, 25, cancel_title[li]);
   cancel_button->callback(this->cancel_clicked, this);
   cancel_button->callback(this->cancel_clicked, this);
 
 
   end();
   end();

+ 3 - 3
direct/src/plugin/p3dCert.h

@@ -92,9 +92,9 @@ private:
   X509 *_cert;
   X509 *_cert;
   STACK_OF(X509) *_stack;
   STACK_OF(X509) *_stack;
 
 
-  char _header[32];
-  char _text[512];
-  char _text_clean[1024];
+  char _header[64];
+  char _text[1024];
+  char _text_clean[2048];
 
 
   string _friendly_name;
   string _friendly_name;
   int _verify_result;
   int _verify_result;

+ 571 - 0
direct/src/plugin/p3dCert_strings.cxx

@@ -0,0 +1,571 @@
+// Filename: p3dCert_strings.h
+// Created by:  rdb (25Mar15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "p3dCert_strings.h"
+
+// Translations kindly provided by:
+// eng: drwr
+// nld: rdb
+// deu: Sebastian Hoffmann <TheCheapestPixels at googlemail dot com>
+// spa: Imanol Celaya <imanol at celaya dot me>
+// ita: Flavio Clava
+// rus: montreal
+
+const char *language_codes[LI_COUNT] =
+  {"en", "nl", "de", "es", "it", "eo", "ru"};
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/dd318693%28v=vs.85%29.aspx
+const unsigned char language_ids[LI_COUNT] =
+  {0x09, 0x13, 0x07, 0x0A, 0x10, 0x8F, 0x19};
+
+const char *
+run_title[LI_COUNT] = {
+  "Run",
+  "Uitvoeren",
+  "Starten",
+  "Ejecutar",
+  "Lancia",
+  "Lan\304\211i",
+  "\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214",
+};
+
+const char *
+cancel_title[LI_COUNT] = {
+  "Cancel",
+  "Annuleren",
+  "Abbrechen",
+  "Cancelar",
+  "Cancella",
+  "Nuligi",
+  "\320\236\321\202\320\274\320\265\320\275\320\260",
+};
+
+const char *
+show_cert_title[LI_COUNT] = {
+  "Show Certificate",
+  "Toon Certificaat",
+  "Zertifikat anzeigen",
+  "Mostrar certificado",
+  "Mostra certificato",
+  "Montri Ateston",
+  "\320\237\320\276\320\272\320\260\320\267\320\260\321\202\321\214 \321\201"
+  "\320\265\321\200\321\202\320\270\321\204\320\270\320\272\320\260\321\202",
+};
+
+const char *
+new_application_title[LI_COUNT] = {
+  "New Panda3D Application",
+  "Nieuwe Panda3D Applicatie",
+  "Neue Panda3D-Anwendung",
+  "Nueva aplicaci\303\263n de Panda3D",
+  "Nuova applicazione Panda3D",
+  "Nova aplika\304\265o de Panda3D",
+  "\320\235\320\276\320\262\320\276\320\265 Panda3D-\320\277\321\200\320\270"
+  "\320\273\320\276\320\266\320\265\320\275\320\270\320\265",
+};
+
+const char *
+no_cert_title[LI_COUNT] = {
+  "No signature!",
+  "Geen handtekening!",
+  "Keine Signatur!",
+  "Sin firma!",
+  "Nessuna firma!",
+  "Neniu subskribo!",
+  "\320\235\320\265\321\202 \320\277\320\276\320\264\320\277\320\270\321\201"
+  "\320\270!",
+};
+
+const char *
+unverified_cert_title[LI_COUNT] = {
+  "Unverified signature!",
+  "Ongeverifieerde handtekening!",
+  "Unbest\303\244tigte Signatur!",
+  "Firma sin verificar!",
+  "Firma non verificata!",
+  "Nekontrolita subskribo!",
+  "\320\235\320\265\320\277\321\200\320\276\320\262\320\265\321\200\320\265"
+  "\320\275\320\275\320\260\321\217 \320\277\320\276\320\264\320\277\320\270"
+  "\321\201\321\214!",
+};
+
+const char *
+expired_cert_title[LI_COUNT] = {
+  "Expired signature!",
+  "Verlopen handtekening!",
+  "Abgelaufene Signatur!",
+  "Firma caducada!",
+  "Firma scaduta!",
+  "Eksvalidi\304\235inta subskribo!",
+  "\320\241\321\200\320\276\320\272 \320\264\320\265\320\271\321\201\321\202"
+  "\320\262\320\270\321\217 \320\277\320\276\320\264\320\277\320\270\321\201"
+  "\320\270 \320\270\321\201\321\202\321\221\320\272!",
+};
+
+
+const char *
+self_signed_cert_text[LI_COUNT] = {
+  // eng
+  "This Panda3D application uses a self-signed certificate.  "
+  "This means the author's name can't be verified, and you have "
+  "no way of knowing for sure who wrote it.\n"
+  "\n"
+  "We recommend you click Cancel to avoid running this application.",
+
+  // nld
+  "Deze Panda3D applicatie gebruikt een zelf-getekend certificaat.  "
+  "Dit betekent dat de auteursnaam niet kan worden geverifieerd, en het "
+  "niet zeker is of de applicatie te vertrouwen is.\n"
+  "\n"
+  "Het is aanbevolen om op Annuleren te klikken om de applicatie af te "
+  "sluiten.",
+
+  // deu
+  "Diese Panda3D-Anwendung benutzt ein selbst-signiertes Zertifikat.  Dies "
+  "bedeutet, dass weder der Name des Autors \303\274berpr\303\274ft werden "
+  "kann, noch dass garantiert werden kann, dass tats\303\244chlich die "
+  "angegebene Person diese Anwendung geschrieben hat.\n"
+  "\n"
+  "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht "
+  "auszuf\303\274hren.",
+
+  // spa
+  "Esta aplicaci\303\263n de Panda3D usa un certificado autofirmado.  "
+  "Esto significa que el nombre del autor no puede ser verificado y no se "
+  "puede conocer seguro quien la ha escrito.\n"
+  "\n"
+  "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.",
+
+  // ita
+  "Questa applicazione Panda3D usa un certificato autofirmato.  Ci\303\262 "
+  "significa che il nome dell'autore non pu\303\262 essere verificato, e "
+  "che non hai modo di assicurarti circa chi la abbia scritta.\n"
+  "\n"
+  "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa "
+  "applicazione.",
+
+  // epo
+  "\304\210i tiu aplika\304\265o de Panda3D uzas memsubskribitan ateston.  "
+  "Tio signifas ke la nomo de la verkanto ne povas esti kontrolita, kaj vi "
+  "ne havas certan scimanieron pri la vera verkanto de la aplika\304\265o.\n"
+  "\n"
+  "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on de "
+  "\304\211i tiu aplika\304\265o.",
+
+  // rus
+  "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266"
+  "\320\265\320\275\320\270\320\265 \320\270\321\201\320\277\320\276\320\273"
+  "\321\214\320\267\321\203\320\265\321\202 \321\201\320\260\320\274\320\276"
+  "\320\267\320\260\320\262\320\265\321\200\320\265\320\275\320\275\321\213"
+  "\320\271 \321\201\320\265\321\200\321\202\320\270\321\204\320\270\320\272"
+  "\320\260\321\202.  \320\255\321\202\320\276 \320\276\320\267\320\275"
+  "\320\260\321\207\320\260\320\265\321\202, \321\207\321\202\320\276 "
+  "\320\270\320\274\321\217 \320\260\320\262\321\202\320\276\321\200\320\260 "
+  "\320\275\320\265 \320\274\320\276\320\266\320\265\321\202 \320\261\321\213"
+  "\321\202\321\214 \320\277\320\276\320\264\321\202\320\262\320\265\320\266"
+  "\320\264\320\265\320\275\320\276, \320\270 \320\262\321\213 \320\275"
+  "\320\265 \320\274\320\276\320\266\320\265\321\202\320\265 \320\267\320\275"
+  "\320\260\321\202\321\214, \320\272\321\202\320\276 \320\275\320\260"
+  "\320\277\320\270\321\201\320\260\320\273 \321\215\321\202\320\276 \320\277"
+  "\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265.\n"
+  "\n"
+  "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275"
+  "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260"
+  "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265"
+  "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
+  "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
+  "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
+  "\320\265.",
+};
+
+
+const char *
+unknown_auth_cert_text[LI_COUNT] = {
+  "This Panda3D application has been signed, but we don't recognize "
+  "the authority that verifies the signature.  This means the author's "
+  "name can't be verified, and you have no way of knowing "
+  "for sure who wrote it.\n"
+  "\n"
+  "We recommend you click Cancel to avoid running this application.",
+
+   // nld
+  "Deze Panda3D applicatie is ondertekend, maar de certificaatautoriteit "
+  "die het certificaat heeft uitgegeven wordt niet herkend.  Dit betekent "
+  "dat de auteursnaam niet te vertrouwen is, en het niet zeker is wie de "
+  "applicatie gemaakt heeft.\n"
+  "\n"
+  "Het is aanbevolen om op Annuleren te klikken om de applicatie af te "
+  "sluiten.",
+
+  // deu
+  "Diese Panda3D-Anwendung wurde signiert, aber die "
+  "\303\234berpr\303\274fungsstelle wurde nicht anerkannt.  Dies bedeutet, "
+  "dass weder der Name des Autors \303\274berpr\303\274ft werden kann, noch "
+  "dass garantiert werden kann, dass tats\303\244chlich die angegebene "
+  "Person diese Anwendung geschrieben hat.\n"
+  "\n"
+  "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht "
+  "auszuf\303\274hren.",
+
+  // spa
+  "Esta aplicaci\303\263n de Panda3D esta firmada, pero no reconocemos la "
+  "autoridad que la verifica.  Esto significa que el nombre del autor no "
+  "puede ser verificado y no se puede conocer seguro quien la ha escrito.\n"
+  "\n"
+  "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.",
+
+  // ita
+  "Questa applicazione Panda3D \303\250 stata firmata, ma non riconosciamo "
+  "l'autorit\303\240 che verifica la firma. Ci\303\262 significa che il "
+  "nome dell'autore non pu\303\262 essere verificato, e che non hai modo "
+  "di assicurarti circa chi la abbia scritta.\n"
+  "\n"
+  "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa "
+  "applicazione.",
+
+  // epo
+  "\304\210i tiu aplika\304\265o estas subskribita, sed ni ne rekonas "
+  "la a\305\255toritaton, kiu kontrolas la subskribon. Tio signifas ke la "
+  "nomo de la verkanto ne povas esti konfidata, kaj vi ne havas certan "
+  "scimanieron pri la vera verkanto de la aplika\304\265o.\n"
+  "\n"
+  "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on "
+  "de \304\211i tiu aplika\304\265o.",
+
+  // rus
+  "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270"
+  "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\265\321\201"
+  "\321\202\321\214 \320\277\320\276\320\264\320\277\320\270\321\201\321\214,"
+  " \320\277\320\276 \320\272\320\276\321\202\320\276\321\200\320\276\320\271"
+  " \320\274\321\213 \320\275\320\265 \320\274\320\276\320\266\320\265"
+  "\320\274 \321\200\320\260\321\201\320\277\320\276\320\267\320\275\320\260"
+  "\321\202\321\214 \320\262\320\273\320\260\320\264\320\265\320\273\321\214"
+  "\321\206\320\260.  \320\255\321\202\320\276 \320\276\320\267\320\275"
+  "\320\260\321\207\320\260\320\265\321\202, \321\207\321\202\320\276 "
+  "\320\270\320\274\321\217 \320\260\320\262\321\202\320\276\321\200\320\260 "
+  "\320\275\320\265 \320\274\320\276\320\266\320\265\321\202 \320\261\321\213"
+  "\321\202\321\214 \320\276\320\277\321\200\320\265\320\264\320\265\320\273"
+  "\320\265\320\275\320\276, \320\270 \320\275\320\265\320\262\320\276"
+  "\320\267\320\274\320\276\320\266\320\275\320\276 \321\202\320\276\321\207"
+  "\320\275\320\276 \321\203\320\267\320\275\320\260\321\202\321\214, "
+  "\320\272\321\202\320\276 \320\275\320\260\320\277\320\270\321\201\320\260"
+  "\320\273 \321\215\321\202\320\276 \320\277\321\200\320\270\320\273\320\276"
+  "\320\266\320\265\320\275\320\270\320\265.\n"
+  "\n"
+  "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275"
+  "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260"
+  "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265"
+  "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
+  "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
+  "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
+  "\320\265.",
+};
+
+
+const char *
+verified_cert_text[LI_COUNT] = {
+  // eng
+  "This Panda3D application has been signed by %s.  "
+  "If you trust %s, then click the Run button below "
+  "to run this application on your computer.  This will also "
+  "automatically approve this and any other applications signed by "
+  "%s in the future.\n"
+  "\n"
+  "If you are unsure about this application, "
+  "you should click Cancel instead.",
+
+  // nld
+  "Deze Panda3D applicatie is ondertekend door %s.  "
+  "Als u %s vertrouwt, klik dan de onderstaande knop Uitvoeren om de applicatie "
+  "op uw computer uit te voeren.  Dit zal ook deze en andere door %s getekende "
+  "applicaties in het vervolg toestaan.\n"
+  "\n"
+  "Als u niet zeker bent over deze applicatie is het aanbevolen om op "
+  "Annuleren te klikken.",
+
+  // deu
+  "Diese Panda3D-Anwendung wurde von %s signiert.  Falls Sie %s vertrauen, "
+  "dr\303\274cken Sie den Starten-Knopf, um diese Anwendung auszuf\303\274hren.  "
+  "Zudem werden in der Zukunft diese und alle anderen von %s signierten "
+  "Anwendungen automatisch als vertrauensw\303\274rdig anerkannt.\n"
+  "\n"
+  "Falls Sie sich unsicher \303\274ber diese Anwendung sind, sollten Sie "
+  "Abbrechen dr\303\274cken.",
+
+  // spa
+  "Esta aplicaci\303\263n de Panda3D ha sido firmada por %s.  Si se considera %s"
+  "de confianza el bot\303\263n inferior ejecutar\303\241 la aplicaci\303\263n."
+  "Esto validara esta y cualquier otra applizacion firmada por %s en el futuro.\n"
+  "\n"
+  "Si se duda de la aplicaci\303\263nse recomienda cancelar."
+
+  // ita
+  "Questa applicazione Panda3D \303\250 stata firmata da %s.  Se %s \303\250 "
+  "un'entit\303\240 fidata, allora clicca il bottone Lancia sottostante per "
+  "lanciare questa applicazione sul tuo computer. Inoltre, ci\303\262 "
+  "approver\303\240 automaticamente questa e ogni altra applicazione "
+  "firmata da %s in futuro.\n"
+  "\n"
+  "Se non sei sicuro circa questa applicazione, dovresti invece cliccare "
+  "su Cancella.",
+
+  // epo
+  "\304\210i tiu aplika\304\265o estas subskribita de %s.  "
+  "Se %s estas fidinda la\305\255 vi, premu la butonon 'Lan\304\211i' sube por "
+  "lan\304\211i \304\211i tiun aplika\304\265on per via komputilo.  Anka\305\255 "
+  "tio a\305\255tomate estonece aprobos \304\211i tiun kaj alian ajn "
+  "aplika\304\265on, kiu estas subskribita de %s.\n"
+  "\n"
+  "Se vi ne estas certa pri \304\211i tiu aplika\304\265o, "
+  "vi anstata\305\255e povus premi la butonon 'Nuligi'.",
+
+  // rus
+  "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266"
+  "\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\277\320\270"
+  "\321\201\320\260\320\275\320\276 %s.  "
+  "\320\225\321\201\320\273\320\270 \320\262\321\213 \320\264\320\276\320\262"
+  "\320\265\321\200\321\217\320\265\321\202\320\265 %s, \320\275\320\260"
+  "\320\266\320\274\320\270\321\202\320\265 \320\272\320\275\320\276\320\277"
+  "\320\272\321\203 \"\320\227\320\260\320\277\321\203\321\201\321\202"
+  "\320\270\321\202\321\214\" \320\262\320\275\320\270\320\267\321\203, "
+  "\321\207\321\202\320\276\320\261\321\213 \320\267\320\260\320\277\321\203"
+  "\321\201\321\202\320\270\321\202\321\214 \321\215\321\202\320\276 \320\277"
+  "\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265 "
+  "\320\275\320\260 \320\262\320\260\321\210\320\265\320\274 \320\272\320\276"
+  "\320\274\320\277\321\214\321\216\321\202\320\265\321\200\320\265. \320\255"
+  "\321\202\320\276 \321\202\320\260\320\272\320\266\320\265 \320\260\320\262"
+  "\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201"
+  "\320\272\320\270 \320\277\320\276\320\264\321\202\320\262\320\265\321\200"
+  "\320\264\320\270\321\202 \321\215\321\202\320\276 \320\270 \320\264"
+  "\321\200\321\203\320\263\320\270\320\265 \320\277\321\200\320\270\320\273"
+  "\320\276\320\266\320\265\320\275\320\270\321\217, \320\275\320\260\320\277"
+  "\320\270\321\201\320\260\320\275\320\275\321\213\320\265 %s \320\262 "
+  "\320\261\321\203\320\264\321\203\321\211\320\265\320\274.\n"
+  "\n"
+  "\320\225\321\201\320\273\320\270 \320\262\321\213 \320\275\320\265 "
+  "\321\203\320\262\320\265\321\200\320\265\320\275\321\213 \320\262 \321\215"
+  "\321\202\320\276\320\274 \320\277\321\200\320\270\320\273\320\276\320\266"
+  "\320\265\320\275\320\270\320\270, \320\275\320\260\320\266\320\274\320\270"
+  "\321\202\320\265 \320\272\320\275\320\276\320\277\320\272\321\203 \""
+  "\320\236\321\202\320\274\320\265\320\275\320\260\".",
+};
+
+
+const char *
+expired_cert_text[LI_COUNT] = {
+  // eng
+  "This Panda3D application has been signed by %s, "
+  "but the certificate has expired.\n"
+  "\n"
+  "You should check the current date set on your computer's clock "
+  "to make sure it is correct.\n"
+  "\n"
+  "If your computer's date is correct, we recommend "
+  "you click Cancel to avoid running this application.",
+
+  // nld
+  "Deze Panda3D applicatie is ondertekend door %s, maar de geldigheidsdatum "
+  "van het certificaat is verstreken.\n"
+  "\n"
+  "Controleer de datum op uw computerklok om te zorgen dat deze juist is "
+  "ingesteld.\n"
+  "\n"
+  "Als de datum op uw computer juist is, is het aanbevolen om op Annuleren te "
+  "klikken om de applicatie af te sluiten.",
+
+  // deu
+  "Diese Anwendung wurde von %s signiert, aber das Zertifikat ist abgelaufen.\n"
+  "\n"
+  "Sie sollten die aktuelle Uhrzeit auf Ihrem Computer "
+  "\303\274berpr\303\274fen, um sicherzugehen, dass sie korrekt ist.\n"
+  "\n"
+  "Falls die Uhrzeit auf Ihrem Computer korrekt ist, empfehlen wir Ihnen "
+  "Abbrechen zu dr\303\274cken.",
+
+  // spa
+  "Esta aplicaci\303\263n Panda3D ha sido firmada por %s pero el certificado ha"
+  "caducado.\n"
+  "\n"
+  "Se recomienda comprobar la fecha del reloj.\n"
+  "\n"
+  "Si la fecha del reloj es correcta se recomienda cancelar.",
+
+  // ita
+  "Questa applicazione Panda3D \303\250 stata firmata da %s, ma il "
+  "certificato \303\250 scaduto.\n"
+  "\n"
+  "Dovresti controllare la data attuale impostata nell'orologio del tuo "
+  "computer per assicurarti che sia corretta.\n"
+  "\n"
+  "Se la data del tuo computer \303\250 corretta, raccomandiamo di cliccare "
+  "Cancella per evitare di lanciare questa applicazione."
+
+  // epo
+  "\304\210i tiu aplika\304\265o de Panda3D estas subskribita de %s, "
+  "sed la atesto eksvalidi\304\235is.\n"
+  "\n"
+  "Vi povus kontroli la aktualan daton, kiu estas agordata sur la horlo\304\235o de "
+  "via komputilo por certigi ke \304\235i estas korekta.\n"
+  "\n"
+  "Se la dato de via komputilo estas korekta, ni rekomendas ke vi premas la "
+  "butonon 'Nuligi' por eviti lan\304\211on de \304\211i tiu aplika\304\265o.",
+
+  // rus
+  "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266"
+  "\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\277\320\270"
+  "\321\201\320\260\320\275\320\276 %s, \320\276\320\264\320\275\320\260"
+  "\320\272\320\276 \321\201\321\200\320\276\320\272 \320\264\320\265\320\271"
+  "\321\201\321\202\320\262\320\270\321\217 \321\201\320\265\321\200\321\202"
+  "\320\270\321\204\320\270\320\272\320\260\321\202\320\260 \320\270\321\201"
+  "\321\202\321\221\320\272.\n"
+  "\n"
+  "\320\237\321\200\320\276\320\262\320\265\321\200\321\214\321\202\320\265, "
+  "\320\277\320\276\320\266\320\260\320\273\321\203\320\271\321\201\321\202"
+  "\320\260, \320\264\320\260\321\202\321\203 \320\275\320\260 \320\262"
+  "\320\260\321\210\320\265\320\274 \320\272\320\276\320\274\320\277\321\214"
+  "\321\216\321\202\320\265\321\200\320\265.\n"
+  "\n"
+  "\320\225\321\201\320\273\320\270 \320\275\320\260 \320\262\320\260\321\210"
+  "\320\265\320\274 \320\272\320\276\320\274\320\277\321\214\321\216\321\202"
+  "\320\265\321\200\320\265 \321\203\321\201\321\202\320\260\320\275\320\276"
+  "\320\262\320\273\320\265\320\275\320\276 \320\277\321\200\320\260\320\262"
+  "\320\270\320\273\321\214\320\275\320\276\320\265 \320\262\321\200\320\265"
+  "\320\274\321\217, \320\274\321\213 \321\200\320\265\320\272\320\276"
+  "\320\274\320\265\320\275\320\264\321\203\320\265\320\274 \320\262\320\260"
+  "\320\274 \320\275\320\260\320\266\320\260\321\202\321\214 \320\272\320\275"
+  "\320\276\320\277\320\272\321\203 \"\320\236\321\202\320\274\320\265"
+  "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
+  "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
+  "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
+  "\320\265.",
+};
+
+
+const char *
+generic_error_cert_text[LI_COUNT] = {
+  // eng
+  "This Panda3D application has been signed, but there is a problem "
+  "with the certificate (OpenSSL error code %d).\n"
+  "\n"
+  "We recommend you click Cancel to avoid running this application.",
+
+  // nld
+  "Deze Panda3D applicatie is ondertekend, maar er is een probleem "
+  "met het certificaat opgetreden (OpenSSL foutcode %d).\n"
+  "\n"
+  "Het is aanbevolen om op Annuleren te klikken om de applicatie af te "
+  "sluiten.",
+
+  // deu
+  "Diese Panda3D-Anwendung wurde signiert, aber es gibt ein Problem mit "
+  "dem Zertifikat (OpenSSL Fehlercode %d).\n"
+  "\n"
+  "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht "
+  "auszuf\303\274hren.",
+
+  // spa
+  "Esta aplicaci\303\263n de Panda3D esta firmada pero hay un problema con el "
+  "certificado (Error de OpenSSL %d).\n"
+  "\n"
+  "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.",
+
+  // ita
+  "Questa applicazione Panda3D \303\250 stata firmata, ma c'\303\250 un "
+  "problema col certificato (codice di errore OpenSSL %s).\n"
+  "\n"
+  "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa "
+  "applicazione.",
+
+  // epo
+  "\304\210i tiu aplika\304\265o de Panda3D estas subskribita, sed la "
+  "atesto havas problemon (OpenSSL erarkodo %d).\n"
+  "\n"
+  "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on "
+  "de \304\211i tiu aplika\304\265o.",
+
+  // rus
+  "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270"
+  "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\265\321\201"
+  "\321\202\321\214 \320\277\320\276\320\264\320\277\320\270\321\201\321\214,"
+  " \320\275\320\276 \320\262\320\276\320\267\320\275\320\270\320\272\320\273"
+  "\320\260 \320\277\321\200\320\276\320\261\320\273\320\265\320\274\320\260 "
+  "\321\201 \321\201\320\265\321\200\321\202\320\270\321\204\320\270\320\272"
+  "\320\260\321\202\320\276\320\274 (\320\232\320\276\320\264 \320\276"
+  "\321\210\320\270\320\261\320\272\320\270 OpenSSL %d).\n"
+  "\n"
+  "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275"
+  "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260"
+  "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265"
+  "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
+  "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
+  "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
+  "\320\265.",
+};
+
+
+const char *
+no_cert_text[LI_COUNT] = {
+  "This Panda3D application has not been signed.  This means you have "
+  "no way of knowing for sure who wrote it.\n"
+  "\n"
+  "Click Cancel to avoid running this application.",
+
+  // nld
+  "Deze Panda3D applicatie is niet ondertekend.  Dit betekent dat het niet "
+  "mogelijk is om de auteur te verifi\303\253ren.\n"
+  "\n"
+  "Klik op Annuleren om de applicatie af te sluiten.",
+
+  // deu
+  "Diese Panda3D-Anwendung wurde nicht signiert.  Es gibt keine "
+  "M\303\266glichkeit festzustellen, wer diese entwickelt hat.\n"
+  "\n"
+  "Dr\303\274cken Sie Abbrechen, um diese Anwendung nicht auszuf\303\274hren.",
+
+  // spa
+  "Esta aplicaci\303\263n de Panda3D no esta firmada, no hay forma de conocer "
+  "quien la ha escrito.\n"
+  "\n"
+  "Cancelar para evitar ejecutar la aplicaci\303\263n.",
+
+  // ita
+  "Questa applicazione Panda3D non \303\250 stata firmata.  Ci\303\262 "
+  "significa che non hai modo di assicurarti circa chi la abbia scritta.\n"
+  "\n"
+  "Clicca Cancella per evitare di lanciare questa applicazione.",
+
+  // epo
+  "\304\210i tiu aplika\304\265o de Panda3D ne estas subskribita.  Tio "
+  "signifas ke vi ne havas certan scimanieron pri la vera verkanto de "
+  "la aplika\304\265o.\n"
+  "\n"
+  "Premu la butonon 'Nuligi' por eviti lan\304\211on de \304\211i tiu "
+  "aplika\304\265o.",
+
+  // rus
+  "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270"
+  "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\275\320\265"
+  "\321\202 \320\277\320\276\320\264\320\277\320\270\321\201\320\270. "
+  "\320\255\321\202\320\276 \320\276\320\267\320\275\320\260\321\207\320\260"
+  "\320\265\321\202, \321\207\321\202\320\276 \320\262\321\213 \320\275"
+  "\320\265 \320\274\320\276\320\266\320\265\321\202\320\265 \320\267\320\275"
+  "\320\260\321\202\321\214, \320\272\321\202\320\276 \320\265\320\263"
+  "\320\276 \320\275\320\260\320\277\320\270\321\201\320\260\320\273.\n"
+  "\n"
+  "\320\235\320\260\320\266\320\274\320\270\321\202\320\265 \"\320\236"
+  "\321\202\320\274\320\265\320\275\320\260\", \321\207\321\202\320\276"
+  "\320\261\321\213 \320\275\320\265 \320\267\320\260\320\277\321\203\321\201"
+  "\320\272\320\260\321\202\321\214 \320\277\321\200\320\270\320\273\320\276"
+  "\320\266\320\265\320\275\320\270\320\265.",
+};

+ 45 - 0
direct/src/plugin/p3dCert_strings.h

@@ -0,0 +1,45 @@
+// Filename: p3dCert_strings.h
+// Created by:  rdb (25Mar15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+enum LanguageIndex {
+  LI_en, // English
+  LI_nl, // Dutch
+  LI_de, // German
+  LI_es, // Spanish
+  LI_it, // Italian
+  LI_eo, // Esperanto
+  LI_ru, // Russian
+  LI_COUNT,
+
+  LI_default = LI_en
+};
+
+extern const char *language_codes[LI_COUNT];
+extern const unsigned char language_ids[LI_COUNT];
+
+extern const char *run_title[LI_COUNT];
+extern const char *cancel_title[LI_COUNT];
+extern const char *show_cert_title[LI_COUNT];
+
+extern const char *new_application_title[LI_COUNT];
+extern const char *no_cert_title[LI_COUNT];
+extern const char *unverified_cert_title[LI_COUNT];
+extern const char *expired_cert_title[LI_COUNT];
+
+extern const char *self_signed_cert_text[LI_COUNT];
+extern const char *unknown_auth_cert_text[LI_COUNT];
+extern const char *verified_cert_text[LI_COUNT];
+extern const char *expired_cert_text[LI_COUNT];
+extern const char *generic_error_cert_text[LI_COUNT];
+extern const char *no_cert_text[LI_COUNT];

+ 91 - 3
direct/src/plugin/p3dInstance.cxx

@@ -1564,7 +1564,7 @@ uninstall_packages() {
   if (inst_mgr->get_verify_contents() != P3D_VC_never) {
   if (inst_mgr->get_verify_contents() != P3D_VC_never) {
     string start_dir_suffix = get_start_dir_suffix();
     string start_dir_suffix = get_start_dir_suffix();
     if (!start_dir_suffix.empty()) {
     if (!start_dir_suffix.empty()) {
-      string start_dir = inst_mgr->get_root_dir() + "/start" + start_dir_suffix;
+      string start_dir = inst_mgr->get_start_dir() + start_dir_suffix;
       nout << "Cleaning up start directory " << start_dir << "\n";
       nout << "Cleaning up start directory " << start_dir << "\n";
       inst_mgr->delete_directory_recursively(start_dir);
       inst_mgr->delete_directory_recursively(start_dir);
     }
     }
@@ -2769,6 +2769,7 @@ make_splash_window() {
     int r, g, b;
     int r, g, b;
     if (parse_color(r, g, b, _fparams.lookup_token("bgcolor"))) {
     if (parse_color(r, g, b, _fparams.lookup_token("bgcolor"))) {
       _splash_window->set_bgcolor(r, g, b);
       _splash_window->set_bgcolor(r, g, b);
+      _splash_window->set_bar_bgcolor(r, g, b);
     } else {
     } else {
       nout << "parse failure on bgcolor " << _fparams.lookup_token("bgcolor") << "\n";
       nout << "parse failure on bgcolor " << _fparams.lookup_token("bgcolor") << "\n";
     }
     }
@@ -2781,11 +2782,98 @@ make_splash_window() {
       nout << "parse failure on barcolor " << _fparams.lookup_token("barcolor") << "\n";
       nout << "parse failure on barcolor " << _fparams.lookup_token("barcolor") << "\n";
     }
     }
   }
   }
+  if (_fparams.has_token("bar_bgcolor")) {
+    int r, g, b;
+    if (parse_color(r, g, b, _fparams.lookup_token("bar_bgcolor"))) {
+      _splash_window->set_bar_bgcolor(r, g, b);
+    } else {
+      nout << "parse failure on bar_bgcolor " << _fparams.lookup_token("bar_bgcolor") << "\n";
+    }
+  }
+  if (_fparams.has_token("bar_border")) {
+    int border = _fparams.lookup_token_int("bar_border");
+    _splash_window->set_bar_border(border);
+  }
+  if (_fparams.has_token("bar_bottom")) {
+    int bottom = _fparams.lookup_token_int("bar_bottom");
+    _splash_window->set_bar_bottom(bottom);
+  }
+  if (_fparams.has_token("bar_width")) {
+    string bar_width = _fparams.lookup_token("bar_width");
+    char *endptr;
+    long width = strtol(bar_width.c_str(), &endptr, 10);
+    bool percent = false;
+
+    if (*endptr == '%') {
+      percent = true;
+      ++endptr;
+    }
+    if (*endptr == '\0' && width >= 0 && width < 65536) {
+      _splash_window->set_bar_width((int)width, percent);
+    } else {
+      nout << "parse failure on bar_width " << bar_width << "\n";
+    }
+  }
+  if (_fparams.has_token("bar_height")) {
+    string bar_height = _fparams.lookup_token("bar_height");
+    char *endptr;
+    long height = strtol(bar_height.c_str(), &endptr, 10);
+    bool percent = false;
+
+    if (*endptr == '%') {
+      percent = true;
+      ++endptr;
+    }
+    if (*endptr == '\0' && height >= 0 && height < 65536) {
+      _splash_window->set_bar_height((int)height, percent);
+    } else {
+      nout << "parse failure on bar_height " << bar_height << "\n";
+    }
+  }
+  if (_fparams.has_token("font_family")) {
+    string family = _fparams.lookup_token("font_family");
+    _splash_window->set_font_family(family);
+  }
+  if (_fparams.has_token("font_size")) {
+    int size = _fparams.lookup_token_int("font_size");
+    _splash_window->set_font_size(size);
+  }
+  if (_fparams.has_token("font_style")) {
+    string style = _fparams.lookup_token("font_style");
+
+    if (style == "normal") {
+      _splash_window->set_font_style(P3DSplashWindow::FS_normal);
+    } else if (style == "oblique") {
+      _splash_window->set_font_style(P3DSplashWindow::FS_oblique);
+    } else if (style == "italic") {
+      _splash_window->set_font_style(P3DSplashWindow::FS_italic);
+    } else {
+      nout << "parse_failure on font_style " << style << "\n";
+    }
+  }
+  if (_fparams.has_token("font_weight")) {
+    string weight = _fparams.lookup_token("font_weight");
+
+    if (weight == "normal") {
+      _splash_window->set_font_weight(400);
+    } else if (weight == "bold") {
+      _splash_window->set_font_weight(700);
+    } else if (weight == "bolder") {
+      _splash_window->set_font_weight(700);
+    } else if (weight == "lighter") {
+      _splash_window->set_font_weight(100);
+    } else if (weight.size() == 3 &&
+               weight[0] >= '1' && weight[0] <= '9' &&
+               weight[1] == '0' && weight[2] == '0') {
+      _splash_window->set_font_weight(((int)weight[0] - 48) * 100);
+    } else {
+      nout << "parse_failure on font_weight " << weight << "\n";
+    }
+  }
 
 
   _splash_window->set_wparams(_wparams);
   _splash_window->set_wparams(_wparams);
   _splash_window->set_install_label(_install_label);
   _splash_window->set_install_label(_install_label);
-  
-    
+
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
 
   // Go get the required images.
   // Go get the required images.

+ 12 - 0
direct/src/plugin/p3dInstanceManager.I

@@ -111,6 +111,18 @@ get_root_dir() const {
   return _root_dir;
   return _root_dir;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::get_start_dir
+//       Access: Public
+//  Description: Returns the directory that the .p3d file should be
+//               mounted to and run from.  This is usually the
+//               "start" subdirectory of the root_dir.
+////////////////////////////////////////////////////////////////////
+inline const string &P3DInstanceManager::
+get_start_dir() const {
+  return _start_dir;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::get_platform
 //     Function: P3DInstanceManager::get_platform
 //       Access: Public
 //       Access: Public

+ 23 - 6
direct/src/plugin/p3dInstanceManager.cxx

@@ -201,7 +201,8 @@ initialize(int api_version, const string &contents_filename,
            const string &platform, const string &log_directory,
            const string &platform, const string &log_directory,
            const string &log_basename, bool trusted_environment,
            const string &log_basename, bool trusted_environment,
            bool console_environment,
            bool console_environment,
-           const string &root_dir, const string &host_dir) {
+           const string &root_dir, const string &host_dir,
+           const string &start_dir) {
   _api_version = api_version;
   _api_version = api_version;
   _host_url = host_url;
   _host_url = host_url;
   _verify_contents = verify_contents;
   _verify_contents = verify_contents;
@@ -258,14 +259,22 @@ initialize(int api_version, const string &contents_filename,
 
 
     // TODO: Linux multiplatform support.  Just add the
     // TODO: Linux multiplatform support.  Just add the
     // appropriate platform strings to _supported_platforms.
     // appropriate platform strings to _supported_platforms.
+  } else {
+    nout << "Platform string was set by plugin to " << _platform << "\n";
   }
   }
 
 
   if (_supported_platforms.empty()) {
   if (_supported_platforms.empty()) {
+    // Hack for older plug-ins, which should still remain compatible with
+    // newer versions of the runtime distribution.
+    if (_platform == "win32") {
+      _supported_platforms.push_back("win_i386");
+    }
+
     // We always support at least the specific platform on which we're
     // We always support at least the specific platform on which we're
     // running.
     // running.
     _supported_platforms.push_back(_platform);
     _supported_platforms.push_back(_platform);
   }
   }
-      
+
 #ifdef P3D_PLUGIN_LOG_DIRECTORY
 #ifdef P3D_PLUGIN_LOG_DIRECTORY
   if (_log_directory.empty()) {
   if (_log_directory.empty()) {
     _log_directory = P3D_PLUGIN_LOG_DIRECTORY;
     _log_directory = P3D_PLUGIN_LOG_DIRECTORY;
@@ -290,12 +299,18 @@ initialize(int api_version, const string &contents_filename,
   } else {
   } else {
     _root_dir = root_dir;
     _root_dir = root_dir;
   }
   }
-  
+
   _host_dir = host_dir;
   _host_dir = host_dir;
 
 
+  if (start_dir.empty()) {
+    _start_dir = _root_dir + "/start";
+  } else {
+    _start_dir = start_dir;
+  }
+
   // Allow the caller (e.g. panda3d.exe) to specify a log directory.
   // Allow the caller (e.g. panda3d.exe) to specify a log directory.
   // Or, allow the developer to compile one in.
   // Or, allow the developer to compile one in.
-  
+  //
   // Failing that, we write logfiles to Panda3D/log.
   // Failing that, we write logfiles to Panda3D/log.
   if (_log_directory.empty()) {
   if (_log_directory.empty()) {
     _log_directory = _root_dir + "/log";
     _log_directory = _root_dir + "/log";
@@ -1507,8 +1522,10 @@ create_runtime_environment() {
 
 
   // Make the certificate directory.
   // Make the certificate directory.
   _certs_dir = _root_dir + "/certs";
   _certs_dir = _root_dir + "/certs";
-  if (!mkdir_complete(_certs_dir, nout)) {
-    nout << "Couldn't mkdir " << _certs_dir << "\n";
+  if (!get_trusted_environment()) {
+    if (!mkdir_complete(_certs_dir, nout)) {
+      nout << "Couldn't mkdir " << _certs_dir << "\n";
+    }
   }
   }
 
 
   _created_runtime_environment = true;
   _created_runtime_environment = true;

+ 4 - 1
direct/src/plugin/p3dInstanceManager.h

@@ -59,7 +59,8 @@ public:
                   bool trusted_environment,
                   bool trusted_environment,
                   bool console_environment,
                   bool console_environment,
                   const string &root_dir = "",
                   const string &root_dir = "",
-                  const string &host_dir = "");
+                  const string &host_dir = "",
+                  const string &start_dir = "");
 
 
   inline bool is_initialized() const;
   inline bool is_initialized() const;
   inline void reconsider_runtime_environment();
   inline void reconsider_runtime_environment();
@@ -69,6 +70,7 @@ public:
   inline int get_api_version() const;
   inline int get_api_version() const;
   inline const string &get_host_url() const;
   inline const string &get_host_url() const;
   inline const string &get_root_dir() const;
   inline const string &get_root_dir() const;
+  inline const string &get_start_dir() const;
   inline const string &get_platform() const;
   inline const string &get_platform() const;
   inline const string &get_temp_directory() const;
   inline const string &get_temp_directory() const;
   inline const string &get_log_directory() const;
   inline const string &get_log_directory() const;
@@ -173,6 +175,7 @@ private:
   string _host_url;
   string _host_url;
   string _root_dir;
   string _root_dir;
   string _host_dir;
   string _host_dir;
+  string _start_dir;
   string _certs_dir;
   string _certs_dir;
   P3D_verify_contents _verify_contents;
   P3D_verify_contents _verify_contents;
   string _platform;
   string _platform;

+ 98 - 70
direct/src/plugin/p3dOsxSplashWindow.cxx

@@ -36,6 +36,7 @@ P3DOsxSplashWindow::
 P3DOsxSplashWindow(P3DInstance *inst, bool make_visible) : 
 P3DOsxSplashWindow(P3DInstance *inst, bool make_visible) : 
   P3DSplashWindow(inst, make_visible)
   P3DSplashWindow(inst, make_visible)
 {
 {
+  _font_attribs = NULL;
   _install_progress = 0;
   _install_progress = 0;
   _progress_known = true;
   _progress_known = true;
   _received_data = 0;
   _received_data = 0;
@@ -59,6 +60,9 @@ P3DOsxSplashWindow::
     DisposeWindow(_toplevel_window);
     DisposeWindow(_toplevel_window);
     _toplevel_window = NULL;
     _toplevel_window = NULL;
   }
   }
+  if (_font_attribs != NULL) {
+    CFRelease(_font_attribs);
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -123,6 +127,41 @@ set_wparams(const P3DWindowParams &wparams) {
       }
       }
     }
     }
   }
   }
+
+  // Determine the attributes of the font we're going to use.
+  int symbolic = 0;
+  if (_font_style != FS_normal) {
+    symbolic |= kCTFontItalicTrait;
+  }
+  if (_font_weight > 500) {
+    symbolic |= kCTFontBoldTrait;
+  }
+  CFNumberRef symbolic_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &symbolic);
+
+  CFStringRef traits_keys[1] = { kCTFontSymbolicTrait };
+  CFTypeRef traits_values[1] = { symbolic_ref };
+  CFDictionaryRef traits = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&traits_keys, (const void **)&traits_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+  CFStringRef family = CFStringCreateWithCString(NULL, _font_family.c_str(), kCFStringEncodingUTF8);
+
+  CFStringRef attribs_keys[2] = { kCTFontFamilyNameAttribute, kCTFontTraitsAttribute };
+  CFTypeRef attribs_values[2] = { family, traits };
+  CFDictionaryRef attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&attribs_keys, (const void **)&attribs_values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+  // Create the font object.
+  CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes(attribs);
+  CTFontRef font = CTFontCreateWithFontDescriptor(font_desc, _font_size, NULL);
+
+  CFStringRef keys[1] = { kCTFontAttributeName };
+  CFTypeRef values[1] = { font };
+  _font_attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys, (const void **)&values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+  CFRelease(font);
+  CFRelease(font_desc);
+  CFRelease(attribs);
+  CFRelease(traits);
+  CFRelease(family);
+  CFRelease(symbolic_ref);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -171,6 +210,9 @@ set_image_filename(const string &image_filename, ImagePlacement image_placement)
   case IP_button_click:
   case IP_button_click:
     load_image(_button_click_image, image_filename);
     load_image(_button_click_image, image_filename);
     break;
     break;
+
+  default:
+    return;
   }
   }
 
 
   refresh();
   refresh();
@@ -333,10 +375,8 @@ paint_window_osx_cgcontext(CGContextRef context) {
   CGColorRef bg = CGColorCreate(rgb_space, bg_components);
   CGColorRef bg = CGColorCreate(rgb_space, bg_components);
 
 
   CGRect region = { { 0, 0 }, { _win_width, _win_height } };
   CGRect region = { { 0, 0 }, { _win_width, _win_height } };
-  CGContextBeginPath(context);
   CGContextSetFillColorWithColor(context, bg);
   CGContextSetFillColorWithColor(context, bg);
-  CGContextAddRect(context, region);
-  CGContextFillPath(context);
+  CGContextFillRect(context, region);
 
 
   CGColorRelease(bg);
   CGColorRelease(bg);
   CGColorSpaceRelease(rgb_space);
   CGColorSpaceRelease(rgb_space);
@@ -445,8 +485,6 @@ handle_event_osx_event_record(const P3D_event_data &event) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool P3DOsxSplashWindow::
 bool P3DOsxSplashWindow::
 handle_event_osx_cocoa(const P3D_event_data &event) {
 handle_event_osx_cocoa(const P3D_event_data &event) {
-  bool retval = false;
-
   assert(event._event_type == P3D_ET_osx_cocoa);
   assert(event._event_type == P3D_ET_osx_cocoa);
   const P3DCocoaEvent &ce = event._event._osx_cocoa._event;
   const P3DCocoaEvent &ce = event._event._osx_cocoa._event;
 
 
@@ -455,33 +493,31 @@ handle_event_osx_cocoa(const P3D_event_data &event) {
     if (_visible) {
     if (_visible) {
       CGContextRef context = ce.data.draw.context;
       CGContextRef context = ce.data.draw.context;
       paint_window_osx_cgcontext(context);
       paint_window_osx_cgcontext(context);
-      retval = true;
+      return true;
+    } else {
+      return false;
     }
     }
-    break;
 
 
   case P3DCocoaEventMouseDown:
   case P3DCocoaEventMouseDown:
     set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, true);
     set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, true);
-    retval = true;
-    break;
+    return true;
 
 
   case P3DCocoaEventMouseUp:
   case P3DCocoaEventMouseUp:
     set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, false);
     set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, false);
-    retval = true;
-    break;
+    return true;
 
 
   case P3DCocoaEventMouseMoved:
   case P3DCocoaEventMouseMoved:
   case P3DCocoaEventMouseDragged:
   case P3DCocoaEventMouseDragged:
     set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, _mouse_down);
     set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, _mouse_down);
-    retval = true;
-    break;
+    return true;
 
 
   case P3DCocoaEventFocusChanged:
   case P3DCocoaEventFocusChanged:
     _mouse_active = (ce.data.focus.hasFocus != 0);
     _mouse_active = (ce.data.focus.hasFocus != 0);
-    retval = true;
-    break;
-  }
+    return true;
 
 
-  return retval;
+  default:
+    return false;
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -612,42 +648,33 @@ paint_image(CGContextRef context, const OsxImageData &image) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DOsxSplashWindow::
 void P3DOsxSplashWindow::
 paint_progress_bar(CGContextRef context) {
 paint_progress_bar(CGContextRef context) {
-  // Get some colors we'll need.  We can't just use
-  // CGColorGetConstantColor(), since that requires 10.5.
   CGFloat fg_components[] = { _fgcolor_r / 255.0f, _fgcolor_g / 255.0f, _fgcolor_b / 255.0f, 1 };
   CGFloat fg_components[] = { _fgcolor_r / 255.0f, _fgcolor_g / 255.0f, _fgcolor_b / 255.0f, 1 };
   CGFloat bg_components[] = { _bgcolor_r / 255.0f, _bgcolor_g / 255.0f, _bgcolor_b / 255.0f, 1 };
   CGFloat bg_components[] = { _bgcolor_r / 255.0f, _bgcolor_g / 255.0f, _bgcolor_b / 255.0f, 1 };
   CGFloat bar_components[] = { _barcolor_r / 255.0f, _barcolor_g / 255.0f, _barcolor_b / 255.0f, 1 };
   CGFloat bar_components[] = { _barcolor_r / 255.0f, _barcolor_g / 255.0f, _barcolor_b / 255.0f, 1 };
+  CGFloat bar_bg_components[] = { _bar_bgcolor_r / 255.0f, _bar_bgcolor_g / 255.0f, _bar_bgcolor_b / 255.0f, 1 };
   CGColorSpaceRef rgb_space = CGColorSpaceCreateDeviceRGB();
   CGColorSpaceRef rgb_space = CGColorSpaceCreateDeviceRGB();
   CGColorRef fg = CGColorCreate(rgb_space, fg_components);
   CGColorRef fg = CGColorCreate(rgb_space, fg_components);
   CGColorRef bg = CGColorCreate(rgb_space, bg_components);
   CGColorRef bg = CGColorCreate(rgb_space, bg_components);
   CGColorRef bar = CGColorCreate(rgb_space, bar_components);
   CGColorRef bar = CGColorCreate(rgb_space, bar_components);
+  CGColorRef bar_bg = CGColorCreate(rgb_space, bar_bg_components);
 
 
+  // Get the proper placement for the progress bar.
   int bar_x, bar_y, bar_width, bar_height;
   int bar_x, bar_y, bar_width, bar_height;
   get_bar_placement(bar_x, bar_y, bar_width, bar_height);
   get_bar_placement(bar_x, bar_y, bar_width, bar_height);
 
 
-  // We offset the bar by half a pixel, so we'll be drawing the
-  // one-pixel line through the middle of a pixel, and it won't try to
-  // antialias itself into a half-black two-pixel line.
-  float bar_xf = bar_x + 0.5;
-  float bar_yf = bar_y - 0.5;
-
-  CGRect bar_rect = { { bar_xf, bar_yf }, { bar_width, bar_height } };
+  CGRect bar_rect = { { bar_x, bar_y }, { bar_width, bar_height } };
 
 
   // Clear the entire progress bar to white (or the background color).
   // Clear the entire progress bar to white (or the background color).
-  CGContextBeginPath(context);
-  CGContextSetFillColorWithColor(context, bg);
-  CGContextAddRect(context, bar_rect);
-  CGContextFillPath(context);
+  CGContextSetFillColorWithColor(context, bar_bg);
+  CGContextFillRect(context, bar_rect);
 
 
   // Draw the interior of the progress bar in blue (or the bar color).
   // Draw the interior of the progress bar in blue (or the bar color).
   if (_progress_known) {
   if (_progress_known) {
     int progress_width = (int)(bar_width * _install_progress + 0.5);
     int progress_width = (int)(bar_width * _install_progress + 0.5);
     if (progress_width != 0) {
     if (progress_width != 0) {
-      CGRect prog = { { bar_xf, bar_yf }, { progress_width, bar_height } };
-      CGContextBeginPath(context);
+      CGRect prog = { { bar_x, bar_y }, { progress_width, bar_height } };
       CGContextSetFillColorWithColor(context, bar);
       CGContextSetFillColorWithColor(context, bar);
-      CGContextAddRect(context, prog);
-      CGContextFillPath(context);
+      CGContextFillRect(context, prog);
     }
     }
   } else {
   } else {
     // Progress is unknown.  Draw a moving block, not a progress bar
     // Progress is unknown.  Draw a moving block, not a progress bar
@@ -660,57 +687,58 @@ paint_progress_bar(CGContextRef context) {
       progress = block_travel * 2 - progress;
       progress = block_travel * 2 - progress;
     }
     }
 
 
-    CGRect prog = { { bar_xf + progress, bar_yf }, { block_width, bar_height } };
-    CGContextBeginPath(context);
+    CGRect prog = { { bar_x + progress, bar_y }, { block_width, bar_height } };
     CGContextSetFillColorWithColor(context, bar);
     CGContextSetFillColorWithColor(context, bar);
-    CGContextAddRect(context, prog);
-    CGContextFillPath(context);
+    CGContextFillRect(context, prog);
   }
   }
-    
+
   // Draw the black stroke around the progress bar.
   // Draw the black stroke around the progress bar.
-  CGContextBeginPath(context);
-  CGContextSetLineWidth(context, 1);
-  CGContextSetStrokeColorWithColor(context, fg);
-  CGContextAddRect(context, bar_rect);
-  CGContextStrokePath(context);
+  if (_bar_border > 0) {
+    // We offset the border by half a pixel, so we'll be drawing the
+    // one-pixel line through the middle of a pixel, and it won't try to
+    // antialias itself into a half-black two-pixel line.
+    CGRect border_rect = { { bar_x - 0.5, bar_y - 0.5 },
+                           { bar_width + 1, bar_height + 1 } };
 
 
-  if (!_install_label.empty()) {
-    // Now draw the install_label right above it.
+    CGContextBeginPath(context);
+    CGContextSetLineWidth(context, 1);
+    CGContextSetStrokeColorWithColor(context, fg);
+
+    for (int i = 0; i < _bar_border; ++i) {
+      CGContextAddRect(context, border_rect);
+      border_rect.origin.x -= 1;
+      border_rect.origin.y -= 1;
+      border_rect.size.width += 2;
+      border_rect.size.height += 2;
+    }
+    CGContextStrokePath(context);
+  }
 
 
+  if (!_install_label.empty()) {
     // Need to invert the text so it won't be upside-down.
     // Need to invert the text so it won't be upside-down.
     CGAffineTransform text_xform = CGAffineTransformMakeScale(1, -1);
     CGAffineTransform text_xform = CGAffineTransformMakeScale(1, -1);
     CGContextSetTextMatrix(context, text_xform);
     CGContextSetTextMatrix(context, text_xform);
 
 
-    // Choose a suitable font.
-    float text_height = 15.0;
-    CGContextSelectFont(context, "Helvetica", text_height, kCGEncodingMacRoman);
-
-    // Measure the text, for centering.
-    CGContextSetTextPosition(context, 0, 0);
-    CGContextSetTextDrawingMode(context, kCGTextInvisible);
-    CGContextShowText(context, _install_label.data(), _install_label.length());
-    CGPoint end_point = CGContextGetTextPosition(context);
-    float text_width = end_point.x;
-
-    int text_x = (int)(_win_width - text_width) / 2;
-    int text_y = (int)(bar_y - text_height * 1.5);
-
-    // Clear the rectangle behind the text to bg.
-    CGRect text_rect = { { text_x - 2, text_y - 2 }, { text_width + 4, text_height + 4 } };
+    // Now draw the install_label right above it.
+    CFStringRef string = CFStringCreateWithCString(NULL, _install_label.c_str(), kCFStringEncodingUTF8);
+    CFAttributedStringRef attr_string = CFAttributedStringCreate(NULL, string, _font_attribs);
+    CTLineRef line = CTLineCreateWithAttributedString(attr_string);
 
 
-    CGContextBeginPath(context);
-    CGContextAddRect(context, text_rect);
-    CGContextSetFillColorWithColor(context, bg);
-    CGContextFillPath(context);
+    // Determine the placement based on the size of the text.
+    CGRect bounds = CTLineGetImageBounds(line, context);
+    float text_x = (_win_width - bounds.size.width) / 2.0f - bounds.origin.x;
+    float text_y = bar_y + bounds.origin.y - 4 - _bar_border;
 
 
     // And finally, draw the text.
     // And finally, draw the text.
-    CGContextSetTextDrawingMode(context, kCGTextFill);
-    CGContextSetFillColorWithColor(context, fg);
+    CGContextSetTextPosition(context, text_x, text_y);
+    CTLineDraw(line, context);
 
 
-    CGContextShowTextAtPoint(context, text_x, text_y + text_height, 
-                             _install_label.data(), _install_label.length());
+    CFRelease(line);
+    CFRelease(attr_string);
+    CFRelease(string);
   }
   }
 
 
+  CGColorRelease(bar_bg);
   CGColorRelease(bar);
   CGColorRelease(bar);
   CGColorRelease(bg);
   CGColorRelease(bg);
   CGColorRelease(fg);
   CGColorRelease(fg);

+ 2 - 0
direct/src/plugin/p3dOsxSplashWindow.h

@@ -83,6 +83,8 @@ private:
   OsxImageData _button_rollover_image;
   OsxImageData _button_rollover_image;
   OsxImageData _button_click_image;
   OsxImageData _button_click_image;
 
 
+  CFDictionaryRef _font_attribs;
+
   string _install_label;
   string _install_label;
   double _install_progress;
   double _install_progress;
   bool _progress_known;
   bool _progress_known;

+ 4 - 4
direct/src/plugin/p3dPythonMain.cxx

@@ -150,10 +150,10 @@ main(int argc, char *argv[]) {
     }
     }
   }
   }
 
 
-  if (!run_p3dpython(program_name, archive_file, input_handle, output_handle, 
-                     NULL, interactive_console)) {
+  int status = run_p3dpython(program_name, archive_file, input_handle,
+                             output_handle, NULL, interactive_console);
+  if (status != 0) {
     cerr << "Failure on startup.\n";
     cerr << "Failure on startup.\n";
-    return 1;
   }
   }
-  return 0;
+  return status;
 }
 }

+ 174 - 52
direct/src/plugin/p3dPythonRun.cxx

@@ -21,6 +21,11 @@
 
 
 #include "py_panda.h"
 #include "py_panda.h"
 
 
+extern "C" {
+  // This has been compiled-in by the build system, if all is well.
+  extern struct _frozen _PyImport_FrozenModules[];
+};
+
 // There is only one P3DPythonRun object in any given process space.
 // There is only one P3DPythonRun object in any given process space.
 // Makes the statics easier to deal with, and we don't need multiple
 // Makes the statics easier to deal with, and we don't need multiple
 // instances of this thing.
 // instances of this thing.
@@ -54,16 +59,26 @@ P3DPythonRun(const char *program_name, const char *archive_file,
   _interactive_console = interactive_console;
   _interactive_console = interactive_console;
 
 
   if (program_name != NULL) {
   if (program_name != NULL) {
+#if PY_MAJOR_VERSION >= 3
+    // Python 3 case: we have to convert it to a wstring.
+    TextEncoder enc;
+    enc.set_text(program_name);
+    _program_name = enc.get_wtext();
+#else
+    // Python 2 is happy with a regular string.
     _program_name = program_name;
     _program_name = program_name;
+#endif
   }
   }
   if (archive_file != NULL) {
   if (archive_file != NULL) {
     _archive_file = Filename::from_os_specific(archive_file);
     _archive_file = Filename::from_os_specific(archive_file);
   }
   }
 
 
   _py_argc = 1;
   _py_argc = 1;
-  _py_argv = (char **)malloc(2 * sizeof(char *));
-
+#if PY_MAJOR_VERSION >= 3
+  _py_argv[0] = (wchar_t *)_program_name.c_str();
+#else
   _py_argv[0] = (char *)_program_name.c_str();
   _py_argv[0] = (char *)_program_name.c_str();
+#endif
   _py_argv[1] = NULL;
   _py_argv[1] = NULL;
 
 
 #ifdef NDEBUG
 #ifdef NDEBUG
@@ -75,13 +90,30 @@ P3DPythonRun(const char *program_name, const char *archive_file,
   // Turn off the automatic load of site.py at startup.
   // Turn off the automatic load of site.py at startup.
   extern int Py_NoSiteFlag;
   extern int Py_NoSiteFlag;
   Py_NoSiteFlag = 1;
   Py_NoSiteFlag = 1;
+  Py_NoUserSiteDirectory = 1;
+
+  // Tell Python not to write bytecode files for loaded modules.
+  Py_DontWriteBytecodeFlag = 1;
+
+  // Prevent Python from complaining about finding the standard modules.
+  Py_FrozenFlag = 1;
+
+  // This contains the modules we need in order to call Py_Initialize,
+  // as well as the VFSImporter.
+  PyImport_FrozenModules = _PyImport_FrozenModules;
 
 
   // Initialize Python.  It appears to be important to do this before
   // Initialize Python.  It appears to be important to do this before
   // we open the pipe streams and spawn the thread, below.
   // we open the pipe streams and spawn the thread, below.
+#if PY_MAJOR_VERSION >= 3
+  Py_SetProgramName((wchar_t *)_program_name.c_str());
+  Py_SetPythonHome((wchar_t *)L"");
+#else
   Py_SetProgramName((char *)_program_name.c_str());
   Py_SetProgramName((char *)_program_name.c_str());
+  Py_SetPythonHome((char *)"");
+#endif
   Py_Initialize();
   Py_Initialize();
   PyEval_InitThreads();
   PyEval_InitThreads();
-  PySys_SetArgv(_py_argc, _py_argv);
+  PySys_SetArgvEx(_py_argc, _py_argv, 0);
 
 
   // Open the error output before we do too much more.
   // Open the error output before we do too much more.
   if (log_pathname != NULL && *log_pathname != '\0') {
   if (log_pathname != NULL && *log_pathname != '\0') {
@@ -137,8 +169,10 @@ P3DPythonRun::
 //       Access: Public
 //       Access: Public
 //  Description: Runs the embedded Python process.  This method does
 //  Description: Runs the embedded Python process.  This method does
 //               not return until the plugin is ready to exit.
 //               not return until the plugin is ready to exit.
+//
+//               Returns the exit status, which will be 0 on success.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool P3DPythonRun::
+int P3DPythonRun::
 run_python() {
 run_python() {
 #if defined(_WIN32) && defined(USE_DEBUG_PYTHON)
 #if defined(_WIN32) && defined(USE_DEBUG_PYTHON)
   // On Windows, in a debug build, we have to preload sys.dll_suffix =
   // On Windows, in a debug build, we have to preload sys.dll_suffix =
@@ -154,36 +188,42 @@ run_python() {
   // setting __path__ of frozen modules properly.
   // setting __path__ of frozen modules properly.
   PyObject *panda3d_module = PyImport_AddModule("panda3d");
   PyObject *panda3d_module = PyImport_AddModule("panda3d");
   if (panda3d_module == NULL) {
   if (panda3d_module == NULL) {
-    nout << "Failed to create panda3d module:\n";
+    nout << "Failed to add panda3d module:\n";
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
 
 
-  // Set the __path__ such that it can find panda3d/core.pyd, etc.
+  // Set the __path__ such that it can find panda3d/_core.pyd, etc.
   Filename panda3d_dir(dir, "panda3d");
   Filename panda3d_dir(dir, "panda3d");
   string dir_str = panda3d_dir.to_os_specific();
   string dir_str = panda3d_dir.to_os_specific();
-  PyObject *panda3d_dict = PyModule_GetDict(panda3d_module);
-  PyObject *panda3d_path = Py_BuildValue("[s#]", dir_str.data(), dir_str.length());
-  PyDict_SetItemString(panda3d_dict, "__path__", panda3d_path);
-  Py_DECREF(panda3d_path);
-
-  // Now we can load _vfsimporter.pyd.  Since this is a magic frozen
-  // pyd, importing it automatically makes all of its frozen contents
-  // available to import as well.
-  PyObject *vfsimporter = PyImport_ImportModule("_vfsimporter");
-  if (vfsimporter == NULL) {
-    nout << "Failed to import _vfsimporter:\n";
-    PyErr_Print();
-    return false;
-  }
-  Py_DECREF(vfsimporter);
+  PyModule_AddObject(panda3d_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length()));
+  PyModule_AddStringConstant(panda3d_module, "__package__", "panda3d");
 
 
-  // And now we can import the VFSImporter module that was so defined.
-  PyObject *vfsimporter_module = PyImport_ImportModule("VFSImporter");
+  // Import the VFSImporter module that was frozen in.
+  PyObject *vfsimporter_module = PyImport_ImportModule("direct.showbase.VFSImporter");
   if (vfsimporter_module == NULL) {
   if (vfsimporter_module == NULL) {
     nout << "Failed to import VFSImporter:\n";
     nout << "Failed to import VFSImporter:\n";
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
+  }
+
+  // Now repair the "direct" and "direct.showbase" trees, which were
+  // presumably frozen along with the VFSImporter, by setting their
+  // __path__ such that we can still find the other direct modules.
+  Filename direct_dir(dir, "direct");
+  PyObject *direct_module = PyImport_AddModule("direct");
+  if (direct_module != NULL) {
+    dir_str = direct_dir.to_os_specific();
+    PyModule_AddObject(direct_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length()));
+    PyModule_AddStringConstant(direct_module, "__package__", "direct");
+  }
+
+  PyObject *showbase_module = PyImport_AddModule("direct.showbase");
+  if (showbase_module != NULL) {
+    Filename showbase_dir(direct_dir, "showbase");
+    dir_str = showbase_dir.to_os_specific();
+    PyModule_AddObject(showbase_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length()));
+    PyModule_AddStringConstant(showbase_module, "__package__", "direct.showbase");
   }
   }
 
 
   // And register the VFSImporter.
   // And register the VFSImporter.
@@ -191,7 +231,7 @@ run_python() {
   if (result == NULL) {
   if (result == NULL) {
     nout << "Failed to call VFSImporter.register():\n";
     nout << "Failed to call VFSImporter.register():\n";
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
   Py_DECREF(result);
   Py_DECREF(result);
   Py_DECREF(vfsimporter_module);
   Py_DECREF(vfsimporter_module);
@@ -203,12 +243,12 @@ run_python() {
   PT(Multifile) mf = new Multifile;
   PT(Multifile) mf = new Multifile;
   if (!mf->open_read(_archive_file)) {
   if (!mf->open_read(_archive_file)) {
     nout << "Could not read " << _archive_file << "\n";
     nout << "Could not read " << _archive_file << "\n";
-    return false;
+    return 1;
   }
   }
   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
   if (!vfs->mount(mf, dir, VirtualFileSystem::MF_read_only)) {
   if (!vfs->mount(mf, dir, VirtualFileSystem::MF_read_only)) {
     nout << "Could not mount " << _archive_file << "\n";
     nout << "Could not mount " << _archive_file << "\n";
-    return false;
+    return 1;
   }
   }
 
 
   // And finally, we can import the startup module.
   // And finally, we can import the startup module.
@@ -216,7 +256,7 @@ run_python() {
   if (app_runner_module == NULL) {
   if (app_runner_module == NULL) {
     nout << "Failed to import direct.p3d.AppRunner\n";
     nout << "Failed to import direct.p3d.AppRunner\n";
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
 
 
   // Get the pointers to the objects needed within the module.
   // Get the pointers to the objects needed within the module.
@@ -224,7 +264,7 @@ run_python() {
   if (app_runner_class == NULL) {
   if (app_runner_class == NULL) {
     nout << "Failed to get AppRunner class\n";
     nout << "Failed to get AppRunner class\n";
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
 
 
   // Construct an instance of AppRunner.
   // Construct an instance of AppRunner.
@@ -232,47 +272,55 @@ run_python() {
   if (_runner == NULL) {
   if (_runner == NULL) {
     nout << "Failed to construct AppRunner instance\n";
     nout << "Failed to construct AppRunner instance\n";
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
   Py_DECREF(app_runner_class);
   Py_DECREF(app_runner_class);
 
 
+  // Import the JavaScript module.
+  PyObject *javascript_module = PyImport_ImportModule("direct.p3d.JavaScript");
+  if (javascript_module == NULL) {
+    nout << "Failed to import direct.p3d.JavaScript\n";
+    PyErr_Print();
+    return false;
+  }
+
   // Get the UndefinedObject class.
   // Get the UndefinedObject class.
-  _undefined_object_class = PyObject_GetAttrString(app_runner_module, "UndefinedObject");
+  _undefined_object_class = PyObject_GetAttrString(javascript_module, "UndefinedObject");
   if (_undefined_object_class == NULL) {
   if (_undefined_object_class == NULL) {
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
 
 
   // And the "Undefined" instance.
   // And the "Undefined" instance.
-  _undefined = PyObject_GetAttrString(app_runner_module, "Undefined");
+  _undefined = PyObject_GetAttrString(javascript_module, "Undefined");
   if (_undefined == NULL) {
   if (_undefined == NULL) {
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
 
 
   // Get the ConcreteStruct class.
   // Get the ConcreteStruct class.
-  _concrete_struct_class = PyObject_GetAttrString(app_runner_module, "ConcreteStruct");
+  _concrete_struct_class = PyObject_GetAttrString(javascript_module, "ConcreteStruct");
   if (_concrete_struct_class == NULL) {
   if (_concrete_struct_class == NULL) {
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
 
 
   // Get the BrowserObject class.
   // Get the BrowserObject class.
-  _browser_object_class = PyObject_GetAttrString(app_runner_module, "BrowserObject");
+  _browser_object_class = PyObject_GetAttrString(javascript_module, "BrowserObject");
   if (_browser_object_class == NULL) {
   if (_browser_object_class == NULL) {
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
 
 
   // Get the global TaskManager.
   // Get the global TaskManager.
   _taskMgr = PyObject_GetAttrString(app_runner_module, "taskMgr");
   _taskMgr = PyObject_GetAttrString(app_runner_module, "taskMgr");
   if (_taskMgr == NULL) {
   if (_taskMgr == NULL) {
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
 
 
   Py_DECREF(app_runner_module);
   Py_DECREF(app_runner_module);
-
+  Py_DECREF(javascript_module);
 
 
   // Construct a Python wrapper around our methods we need to expose to Python.
   // Construct a Python wrapper around our methods we need to expose to Python.
   static PyMethodDef p3dpython_methods[] = {
   static PyMethodDef p3dpython_methods[] = {
@@ -282,15 +330,28 @@ run_python() {
       "Send an asynchronous request to the plugin host" },
       "Send an asynchronous request to the plugin host" },
     { NULL, NULL, 0, NULL }        /* Sentinel */
     { NULL, NULL, 0, NULL }        /* Sentinel */
   };
   };
+
+#if PY_MAJOR_VERSION >= 3
+  static PyModuleDef p3dpython_module = {
+    PyModuleDef_HEAD_INIT,
+    "p3dpython",
+    NULL,
+    -1,
+    p3dpython_methods,
+    NULL, NULL, NULL, NULL
+  };
+  PyObject *p3dpython = PyModule_Create(&p3dpython_module);
+#else
   PyObject *p3dpython = Py_InitModule("p3dpython", p3dpython_methods);
   PyObject *p3dpython = Py_InitModule("p3dpython", p3dpython_methods);
+#endif
   if (p3dpython == NULL) {
   if (p3dpython == NULL) {
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
   PyObject *request_func = PyObject_GetAttrString(p3dpython, "request_func");
   PyObject *request_func = PyObject_GetAttrString(p3dpython, "request_func");
   if (request_func == NULL) {
   if (request_func == NULL) {
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
 
 
   // Now pass that func pointer back to our AppRunner instance, so it
   // Now pass that func pointer back to our AppRunner instance, so it
@@ -298,7 +359,7 @@ run_python() {
   result = PyObject_CallMethod(_runner, (char *)"setRequestFunc", (char *)"N", request_func);
   result = PyObject_CallMethod(_runner, (char *)"setRequestFunc", (char *)"N", request_func);
   if (result == NULL) {
   if (result == NULL) {
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
   Py_DECREF(result);
   Py_DECREF(result);
 
 
@@ -318,7 +379,7 @@ run_python() {
   PyObject *check_comm = PyObject_GetAttrString(p3dpython, "check_comm");
   PyObject *check_comm = PyObject_GetAttrString(p3dpython, "check_comm");
   if (check_comm == NULL) {
   if (check_comm == NULL) {
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
 
 
   // Add it to the task manager.  We do this instead of constructing a
   // Add it to the task manager.  We do this instead of constructing a
@@ -326,7 +387,7 @@ run_python() {
   result = PyObject_CallMethod(_taskMgr, (char *)"add", (char *)"Ns", check_comm, "check_comm");
   result = PyObject_CallMethod(_taskMgr, (char *)"add", (char *)"Ns", check_comm, "check_comm");
   if (result == NULL) {
   if (result == NULL) {
     PyErr_Print();
     PyErr_Print();
-    return false;
+    return 1;
   }
   }
   Py_DECREF(result);
   Py_DECREF(result);
 
 
@@ -334,18 +395,48 @@ run_python() {
   // taskMgr.run()).
   // taskMgr.run()).
   PyObject *done = PyObject_CallMethod(_runner, (char *)"run", (char *)"");
   PyObject *done = PyObject_CallMethod(_runner, (char *)"run", (char *)"");
   if (done == NULL) {
   if (done == NULL) {
+    int status = 1;
+
     // An uncaught application exception, and not handled by
     // An uncaught application exception, and not handled by
-    // appRunner.exceptionHandler.
-    PyErr_Print();
+    // appRunner.exceptionHandler.  If it is a SystemExit, extract
+    // the exit status that we should return.
+    if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
+      PyObject *ptype, *ptraceback;
+      PyObject *value = NULL;
+      PyErr_Fetch(&ptype, &value, &ptraceback);
+
+      if (value != NULL && PyExceptionInstance_Check(value)) {
+        PyObject *code = PyObject_GetAttrString(value, "code");
+        if (code) {
+          Py_DECREF(value);
+          value = code;
+        }
+      }
+
+      if (value == NULL || value == Py_None) {
+        status = 0;
+#if PY_MAJOR_VERSION >= 3
+      } else if (PyLong_Check(value)) {
+        status = (int)PyLong_AsLong(value);
+#else
+      } else if (PyInt_Check(value)) {
+        status = (int)PyInt_AsLong(value);
+#endif
+      } else {
+        status = 1;
+      }
+    } else {
+      PyErr_Print();
+    }
 
 
     if (_interactive_console) {
     if (_interactive_console) {
       // Give an interactive user a chance to explore the exception.
       // Give an interactive user a chance to explore the exception.
       run_interactive_console();
       run_interactive_console();
-      return true;
+      return 0;
     }
     }
 
 
     // We're done.
     // We're done.
-    return false;
+    return status;
   }
   }
 
 
   // A normal exit from the taskManager.  We're presumably done.
   // A normal exit from the taskManager.  We're presumably done.
@@ -355,7 +446,7 @@ run_python() {
     run_interactive_console();
     run_interactive_console();
   }
   }
 
 
-  return true;
+  return 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -636,8 +727,11 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
           result = PyBool_FromLong(PyObject_IsTrue(obj));
           result = PyBool_FromLong(PyObject_IsTrue(obj));
 
 
         } else if (strcmp(method_name, "__int__") == 0) {
         } else if (strcmp(method_name, "__int__") == 0) {
+#if PY_MAJOR_VERSION >= 3
+          result = PyNumber_Long(obj);
+#else
           result = PyNumber_Int(obj);
           result = PyNumber_Int(obj);
-
+#endif
         } else if (strcmp(method_name, "__float__") == 0) {
         } else if (strcmp(method_name, "__float__") == 0) {
           result = PyNumber_Float(obj);
           result = PyNumber_Float(obj);
 
 
@@ -1502,10 +1596,12 @@ pyobj_to_xml(PyObject *value) {
     xvalue->SetAttribute("type", "bool");
     xvalue->SetAttribute("type", "bool");
     xvalue->SetAttribute("value", PyObject_IsTrue(value));
     xvalue->SetAttribute("value", PyObject_IsTrue(value));
 
 
+#if PY_MAJOR_VERSION < 3
   } else if (PyInt_Check(value)) {
   } else if (PyInt_Check(value)) {
     // A plain integer value.
     // A plain integer value.
     xvalue->SetAttribute("type", "int");
     xvalue->SetAttribute("type", "int");
     xvalue->SetAttribute("value", PyInt_AsLong(value));
     xvalue->SetAttribute("value", PyInt_AsLong(value));
+#endif
 
 
   } else if (PyLong_Check(value)) {
   } else if (PyLong_Check(value)) {
     // A long integer value.  This gets converted either as an integer
     // A long integer value.  This gets converted either as an integer
@@ -1527,9 +1623,21 @@ pyobj_to_xml(PyObject *value) {
     xvalue->SetAttribute("type", "float");
     xvalue->SetAttribute("type", "float");
     xvalue->SetDoubleAttribute("value", PyFloat_AsDouble(value));
     xvalue->SetDoubleAttribute("value", PyFloat_AsDouble(value));
 
 
+
   } else if (PyUnicode_Check(value)) {
   } else if (PyUnicode_Check(value)) {
     // A unicode value.  Convert to utf-8 for the XML encoding.
     // A unicode value.  Convert to utf-8 for the XML encoding.
     xvalue->SetAttribute("type", "string");
     xvalue->SetAttribute("type", "string");
+
+#if PY_MAJOR_VERSION >= 3
+    // In Python 3, there are only unicode strings, and there is a
+    // handy function for getting the UTF-8 encoded version.
+    Py_ssize_t length = 0;
+    char *buffer = PyUnicode_AsUTF8AndSize(value, &length);
+    if (buffer != NULL) {
+      string str(buffer, length);
+      xvalue->SetAttribute("value", str);
+    }
+#else
     PyObject *as_str = PyUnicode_AsUTF8String(value);
     PyObject *as_str = PyUnicode_AsUTF8String(value);
     if (as_str != NULL) {
     if (as_str != NULL) {
       char *buffer;
       char *buffer;
@@ -1563,6 +1671,7 @@ pyobj_to_xml(PyObject *value) {
       }
       }
       Py_DECREF(ustr);
       Py_DECREF(ustr);
     }
     }
+#endif  // PY_MAJOR_VERSION
 
 
   } else if (PyTuple_Check(value)) {
   } else if (PyTuple_Check(value)) {
     // An immutable sequence.  Pass it as a concrete.
     // An immutable sequence.  Pass it as a concrete.
@@ -1601,7 +1710,11 @@ pyobj_to_xml(PyObject *value) {
             PyObject *as_str;
             PyObject *as_str;
             if (PyUnicode_Check(a)) {
             if (PyUnicode_Check(a)) {
               // The key is a unicode value.
               // The key is a unicode value.
+#if PY_MAJOR_VERSION >= 3
+              as_str = a;
+#else
               as_str = PyUnicode_AsUTF8String(a);
               as_str = PyUnicode_AsUTF8String(a);
+#endif
             } else {
             } else {
               // The key is a string value or something else.  Make it
               // The key is a string value or something else.  Make it
               // a string.
               // a string.
@@ -1609,7 +1722,12 @@ pyobj_to_xml(PyObject *value) {
             }
             }
             char *buffer;
             char *buffer;
             Py_ssize_t length;
             Py_ssize_t length;
+#if PY_MAJOR_VERSION >= 3
+            buffer = PyUnicode_AsUTF8AndSize(as_str, &length);
+            if (buffer != NULL) {
+#else
             if (PyString_AsStringAndSize(as_str, &buffer, &length) != -1) {
             if (PyString_AsStringAndSize(as_str, &buffer, &length) != -1) {
+#endif
               string str(buffer, length);
               string str(buffer, length);
               xitem->SetAttribute("key", str);
               xitem->SetAttribute("key", str);
             }
             }
@@ -1694,7 +1812,11 @@ xml_to_pyobj(TiXmlElement *xvalue) {
   } else if (strcmp(type, "int") == 0) {
   } else if (strcmp(type, "int") == 0) {
     int value;
     int value;
     if (xvalue->QueryIntAttribute("value", &value) == TIXML_SUCCESS) {
     if (xvalue->QueryIntAttribute("value", &value) == TIXML_SUCCESS) {
+#if PY_MAJOR_VERSION >= 3
+      return PyLong_FromLong(value);
+#else
       return PyInt_FromLong(value);
       return PyInt_FromLong(value);
+#endif
     }
     }
 
 
   } else if (strcmp(type, "float") == 0) {
   } else if (strcmp(type, "float") == 0) {

+ 8 - 3
direct/src/plugin/p3dPythonRun.h

@@ -71,7 +71,7 @@ public:
                const char *log_pathname, bool interactive_console);
                const char *log_pathname, bool interactive_console);
   ~P3DPythonRun();
   ~P3DPythonRun();
 
 
-  bool run_python();
+  int run_python();
 
 
   void set_window_open(P3DCInstance *inst, bool is_open);
   void set_window_open(P3DCInstance *inst, bool is_open);
   void request_keyboard_focus(P3DCInstance *inst);
   void request_keyboard_focus(P3DCInstance *inst);
@@ -159,10 +159,15 @@ private:
 
 
   int _session_id;
   int _session_id;
 
 
-  string _program_name;
   Filename _archive_file;
   Filename _archive_file;
   int _py_argc;
   int _py_argc;
-  char **_py_argv;
+#if PY_MAJOR_VERSION >= 3
+  wchar_t *_py_argv[2];
+  wstring _program_name;
+#else
+  char *_py_argv[2];
+  string _program_name;
+#endif
   bool _interactive_console;
   bool _interactive_console;
 
 
   PyObject *_runner;
   PyObject *_runner;

+ 51 - 15
direct/src/plugin/p3dSession.cxx

@@ -100,6 +100,8 @@ void P3DSession::
 shutdown() {
 shutdown() {
   set_failed();
   set_failed();
 
 
+  int exit_code = 0;
+
   if (_p3dpython_started) {
   if (_p3dpython_started) {
     // Tell the process we're going away.
     // Tell the process we're going away.
     TiXmlDocument doc;
     TiXmlDocument doc;
@@ -139,7 +141,14 @@ shutdown() {
         nout << "Force-killing python process.\n";
         nout << "Force-killing python process.\n";
         TerminateProcess(_p3dpython_handle, 2);
         TerminateProcess(_p3dpython_handle, 2);
       }
       }
-      
+
+      DWORD dw_exit_code = 0;
+      if (!GetExitCodeProcess(_p3dpython_handle, &dw_exit_code)) {
+        nout << "GetExitCodeProcess failed, error: " << GetLastError() << "\n";
+      } else {
+        exit_code = (int)dw_exit_code;
+      }
+
       CloseHandle(_p3dpython_handle);
       CloseHandle(_p3dpython_handle);
       _p3dpython_handle = INVALID_HANDLE_VALUE;
       _p3dpython_handle = INVALID_HANDLE_VALUE;
 
 
@@ -179,18 +188,23 @@ shutdown() {
         result = waitpid(_p3dpython_pid, &status, WNOHANG);
         result = waitpid(_p3dpython_pid, &status, WNOHANG);
       }
       }
       _p3dpython_pid = -1;
       _p3dpython_pid = -1;
-      
+
       nout << "Python process has successfully stopped.\n";
       nout << "Python process has successfully stopped.\n";
       if (WIFEXITED(status)) {
       if (WIFEXITED(status)) {
-        nout << "  exited normally, status = "
-             << WEXITSTATUS(status) << "\n";
+        exit_code = WEXITSTATUS(status);
+        nout << "  exited normally, status = " << exit_code << "\n";
+
       } else if (WIFSIGNALED(status)) {
       } else if (WIFSIGNALED(status)) {
-        nout << "  signalled by " << WTERMSIG(status) << ", core = " 
+        nout << "  signalled by " << WTERMSIG(status) << ", core = "
              << WCOREDUMP(status) << "\n";
              << WCOREDUMP(status) << "\n";
+
+        // This seems to be a popular shell convention.
+        exit_code = 128 + WTERMSIG(status);
+
       } else if (WIFSTOPPED(status)) {
       } else if (WIFSTOPPED(status)) {
         nout << "  stopped by " << WSTOPSIG(status) << "\n";
         nout << "  stopped by " << WSTOPSIG(status) << "\n";
       }
       }
-      
+
 #endif  // _WIN32
 #endif  // _WIN32
     }
     }
 
 
@@ -211,6 +225,11 @@ shutdown() {
 
 
   // Close the pipe now.
   // Close the pipe now.
   _pipe_read.close();
   _pipe_read.close();
+
+  // If we had an exit status, pass it on to the runtime process.
+  if (exit_code != 0) {
+    _exit(exit_code);
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -717,7 +736,7 @@ start_p3dpython(P3DInstance *inst) {
     _keep_user_env = true;
     _keep_user_env = true;
   }
   }
   if (!_keep_user_env) {
   if (!_keep_user_env) {
-    _start_dir = inst_mgr->get_root_dir() + "/start" + inst->get_start_dir_suffix();
+    _start_dir = inst_mgr->get_start_dir() + inst->get_start_dir_suffix();
     mkdir_complete(_start_dir, nout);
     mkdir_complete(_start_dir, nout);
   }
   }
   replace_slashes(_start_dir);
   replace_slashes(_start_dir);
@@ -872,11 +891,13 @@ start_p3dpython(P3DInstance *inst) {
     // These are the enviroment variables we forward from the current
     // These are the enviroment variables we forward from the current
     // environment, if they are set.
     // environment, if they are set.
     const char *keep[] = {
     const char *keep[] = {
-      "HOME", "USER", 
+      "HOME", "USER",
 #ifdef _WIN32
 #ifdef _WIN32
       "SYSTEMROOT", "USERPROFILE", "COMSPEC",
       "SYSTEMROOT", "USERPROFILE", "COMSPEC",
       "SYSTEMDRIVE", "ALLUSERSPROFILE", "APPDATA", "COMMONPROGRAMFILES",
       "SYSTEMDRIVE", "ALLUSERSPROFILE", "APPDATA", "COMMONPROGRAMFILES",
       "PROGRAMFILES", "WINDIR", "PROGRAMDATA", "USERDOMAIN",
       "PROGRAMFILES", "WINDIR", "PROGRAMDATA", "USERDOMAIN",
+#else
+      "LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG",
 #endif
 #endif
 #ifdef HAVE_X11
 #ifdef HAVE_X11
       "DISPLAY", "XAUTHORITY",
       "DISPLAY", "XAUTHORITY",
@@ -928,7 +949,11 @@ start_p3dpython(P3DInstance *inst) {
         const char *varc = var.c_str();
         const char *varc = var.c_str();
         bool found = false;
         bool found = false;
         for (int i = 0; dont_keep[i] != NULL && !found; ++i) {
         for (int i = 0; dont_keep[i] != NULL && !found; ++i) {
+#ifdef _WIN32
+          found = (_stricmp(dont_keep[i], varc) == 0);
+#else
           found = (strcmp(dont_keep[i], varc) == 0);
           found = (strcmp(dont_keep[i], varc) == 0);
+#endif
         }
         }
         if (!found) {
         if (!found) {
           // This variable is OK, keep it.
           // This variable is OK, keep it.
@@ -1704,15 +1729,24 @@ posix_create_process() {
   // its process.  Report an error condition.
   // its process.  Report an error condition.
   nout << "Python process stopped immediately.\n";
   nout << "Python process stopped immediately.\n";
   if (WIFEXITED(status)) {
   if (WIFEXITED(status)) {
-    nout << "  exited normally, status = "
-         << WEXITSTATUS(status) << "\n";
+    int code = WEXITSTATUS(status);
+
+    nout << "  exited normally, status = " << code << "\n";
+    if (code != 0) {
+      _exit(code);
+    }
+
   } else if (WIFSIGNALED(status)) {
   } else if (WIFSIGNALED(status)) {
-    nout << "  signalled by " << WTERMSIG(status) << ", core = " 
+    nout << "  signalled by " << WTERMSIG(status) << ", core = "
          << WCOREDUMP(status) << "\n";
          << WCOREDUMP(status) << "\n";
+
+    // This seems to be a popular shell convention.
+    _exit(128 + WTERMSIG(status));
+
   } else if (WIFSTOPPED(status)) {
   } else if (WIFSTOPPED(status)) {
     nout << "  stopped by " << WSTOPSIG(status) << "\n";
     nout << "  stopped by " << WSTOPSIG(status) << "\n";
   }
   }
-  
+
   return -1;
   return -1;
 }
 }
 #endif  // _WIN32
 #endif  // _WIN32
@@ -1794,10 +1828,12 @@ p3dpython_thread_run() {
     return;
     return;
   }
   }
 
 
-  if (!run_p3dpython(libp3dpython.c_str(), _mf_filename.c_str(),
-                     _input_handle, _output_handle, _log_pathname.c_str(),
-                     _interactive_console)) {
+  int status = run_p3dpython(libp3dpython.c_str(), _mf_filename.c_str(),
+                             _input_handle, _output_handle, _log_pathname.c_str(),
+                             _interactive_console);
+  if (status != 0) {
     nout << "Failure on startup.\n";
     nout << "Failure on startup.\n";
+    _exit(status);
   }
   }
 }
 }
 
 

+ 186 - 6
direct/src/plugin/p3dSplashWindow.cxx

@@ -48,6 +48,19 @@ P3DSplashWindow(P3DInstance *inst, bool make_visible) :
   _barcolor_r = 0x6c;
   _barcolor_r = 0x6c;
   _barcolor_g = 0xa5;
   _barcolor_g = 0xa5;
   _barcolor_b = 0xe0;
   _barcolor_b = 0xe0;
+  _bar_bgcolor_r = _bgcolor_r;
+  _bar_bgcolor_g = _bgcolor_g;
+  _bar_bgcolor_b = _bgcolor_b;
+  _bar_border = 1;
+  _bar_bottom = 24;
+  _bar_width = 398;
+  _bar_height = 22;
+  _bar_width_ratio = 0.6;
+  _bar_height_ratio = 0.1;
+  _font_family = "Helvetica";
+  _font_size = 12;
+  _font_style = FS_normal;
+  _font_weight = FW_normal;
   _button_width = 0;
   _button_width = 0;
   _button_height = 0;
   _button_height = 0;
   _button_x = 0;
   _button_x = 0;
@@ -113,7 +126,7 @@ set_image_filename(const string &image_filename, ImagePlacement image_placement)
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSplashWindow::set_fgcolor
 //     Function: P3DSplashWindow::set_fgcolor
-//       Access: Public, Virtual
+//       Access: Public
 //  Description: Specifies the color that is used to display the text
 //  Description: Specifies the color that is used to display the text
 //               above the loading bar.
 //               above the loading bar.
 //
 //
@@ -129,7 +142,7 @@ set_fgcolor(int r, int g, int b) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSplashWindow::set_bgcolor
 //     Function: P3DSplashWindow::set_bgcolor
-//       Access: Public, Virtual
+//       Access: Public
 //  Description: Specifies the solid color that is displayed behind
 //  Description: Specifies the solid color that is displayed behind
 //               the splash image, if any, or before the splash image
 //               the splash image, if any, or before the splash image
 //               is loaded.
 //               is loaded.
@@ -146,7 +159,7 @@ set_bgcolor(int r, int g, int b) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSplashWindow::set_barcolor
 //     Function: P3DSplashWindow::set_barcolor
-//       Access: Public, Virtual
+//       Access: Public
 //  Description: Specifies the color that is used to fill the
 //  Description: Specifies the color that is used to fill the
 //               loading bar.
 //               loading bar.
 //
 //
@@ -160,6 +173,162 @@ set_barcolor(int r, int g, int b) {
   _barcolor_b = b;
   _barcolor_b = b;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_bar_bgcolor
+//       Access: Public
+//  Description: Specifies the solid color that is displayed behind
+//               the loading bar.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_bar_bgcolor(int r, int g, int b) {
+  nout << "bar_bgcolor " << r << ", " << g << ", " << b << "\n";
+  _bar_bgcolor_r = r;
+  _bar_bgcolor_g = g;
+  _bar_bgcolor_b = b;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_bar_border
+//       Access: Public
+//  Description: Sets the width in pixels of the border around the
+//               loading bar, or 0 not to draw a bar at all.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_bar_border(int border) {
+  nout << "bar_border " << border << "\n";
+  _bar_border = border;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_bar_bottom
+//       Access: Public
+//  Description: Sets the amount of background pixels between the
+//               bottom edge of the window and the bottom edge of
+//               the loading bar border.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_bar_bottom(int bottom) {
+  nout << "bar_bottom " << bottom << "\n";
+  _bar_bottom = bottom;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_bar_width
+//       Access: Public
+//  Description: Sets the width of the loading bar.  If percent is
+//               true, it is interpreted as a percentage of the
+//               window width.  If false, it is interpreted as an
+//               absolute width in pixels.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_bar_width(int width, bool percent) {
+  nout << "bar_width " << width;
+  if (percent) {
+    nout << '%';
+  }
+  nout << '\n';
+
+  if (percent) {
+    _bar_width = -1;
+    _bar_width_ratio = abs(width) / 100.0;
+  } else {
+    _bar_width = abs(width);
+    _bar_width_ratio = 1.0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_bar_height
+//       Access: Public
+//  Description: Sets the height of the loading bar.  If percent is
+//               true, it is interpreted as a percentage of the
+//               window height.  If false, it is interpreted as an
+//               absolute height in pixels.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_bar_height(int height, bool percent) {
+  nout << "bar_height " << height;
+  if (percent) {
+    nout << '%';
+  }
+  nout << '\n';
+
+  if (percent) {
+    _bar_height = -1;
+    _bar_height_ratio = abs(height) / 100.0;
+  } else {
+    _bar_height = abs(height);
+    _bar_height_ratio = 1.0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_font_family
+//       Access: Public
+//  Description: Sets the font family of the text above the loading
+//               bar.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_font_family(const string &family) {
+  nout << "font_family " << family << "\n";
+  _font_family = family;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_font_size
+//       Access: Public
+//  Description: Sets the font size in pixels of the text above the
+//               loading bar.  The default value is 12.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_font_size(int size) {
+  nout << "font_size " << size << "\n";
+  _font_size = size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_font_style
+//       Access: Public
+//  Description: Sets the font style of the text above the loading
+//               bar.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_font_style(FontStyle style) {
+  nout << "font_style " << style << "\n";
+  _font_style = style;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_font_weight
+//       Access: Public
+//  Description: Sets the font weight of the text above the loading
+//               bar.  The default is FW_normal.  It should be
+//               a multiple of 100 in the range 100-900.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_font_weight(int weight) {
+  nout << "font_weight " << weight << "\n";
+  _font_weight = weight;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSplashWindow::set_install_label
 //     Function: P3DSplashWindow::set_install_label
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -271,10 +440,21 @@ read_image_data(ImageData &image, string &data,
 void P3DSplashWindow::
 void P3DSplashWindow::
 get_bar_placement(int &bar_x, int &bar_y,
 get_bar_placement(int &bar_x, int &bar_y,
                   int &bar_width, int &bar_height) {
                   int &bar_width, int &bar_height) {
-  bar_width = min((int)(_win_width * 0.6), 400);
-  bar_height = min((int)(_win_height * 0.1), 24);
+
+  bar_width = (int)(_win_width * _bar_width_ratio);
+  bar_height = (int)(_win_height * _bar_height_ratio);
+
+  if (_bar_width >= 0) {
+    bar_width = min(bar_width, _bar_width);
+  }
+  if (_bar_height >= 0) {
+    bar_height = min(bar_height, _bar_height);
+  }
+
+  // Horizontally center the bar, and set it at a fixed distance
+  // from the bottom edge of the splash window.
   bar_x = (_win_width - bar_width) / 2;
   bar_x = (_win_width - bar_width) / 2;
-  bar_y = (_win_height - bar_height * 2);
+  bar_y = _win_height - _bar_bottom - _bar_border - bar_height;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 36 - 3
direct/src/plugin/p3dSplashWindow.h

@@ -51,12 +51,32 @@ public:
     IP_button_click,
     IP_button_click,
     IP_none
     IP_none
   };
   };
+  enum FontStyle {
+    FS_normal,
+    FS_italic,
+    FS_oblique
+  };
+  enum FontWeight {
+    FW_normal = 400,
+    FW_medium = 500,
+    FW_bold = 700,
+    FW_black = 900
+  };
 
 
   virtual void set_image_filename(const string &image_filename,
   virtual void set_image_filename(const string &image_filename,
                                   ImagePlacement image_placement);
                                   ImagePlacement image_placement);
-  virtual void set_fgcolor(int r, int g, int b);
-  virtual void set_bgcolor(int r, int g, int b);
-  virtual void set_barcolor(int r, int g, int b);
+  void set_fgcolor(int r, int g, int b);
+  void set_bgcolor(int r, int g, int b);
+  void set_barcolor(int r, int g, int b);
+  void set_bar_bgcolor(int r, int g, int b);
+  void set_bar_border(int border);
+  void set_bar_bottom(int bottom);
+  void set_bar_width(int width, bool percent=false);
+  void set_bar_height(int height, bool percent=false);
+  void set_font_family(const string &family);
+  void set_font_size(int size);
+  void set_font_style(FontStyle style);
+  void set_font_weight(int weight);
   virtual void set_install_label(const string &install_label);
   virtual void set_install_label(const string &install_label);
   virtual void set_install_progress(double install_progress,
   virtual void set_install_progress(double install_progress,
                                     bool is_progress_known, size_t received_data);
                                     bool is_progress_known, size_t received_data);
@@ -105,6 +125,19 @@ protected:
   int _fgcolor_r, _fgcolor_g, _fgcolor_b;
   int _fgcolor_r, _fgcolor_g, _fgcolor_b;
   int _bgcolor_r, _bgcolor_g, _bgcolor_b;
   int _bgcolor_r, _bgcolor_g, _bgcolor_b;
   int _barcolor_r, _barcolor_g, _barcolor_b;
   int _barcolor_r, _barcolor_g, _barcolor_b;
+  int _bar_bgcolor_r, _bar_bgcolor_g, _bar_bgcolor_b;
+  int _bar_border;
+
+private:
+  int _bar_bottom;
+  int _bar_width, _bar_height;
+  double _bar_width_ratio, _bar_height_ratio;
+
+protected:
+  string _font_family;
+  int _font_size;
+  FontStyle _font_style;
+  int _font_weight;
 
 
   // The region of the window for accepting button clicks.
   // The region of the window for accepting button clicks.
   int _button_width, _button_height;
   int _button_width, _button_height;

+ 34 - 8
direct/src/plugin/p3dWinSplashWindow.cxx

@@ -34,9 +34,11 @@ P3DWinSplashWindow(P3DInstance *inst, bool make_visible) :
   _thread = NULL;
   _thread = NULL;
   _thread_id = 0;
   _thread_id = 0;
   _hwnd = NULL;
   _hwnd = NULL;
+  _font = NULL;
   _fg_brush = NULL;
   _fg_brush = NULL;
   _bg_brush = NULL;
   _bg_brush = NULL;
   _bar_brush = NULL;
   _bar_brush = NULL;
+  _bar_bg_brush = NULL;
   _thread_running = false;
   _thread_running = false;
   _install_progress = 0.0;
   _install_progress = 0.0;
   _progress_known = true;
   _progress_known = true;
@@ -497,9 +499,21 @@ make_window() {
     ShowWindow(_hwnd, SW_HIDE);
     ShowWindow(_hwnd, SW_HIDE);
   }
   }
 
 
+  // Load the requested font.
+  _font = CreateFontA(-_font_size, 0, 0, 0, _font_weight,
+                      (_font_style != FS_normal), FALSE, FALSE,
+                      ANSI_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS,
+                      CLEARTYPE_QUALITY, VARIABLE_PITCH, _font_family.c_str());
+
+  if (_font == NULL) {
+    nout << "CreateFont failed: " << GetLastError() << "\n";
+    _font = (HFONT)GetStockObject(ANSI_VAR_FONT);
+  }
+
   _fg_brush = CreateSolidBrush(RGB(_fgcolor_r, _fgcolor_g, _fgcolor_b));
   _fg_brush = CreateSolidBrush(RGB(_fgcolor_r, _fgcolor_g, _fgcolor_b));
   _bg_brush = CreateSolidBrush(RGB(_bgcolor_r, _bgcolor_g, _bgcolor_b));
   _bg_brush = CreateSolidBrush(RGB(_bgcolor_r, _bgcolor_g, _bgcolor_b));
   _bar_brush = CreateSolidBrush(RGB(_barcolor_r, _barcolor_g, _barcolor_b));
   _bar_brush = CreateSolidBrush(RGB(_barcolor_r, _barcolor_g, _barcolor_b));
+  _bar_bg_brush = CreateSolidBrush(RGB(_bar_bgcolor_r, _bar_bgcolor_g, _bar_bgcolor_b));
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -635,6 +649,10 @@ close_window() {
     DeleteObject(_bar_brush);
     DeleteObject(_bar_brush);
     _bar_brush = NULL;
     _bar_brush = NULL;
   }
   }
+  if (_bar_bg_brush != NULL) {
+    DeleteObject(_bar_bg_brush);
+    _bar_bg_brush = NULL;
+  }
 
 
   _background_image.dump_image();
   _background_image.dump_image();
   _button_ready_image.dump_image();
   _button_ready_image.dump_image();
@@ -794,7 +812,7 @@ paint_progress_bar(HDC dc) {
   RECT bar_rect = { bar_x, bar_y, bar_x + bar_width, bar_y + bar_height };
   RECT bar_rect = { bar_x, bar_y, bar_x + bar_width, bar_y + bar_height };
 
 
   // Clear the entire progress bar to white (or the background color).
   // Clear the entire progress bar to white (or the background color).
-  FillRect(dc, &bar_rect, _bg_brush);
+  FillRect(dc, &bar_rect, _bar_bg_brush);
 
 
   // Draw the interior of the progress bar in blue (or the bar color).
   // Draw the interior of the progress bar in blue (or the bar color).
   if (_drawn_progress_known) {
   if (_drawn_progress_known) {
@@ -820,23 +838,31 @@ paint_progress_bar(HDC dc) {
   }
   }
 
 
   // Now draw a black (or foreground) border around the progress bar.
   // Now draw a black (or foreground) border around the progress bar.
-  FrameRect(dc, &bar_rect, _fg_brush);
+  if (_bar_border >= 0) {
+    RECT border_rect = bar_rect;
+
+    for (int i = 0; i < _bar_border; ++i) {
+      --border_rect.left;
+      --border_rect.top;
+      ++border_rect.right;
+      ++border_rect.bottom;
+      FrameRect(dc, &border_rect, _fg_brush);
+    }
+  }
 
 
   if (!_drawn_label.empty()) {
   if (!_drawn_label.empty()) {
     // Now draw the install_label right above it.
     // Now draw the install_label right above it.
-
     const char *text = _drawn_label.c_str();
     const char *text = _drawn_label.c_str();
-    HFONT font = (HFONT)GetStockObject(ANSI_VAR_FONT); 
 
 
     // Measure the text, for centering.
     // Measure the text, for centering.
-    SelectObject(dc, font);
+    SelectObject(dc, _font);
     SIZE text_size;
     SIZE text_size;
     GetTextExtentPoint32(dc, text, strlen(text), &text_size);
     GetTextExtentPoint32(dc, text, strlen(text), &text_size);
 
 
     int text_width = text_size.cx;
     int text_width = text_size.cx;
     int text_height = text_size.cy;
     int text_height = text_size.cy;
     int text_x = (_win_width - text_width) / 2;
     int text_x = (_win_width - text_width) / 2;
-    int text_y = bar_y - (int)(text_height * 1.5);
+    int text_y = bar_y - (int)(text_height * 1.5) - _bar_border;
 
 
     // Clear the rectangle behind the text to white.
     // Clear the rectangle behind the text to white.
     RECT text_rect = { text_x - 2, text_y - 2, text_x + text_width + 4, text_y + text_height + 4 };
     RECT text_rect = { text_x - 2, text_y - 2, text_x + text_width + 4, text_y + text_height + 4 };
@@ -856,7 +882,7 @@ paint_progress_bar(HDC dc) {
 //       Access: Private
 //       Access: Private
 //  Description: The windows event-processing handler.
 //  Description: The windows event-processing handler.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-LONG P3DWinSplashWindow::
+LRESULT P3DWinSplashWindow::
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
   switch (msg) {
   switch (msg) {
   case WM_DESTROY:
   case WM_DESTROY:
@@ -937,7 +963,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 //       Access: Private, Static
 //       Access: Private, Static
 //  Description: The windows event-processing handler, static version.
 //  Description: The windows event-processing handler, static version.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-LONG P3DWinSplashWindow::
+LRESULT P3DWinSplashWindow::
 st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
   LONG_PTR self = GetWindowLongPtr(hwnd, GWLP_USERDATA);
   LONG_PTR self = GetWindowLongPtr(hwnd, GWLP_USERDATA);
   if (self == NULL) {
   if (self == NULL) {

+ 4 - 2
direct/src/plugin/p3dWinSplashWindow.h

@@ -68,8 +68,8 @@ private:
   void paint_window(HDC dc);
   void paint_window(HDC dc);
   bool paint_image(HDC dc, const WinImageData &image, bool use_alpha);
   bool paint_image(HDC dc, const WinImageData &image, bool use_alpha);
   void paint_progress_bar(HDC dc);
   void paint_progress_bar(HDC dc);
-  LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
-  static LONG WINAPI st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+  LRESULT window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+  static LRESULT WINAPI st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
 
 
 private:
 private:
   class WinImageData : public ImageData {
   class WinImageData : public ImageData {
@@ -109,9 +109,11 @@ private:
   HANDLE _thread;
   HANDLE _thread;
   DWORD _thread_id;
   DWORD _thread_id;
   HWND _hwnd;
   HWND _hwnd;
+  HFONT _font;
   HBRUSH _fg_brush;
   HBRUSH _fg_brush;
   HBRUSH _bg_brush;
   HBRUSH _bg_brush;
   HBRUSH _bar_brush;
   HBRUSH _bar_brush;
+  HBRUSH _bar_bg_brush;
 
 
   static bool _registered_window_class;
   static bool _registered_window_class;
 };
 };

+ 147 - 16
direct/src/plugin/p3dX11SplashWindow.cxx

@@ -47,11 +47,14 @@ P3DX11SplashWindow(P3DInstance *inst, bool make_visible) :
   _display = None;
   _display = None;
   _window = None;
   _window = None;
   _screen = 0;
   _screen = 0;
+  _font = NULL;
   _graphics_context = None;
   _graphics_context = None;
   _bar_context = None;
   _bar_context = None;
+  _bar_bg_context = None;
   _fg_pixel = -1;
   _fg_pixel = -1;
   _bg_pixel = -1;
   _bg_pixel = -1;
   _bar_pixel = -1;
   _bar_pixel = -1;
+  _bar_bg_pixel = -1;
   _own_display = false;
   _own_display = false;
   _install_progress = 0.0;
   _install_progress = 0.0;
   _progress_known = true;
   _progress_known = true;
@@ -609,10 +612,15 @@ subprocess_run() {
       get_bar_placement(bar_x, bar_y, bar_width, bar_height);
       get_bar_placement(bar_x, bar_y, bar_width, bar_height);
 
 
       if (needs_draw_label) {
       if (needs_draw_label) {
-        int text_width = _install_label.size() * 6;
-        int text_height = 10;
+        int direction, ascent, descent;
+        XCharStruct extents;
+        XTextExtents(_font, _install_label.c_str(), _install_label.size(),
+                     &direction, &ascent, &descent, &extents);
+
+        int text_width = extents.width;
+        int text_height = extents.ascent + extents.descent;
         int text_x = (_win_width - text_width) / 2;
         int text_x = (_win_width - text_width) / 2;
-        int text_y = bar_y - 4;
+        int text_y = bar_y - descent - _bar_border - 4;
 
 
         XClearArea(_display, _window,
         XClearArea(_display, _window,
                    text_x - 2, text_y - text_height - 2,
                    text_x - 2, text_y - text_height - 2,
@@ -624,25 +632,44 @@ subprocess_run() {
       }
       }
 
 
       if (needs_redraw_progress) {
       if (needs_redraw_progress) {
-        XClearArea(_display, _window,
-                   bar_x, bar_y, bar_width, bar_height, false);
-        XDrawRectangle(_display, _window, _graphics_context,
-                       bar_x, bar_y, bar_width, bar_height);
+        // Draw the bar background.
+        if (_bar_bg_context != None) {
+          XFillRectangle(_display, _window, _bar_bg_context,
+                         bar_x, bar_y, bar_width, bar_height);
+        } else {
+          XClearArea(_display, _window,
+                     bar_x, bar_y, bar_width, bar_height, false);
+        }
+
+        // Draw the border around the bar.
+        int border_x = bar_x - 1;
+        int border_y = bar_y - 1;
+        int border_width = bar_width + 1;
+        int border_height = bar_height + 1;
+
+        for (int i = 0; i < _bar_border; ++i) {
+          XDrawRectangle(_display, _window, _graphics_context,
+                         border_x, border_y, border_width, border_height);
+          border_x -= 1;
+          border_y -= 1;
+          border_width += 2;
+          border_height += 2;
+        }
         needs_update_progress = true;
         needs_update_progress = true;
         needs_redraw_progress = false;
         needs_redraw_progress = false;
       }
       }
 
 
       if (needs_update_progress) {
       if (needs_update_progress) {
         if (_progress_known) {
         if (_progress_known) {
-          int progress_width = (int)((bar_width - 2) * _install_progress + 0.5);
+          int progress_width = (int)((bar_width - 1) * _install_progress + 0.5);
           XFillRectangle(_display, _window, _bar_context,
           XFillRectangle(_display, _window, _bar_context,
-                         bar_x + 1, bar_y + 1,
-                         progress_width + 1, bar_height - 1);
+                         bar_x, bar_y,
+                         progress_width + 1, bar_height);
         } else {
         } else {
           // Progress is unknown.  Draw a moving block, not a progress bar
           // Progress is unknown.  Draw a moving block, not a progress bar
           // filling up.
           // filling up.
           int block_width = (int)(bar_width * 0.1 + 0.5);
           int block_width = (int)(bar_width * 0.1 + 0.5);
-          int block_travel = (bar_width - 2) - block_width;
+          int block_travel = (bar_width - 1) - block_width;
           int progress = (int)(_received_data * _unknown_progress_rate);
           int progress = (int)(_received_data * _unknown_progress_rate);
           progress = progress % (block_travel * 2);
           progress = progress % (block_travel * 2);
           if (progress > block_travel) {
           if (progress > block_travel) {
@@ -650,8 +677,8 @@ subprocess_run() {
           }
           }
 
 
           XFillRectangle(_display, _window, _bar_context,
           XFillRectangle(_display, _window, _bar_context,
-                         bar_x + 1 + progress, bar_y + 1,
-                         block_width + 1, bar_height - 1);
+                         bar_x + progress, bar_y,
+                         block_width + 1, bar_height);
         }
         }
         needs_update_progress = false;
         needs_update_progress = false;
       }
       }
@@ -681,10 +708,13 @@ subprocess_run() {
       }
       }
     }
     }
 
 
-    if (input_ready) {
+    while (input_ready) {
+      // Empty the pipe of whatever is in it.
       receive_command();
       receive_command();
+      input_ready = _pipe_read.has_gdata();
     }
     }
 
 
+    // Sleep a good amount in order not to lock up the system.
     struct timespec req;
     struct timespec req;
     req.tv_sec = 0;
     req.tv_sec = 0;
     req.tv_nsec = 50000000;  // 50 ms
     req.tv_nsec = 50000000;  // 50 ms
@@ -955,10 +985,82 @@ setup_gc() {
     return;
     return;
   }
   }
 
 
-  XFontStruct* fs = XLoadQueryFont(_display, "6x13");
+  char style = 'r';
+  if (_font_style == FS_oblique) {
+    style = 'o';
+  } else if (_font_style == FS_italic) {
+    style = 'i';
+  }
+
+  // Determine the order at which to try the various weights.  From:
+  // https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
+  const char *const *try_weights;
+  const int num_weights = 7;
+
+  if (_font_weight > 700) {
+    static const char *const weights[] = {"black", "bold", "demibold", "semibold", "medium", "regular", "extralight"};
+    try_weights = weights;
+
+  } else if (_font_weight > 600) {
+    static const char *const weights[] = {"bold", "black", "demibold", "semibold", "medium", "regular", "extralight"};
+    try_weights = weights;
+
+  } else if (_font_weight > 500) {
+    static const char *const weights[] = {"demibold", "semibold", "bold", "black", "medium", "regular", "extralight"};
+    try_weights = weights;
+
+  } else if (_font_weight == 500) {
+    static const char *const weights[] = {"medium", "regular", "extralight", "demibold", "semibold", "bold", "black"};
+    try_weights = weights;
+
+  } else if (_font_weight >= 400) {
+    static const char *const weights[] = {"regular", "medium", "extralight", "demibold", "semibold", "bold", "black"};
+    try_weights = weights;
+
+  } else {
+    static const char *const weights[] = {"extralight", "regular", "medium", "demibold", "semibold", "bold", "black"};
+    try_weights = weights;
+  }
+
+  char font_name[1024];
+
+  // Go through the weights array to find the best matching font.
+  for (int i = 0; i < num_weights; ++i) {
+    const char *weight_name = try_weights[i];
+
+    // Compose the proper pattern for finding the desired font face.
+    snprintf(font_name, 1024, "-*-%s-%s-%c-normal--%d-*-*-*-*-*-iso8859-1",
+             _font_family.c_str(), weight_name, style, _font_size);
+
+    _font = XLoadQueryFont(_display, font_name);
+    if (_font != NULL) {
+      break;
+    }
+    nout << "Font " << font_name << " unavailable.\n";
+
+    if (style == 'i' || style == 'o') {
+      // If oblique is not found, try italic, and vice versa.
+      char style2 = 216 - style;
+      snprintf(font_name, 1024, "-*-%s-%s-%c-normal--%d-*-*-*-*-*-iso8859-1",
+               _font_family.c_str(), weight_name, style2, _font_size);
+
+      _font = XLoadQueryFont(_display, font_name);
+      if (_font != NULL) {
+        break;
+      }
+      nout << "Font " << font_name << " unavailable.\n";
+    }
+  }
+
+  if (_font != NULL) {
+    nout << "Loaded font " << font_name << "\n";
+  } else {
+    nout << "Using fallback font 6x13.\n";
+    _font = XLoadQueryFont(_display, "6x13");
+  }
 
 
   XGCValues gcval;
   XGCValues gcval;
-  gcval.font = fs->fid;
+  gcval.font = _font->fid;
   gcval.function = GXcopy;
   gcval.function = GXcopy;
   gcval.plane_mask = AllPlanes;
   gcval.plane_mask = AllPlanes;
   gcval.foreground = BlackPixel(_display, _screen);
   gcval.foreground = BlackPixel(_display, _screen);
@@ -988,6 +1090,24 @@ setup_gc() {
 
 
   _bar_context = XCreateGC(_display, _window,
   _bar_context = XCreateGC(_display, _window,
     GCFont | GCFunction | GCPlaneMask | GCForeground | GCBackground, &gcval);
     GCFont | GCFunction | GCPlaneMask | GCForeground | GCBackground, &gcval);
+
+  // And another for the background color of the bar.
+  if (_bar_bgcolor_r != _bgcolor_r || _bar_bgcolor_g != _bgcolor_g ||
+      _bar_bgcolor_b != _bgcolor_b) {
+    XColor bar_bg;
+    bar_bg.red = _bar_bgcolor_r * 0x101;
+    bar_bg.green = _bar_bgcolor_g * 0x101;
+    bar_bg.blue = _bar_bgcolor_b * 0x101;
+    bar_bg.flags = DoRed | DoGreen | DoBlue;
+
+    if (XAllocColor(_display, colormap, &bar_bg)) {
+      _bar_bg_pixel = bar_bg.pixel;
+      gcval.foreground = bar_bg.pixel;
+    }
+
+    _bar_bg_context = XCreateGC(_display, _window,
+      GCFont | GCFunction | GCPlaneMask | GCForeground | GCBackground, &gcval);
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1013,6 +1133,17 @@ close_window() {
     XFreeColors(_display, colormap, &_bar_pixel, 1, 0);
     XFreeColors(_display, colormap, &_bar_pixel, 1, 0);
   }
   }
 
 
+  if (_bar_bg_context != None) {
+    if (_bar_bg_context != _graphics_context) {
+      XFreeGC(_display, _bar_bg_context);
+    }
+    _bar_bg_context = None;
+
+    // Also free the color we allocated.
+    Colormap colormap = DefaultColormap(_display, _screen);
+    XFreeColors(_display, colormap, &_bar_bg_pixel, 1, 0);
+  }
+
   if (_fg_pixel != -1) {
   if (_fg_pixel != -1) {
     Colormap colormap = DefaultColormap(_display, _screen);
     Colormap colormap = DefaultColormap(_display, _screen);
     XFreeColors(_display, colormap, &_fg_pixel, 1, 0);
     XFreeColors(_display, colormap, &_fg_pixel, 1, 0);

+ 6 - 3
direct/src/plugin/p3dX11SplashWindow.h

@@ -86,7 +86,7 @@ private:
   void setup_gc();
   void setup_gc();
   void close_window();
   void close_window();
 
 
-  void update_image(X11ImageData &image); 
+  void update_image(X11ImageData &image);
   void compose_image();
   void compose_image();
   bool scale_image(vector<unsigned char> &image0, int &image0_width, int &image0_height,
   bool scale_image(vector<unsigned char> &image0, int &image0_width, int &image0_height,
                    X11ImageData &image);
                    X11ImageData &image);
@@ -123,17 +123,20 @@ private:
   double _install_progress;
   double _install_progress;
   bool _progress_known;
   bool _progress_known;
   size_t _received_data;
   size_t _received_data;
-  
+
   string _label_text;
   string _label_text;
 
 
   X11_Display *_display;
   X11_Display *_display;
   int _screen;
   int _screen;
+  XFontStruct *_font;
   GC _graphics_context;
   GC _graphics_context;
   GC _bar_context;
   GC _bar_context;
+  GC _bar_bg_context;
   unsigned long _fg_pixel;
   unsigned long _fg_pixel;
   unsigned long _bg_pixel;
   unsigned long _bg_pixel;
   unsigned long _bar_pixel;
   unsigned long _bar_pixel;
-  
+  unsigned long _bar_bg_pixel;
+
   X11_Window _window;
   X11_Window _window;
 };
 };
 
 

+ 7 - 2
direct/src/plugin/p3d_plugin.cxx

@@ -39,7 +39,8 @@ P3D_initialize(int api_version, const char *contents_filename,
                const char *platform, const char *log_directory,
                const char *platform, const char *log_directory,
                const char *log_basename, bool trusted_environment,
                const char *log_basename, bool trusted_environment,
                bool console_environment,
                bool console_environment,
-               const char *root_dir, const char *host_dir) {
+               const char *root_dir, const char *host_dir,
+               const char *start_dir) {
   if (api_version < 10 || api_version > P3D_API_VERSION) {
   if (api_version < 10 || api_version > P3D_API_VERSION) {
     // Can't accept an incompatible version.
     // Can't accept an incompatible version.
     return false;
     return false;
@@ -89,12 +90,16 @@ P3D_initialize(int api_version, const char *contents_filename,
     host_dir = "";
     host_dir = "";
   }
   }
 
 
+  if (api_version < 17 || start_dir == NULL) {
+    start_dir = "";
+  }
+
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   bool result = inst_mgr->initialize(api_version, contents_filename, host_url,
   bool result = inst_mgr->initialize(api_version, contents_filename, host_url,
                                      verify_contents, platform,
                                      verify_contents, platform,
                                      log_directory, log_basename,
                                      log_directory, log_basename,
                                      trusted_environment, console_environment,
                                      trusted_environment, console_environment,
-                                     root_dir, host_dir);
+                                     root_dir, host_dir, start_dir);
   RELEASE_LOCK(_api_lock);
   RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }

+ 7 - 2
direct/src/plugin/p3d_plugin.h

@@ -53,7 +53,11 @@
 #endif
 #endif
 
 
 #else  /* _WIN32 */
 #else  /* _WIN32 */
+#if defined(BUILDING_P3D_PLUGIN) && defined(__GNUC__)
+#define EXPCL_P3D_PLUGIN __attribute__((visibility("default")))
+#else
 #define EXPCL_P3D_PLUGIN
 #define EXPCL_P3D_PLUGIN
+#endif
 
 
 #endif  /* _WIN32 */
 #endif  /* _WIN32 */
 
 
@@ -79,7 +83,7 @@ extern "C" {
    (below). This number will be incremented whenever there are changes
    (below). This number will be incremented whenever there are changes
    to any of the interface specifications defined in this header
    to any of the interface specifications defined in this header
    file. */
    file. */
-#define P3D_API_VERSION 16
+#define P3D_API_VERSION 17
 
 
 /************************ GLOBAL FUNCTIONS **************************/
 /************************ GLOBAL FUNCTIONS **************************/
 
 
@@ -161,7 +165,8 @@ P3D_initialize_func(int api_version, const char *contents_filename,
                     const char *platform,
                     const char *platform,
                     const char *log_directory, const char *log_basename,
                     const char *log_directory, const char *log_basename,
                     bool trusted_environment, bool console_environment,
                     bool trusted_environment, bool console_environment,
-                    const char *root_dir, const char *host_dir);
+                    const char *root_dir, const char *host_dir,
+                    const char *start_dir);
 
 
 /* This function should be called to unload the core API.  It will
 /* This function should be called to unload the core API.  It will
    release all internally-allocated memory and return the core API to
    release all internally-allocated memory and return the core API to

+ 7 - 6
direct/src/plugin/run_p3dpython.cxx

@@ -19,16 +19,17 @@
 //     Function: run_p3dpython
 //     Function: run_p3dpython
 //  Description: This externally-visible function is the main entry
 //  Description: This externally-visible function is the main entry
 //               point to this DLL, and it starts the whole thing
 //               point to this DLL, and it starts the whole thing
-//               running.  Returns true on success, false on failure.
+//               running.  Returns the exit status, which will be
+//               0 on success, 1 or otherwise on failure.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool
+int
 run_p3dpython(const char *program_name, const char *archive_file,
 run_p3dpython(const char *program_name, const char *archive_file,
-              FHandle input_handle, FHandle output_handle, 
+              FHandle input_handle, FHandle output_handle,
               const char *log_pathname, bool interactive_console) {
               const char *log_pathname, bool interactive_console) {
-  P3DPythonRun::_global_ptr = 
-    new P3DPythonRun(program_name, archive_file, input_handle, output_handle, 
+  P3DPythonRun::_global_ptr =
+    new P3DPythonRun(program_name, archive_file, input_handle, output_handle,
                      log_pathname, interactive_console);
                      log_pathname, interactive_console);
-  bool result = P3DPythonRun::_global_ptr->run_python();
+  int result = P3DPythonRun::_global_ptr->run_python();
   delete P3DPythonRun::_global_ptr;
   delete P3DPythonRun::_global_ptr;
   P3DPythonRun::_global_ptr = NULL;
   P3DPythonRun::_global_ptr = NULL;
   return result;
   return result;

+ 4 - 4
direct/src/plugin/run_p3dpython.h

@@ -26,14 +26,14 @@
 #define EXPCL_P3DPYTHON
 #define EXPCL_P3DPYTHON
 #endif
 #endif
 
 
-typedef bool 
+typedef int
 run_p3dpython_func(const char *program_name, const char *archive_file,
 run_p3dpython_func(const char *program_name, const char *archive_file,
-                   FHandle input_handle, FHandle output_handle, 
+                   FHandle input_handle, FHandle output_handle,
                    const char *log_pathname, bool interactive_console);
                    const char *log_pathname, bool interactive_console);
 
 
-extern "C" EXPCL_P3DPYTHON bool
+extern "C" EXPCL_P3DPYTHON int
 run_p3dpython(const char *program_name, const char *archive_file,
 run_p3dpython(const char *program_name, const char *archive_file,
-              FHandle input_handle, FHandle output_handle, 
+              FHandle input_handle, FHandle output_handle,
               const char *log_pathname, bool interactive_console);
               const char *log_pathname, bool interactive_console);
 
 
 #endif
 #endif

+ 3 - 3
direct/src/plugin_activex/PPInstance.cpp

@@ -581,12 +581,12 @@ int PPInstance::LoadPlugin( const std::string& dllFilename )
         pathname = override_filename;
         pathname = override_filename;
       }
       }
 #endif  // P3D_PLUGIN_P3D_PLUGIN
 #endif  // P3D_PLUGIN_P3D_PLUGIN
-      
+
       nout << "Attempting to load core API from " << pathname << "\n";
       nout << "Attempting to load core API from " << pathname << "\n";
       string contents_filename = m_rootDir + "/contents.xml";
       string contents_filename = m_rootDir + "/contents.xml";
       if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL,
       if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL,
-                       P3D_VC_normal, "", "", "", false, false, 
-                       m_rootDir, "", nout)) {
+                       P3D_VC_normal, "", "", "", false, false,
+                       m_rootDir, "", "", nout)) {
         nout << "Unable to launch core API in " << pathname << "\n";
         nout << "Unable to launch core API in " << pathname << "\n";
         error = 1;
         error = 1;
       } else {
       } else {

+ 2 - 2
direct/src/plugin_npapi/ppInstance.cxx

@@ -1738,8 +1738,8 @@ do_load_plugin() {
   nout << "Attempting to load core API from " << pathname << "\n";
   nout << "Attempting to load core API from " << pathname << "\n";
   string contents_filename = _root_dir + "/contents.xml";
   string contents_filename = _root_dir + "/contents.xml";
   if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL,
   if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL,
-                   P3D_VC_normal, "", "", "", false, false, 
-                   _root_dir, "", nout)) {
+                   P3D_VC_normal, "", "", "", false, false,
+                   _root_dir, "", "", nout)) {
     nout << "Unable to launch core API in " << pathname << "\n";
     nout << "Unable to launch core API in " << pathname << "\n";
     set_failed();
     set_failed();
     return;
     return;

+ 23 - 6
direct/src/plugin_standalone/p3dEmbed.cxx

@@ -53,7 +53,8 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
   // the + 1 from the test.
   // the + 1 from the test.
   _read_offset_check = read_offset + (streampos)1;
   _read_offset_check = read_offset + (streampos)1;
   if (_read_offset_check == (streampos)0xFF3D3D01) {
   if (_read_offset_check == (streampos)0xFF3D3D01) {
-    cerr << "This program is not intended to be run directly.\nIt is used by pdeploy to construct an embedded Panda3D application.\n";
+    cerr << "This program is not intended to be run directly.\nIt is used "
+            "by pdeploy to construct an embedded Panda3D application.\n";
     return 1;
     return 1;
   }
   }
 
 
@@ -86,22 +87,30 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
   string value;
   string value;
   string root_dir;
   string root_dir;
   string host_dir;
   string host_dir;
+  string start_dir;
+
   while (true) {
   while (true) {
     if (curchr == EOF) {
     if (curchr == EOF) {
       cerr << "Truncated stream\n";
       cerr << "Truncated stream\n";
-      return(1);
+      return 1;
 
 
     } else if (curchr == 0) {
     } else if (curchr == 0) {
       // Two null bytes in a row means we've reached the end of the data.
       // Two null bytes in a row means we've reached the end of the data.
       if (havenull) {
       if (havenull) {
         break;
         break;
       }
       }
-      
+
       // This means we haven't seen an '=' character yet.
       // This means we haven't seen an '=' character yet.
       if (keyword == "") {
       if (keyword == "") {
         if (curstr != "") {
         if (curstr != "") {
           cerr << "Ignoring token '" << curstr << "' without value\n";
           cerr << "Ignoring token '" << curstr << "' without value\n";
         }
         }
+
+      } else if (keyword == "start_dir") {
+        // Don't pass this on as a token, since it has slightly different
+        // semantics when used as an HTML token.
+        start_dir = curstr;
+
       } else {
       } else {
         value.assign(curstr);
         value.assign(curstr);
         P3D_token token;
         P3D_token token;
@@ -118,6 +127,8 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
           _got_win_size = true;
           _got_win_size = true;
         } else if (keyword == "log_basename") {
         } else if (keyword == "log_basename") {
           _log_basename = value;
           _log_basename = value;
+        } else if (keyword == "log_directory") {
+          _log_dirname = value;
         } else if (keyword == "root_dir") {
         } else if (keyword == "root_dir") {
           root_dir = value;
           root_dir = value;
         } else if (keyword == "host_dir") {
         } else if (keyword == "host_dir") {
@@ -168,6 +179,13 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
     _host_dir = host_dir_f.to_os_specific();
     _host_dir = host_dir_f.to_os_specific();
   }
   }
 
 
+  // Make the start directory absolute
+  if (!start_dir.empty()) {
+    Filename start_dir_f(start_dir);
+    start_dir_f.make_absolute(f.get_dirname());
+    _start_dir = start_dir_f.to_os_specific();
+  }
+
   // Initialize the core API by directly assigning all of the function
   // Initialize the core API by directly assigning all of the function
   // pointers.
   // pointers.
   P3D_initialize_ptr = &P3D_initialize;
   P3D_initialize_ptr = &P3D_initialize;
@@ -229,18 +247,17 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
   // function pointers.  This will also call P3D_initialize().
   // function pointers.  This will also call P3D_initialize().
   if (!init_plugin("", _host_url, _verify_contents, _this_platform, 
   if (!init_plugin("", _host_url, _verify_contents, _this_platform, 
                    _log_dirname, _log_basename, true, _console_environment,
                    _log_dirname, _log_basename, true, _console_environment,
-                   _root_dir, _host_dir, cerr)) {
+                   _root_dir, _host_dir, _start_dir, cerr)) {
     cerr << "Unable to launch core API\n";
     cerr << "Unable to launch core API\n";
     return 1;
     return 1;
   }
   }
-  
+
   // Create a plugin instance and run the program
   // Create a plugin instance and run the program
   P3D_instance *inst = create_instance(f, true, argv, argc, read_offset);
   P3D_instance *inst = create_instance(f, true, argv, argc, read_offset);
   _instances.insert(inst);
   _instances.insert(inst);
   
   
   run_main_loop();
   run_main_loop();
 
 
-
   unload_plugin(cerr);
   unload_plugin(cerr);
   return 0;
   return 0;
 }
 }

+ 185 - 135
direct/src/plugin_standalone/panda3d.cxx

@@ -356,6 +356,7 @@ bool Panda3D::
 get_plugin() {
 get_plugin() {
   // First, look for the existing contents.xml file.
   // First, look for the existing contents.xml file.
   bool success = false;
   bool success = false;
+  bool is_fresh = false;
 
 
   Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
   Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
   if (_verify_contents != P3D_VC_force) {
   if (_verify_contents != P3D_VC_force) {
@@ -370,75 +371,100 @@ get_plugin() {
   if (!success) {
   if (!success) {
     // Couldn't read it (or it wasn't current enough), so go get a new
     // Couldn't read it (or it wasn't current enough), so go get a new
     // one.
     // one.
-    HTTPClient *http = HTTPClient::get_global_ptr();
-    
-    // Try the super_mirror first.
-    if (!_super_mirror_url_prefix.empty()) {
-      // We don't bother putting a uniquifying query string when we're
-      // downloading this file from the super_mirror.  The super_mirror
-      // is by definition a cache, so it doesn't make sense to bust
-      // caches here.
-      string url = _super_mirror_url_prefix + "contents.xml";
-      PT(HTTPChannel) channel = http->make_channel(false);
-      channel->get_document(url);
-      
-      Filename tempfile = Filename::temporary("", "p3d_");
-      if (!channel->download_to_file(tempfile)) {
-        cerr << "Unable to download " << url << "\n";
-        tempfile.unlink();
-      } else {
-        // Successfully downloaded from the super_mirror; try to read it.
-        success = read_contents_file(tempfile, true);
-        tempfile.unlink();
-      }
+    if (!download_contents_file(contents_filename)) {
+      // We don't have a usable contents.xml file.
+      return false;
     }
     }
+    is_fresh = true;
+  }
 
 
-    if (!success) {
-      // Go download contents.xml from the actual host.
-      ostringstream strm;
-      strm << _host_url_prefix << "contents.xml";
-      // Append a uniquifying query string to the URL to force the
-      // download to go all the way through any caches.  We use the time
-      // in seconds; that's unique enough.
-      strm << "?" << time(NULL);
-      string url = strm.str();
-      
-      // We might as well explicitly request the cache to be disabled too,
-      // since we have an interface for that via HTTPChannel.
-      DocumentSpec request(url);
-      request.set_cache_control(DocumentSpec::CC_no_cache);
-      
-      PT(HTTPChannel) channel = http->make_channel(false);
-      channel->get_document(request);
-      
-      // Since we have to download some of it, might as well ask the core
-      // API to check all of it.
-      if (_verify_contents == P3D_VC_none) {
-        _verify_contents = P3D_VC_normal;
-      }
-      
-      // First, download it to a temporary file.
-      Filename tempfile = Filename::temporary("", "p3d_");
-      if (!channel->download_to_file(tempfile)) {
-        cerr << "Unable to download " << url << "\n";
-        
-        // Couldn't download, but try to read the existing contents.xml
-        // file anyway.  Maybe it's good enough.
-        success = read_contents_file(contents_filename, false);
-        
-      } else {
-        // Successfully downloaded; read it and move it into place.
-        success = read_contents_file(tempfile, true);
-      }
+  // Now that we've downloaded the contents file successfully, start
+  // the Core API.
+  if (!get_core_api()) {
+      // We failed.  Make sure contents.xml is up-to-date and try again.
+    if (!is_fresh && download_contents_file(contents_filename) && get_core_api()) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::download_contents_file
+//       Access: Protected
+//  Description: Redownloads the contents.xml file from the named
+//               URL without first checking if it is up to date.
+//               Returns true if we have a contents.xml file that
+//               might be usable, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool Panda3D::
+download_contents_file(const Filename &contents_filename) {
+  bool success = false;
+  HTTPClient *http = HTTPClient::get_global_ptr();
+
+  // Try the super_mirror first.
+  if (!_super_mirror_url_prefix.empty()) {
+    // We don't bother putting a uniquifying query string when we're
+    // downloading this file from the super_mirror.  The super_mirror
+    // is by definition a cache, so it doesn't make sense to bust
+    // caches here.
+    string url = _super_mirror_url_prefix + "contents.xml";
+    PT(HTTPChannel) channel = http->make_channel(false);
+    channel->get_document(url);
 
 
+    Filename tempfile = Filename::temporary("", "p3d_");
+    if (!channel->download_to_file(tempfile)) {
+      cerr << "Unable to download " << url << "\n";
+      tempfile.unlink();
+    } else {
+      // Successfully downloaded from the super_mirror; try to read it.
+      success = read_contents_file(tempfile, true);
       tempfile.unlink();
       tempfile.unlink();
     }
     }
   }
   }
 
 
-  if (success) {
-    // Now that we've downloaded the contents file successfully, start
-    // the Core API.
-    success = get_core_api();
+  if (!success) {
+    // Go download contents.xml from the actual host.
+    ostringstream strm;
+    strm << _host_url_prefix << "contents.xml";
+    // Append a uniquifying query string to the URL to force the
+    // download to go all the way through any caches.  We use the time
+    // in seconds; that's unique enough.
+    strm << "?" << time(NULL);
+    string url = strm.str();
+
+    // We might as well explicitly request the cache to be disabled too,
+    // since we have an interface for that via HTTPChannel.
+    DocumentSpec request(url);
+    request.set_cache_control(DocumentSpec::CC_no_cache);
+
+    PT(HTTPChannel) channel = http->make_channel(false);
+    channel->get_document(request);
+
+    // Since we have to download some of it, might as well ask the core
+    // API to check all of it.
+    if (_verify_contents == P3D_VC_none) {
+      _verify_contents = P3D_VC_normal;
+    }
+
+    // First, download it to a temporary file.
+    Filename tempfile = Filename::temporary("", "p3d_");
+    if (!channel->download_to_file(tempfile)) {
+      cerr << "Unable to download " << url << "\n";
+
+      // Couldn't download, but try to read the existing contents.xml
+      // file anyway.  Maybe it's good enough.
+      success = read_contents_file(contents_filename, false);
+
+    } else {
+      // Successfully downloaded; read it and move it into place.
+      success = read_contents_file(tempfile, true);
+    }
+
+    tempfile.unlink();
   }
   }
 
 
   return success;
   return success;
@@ -690,7 +716,6 @@ choose_random_mirrors(vector_string &result, int num_mirrors) {
   }
   }
 }
 }
 
 
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::get_core_api
 //     Function: Panda3D::get_core_api
 //       Access: Protected
 //       Access: Protected
@@ -700,78 +725,12 @@ choose_random_mirrors(vector_string &result, int num_mirrors) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool Panda3D::
 bool Panda3D::
 get_core_api() {
 get_core_api() {
+  bool is_fresh = false;
   if (!_coreapi_dll.quick_verify(_root_dir)) {
   if (!_coreapi_dll.quick_verify(_root_dir)) {
-    // The DLL file needs to be downloaded.  Build up our list of
-    // URL's to attempt to download it from, in reverse order.
-    string url;
-    vector_string core_urls;
-
-    // Our last act of desperation: hit the original host, with a
-    // query uniquifier, to break through any caches.
-    ostringstream strm;
-    strm << _download_url_prefix << _coreapi_dll.get_filename()
-         << "?" << time(NULL);
-    url = strm.str();
-    core_urls.push_back(url);
-
-    // Before we try that, we'll hit the original host, without a
-    // uniquifier.
-    url = _download_url_prefix;
-    url += _coreapi_dll.get_filename();
-    core_urls.push_back(url);
-
-    // And before we try that, we'll try two mirrors, at random.
-    vector_string mirrors;
-    choose_random_mirrors(mirrors, 2);
-    for (vector_string::iterator si = mirrors.begin();
-         si != mirrors.end(); 
-         ++si) {
-      url = (*si) + _coreapi_dll.get_filename();
-      core_urls.push_back(url);
-    }
-
-    // The very first thing we'll try is the super_mirror, if we have
-    // one.
-    if (!_super_mirror_url_prefix.empty()) {
-      url = _super_mirror_url_prefix + _coreapi_dll.get_filename();
-      core_urls.push_back(url);
-    }
-
-    // Now pick URL's off the list, and try them, until we have
-    // success.
-    Filename pathname = Filename::from_os_specific(_coreapi_dll.get_pathname(_root_dir));
-    pathname.make_dir();
-    HTTPClient *http = HTTPClient::get_global_ptr();
-
-    bool success = false;
-    while (!core_urls.empty()) {
-      url = core_urls.back();
-      core_urls.pop_back();
-    
-      PT(HTTPChannel) channel = http->get_document(url);
-      if (!channel->download_to_file(pathname)) {
-        cerr << "Unable to download " << url << "\n";
-
-      } else if (!_coreapi_dll.full_verify(_root_dir)) {
-        cerr << "Mismatched download for " << url << "\n";
-
-      } else {
-        // successfully downloaded!
-        success = true;
-        break;
-      }
-    }
-
-    if (!success) {
-      cerr << "Couldn't download core API.\n";
+    if (!download_core_api()) {
       return false;
       return false;
     }
     }
-
-    // Since we had to download some of it, might as well ask the core
-    // API to check all of it.
-    if (_verify_contents == P3D_VC_none) {
-      _verify_contents = P3D_VC_normal;
-    }
+    is_fresh = true;
   }
   }
 
 
   // Now we've got the DLL.  Load it.
   // Now we've got the DLL.  Load it.
@@ -795,9 +754,19 @@ get_core_api() {
   if (!load_plugin(pathname, contents_filename.to_os_specific(),
   if (!load_plugin(pathname, contents_filename.to_os_specific(),
                    _host_url, _verify_contents, _this_platform, _log_dirname,
                    _host_url, _verify_contents, _this_platform, _log_dirname,
                    _log_basename, trusted_environment, _console_environment,
                    _log_basename, trusted_environment, _console_environment,
-                   _root_dir, "", cerr)) {
-    cerr << "Unable to launch core API in " << pathname << "\n";
-    return false;
+                   _root_dir, _host_dir, _start_dir, cerr)) {
+
+    // If we're not sure this is the latest version, make sure it is
+    // up-to-date, and then try again.
+    if (is_fresh || !download_core_api() ||
+        !load_plugin(pathname, contents_filename.to_os_specific(),
+                     _host_url, _verify_contents, _this_platform, _log_dirname,
+                     _log_basename, trusted_environment, _console_environment,
+                     _root_dir, _host_dir, _start_dir, cerr)) {
+
+      cerr << "Unable to launch core API in " << pathname << "\n";
+      return false;
+    }
   }
   }
 
 
   // Successfully loaded.
   // Successfully loaded.
@@ -822,6 +791,87 @@ get_core_api() {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::download_core_api
+//       Access: Protected
+//  Description: Downloads the latest version of the core API from
+//               the plug-in server.
+////////////////////////////////////////////////////////////////////
+bool Panda3D::
+download_core_api() {
+  // The DLL file needs to be downloaded.  Build up our list of
+  // URL's to attempt to download it from, in reverse order.
+  string url;
+  vector_string core_urls;
+
+  // Our last act of desperation: hit the original host, with a
+  // query uniquifier, to break through any caches.
+  ostringstream strm;
+  strm << _download_url_prefix << _coreapi_dll.get_filename()
+       << "?" << time(NULL);
+  url = strm.str();
+  core_urls.push_back(url);
+
+  // Before we try that, we'll hit the original host, without a
+  // uniquifier.
+  url = _download_url_prefix;
+  url += _coreapi_dll.get_filename();
+  core_urls.push_back(url);
+
+  // And before we try that, we'll try two mirrors, at random.
+  vector_string mirrors;
+  choose_random_mirrors(mirrors, 2);
+  for (vector_string::iterator si = mirrors.begin();
+       si != mirrors.end();
+       ++si) {
+    url = (*si) + _coreapi_dll.get_filename();
+    core_urls.push_back(url);
+  }
+
+  // The very first thing we'll try is the super_mirror, if we have
+  // one.
+  if (!_super_mirror_url_prefix.empty()) {
+    url = _super_mirror_url_prefix + _coreapi_dll.get_filename();
+    core_urls.push_back(url);
+  }
+
+  // Now pick URL's off the list, and try them, until we have
+  // success.
+  Filename pathname = Filename::from_os_specific(_coreapi_dll.get_pathname(_root_dir));
+  pathname.make_dir();
+  HTTPClient *http = HTTPClient::get_global_ptr();
+
+  bool success = false;
+  while (!core_urls.empty()) {
+    url = core_urls.back();
+    core_urls.pop_back();
+
+    PT(HTTPChannel) channel = http->get_document(url);
+    if (!channel->download_to_file(pathname)) {
+      cerr << "Unable to download " << url << "\n";
+
+    } else if (!_coreapi_dll.full_verify(_root_dir)) {
+      cerr << "Mismatched download for " << url << "\n";
+
+    } else {
+      // successfully downloaded!
+      success = true;
+      break;
+    }
+  }
+
+  if (!success) {
+    return false;
+  }
+
+  // Since we had to download some of it, might as well ask the core
+  // API to check all of it.
+  if (_verify_contents == P3D_VC_none) {
+    _verify_contents = P3D_VC_normal;
+  }
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::usage
 //     Function: Panda3D::usage
 //       Access: Protected
 //       Access: Protected

+ 2 - 0
direct/src/plugin_standalone/panda3d.h

@@ -42,12 +42,14 @@ public:
 protected:
 protected:
   bool post_arg_processing();
   bool post_arg_processing();
   bool get_plugin();
   bool get_plugin();
+  bool download_contents_file(const Filename &contents_filename);
   bool read_contents_file(const Filename &contents_filename, bool fresh_download);
   bool read_contents_file(const Filename &contents_filename, bool fresh_download);
   void find_host(TiXmlElement *xcontents);
   void find_host(TiXmlElement *xcontents);
   void read_xhost(TiXmlElement *xhost);
   void read_xhost(TiXmlElement *xhost);
   void add_mirror(string mirror_url);
   void add_mirror(string mirror_url);
   void choose_random_mirrors(vector_string &result, int num_mirrors);
   void choose_random_mirrors(vector_string &result, int num_mirrors);
   bool get_core_api();
   bool get_core_api();
+  bool download_core_api();
 
 
   void usage();
   void usage();
 
 

+ 5 - 5
direct/src/plugin_standalone/panda3dBase.cxx

@@ -342,7 +342,7 @@ make_parent_window() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3D_instance *Panda3DBase::
 P3D_instance *Panda3DBase::
 create_instance(const string &p3d, bool start_instance,
 create_instance(const string &p3d, bool start_instance,
-                char **args, int num_args, const int &p3d_offset) {
+                char **args, int num_args, int p3d_offset) {
   // Check to see if the p3d filename we were given is a URL, or a
   // Check to see if the p3d filename we were given is a URL, or a
   // local file.
   // local file.
   Filename p3d_filename = Filename::from_os_specific(p3d);
   Filename p3d_filename = Filename::from_os_specific(p3d);
@@ -356,9 +356,9 @@ create_instance(const string &p3d, bool start_instance,
 
 
     p3d_filename.make_absolute();
     p3d_filename.make_absolute();
     os_p3d_filename = p3d_filename.to_os_specific();
     os_p3d_filename = p3d_filename.to_os_specific();
-  } 
+  }
   if (is_local) {
   if (is_local) {
-    read_p3d_info(p3d_filename);
+    read_p3d_info(p3d_filename, p3d_offset);
   }
   }
 
 
   // Build up the token list.
   // Build up the token list.
@@ -454,9 +454,9 @@ delete_instance(P3D_instance *inst) {
 //               parameters (like width and height).
 //               parameters (like width and height).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool Panda3DBase::
 bool Panda3DBase::
-read_p3d_info(const Filename &p3d_filename) {
+read_p3d_info(const Filename &p3d_filename, int p3d_offset) {
   PT(Multifile) mf = new Multifile;
   PT(Multifile) mf = new Multifile;
-  if (!mf->open_read(p3d_filename)) {
+  if (!mf->open_read(p3d_filename, p3d_offset)) {
     return false;
     return false;
   }
   }
   int si = mf->find_subfile("p3d_info.xml");
   int si = mf->find_subfile("p3d_info.xml");

+ 3 - 2
direct/src/plugin_standalone/panda3dBase.h

@@ -49,10 +49,10 @@ protected:
 
 
   P3D_instance *
   P3D_instance *
   create_instance(const string &p3d, bool start_instance,
   create_instance(const string &p3d, bool start_instance,
-                  char **args, int num_args, const int &p3d_offset = 0);
+                  char **args, int num_args, int p3d_offset = 0);
   void delete_instance(P3D_instance *instance);
   void delete_instance(P3D_instance *instance);
 
 
-  bool read_p3d_info(const Filename &p3d_filename);
+  bool read_p3d_info(const Filename &p3d_filename, int p3d_offset = 0);
   bool parse_token(const char *arg);
   bool parse_token(const char *arg);
   bool parse_int_pair(const char *arg, int &x, int &y);
   bool parse_int_pair(const char *arg, int &x, int &y);
   string lookup_token(const string &keyword) const;
   string lookup_token(const string &keyword) const;
@@ -74,6 +74,7 @@ protected:
   string _host_url;
   string _host_url;
   string _root_dir;
   string _root_dir;
   string _host_dir;
   string _host_dir;
+  string _start_dir;
   string _log_dirname;
   string _log_dirname;
   string _log_basename;
   string _log_basename;
   string _this_platform;
   string _this_platform;

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

@@ -855,7 +855,7 @@ class Loader(DirectObject):
     def loadShader (self, shaderPath, okMissing = False):
     def loadShader (self, shaderPath, okMissing = False):
         shader = ShaderPool.loadShader (shaderPath)
         shader = ShaderPool.loadShader (shaderPath)
         if not shader and not okMissing:
         if not shader and not okMissing:
-            message = 'Could not shader file: %s' % (shaderPath)
+            message = 'Could not load shader file: %s' % (shaderPath)
             raise IOError, message
             raise IOError, message
         return shader
         return shader
 
 

+ 1 - 1
direct/src/showbase/PythonUtil.py

@@ -1993,7 +1993,7 @@ def pstatcollect(scope, level = None):
 
 
     try:
     try:
 
 
-        if not (__dev__ or config.GetBool('force-pstatcollect', 0)) or \
+        if not (__dev__ or ConfigVariableBool('force-pstatcollect', False)) or \
            not scope:
            not scope:
             return decorator
             return decorator
 
 

+ 8 - 6
direct/src/showbase/ShowBase.py

@@ -9,6 +9,8 @@ __all__ = ['ShowBase', 'WindowControls']
 
 
 from panda3d.core import *
 from panda3d.core import *
 from panda3d.direct import get_config_showbase, throw_new_frame, init_app_for_gui
 from panda3d.direct import get_config_showbase, throw_new_frame, init_app_for_gui
+from panda3d.direct import storeAccessibilityShortcutKeys, allowAccessibilityShortcutKeys
+
 
 
 # This needs to be available early for DirectGUI imports
 # This needs to be available early for DirectGUI imports
 import __builtin__ as builtins
 import __builtin__ as builtins
@@ -70,6 +72,8 @@ class ShowBase(DirectObject.DirectObject):
         if logStackDump or uploadStackDump:
         if logStackDump or uploadStackDump:
             ExceptionVarDump.install(logStackDump, uploadStackDump)
             ExceptionVarDump.install(logStackDump, uploadStackDump)
 
 
+        self.__autoGarbageLogging = self.__dev__ and self.config.GetBool('auto-garbage-logging', False)
+
         ## The directory containing the main Python file of this application.
         ## The directory containing the main Python file of this application.
         self.mainDir = ExecutionEnvironment.getEnvironmentVariable("MAIN_DIR")
         self.mainDir = ExecutionEnvironment.getEnvironmentVariable("MAIN_DIR")
 
 
@@ -289,7 +293,6 @@ class ShowBase(DirectObject.DirectObject):
         self.physicsMgrEnabled = 0
         self.physicsMgrEnabled = 0
         self.physicsMgrAngular = 0
         self.physicsMgrAngular = 0
 
 
-        self.createBaseAudioManagers()
         self.createStats()
         self.createStats()
 
 
         self.AppHasAudioFocus = 1
         self.AppHasAudioFocus = 1
@@ -377,6 +380,8 @@ class ShowBase(DirectObject.DirectObject):
         else:
         else:
             ShowBase.notify.info('__dev__ == %s' % __dev__)
             ShowBase.notify.info('__dev__ == %s' % __dev__)
 
 
+        self.createBaseAudioManagers()
+
         # set up recording of Functor creation stacks in __dev__
         # set up recording of Functor creation stacks in __dev__
         PythonUtil.recordFunctorCreationStacks()
         PythonUtil.recordFunctorCreationStacks()
 
 
@@ -526,9 +531,6 @@ class ShowBase(DirectObject.DirectObject):
             del self.winList
             del self.winList
             del self.pipe
             del self.pipe
 
 
-        vfs = VirtualFileSystem.getGlobalPtr()
-        vfs.unmountAll()
-
     def makeDefaultPipe(self, printPipeTypes = True):
     def makeDefaultPipe(self, printPipeTypes = True):
         """
         """
         Creates the default GraphicsPipe, which will be used to make
         Creates the default GraphicsPipe, which will be used to make
@@ -2660,7 +2662,7 @@ class ShowBase(DirectObject.DirectObject):
             if not properties.getOpen():
             if not properties.getOpen():
                 # If the user closes the main window, we should exit.
                 # If the user closes the main window, we should exit.
                 self.notify.info("User closed main window.")
                 self.notify.info("User closed main window.")
-                if __dev__ and config.GetBool('auto-garbage-logging', 0):
+                if self.__autoGarbageLogging:
                     GarbageReport.b_checkForGarbageLeaks()
                     GarbageReport.b_checkForGarbageLeaks()
                 self.userExit()
                 self.userExit()
 
 
@@ -2668,7 +2670,7 @@ class ShowBase(DirectObject.DirectObject):
                 self.mainWinForeground = 1
                 self.mainWinForeground = 1
             elif not properties.getForeground() and self.mainWinForeground:
             elif not properties.getForeground() and self.mainWinForeground:
                 self.mainWinForeground = 0
                 self.mainWinForeground = 0
-                if __dev__ and config.GetBool('auto-garbage-logging', 0):
+                if self.__autoGarbageLogging:
                     GarbageReport.b_checkForGarbageLeaks()
                     GarbageReport.b_checkForGarbageLeaks()
 
 
             if properties.getMinimized() and not self.mainWinMinimized:
             if properties.getMinimized() and not self.mainWinMinimized:

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

@@ -166,7 +166,7 @@ class Transitions:
             self.loadFade()
             self.loadFade()
             self.fade.reparentTo(aspect2d, FADE_SORT_INDEX)
             self.fade.reparentTo(aspect2d, FADE_SORT_INDEX)
             self.fade.setColor(self.alphaOn)
             self.fade.setColor(self.alphaOn)
-        elif base.config.GetBool('no-loading-screen',0):
+        elif ConfigVariableBool('no-loading-screen', False):
             if finishIval:
             if finishIval:
                 self.transitionIval = finishIval
                 self.transitionIval = finishIval
                 self.transitionIval.start()               
                 self.transitionIval.start()               

+ 60 - 49
direct/src/showbase/VFSImporter.py

@@ -1,7 +1,7 @@
 __all__ = ['register', 'sharedPackages',
 __all__ = ['register', 'sharedPackages',
            'reloadSharedPackage', 'reloadSharedPackages']
            'reloadSharedPackage', 'reloadSharedPackages']
 
 
-from panda3d.core import Filename, VirtualFileSystem, VirtualFileMountSystem, OFileStream, copyStream
+from panda3d._core import Filename, VirtualFileSystem, VirtualFileMountSystem, OFileStream, copyStream
 import sys
 import sys
 import marshal
 import marshal
 import imp
 import imp
@@ -25,12 +25,6 @@ sharedPackages = {}
 
 
 vfs = VirtualFileSystem.getGlobalPtr()
 vfs = VirtualFileSystem.getGlobalPtr()
 
 
-# Possible file types.
-FTPythonSource = 0
-FTPythonCompiled = 1
-FTExtensionModule = 2
-FTFrozenModule = 3
-
 compiledExtensions = [ 'pyc', 'pyo' ]
 compiledExtensions = [ 'pyc', 'pyo' ]
 if not __debug__:
 if not __debug__:
     # In optimized mode, we prefer loading .pyo files over .pyc files.
     # In optimized mode, we prefer loading .pyo files over .pyc files.
@@ -44,7 +38,10 @@ class VFSImporter:
     (among other places). """
     (among other places). """
 
 
     def __init__(self, path):
     def __init__(self, path):
-        self.dir_path = Filename.fromOsSpecific(path)
+        if isinstance(path, Filename):
+            self.dir_path = Filename(path)
+        else:
+            self.dir_path = Filename.fromOsSpecific(path)
 
 
     def find_module(self, fullname, path = None):
     def find_module(self, fullname, path = None):
         if path is None:
         if path is None:
@@ -60,7 +57,8 @@ class VFSImporter:
         filename.setExtension('py')
         filename.setExtension('py')
         vfile = vfs.getFile(filename, True)
         vfile = vfs.getFile(filename, True)
         if vfile:
         if vfile:
-            return VFSLoader(dir_path, vfile, filename, FTPythonSource)
+            return VFSLoader(dir_path, vfile, filename,
+                             desc=('.py', 'U', imp.PY_SOURCE))
 
 
         # If there's no .py file, but there's a .pyc file, load that
         # If there's no .py file, but there's a .pyc file, load that
         # anyway.
         # anyway.
@@ -69,33 +67,32 @@ class VFSImporter:
             filename.setExtension(ext)
             filename.setExtension(ext)
             vfile = vfs.getFile(filename, True)
             vfile = vfs.getFile(filename, True)
             if vfile:
             if vfile:
-                return VFSLoader(dir_path, vfile, filename, FTPythonCompiled)
+                return VFSLoader(dir_path, vfile, filename,
+                                 desc=('.'+ext, 'rb', imp.PY_COMPILED))
 
 
         # Look for a C/C++ extension module.
         # Look for a C/C++ extension module.
         for desc in imp.get_suffixes():
         for desc in imp.get_suffixes():
             if desc[2] != imp.C_EXTENSION:
             if desc[2] != imp.C_EXTENSION:
                 continue
                 continue
 
 
-            filename = Filename(path)
-            filename.setExtension(desc[0][1:])
+            filename = Filename(path + desc[0])
             vfile = vfs.getFile(filename, True)
             vfile = vfs.getFile(filename, True)
             if vfile:
             if vfile:
-                return VFSLoader(dir_path, vfile, filename, FTExtensionModule,
-                                 desc = desc)
+                return VFSLoader(dir_path, vfile, filename, desc=desc)
 
 
         # Finally, consider a package, i.e. a directory containing
         # Finally, consider a package, i.e. a directory containing
         # __init__.py.
         # __init__.py.
         filename = Filename(path, '__init__.py')
         filename = Filename(path, '__init__.py')
         vfile = vfs.getFile(filename, True)
         vfile = vfs.getFile(filename, True)
         if vfile:
         if vfile:
-            return VFSLoader(dir_path, vfile, filename, FTPythonSource,
-                             packagePath = path)
+            return VFSLoader(dir_path, vfile, filename, packagePath=path,
+                             desc=('.py', 'U', imp.PY_SOURCE))
         for ext in compiledExtensions:
         for ext in compiledExtensions:
             filename = Filename(path, '__init__.' + ext)
             filename = Filename(path, '__init__.' + ext)
             vfile = vfs.getFile(filename, True)
             vfile = vfs.getFile(filename, True)
             if vfile:
             if vfile:
-                return VFSLoader(dir_path, vfile, filename, FTPythonCompiled,
-                                 packagePath = path)
+                return VFSLoader(dir_path, vfile, filename, packagePath=path,
+                                 desc=('.'+ext, 'rb', imp.PY_COMPILED))
 
 
         #print >>sys.stderr, "not found."
         #print >>sys.stderr, "not found."
         return None
         return None
@@ -104,22 +101,20 @@ class VFSLoader:
     """ The second part of VFSImporter, this is created for a
     """ The second part of VFSImporter, this is created for a
     particular .py file or directory. """
     particular .py file or directory. """
 
 
-    def __init__(self, dir_path, vfile, filename, fileType,
-                 desc = None, packagePath = None):
+    def __init__(self, dir_path, vfile, filename, desc, packagePath=None):
         self.dir_path = dir_path
         self.dir_path = dir_path
         self.timestamp = None
         self.timestamp = None
         if vfile:
         if vfile:
             self.timestamp = vfile.getTimestamp()
             self.timestamp = vfile.getTimestamp()
         self.filename = filename
         self.filename = filename
-        self.fileType = fileType
         self.desc = desc
         self.desc = desc
         self.packagePath = packagePath
         self.packagePath = packagePath
 
 
     def load_module(self, fullname, loadingShared = False):
     def load_module(self, fullname, loadingShared = False):
         #print >>sys.stderr, "load_module(%s), dir_path = %s, filename = %s" % (fullname, self.dir_path, self.filename)
         #print >>sys.stderr, "load_module(%s), dir_path = %s, filename = %s" % (fullname, self.dir_path, self.filename)
-        if self.fileType == FTFrozenModule:
+        if self.desc[2] == imp.PY_FROZEN:
             return self._import_frozen_module(fullname)
             return self._import_frozen_module(fullname)
-        if self.fileType == FTExtensionModule:
+        if self.desc[2] == imp.C_EXTENSION:
             return self._import_extension_module(fullname)
             return self._import_extension_module(fullname)
 
 
         # Check if this is a child of a shared package.
         # Check if this is a child of a shared package.
@@ -137,7 +132,7 @@ class VFSLoader:
 
 
         code = self._read_code()
         code = self._read_code()
         if not code:
         if not code:
-            raise ImportError, 'No Python code in %s' % (fullname)
+            raise ImportError('No Python code in %s' % (fullname))
 
 
         mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
         mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
         mod.__file__ = self.filename.toOsSpecific()
         mod.__file__ = self.filename.toOsSpecific()
@@ -153,7 +148,7 @@ class VFSLoader:
         path = Filename(self.dir_path, Filename.fromOsSpecific(path))
         path = Filename(self.dir_path, Filename.fromOsSpecific(path))
         vfile = vfs.getFile(path)
         vfile = vfs.getFile(path)
         if not vfile:
         if not vfile:
-            raise IOError
+            raise IOError("Could not find '%s'" % (path))
         return vfile.readFile(True)
         return vfile.readFile(True)
 
 
     def is_package(self, fullname):
     def is_package(self, fullname):
@@ -172,8 +167,8 @@ class VFSLoader:
         """ Returns the Python source for this file, if it is
         """ Returns the Python source for this file, if it is
         available, or None if it is not.  May raise IOError. """
         available, or None if it is not.  May raise IOError. """
 
 
-        if self.fileType == FTPythonCompiled or \
-           self.fileType == FTExtensionModule:
+        if self.desc[2] == imp.PY_COMPILED or \
+           self.desc[2] == imp.C_EXTENSION:
             return None
             return None
 
 
         filename = Filename(self.filename)
         filename = Filename(self.filename)
@@ -181,7 +176,7 @@ class VFSLoader:
         filename.setText()
         filename.setText()
         vfile = vfs.getFile(filename)
         vfile = vfs.getFile(filename)
         if not vfile:
         if not vfile:
-            raise IOError
+            raise IOError("Could not find '%s'" % (filename))
         return vfile.readFile(True)
         return vfile.readFile(True)
 
 
     def _import_extension_module(self, fullname):
     def _import_extension_module(self, fullname):
@@ -231,6 +226,10 @@ class VFSLoader:
         #print >>sys.stderr, "importing frozen %s" % (fullname)
         #print >>sys.stderr, "importing frozen %s" % (fullname)
         module = imp.load_module(fullname, None, fullname,
         module = imp.load_module(fullname, None, fullname,
                                  ('', '', imp.PY_FROZEN))
                                  ('', '', imp.PY_FROZEN))
+
+        # Workaround for bug in Python 2.
+        if getattr(module, '__path__', None) == fullname:
+            module.__path__ = []
         return module
         return module
 
 
     def _read_code(self):
     def _read_code(self):
@@ -239,14 +238,14 @@ class VFSLoader:
         ValueError, SyntaxError, or a number of other errors generated
         ValueError, SyntaxError, or a number of other errors generated
         by the low-level system. """
         by the low-level system. """
 
 
-        if self.fileType == FTPythonCompiled:
+        if self.desc[2] == imp.PY_COMPILED:
             # It's a pyc file; just read it directly.
             # It's a pyc file; just read it directly.
             pycVfile = vfs.getFile(self.filename, False)
             pycVfile = vfs.getFile(self.filename, False)
             if pycVfile:
             if pycVfile:
                 return self._loadPyc(pycVfile, None)
                 return self._loadPyc(pycVfile, None)
-            raise IOError, 'Could not read %s' % (self.filename)
+            raise IOError('Could not read %s' % (self.filename))
 
 
-        elif self.fileType == FTExtensionModule:
+        elif self.desc[2] == imp.C_EXTENSION:
             return None
             return None
 
 
         # It's a .py file (or an __init__.py file; same thing).  Read
         # It's a .py file (or an __init__.py file; same thing).  Read
@@ -282,16 +281,21 @@ class VFSLoader:
 
 
         code = None
         code = None
         data = vfile.readFile(True)
         data = vfile.readFile(True)
-        if data[:4] == imp.get_magic():
+        if data[:4] != imp.get_magic():
+            raise ValueError("Bad magic number in %s" % (vfile))
+
+        if sys.version_info >= (3, 0):
+            t = int.from_bytes(data[4:8], 'little')
+            data = data[12:]
+        else:
             t = ord(data[4]) + (ord(data[5]) << 8) + \
             t = ord(data[4]) + (ord(data[5]) << 8) + \
                (ord(data[6]) << 16) + (ord(data[7]) << 24)
                (ord(data[6]) << 16) + (ord(data[7]) << 24)
-            if not timestamp or t == timestamp:
-                code = marshal.loads(data[8:])
-            else:
-                raise ValueError, 'Timestamp wrong on %s' % (vfile)
+            data = data[8:]
+
+        if not timestamp or t == timestamp:
+            return marshal.loads(data)
         else:
         else:
-            raise ValueError, 'Bad magic number in %s' % (vfile)
-        return code
+            raise ValueError("Timestamp wrong on %s" % (vfile))
 
 
 
 
     def _compile(self, filename, source):
     def _compile(self, filename, source):
@@ -311,15 +315,16 @@ class VFSLoader:
         except IOError:
         except IOError:
             pass
             pass
         else:
         else:
-            f.write('\0\0\0\0')
-            f.write(chr(self.timestamp & 0xff) +
-                    chr((self.timestamp >> 8) & 0xff) +
-                    chr((self.timestamp >> 16) & 0xff) +
-                    chr((self.timestamp >> 24) & 0xff))
-            f.write(marshal.dumps(code))
-            f.flush()
-            f.seek(0, 0)
             f.write(imp.get_magic())
             f.write(imp.get_magic())
+            if sys.version_info >= (3, 0):
+                f.write((self.timestamp & 0xffffffff).to_bytes(4, 'little'))
+                f.write(b'\0\0\0\0')
+            else:
+                f.write(chr(self.timestamp & 0xff) +
+                        chr((self.timestamp >> 8) & 0xff) +
+                        chr((self.timestamp >> 16) & 0xff) +
+                        chr((self.timestamp >> 24) & 0xff))
+            f.write(marshal.dumps(code))
             f.close()
             f.close()
 
 
         return code
         return code
@@ -389,7 +394,7 @@ class VFSSharedImporter:
         """ Returns the directory name that the indicated
         """ Returns the directory name that the indicated
         conventionally-loaded module must have been loaded from. """
         conventionally-loaded module must have been loaded from. """
 
 
-        if not hasattr(mod, __file__) or mod.__file__ is None:
+        if not getattr(mod, '__file__', None):
             return None
             return None
 
 
         fullname = mod.__name__
         fullname = mod.__name__
@@ -434,6 +439,9 @@ class VFSSharedLoader:
         if self.reload:
         if self.reload:
             mod = sys.modules[fullname]
             mod = sys.modules[fullname]
             path = mod.__path__ or []
             path = mod.__path__ or []
+            if path == fullname:
+                # Work around Python bug setting __path__ of frozen modules.
+                path = []
             vfs_shared_path = getattr(mod, '_vfs_shared_path', [])
             vfs_shared_path = getattr(mod, '_vfs_shared_path', [])
 
 
         for loader in self.loaders:
         for loader in self.loaders:
@@ -451,11 +459,12 @@ class VFSSharedLoader:
 
 
         if mod is None:
         if mod is None:
             # If all of them failed to load, raise ImportError.
             # If all of them failed to load, raise ImportError.
-            raise ImportError, message
+            raise ImportError(message)
 
 
         # If at least one of them loaded successfully, return the
         # If at least one of them loaded successfully, return the
         # union of loaded modules.
         # union of loaded modules.
         mod.__path__ = path
         mod.__path__ = path
+        mod.__package__ = fullname
 
 
         # Also set this special symbol, which records that this is a
         # Also set this special symbol, which records that this is a
         # shared package, and also lists the paths we have already
         # shared package, and also lists the paths we have already
@@ -516,7 +525,9 @@ def reloadSharedPackages():
 
 
     #print >> sys.stderr, "reloadSharedPackages, path = %s, sharedPackages = %s" % (sys.path, sharedPackages.keys())
     #print >> sys.stderr, "reloadSharedPackages, path = %s, sharedPackages = %s" % (sys.path, sharedPackages.keys())
 
 
-    for fullname in sharedPackages.keys():
+    # Sort the list, just to make sure parent packages are reloaded
+    # before child packages are.
+    for fullname in sorted(sharedPackages.keys()):
         mod = sys.modules.get(fullname, None)
         mod = sys.modules.get(fullname, None)
         if not mod:
         if not mod:
             continue
             continue

+ 92 - 91
direct/src/showutil/FreezeTool.py

@@ -8,7 +8,8 @@ import marshal
 import imp
 import imp
 import platform
 import platform
 import types
 import types
-from distutils.sysconfig import PREFIX, get_python_inc, get_python_version
+from StringIO import StringIO
+from distutils.sysconfig import PREFIX, get_python_inc, get_python_version, get_config_var
 
 
 # Temporary (?) try..except to protect against unbuilt p3extend_frozen.
 # Temporary (?) try..except to protect against unbuilt p3extend_frozen.
 try:
 try:
@@ -17,7 +18,6 @@ except ImportError:
     p3extend_frozen = None
     p3extend_frozen = None
 
 
 from panda3d.core import *
 from panda3d.core import *
-from pandac.extension_native_helpers import dll_suffix, dll_ext
 
 
 # Check to see if we are running python_d, which implies we have a
 # Check to see if we are running python_d, which implies we have a
 # debug build, and we have to build the module with debug options.
 # debug build, and we have to build the module with debug options.
@@ -31,14 +31,12 @@ isDebugBuild = (python.lower().endswith('_d'))
 # must be frozen in any main.exe.
 # must be frozen in any main.exe.
 startupModules = [
 startupModules = [
     'site', 'sitecustomize', 'os', 'encodings.cp1252',
     'site', 'sitecustomize', 'os', 'encodings.cp1252',
-    'org',
+    'encodings.latin_1', 'encodings.utf_8', 'io', 'org',
     ]
     ]
 
 
 # These are missing modules that we've reported already this session.
 # These are missing modules that we've reported already this session.
 reportedMissing = {}
 reportedMissing = {}
 
 
-# Our own Python source trees to watch out for.
-sourceTrees = ['direct']
 
 
 class CompilationEnvironment:
 class CompilationEnvironment:
     """ Create an instance of this class to record the commands to
     """ Create an instance of this class to record the commands to
@@ -64,7 +62,7 @@ class CompilationEnvironment:
         # Paths to Python stuff.
         # Paths to Python stuff.
         self.Python = None
         self.Python = None
         self.PythonIPath = get_python_inc()
         self.PythonIPath = get_python_inc()
-        self.PythonVersion = get_python_version()
+        self.PythonVersion = get_config_var("LDVERSION") or get_python_version()
 
 
         # The VC directory of Microsoft Visual Studio (if relevant)
         # The VC directory of Microsoft Visual Studio (if relevant)
         self.MSVC = None
         self.MSVC = None
@@ -445,7 +443,7 @@ extend_frozen_modules(const struct _frozen *new_modules, int new_count) {
 }
 }
 
 
 %(dllexport)svoid init%(moduleName)s() {
 %(dllexport)svoid init%(moduleName)s() {
-  extend_frozen_modules(_PyImport_FrozenModules, %(newcount)s);
+  extend_frozen_modules(_PyImport_FrozenModules, sizeof(_PyImport_FrozenModules) / sizeof(struct _frozen));
   Py_InitModule("%(moduleName)s", nullMethods);
   Py_InitModule("%(moduleName)s", nullMethods);
 }
 }
 """
 """
@@ -458,7 +456,7 @@ programFile = """
 
 
 %(moduleDefs)s
 %(moduleDefs)s
 
 
-static struct _frozen _PyImport_FrozenModules[] = {
+struct _frozen _PyImport_FrozenModules[] = {
 %(moduleList)s
 %(moduleList)s
   {NULL, NULL, 0}
   {NULL, NULL, 0}
 };
 };
@@ -485,7 +483,7 @@ okMissing = [
     'Carbon.Folder', 'Carbon.Folders', 'HouseGlobals', 'Carbon.File',
     'Carbon.Folder', 'Carbon.Folders', 'HouseGlobals', 'Carbon.File',
     'MacOS', '_emx_link', 'ce', 'mac', 'org.python.core', 'os.path',
     'MacOS', '_emx_link', 'ce', 'mac', 'org.python.core', 'os.path',
     'os2', 'posix', 'pwd', 'readline', 'riscos', 'riscosenviron',
     'os2', 'posix', 'pwd', 'readline', 'riscos', 'riscosenviron',
-    'riscospath', 'dbm', 'fcntl', 'win32api',
+    'riscospath', 'dbm', 'fcntl', 'win32api', 'usercustomize',
     '_winreg', 'ctypes', 'ctypes.wintypes', 'nt','msvcrt',
     '_winreg', 'ctypes', 'ctypes.wintypes', 'nt','msvcrt',
     'EasyDialogs', 'SOCKS', 'ic', 'rourl2path', 'termios',
     'EasyDialogs', 'SOCKS', 'ic', 'rourl2path', 'termios',
     'OverrideFrom23._Res', 'email', 'email.Utils', 'email.Generator',
     'OverrideFrom23._Res', 'email', 'email.Utils', 'email.Generator',
@@ -498,7 +496,8 @@ class Freezer:
         def __init__(self, moduleName, filename = None,
         def __init__(self, moduleName, filename = None,
                      implicit = False, guess = False,
                      implicit = False, guess = False,
                      exclude = False, forbid = False,
                      exclude = False, forbid = False,
-                     allowChildren = False, fromSource = None):
+                     allowChildren = False, fromSource = None,
+                     text = None):
             # The Python module name.
             # The Python module name.
             self.moduleName = moduleName
             self.moduleName = moduleName
 
 
@@ -533,6 +532,9 @@ class Freezer:
             # record came from, supplied by the caller.
             # record came from, supplied by the caller.
             self.fromSource = fromSource
             self.fromSource = fromSource
 
 
+            # If this is set, it contains Python code of the module.
+            self.text = text
+
             # Some sanity checks.
             # Some sanity checks.
             if not self.exclude:
             if not self.exclude:
                 self.allowChildren = True
                 self.allowChildren = True
@@ -609,20 +611,14 @@ class Freezer:
 
 
         self.mf = None
         self.mf = None
 
 
-        # Make sure we know how to find "direct".
-        for sourceTree in sourceTrees:
-            try:
-                module = __import__(sourceTree)
-            except:
-                pass
-
         # Actually, make sure we know how to find all of the
         # Actually, make sure we know how to find all of the
         # already-imported modules.  (Some of them might do their own
         # already-imported modules.  (Some of them might do their own
         # special path mangling.)
         # special path mangling.)
         for moduleName, module in sys.modules.items():
         for moduleName, module in sys.modules.items():
             if module and hasattr(module, '__path__'):
             if module and hasattr(module, '__path__'):
                 path = getattr(module, '__path__')
                 path = getattr(module, '__path__')
-                modulefinder.AddPackagePath(moduleName, path[0])
+                if path:
+                    modulefinder.AddPackagePath(moduleName, path[0])
 
 
     def excludeFrom(self, freezer):
     def excludeFrom(self, freezer):
         """ Excludes all modules that have already been processed by
         """ Excludes all modules that have already been processed by
@@ -749,7 +745,8 @@ class Freezer:
         return modules
         return modules
 
 
     def addModule(self, moduleName, implicit = False, newName = None,
     def addModule(self, moduleName, implicit = False, newName = None,
-                  filename = None, guess = False, fromSource = None):
+                  filename = None, guess = False, fromSource = None,
+                  text = None):
         """ Adds a module to the list of modules to be exported by
         """ Adds a module to the list of modules to be exported by
         this tool.  If implicit is true, it is OK if the module does
         this tool.  If implicit is true, it is OK if the module does
         not actually exist.
         not actually exist.
@@ -805,7 +802,7 @@ class Freezer:
                     # It's actually a regular module.
                     # It's actually a regular module.
                     self.modules[newParentName] = self.ModuleDef(
                     self.modules[newParentName] = self.ModuleDef(
                         parentName, implicit = implicit, guess = guess,
                         parentName, implicit = implicit, guess = guess,
-                        fromSource = fromSource)
+                        fromSource = fromSource, text = text)
 
 
                 else:
                 else:
                     # Now get all the py files in the parent directory.
                     # Now get all the py files in the parent directory.
@@ -820,9 +817,9 @@ class Freezer:
             # A normal, explicit module name.
             # A normal, explicit module name.
             self.modules[newName] = self.ModuleDef(
             self.modules[newName] = self.ModuleDef(
                 moduleName, filename = filename, implicit = implicit,
                 moduleName, filename = filename, implicit = implicit,
-                guess = guess, fromSource = fromSource)
+                guess = guess, fromSource = fromSource, text = text)
 
 
-    def done(self, compileToExe = False):
+    def done(self, addStartupModules = False):
         """ Call this method after you have added all modules with
         """ Call this method after you have added all modules with
         addModule().  You may then call generateCode() or
         addModule().  You may then call generateCode() or
         writeMultifile() to dump the resulting output.  After a call
         writeMultifile() to dump the resulting output.  After a call
@@ -833,7 +830,9 @@ class Freezer:
 
 
         # If we are building an exe, we also need to implicitly
         # If we are building an exe, we also need to implicitly
         # bring in Python's startup modules.
         # bring in Python's startup modules.
-        if compileToExe:
+        if addStartupModules:
+            self.modules['_frozen_importlib'] = self.ModuleDef('importlib._bootstrap', implicit = True)
+
             for moduleName in startupModules:
             for moduleName in startupModules:
                 if moduleName not in self.modules:
                 if moduleName not in self.modules:
                     self.modules[moduleName] = self.ModuleDef(moduleName, implicit = True)
                     self.modules[moduleName] = self.ModuleDef(moduleName, implicit = True)
@@ -971,7 +970,10 @@ class Freezer:
                 stuff = ("", "rb", imp.PY_COMPILED)
                 stuff = ("", "rb", imp.PY_COMPILED)
                 self.mf.load_module(mdef.moduleName, fp, pathname, stuff)
                 self.mf.load_module(mdef.moduleName, fp, pathname, stuff)
             else:
             else:
-                fp = open(pathname, modulefinder.READ_MODE)
+                if mdef.text:
+                    fp = StringIO(mdef.text)
+                else:
+                    fp = open(pathname, 'U')
                 stuff = ("", "r", imp.PY_SOURCE)
                 stuff = ("", "r", imp.PY_SOURCE)
                 self.mf.load_module(mdef.moduleName, fp, pathname, stuff)
                 self.mf.load_module(mdef.moduleName, fp, pathname, stuff)
 
 
@@ -1063,8 +1065,12 @@ class Freezer:
 
 
     def __addPyc(self, multifile, filename, code, compressionLevel):
     def __addPyc(self, multifile, filename, code, compressionLevel):
         if code:
         if code:
-            data = imp.get_magic() + '\0\0\0\0' + \
-                   marshal.dumps(code)
+            data = imp.get_magic() + b'\0\0\0\0'
+
+            if sys.version_info >= (3, 0):
+                data += b'\0\0\0\0'
+
+            data += marshal.dumps(code)
 
 
             stream = StringStream(data)
             stream = StringStream(data)
             multifile.addSubfile(filename, stream, compressionLevel)
             multifile.addSubfile(filename, stream, compressionLevel)
@@ -1206,22 +1212,9 @@ class Freezer:
         multifile.flush()
         multifile.flush()
         multifile.repack()
         multifile.repack()
 
 
-    def generateCode(self, basename, compileToExe = False):
-        """ After a call to done(), this freezes all of the
-        accumulated python code into either an executable program (if
-        compileToExe is true) or a dynamic library (if compileToExe is
-        false).  The basename is the name of the file to write,
-        without the extension.
-
-        The return value is the newly-generated filename, including
-        the filename extension.  Additional extension modules are
-        listed in self.extras. """
-
-        if compileToExe:
-            # We must have a __main__ module to make an exe file.
-            if not self.__writingModule('__main__'):
-                message = "Can't generate an executable without a __main__ module."
-                raise StandardError, message
+    def writeCode(self, filename, initCode = ""):
+        """ After a call to done(), this freezes all of the accumulated
+        Python code into a C source file. """
 
 
         self.__replacePaths()
         self.__replacePaths()
 
 
@@ -1239,38 +1232,57 @@ class Freezer:
                 # Allow importing this module.
                 # Allow importing this module.
                 module = self.mf.modules.get(origName, None)
                 module = self.mf.modules.get(origName, None)
                 code = getattr(module, "__code__", None)
                 code = getattr(module, "__code__", None)
-                if not code and moduleName in startupModules:
+                if code:
+                    code = marshal.dumps(code)
+
+                    mangledName = self.mangleName(moduleName)
+                    moduleDefs.append(self.makeModuleDef(mangledName, code))
+                    moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
+
+                elif moduleName in startupModules:
                     # Forbid the loading of this startup module.
                     # Forbid the loading of this startup module.
                     moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
                     moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
+
                 else:
                 else:
-                    if origName in sourceTrees:
-                        # This is one of Panda3D's own Python source
-                        # trees.  These are a special case: we don't
-                        # compile the __init__.py files within them,
-                        # since their only purpose is to munge the
-                        # __path__ variable anyway.  Instead, we
-                        # pretend the __init__.py files are empty.
-                        code = compile('', moduleName, 'exec')
-
-                    if code:
-                        code = marshal.dumps(code)
-
-                        mangledName = self.mangleName(moduleName)
-                        moduleDefs.append(self.makeModuleDef(mangledName, code))
-                        moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
+                    # This is a module with no associated Python
+                    # code.  It must be an extension module.  Get the
+                    # filename.
+                    extensionFilename = getattr(module, '__file__', None)
+                    if extensionFilename:
+                        self.extras.append((moduleName, extensionFilename))
                     else:
                     else:
+                        # It doesn't even have a filename; it must
+                        # be a built-in module.  No worries about
+                        # this one, then.
+                        pass
 
 
-                        # This is a module with no associated Python
-                        # code.  It must be an extension module.  Get the
-                        # filename.
-                        extensionFilename = getattr(module, '__file__', None)
-                        if extensionFilename:
-                            self.extras.append((moduleName, extensionFilename))
-                        else:
-                            # It doesn't even have a filename; it must
-                            # be a built-in module.  No worries about
-                            # this one, then.
-                            pass
+        text = programFile % {
+            'moduleDefs': '\n'.join(moduleDefs),
+            'moduleList': '\n'.join(moduleList),
+            'initCode': initCode
+            }
+
+        if filename is not None:
+            file = open(filename, 'w')
+            file.write(text)
+            file.close()
+
+    def generateCode(self, basename, compileToExe = False):
+        """ After a call to done(), this freezes all of the
+        accumulated python code into either an executable program (if
+        compileToExe is true) or a dynamic library (if compileToExe is
+        false).  The basename is the name of the file to write,
+        without the extension.
+
+        The return value is the newly-generated filename, including
+        the filename extension.  Additional extension modules are
+        listed in self.extras. """
+
+        if compileToExe:
+            # We must have a __main__ module to make an exe file.
+            if not self.__writingModule('__main__'):
+                message = "Can't generate an executable without a __main__ module."
+                raise StandardError, message
 
 
         filename = basename + self.sourceExtension
         filename = basename + self.sourceExtension
 
 
@@ -1309,21 +1321,12 @@ class Freezer:
 
 
             initCode = dllInitCode % {
             initCode = dllInitCode % {
                 'moduleName' : os.path.basename(basename),
                 'moduleName' : os.path.basename(basename),
-                'newcount' : len(moduleList),
                 'dllexport' : dllexport,
                 'dllexport' : dllexport,
                 'dllimport' : dllimport,
                 'dllimport' : dllimport,
                 }
                 }
             compileFunc = self.cenv.compileDll
             compileFunc = self.cenv.compileDll
 
 
-        text = programFile % {
-            'moduleDefs' : '\n'.join(moduleDefs),
-            'moduleList' : '\n'.join(moduleList),
-            'initCode' : initCode,
-            }
-
-        file = open(filename, 'w')
-        file.write(text)
-        file.close()
+        self.writeCode(filename, initCode=initCode)
 
 
         try:
         try:
             compileFunc(filename, basename)
             compileFunc(filename, basename)
@@ -1342,7 +1345,10 @@ class Freezer:
         for i in range(0, len(code), 16):
         for i in range(0, len(code), 16):
             result += '\n  '
             result += '\n  '
             for c in code[i:i+16]:
             for c in code[i:i+16]:
-                result += ('%d,' % ord(c))
+                if isinstance(c, int): # Python 3
+                    result += ('%d,' % c)
+                else: # Python 2
+                    result += ('%d,' % ord(c))
         result += '\n};\n'
         result += '\n};\n'
         return result
         return result
 
 
@@ -1379,9 +1385,13 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
     def __init__(self, *args, **kw):
     def __init__(self, *args, **kw):
         modulefinder.ModuleFinder.__init__(self, *args, **kw)
         modulefinder.ModuleFinder.__init__(self, *args, **kw)
 
 
-    def find_module(self, name, path, parent=None):
+    def find_module(self, name, path, *args, **kwargs):
+        if imp.is_frozen(name):
+            # Don't pick up modules that are frozen into p3dpython.
+            raise ImportError("'%s' is a frozen module" % (name))
+
         try:
         try:
-            return modulefinder.ModuleFinder.find_module(self, name, path, parent = parent)
+            return modulefinder.ModuleFinder.find_module(self, name, path, *args, **kwargs)
         except ImportError:
         except ImportError:
             # It wasn't found through the normal channels.  Maybe it's
             # It wasn't found through the normal channels.  Maybe it's
             # one of ours, or maybe it's frozen?
             # one of ours, or maybe it's frozen?
@@ -1394,15 +1404,6 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
                 # It's a frozen module.
                 # It's a frozen module.
                 return (None, name, ('', '', imp.PY_FROZEN))
                 return (None, name, ('', '', imp.PY_FROZEN))
 
 
-        # Look for a dtool extension.  This loop is roughly lifted
-        # from extension_native_helpers.Dtool_PreloadDLL().
-        filename = name + dll_suffix + dll_ext
-        for dir in sys.path + [sys.prefix]:
-            lib = os.path.join(dir, filename)
-            if os.path.exists(lib):
-                file = open(lib, 'rb')
-                return (file, lib, (dll_ext, 'rb', imp.C_EXTENSION))
-
         message = "DLL loader cannot find %s." % (name)
         message = "DLL loader cannot find %s." % (name)
         raise ImportError, message
         raise ImportError, message
 
 

+ 5 - 1
direct/src/showutil/Rope.py

@@ -10,7 +10,8 @@ class Rope(NodePath):
     thick lines built from triangle strips.
     thick lines built from triangle strips.
     """
     """
 
 
-    showRope = base.config.GetBool('show-rope', 1)
+    showRope = ConfigVariableBool('show-rope', True, \
+      "Set this to false to deactivate the display of ropes.")
     
     
     def __init__(self, name = 'Rope'):
     def __init__(self, name = 'Rope'):
         self.ropeNode = RopeNode(name)
         self.ropeNode = RopeNode(name)
@@ -18,6 +19,9 @@ class Rope(NodePath):
         self.ropeNode.setCurve(self.curve)
         self.ropeNode.setCurve(self.curve)
         NodePath.__init__(self, self.ropeNode)
         NodePath.__init__(self, self.ropeNode)
         self.name = name
         self.name = name
+        self.order = 0
+        self.verts = []
+        self.knots = None
         
         
     def setup(self, order, verts, knots = None):
     def setup(self, order, verts, knots = None):
         """This must be called to define the shape of the curve
         """This must be called to define the shape of the curve

+ 32 - 17
direct/src/showutil/pfreeze.py

@@ -40,6 +40,11 @@ Options:
      of the __path__ variable, and thus must be actually imported to
      of the __path__ variable, and thus must be actually imported to
      determine the true value of __path__.
      determine the true value of __path__.
 
 
+  -s
+     Adds the standard set of modules that are necessary for embedding
+     the Python interpreter.  Implicitly set if an executable is
+     generated.
+
 """
 """
 
 
 import getopt
 import getopt
@@ -58,9 +63,10 @@ def usage(code, msg = ''):
 freezer = FreezeTool.Freezer()
 freezer = FreezeTool.Freezer()
 
 
 basename = None
 basename = None
+addStartupModules = False
 
 
 try:
 try:
-    opts, args = getopt.getopt(sys.argv[1:], 'o:i:x:p:h')
+    opts, args = getopt.getopt(sys.argv[1:], 'o:i:x:p:sh')
 except getopt.error, msg:
 except getopt.error, msg:
     usage(1, msg)
     usage(1, msg)
 
 
@@ -76,48 +82,57 @@ for opt, arg in opts:
     elif opt == '-p':
     elif opt == '-p':
         for module in arg.split(','):
         for module in arg.split(','):
             freezer.handleCustomPath(module)
             freezer.handleCustomPath(module)
+    elif opt == '-s':
+        addStartupModules = True
     elif opt == '-h':
     elif opt == '-h':
         usage(0)
         usage(0)
     else:
     else:
         print 'illegal option: ' + flag
         print 'illegal option: ' + flag
         sys.exit(1)
         sys.exit(1)
 
 
-if not args:
-    usage(0)
-
 if not basename:
 if not basename:
     usage(1, 'You did not specify an output file.')
     usage(1, 'You did not specify an output file.')
 
 
-if len(args) != 1:
+if len(args) > 1:
     usage(1, 'Only one main file may be specified.')
     usage(1, 'Only one main file may be specified.')
 
 
 outputType = 'exe'
 outputType = 'exe'
 bl = basename.lower()
 bl = basename.lower()
 if bl.endswith('.mf'):
 if bl.endswith('.mf'):
     outputType = 'mf'
     outputType = 'mf'
+elif bl.endswith('.c'):
+    outputType = 'c'
 elif bl.endswith('.dll') or bl.endswith('.pyd') or bl.endswith('.so'):
 elif bl.endswith('.dll') or bl.endswith('.pyd') or bl.endswith('.so'):
     basename = os.path.splitext(basename)[0]
     basename = os.path.splitext(basename)[0]
     outputType = 'dll'
     outputType = 'dll'
 elif bl.endswith('.exe'):
 elif bl.endswith('.exe'):
     basename = os.path.splitext(basename)[0]
     basename = os.path.splitext(basename)[0]
 
 
-startfile = args[0]
-startmod = startfile
-if startfile.endswith('.py') or startfile.endswith('.pyw') or \
-   startfile.endswith('.pyc') or startfile.endswith('.pyo'):
-    startmod = os.path.splitext(startfile)[0]
-
 compileToExe = False
 compileToExe = False
-if outputType == 'dll':
-    freezer.addModule(startmod, filename = startfile)
-else:
-    freezer.addModule('__main__', filename = startfile)
-    compileToExe = True
+if args:
+    startfile = args[0]
+    startmod = startfile
+    if startfile.endswith('.py') or startfile.endswith('.pyw') or \
+       startfile.endswith('.pyc') or startfile.endswith('.pyo'):
+        startmod = os.path.splitext(startfile)[0]
+
+    if outputType == 'dll' or outputType == 'c':
+        freezer.addModule(startmod, filename = startfile)
+    else:
+        freezer.addModule('__main__', filename = startfile)
+        compileToExe = True
+        addStartupModules = True
+
+elif outputType == 'exe':
+    # We must have a main module when making an executable.
+    usage(0)
 
 
-freezer.done(compileToExe = compileToExe)
+freezer.done(addStartupModules = addStartupModules)
 
 
 if outputType == 'mf':
 if outputType == 'mf':
     freezer.writeMultifile(basename)
     freezer.writeMultifile(basename)
+elif outputType == 'c':
+    freezer.writeCode(basename)
 else:
 else:
     freezer.generateCode(basename, compileToExe = compileToExe)
     freezer.generateCode(basename, compileToExe = compileToExe)
 
 

+ 213 - 203
direct/src/stdpy/file.py

@@ -5,150 +5,208 @@ SIMPLE_THREADS model, by avoiding blocking all threads while waiting
 for I/O to complete. """
 for I/O to complete. """
 
 
 __all__ = [
 __all__ = [
-    'file', 'open', 'listdir', 'walk', 'join',
+    'open', 'listdir', 'walk', 'join',
     'isfile', 'isdir', 'exists', 'lexists', 'getmtime', 'getsize',
     'isfile', 'isdir', 'exists', 'lexists', 'getmtime', 'getsize',
     'execfile',
     'execfile',
     ]
     ]
 
 
-from panda3d import core
-import types
+import panda3d._core as core
+import sys
+import os
+import io
+import encodings
+from posixpath import join
 
 
 _vfs = core.VirtualFileSystem.getGlobalPtr()
 _vfs = core.VirtualFileSystem.getGlobalPtr()
 
 
-class file:
-    def __init__(self, filename, mode = 'r', bufsize = None,
-                 autoUnwrap = False):
-        self.__stream = None
-        self.__needsVfsClose = False
-        self.__reader = None
-        self.__writer = None
-        self.closed = True
-        self.encoding = None
-        self.errors = None
-        self.__lastWrite = False
+if sys.version_info < (3, 0):
+    # Python 3 defines these subtypes of IOError, but Python 2 doesn't.
+    FileNotFoundError = IOError
+    IsADirectoryError = IOError
+    FileExistsError = IOError
+    PermissionError = IOError
+
+
+def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True):
+    if sys.version_info >= (3, 0):
+        # Python 3 is much stricter than Python 2, which lets
+        # unknown flags fall through.
+        for ch in mode:
+            if ch not in 'rwxabt+U':
+                raise ValueError("invalid mode: '%s'" % (mode))
+
+    creating = 'x' in mode
+    writing = 'w' in mode
+    appending = 'a' in mode
+    updating = '+' in mode
+    binary = 'b' in mode
+    universal = 'U' in mode
+    reading = universal or 'r' in mode
+
+    if binary and 't' in mode:
+        raise ValueError("can't have text and binary mode at once")
+
+    if creating + reading + writing + appending > 1:
+        raise ValueError("must have exactly one of create/read/write/append mode")
+
+    if binary:
+        if encoding:
+            raise ValueError("binary mode doesn't take an encoding argument")
+        if errors:
+            raise ValueError("binary mode doesn't take an errors argument")
+        if newline:
+            raise ValueError("binary mode doesn't take a newline argument")
+
+    if isinstance(file, core.Istream) or isinstance(file, core.Ostream):
+        # If we were given a stream instead of a filename, assign
+        # it directly.
+        raw = StreamIOWrapper(file)
+        raw.mode = mode
+
+    else:
+        vfile = None
+
+        if isinstance(file, core.VirtualFile):
+            # We can also "open" a VirtualFile object for reading.
+            vfile = file
+            filename = vfile.getFilename()
+        elif isinstance(file, unicode):
+            # If a raw string is given, assume it's an os-specific
+            # filename.
+            filename = core.Filename.fromOsSpecificW(file)
+        elif isinstance(file, str):
+            filename = core.Filename.fromOsSpecific(file)
+        else:
+            # If a Filename is given, make a writable copy anyway.
+            filename = core.Filename(file)
 
 
-        self.mode = mode
-        self.name = None
-        self.filename = None
-        self.newlines = None
-        self.softspace = False
+        if binary or sys.version_info >= (3, 0):
+            filename.setBinary()
+        else:
+            filename.setText()
 
 
-        readMode = False
-        writeMode = False
+        if not vfile:
+            vfile = _vfs.getFile(filename)
 
 
-        if isinstance(filename, core.Istream) or isinstance(filename, core.Ostream):
-            # If we were given a stream instead of a filename, assign
-            # it directly.
-            self.__stream = filename
-            readMode = isinstance(filename, core.Istream)
-            writeMode = isinstance(filename, core.Ostream)
+        if not vfile:
+            if reading:
+                raise FileNotFoundError("No such file or directory: '%s'" % (filename))
 
 
-        elif isinstance(filename, core.VirtualFile):
-            # We can also "open" a VirtualFile object for reading.
-            self.__stream = filename.openReadFile(autoUnwrap)
-            if not self.__stream:
-                message = 'Could not read virtual file %s' % (repr(filename))
-                raise IOError, message
-            self.__needsVfsClose = True
-            readMode = True
+            vfile = _vfs.createFile(filename)
+            if not vfile:
+                raise IOError("Failed to create file: '%s'" % (filename))
 
 
-        else:
-            # Otherwise, we must have been given a filename.  Open it.
-            if isinstance(filename, types.StringTypes):
-                # If a raw string is given, assume it's an os-specific
-                # filename.
-                filename = core.Filename.fromOsSpecific(filename)
+        elif creating:
+            # In 'creating' mode, we have to raise FileExistsError
+            # if the file already exists.  Otherwise, it's the same
+            # as 'writing' mode.
+            raise FileExistsError("File exists: '%s'" % (filename))
+
+        elif vfile.isDirectory():
+            raise IsADirectoryError("Is a directory: '%s'" % (filename))
+
+        # Actually open the streams.
+        if reading:
+            if updating:
+                stream = vfile.openReadWriteFile(False)
             else:
             else:
-                # If a Filename is given, make a writable copy anyway.
-                filename = core.Filename(filename)
-
-            self.filename = filename
-            self.name = filename.toOsSpecific()
-
-            binary = False
-            if 'b' in mode:
-                # Strip 'b'.  This means a binary file.
-                i = mode.index('b')
-                mode = mode[:i] + mode[i + 1:]
-                binary = True
-
-            if 'U' in mode:
-                # Strip 'U'.  We don't use it; universal-newline support
-                # is built into Panda, and can't be changed at runtime.
-                i = mode.index('U')
-                mode = mode[:i] + mode[i + 1:]
-                binary = False
-
-            if mode == '':
-                mode = 'r'
-
-            # Per Python docs, we insist this is true.
-            assert mode[0] in 'rwa'
-
-            if binary:
-                filename.setBinary()
+                stream = vfile.openReadFile(False)
+
+            if not stream:
+                raise IOError("Could not open %s for reading" % (filename))
+
+        elif writing or creating:
+            if updating:
+                stream = vfile.openReadWriteFile(True)
             else:
             else:
-                filename.setText()
-
-            # Actually open the streams.
-            if mode == 'w':
-                self.__stream = _vfs.openWriteFile(filename, autoUnwrap, True)
-                if not self.__stream:
-                    message = 'Could not open %s for writing' % (filename)
-                    raise IOError, message
-                writeMode = True
-
-            elif mode == 'a':
-                self.__stream = _vfs.openAppendFile(filename)
-                if not self.__stream:
-                    message = 'Could not open %s for writing' % (filename)
-                    raise IOError, message
-                writeMode = True
-
-            elif mode == 'w+':
-                self.__stream = _vfs.openReadWriteFile(filename, True)
-                if not self.__stream:
-                    message = 'Could not open %s for writing' % (filename)
-                    raise IOError, message
-                readMode = True
-                writeMode = True
-
-            elif mode == 'a+':
-                self.__stream = _vfs.openReadAppendFile(filename)
-                if not self.__stream:
-                    message = 'Could not open %s for writing' % (filename)
-                    raise IOError, message
-                readMode = True
-                writeMode = True
-
-            elif mode == 'r+':
-                self.__stream = _vfs.openReadWriteFile(filename, False)
-                if not self.__stream:
-                    message = 'Could not open %s for writing' % (filename)
-                    raise IOError, message
-                readMode = True
-                writeMode = True
-
-            elif mode == 'r':
-                self.__stream = _vfs.openReadFile(filename, autoUnwrap)
-                if not self.__stream:
-                    if not _vfs.exists(filename):
-                        message = 'No such file: %s' % (filename)
-                    else:
-                        message = 'Could not open %s for reading' % (filename)
-                    raise IOError, message
-                readMode = True
-
-            self.__needsVfsClose = True
-
-        if readMode:
-            self.__reader = core.StreamReader(self.__stream, False)
-        if writeMode:
-            self.__writer = core.StreamWriter(self.__stream, False)
+                stream = vfile.openWriteFile(False, True)
+
+            if not stream:
+                raise IOError("Could not open %s for writing" % (filename))
+
+        elif appending:
+            if updating:
+                stream = vfile.openReadAppendFile()
+            else:
+                stream = vfile.openAppendFile()
+
+            if not stream:
+                raise IOError("Could not open %s for appending" % (filename))
+
+        else:
+            raise ValueError("Must have exactly one of create/read/write/append mode and at most one plus")
+
+        raw = StreamIOWrapper(stream, needsVfsClose=True)
+        raw.mode = mode
+        raw.name = vfile.getFilename().toOsSpecific()
+
+    # If a binary stream was requested, return the stream we've created.
+    if binary:
+        return raw
+
+    # If we're in Python 2, we don't decode unicode strings by default.
+    if not encoding and sys.version_info < (3, 0):
+        return raw
+
+    line_buffering = False
+    if buffering == 1:
+        line_buffering = True
+    elif buffering == 0:
+        raise ValueError("can't have unbuffered text I/O")
+
+    # Otherwise, create a TextIOWrapper object to wrap it.
+    wrapper = io.TextIOWrapper(raw, encoding, errors, newline, line_buffering)
+    wrapper.mode = mode
+    return wrapper
+
+
+if sys.version_info < (3, 0):
+    # Python 2 had an alias for open() called file().
+    __all__.append('file')
+    file = open
+
+
+class StreamIOWrapper(io.IOBase):
+    """ This is a file-like object that wraps around a C++ istream and/or
+    ostream object.  It only deals with binary data; to work with text I/O,
+    create an io.TextIOWrapper object around this, or use the open()
+    function that is also provided with this module. """
+
+    def __init__(self, stream, needsVfsClose=False):
+        self.__stream = stream
+        self.__needsVfsClose = needsVfsClose
+        self.__reader = None
+        self.__writer = None
+        self.__lastWrite = False
+
+        if isinstance(stream, core.Istream):
+            self.__reader = core.StreamReader(stream, False)
+
+        if isinstance(stream, core.Ostream):
+            self.__writer = core.StreamWriter(stream, False)
             self.__lastWrite = True
             self.__lastWrite = True
+            if sys.version_info >= (3, 0):
+                # In Python 3, we use appendData, which only accepts bytes.
+                self.__write = self.__writer.appendData
+            else:
+                # In Python 2.7, we also accept unicode objects, which are
+                # implicitly converted to C++ strings.
+                self.__write = self.__writer.write
+
+    def __repr__(self):
+        s = "<direct.stdpy.file.StreamIOWrapper"
+        if hasattr(self, 'name'):
+            s += " name='%s'" % (self.name)
+        if hasattr(self, 'mode'):
+            s += " mode='%s'" % (self.mode)
+        s += ">"
+        return s
 
 
-    def __del__(self):
-        self.close()
+    def readable(self):
+        return self.__reader is not None
+
+    def writable(self):
+        return self.__writer is not None
 
 
     def close(self):
     def close(self):
         if self.__needsVfsClose:
         if self.__needsVfsClose:
@@ -160,70 +218,51 @@ class file:
                 _vfs.closeWriteFile(self.__stream)
                 _vfs.closeWriteFile(self.__stream)
 
 
             self.__needsVfsClose = False
             self.__needsVfsClose = False
+
         self.__stream = None
         self.__stream = None
-        self.__needsVfsClose = False
         self.__reader = None
         self.__reader = None
         self.__writer = None
         self.__writer = None
 
 
     def flush(self):
     def flush(self):
-        if self.__stream:
+        if self.__writer:
             self.__stream.clear()  # clear eof flag
             self.__stream.clear()  # clear eof flag
             self.__stream.flush()
             self.__stream.flush()
 
 
-    def __iter__(self):
-        return self
-
-    def next(self):
-        line = self.readline()
-        if line:
-            return line
-        raise StopIteration
-
-    def read(self, size = -1):
+    def read(self, size=-1):
         if not self.__reader:
         if not self.__reader:
             if not self.__writer:
             if not self.__writer:
                 # The stream is not even open at all.
                 # The stream is not even open at all.
-                message = 'I/O operation on closed file'
-                raise ValueError, message
+                raise ValueError("I/O operation on closed file")
+
             # The stream is open only in write mode.
             # The stream is open only in write mode.
-            message = 'Attempt to read from write-only stream'
-            raise IOError, message
+            raise IOError("Attempt to read from write-only stream")
 
 
         self.__stream.clear()  # clear eof flag
         self.__stream.clear()  # clear eof flag
         self.__lastWrite = False
         self.__lastWrite = False
-        if size >= 0:
+        if size is not None and size >= 0:
             result = self.__reader.extractBytes(size)
             result = self.__reader.extractBytes(size)
         else:
         else:
             # Read to end-of-file.
             # Read to end-of-file.
-            result = ''
+            result = b''
             while not self.__stream.eof():
             while not self.__stream.eof():
-                result += self.__reader.extractBytes(1024)
+                result += self.__reader.extractBytes(512)
         return result
         return result
 
 
-    def readline(self, size = -1):
+    read1 = read
+
+    def readline(self, size=-1):
         if not self.__reader:
         if not self.__reader:
             if not self.__writer:
             if not self.__writer:
                 # The stream is not even open at all.
                 # The stream is not even open at all.
-                message = 'I/O operation on closed file'
-                raise ValueError, message
+                raise ValueError("I/O operation on closed file")
+
             # The stream is open only in write mode.
             # The stream is open only in write mode.
-            message = 'Attempt to read from write-only stream'
-            raise IOError, message
+            raise IOError("Attempt to read from write-only stream")
 
 
         self.__stream.clear()  # clear eof flag
         self.__stream.clear()  # clear eof flag
         self.__lastWrite = False
         self.__lastWrite = False
         return self.__reader.readline()
         return self.__reader.readline()
 
 
-    def readlines(self, sizehint = -1):
-        lines = []
-        line = self.readline()
-        while line:
-            lines.append(line)
-            line = self.readline()
-        return lines
-
-    xreadlines = readlines
-
     def seek(self, offset, whence = 0):
     def seek(self, offset, whence = 0):
         if self.__stream:
         if self.__stream:
             self.__stream.clear()  # clear eof flag
             self.__stream.clear()  # clear eof flag
@@ -239,58 +278,43 @@ class file:
         else:
         else:
             if self.__reader:
             if self.__reader:
                 return self.__stream.tellg()
                 return self.__stream.tellg()
-        message = 'I/O operation on closed file'
-        raise ValueError, message
+        raise ValueError("I/O operation on closed file")
 
 
-    def truncate(self):
-        """ Sorry, this isn't supported by Panda's low-level I/O,
-        because it isn't supported by the standard C++ library. """
-        raise NotImplementedError
-
-    def write(self, str):
+    def write(self, b):
         if not self.__writer:
         if not self.__writer:
             if not self.__reader:
             if not self.__reader:
                 # The stream is not even open at all.
                 # The stream is not even open at all.
-                message = 'I/O operation on closed file'
-                raise ValueError, message
+                raise ValueError("I/O operation on closed file")
+
             # The stream is open only in read mode.
             # The stream is open only in read mode.
-            message = 'Attempt to write to read-only stream'
-            raise IOError, message
+            raise IOError("Attempt to write to read-only stream")
 
 
         self.__stream.clear()  # clear eof flag
         self.__stream.clear()  # clear eof flag
-        self.__writer.appendData(str)
+        self.__write(b)
         self.__lastWrite = True
         self.__lastWrite = True
 
 
     def writelines(self, lines):
     def writelines(self, lines):
         if not self.__writer:
         if not self.__writer:
             if not self.__reader:
             if not self.__reader:
                 # The stream is not even open at all.
                 # The stream is not even open at all.
-                message = 'I/O operation on closed file'
-                raise ValueError, message
+                raise ValueError("I/O operation on closed file")
+
             # The stream is open only in read mode.
             # The stream is open only in read mode.
-            message = 'Attempt to write to read-only stream'
-            raise IOError, message
+            raise IOError("Attempt to write to read-only stream")
 
 
         self.__stream.clear()  # clear eof flag
         self.__stream.clear()  # clear eof flag
         for line in lines:
         for line in lines:
-            self.__writer.appendData(line)
+            self.__write(line)
         self.__lastWrite = True
         self.__lastWrite = True
 
 
-    def __enter__(self):
-        return self
-
-    def __exit__(self, t, v, tb):
-        self.close()
-
-open = file
 
 
 def listdir(path):
 def listdir(path):
     """ Implements os.listdir over vfs. """
     """ Implements os.listdir over vfs. """
     files = []
     files = []
     dirlist = _vfs.scanDirectory(core.Filename.fromOsSpecific(path))
     dirlist = _vfs.scanDirectory(core.Filename.fromOsSpecific(path))
     if dirlist is None:
     if dirlist is None:
-        message = 'No such file or directory: %s' % (path)
-        raise OSError, message
+        raise OSError("No such file or directory: '%s'" % (path))
+
     for file in dirlist:
     for file in dirlist:
         files.append(file.getFilename().getBasename())
         files.append(file.getFilename().getBasename())
     return files
     return files
@@ -323,20 +347,6 @@ def walk(top, topdown = True, onerror = None, followlinks = True):
     if not topdown:
     if not topdown:
         yield (top, dirnames, filenames)
         yield (top, dirnames, filenames)
 
 
-def join(path, *args):
-    for part in args:
-        if part == '':
-            continue
-
-        if part.startswith('/'):
-            path = part
-        elif path.endswith('/'):
-            path = path + part
-        else:
-            path = '/'.join((path, part))
-
-    return path
-
 def isfile(path):
 def isfile(path):
     return _vfs.isRegularFile(core.Filename.fromOsSpecific(path))
     return _vfs.isRegularFile(core.Filename.fromOsSpecific(path))
 
 

+ 2 - 0
direct/src/tkpanels/ParticlePanel.py

@@ -19,6 +19,8 @@ import Pmw, os,Placer
 
 
 from panda3d.core import *
 from panda3d.core import *
 from panda3d.physics import *
 from panda3d.physics import *
+from panda3d.direct import getParticlePath
+
 
 
 class ParticlePanel(AppShell):
 class ParticlePanel(AppShell):
     # Override class variables
     # Override class variables

File diff suppressed because it is too large
+ 221 - 218
dtool/metalibs/dtoolconfig/pydtool.cxx


File diff suppressed because it is too large
+ 541 - 495
dtool/src/cppparser/cppBison.cxx.prebuilt


+ 220 - 200
dtool/src/cppparser/cppBison.h.prebuilt

@@ -49,214 +49,234 @@ extern int cppyydebug;
      REAL = 258,
      REAL = 258,
      INTEGER = 259,
      INTEGER = 259,
      CHAR_TOK = 260,
      CHAR_TOK = 260,
-     STRING = 261,
+     SIMPLE_STRING = 261,
      SIMPLE_IDENTIFIER = 262,
      SIMPLE_IDENTIFIER = 262,
-     IDENTIFIER = 263,
-     TYPENAME_IDENTIFIER = 264,
-     SCOPING = 265,
-     TYPEDEFNAME = 266,
-     ELLIPSIS = 267,
-     OROR = 268,
-     ANDAND = 269,
-     EQCOMPARE = 270,
-     NECOMPARE = 271,
-     LECOMPARE = 272,
-     GECOMPARE = 273,
-     LSHIFT = 274,
-     RSHIFT = 275,
-     POINTSAT_STAR = 276,
-     DOT_STAR = 277,
-     UNARY = 278,
-     UNARY_NOT = 279,
-     UNARY_NEGATE = 280,
-     UNARY_MINUS = 281,
-     UNARY_STAR = 282,
-     UNARY_REF = 283,
-     POINTSAT = 284,
-     SCOPE = 285,
-     PLUSPLUS = 286,
-     MINUSMINUS = 287,
-     TIMESEQUAL = 288,
-     DIVIDEEQUAL = 289,
-     MODEQUAL = 290,
-     PLUSEQUAL = 291,
-     MINUSEQUAL = 292,
-     OREQUAL = 293,
-     ANDEQUAL = 294,
-     XOREQUAL = 295,
-     LSHIFTEQUAL = 296,
-     RSHIFTEQUAL = 297,
-     KW_BEGIN_PUBLISH = 298,
-     KW_BLOCKING = 299,
-     KW_BOOL = 300,
-     KW_CATCH = 301,
-     KW_CHAR = 302,
-     KW_CHAR16_T = 303,
-     KW_CHAR32_T = 304,
-     KW_CLASS = 305,
-     KW_CONST = 306,
-     KW_DELETE = 307,
-     KW_DOUBLE = 308,
-     KW_DYNAMIC_CAST = 309,
-     KW_ELSE = 310,
-     KW_END_PUBLISH = 311,
-     KW_ENUM = 312,
-     KW_EXTENSION = 313,
-     KW_EXTERN = 314,
-     KW_EXPLICIT = 315,
-     KW_PUBLISHED = 316,
-     KW_FALSE = 317,
-     KW_FLOAT = 318,
-     KW_FRIEND = 319,
-     KW_FOR = 320,
-     KW_GOTO = 321,
-     KW_IF = 322,
-     KW_INLINE = 323,
-     KW_INT = 324,
-     KW_LONG = 325,
-     KW_LONGLONG = 326,
-     KW_MAKE_PROPERTY = 327,
-     KW_MAKE_SEQ = 328,
-     KW_MUTABLE = 329,
-     KW_NAMESPACE = 330,
-     KW_NEW = 331,
-     KW_NOEXCEPT = 332,
-     KW_OPERATOR = 333,
-     KW_PRIVATE = 334,
-     KW_PROTECTED = 335,
-     KW_PUBLIC = 336,
-     KW_REGISTER = 337,
-     KW_RETURN = 338,
-     KW_SHORT = 339,
-     KW_SIGNED = 340,
-     KW_SIZEOF = 341,
-     KW_STATIC = 342,
-     KW_STATIC_CAST = 343,
-     KW_STRUCT = 344,
-     KW_TEMPLATE = 345,
-     KW_THROW = 346,
-     KW_TRUE = 347,
-     KW_TRY = 348,
-     KW_TYPEDEF = 349,
-     KW_TYPENAME = 350,
-     KW_UNION = 351,
-     KW_UNSIGNED = 352,
-     KW_USING = 353,
-     KW_VIRTUAL = 354,
-     KW_VOID = 355,
-     KW_VOLATILE = 356,
-     KW_WCHAR_T = 357,
-     KW_WHILE = 358,
-     START_CPP = 359,
-     START_CONST_EXPR = 360,
-     START_TYPE = 361
+     STRING_LITERAL = 263,
+     CUSTOM_LITERAL = 264,
+     IDENTIFIER = 265,
+     TYPENAME_IDENTIFIER = 266,
+     SCOPING = 267,
+     TYPEDEFNAME = 268,
+     ELLIPSIS = 269,
+     OROR = 270,
+     ANDAND = 271,
+     EQCOMPARE = 272,
+     NECOMPARE = 273,
+     LECOMPARE = 274,
+     GECOMPARE = 275,
+     LSHIFT = 276,
+     RSHIFT = 277,
+     POINTSAT_STAR = 278,
+     DOT_STAR = 279,
+     UNARY = 280,
+     UNARY_NOT = 281,
+     UNARY_NEGATE = 282,
+     UNARY_MINUS = 283,
+     UNARY_STAR = 284,
+     UNARY_REF = 285,
+     POINTSAT = 286,
+     SCOPE = 287,
+     PLUSPLUS = 288,
+     MINUSMINUS = 289,
+     TIMESEQUAL = 290,
+     DIVIDEEQUAL = 291,
+     MODEQUAL = 292,
+     PLUSEQUAL = 293,
+     MINUSEQUAL = 294,
+     OREQUAL = 295,
+     ANDEQUAL = 296,
+     XOREQUAL = 297,
+     LSHIFTEQUAL = 298,
+     RSHIFTEQUAL = 299,
+     KW_ALIGNAS = 300,
+     KW_ALIGNOF = 301,
+     KW_AUTO = 302,
+     KW_BEGIN_PUBLISH = 303,
+     KW_BLOCKING = 304,
+     KW_BOOL = 305,
+     KW_CATCH = 306,
+     KW_CHAR = 307,
+     KW_CHAR16_T = 308,
+     KW_CHAR32_T = 309,
+     KW_CLASS = 310,
+     KW_CONST = 311,
+     KW_CONSTEXPR = 312,
+     KW_DECLTYPE = 313,
+     KW_DEFAULT = 314,
+     KW_DELETE = 315,
+     KW_DOUBLE = 316,
+     KW_DYNAMIC_CAST = 317,
+     KW_ELSE = 318,
+     KW_END_PUBLISH = 319,
+     KW_ENUM = 320,
+     KW_EXTENSION = 321,
+     KW_EXTERN = 322,
+     KW_EXPLICIT = 323,
+     KW_PUBLISHED = 324,
+     KW_FALSE = 325,
+     KW_FLOAT = 326,
+     KW_FRIEND = 327,
+     KW_FOR = 328,
+     KW_GOTO = 329,
+     KW_IF = 330,
+     KW_INLINE = 331,
+     KW_INT = 332,
+     KW_LONG = 333,
+     KW_LONGLONG = 334,
+     KW_MAKE_PROPERTY = 335,
+     KW_MAKE_SEQ = 336,
+     KW_MUTABLE = 337,
+     KW_NAMESPACE = 338,
+     KW_NEW = 339,
+     KW_NOEXCEPT = 340,
+     KW_NULLPTR = 341,
+     KW_OPERATOR = 342,
+     KW_PRIVATE = 343,
+     KW_PROTECTED = 344,
+     KW_PUBLIC = 345,
+     KW_REGISTER = 346,
+     KW_RETURN = 347,
+     KW_SHORT = 348,
+     KW_SIGNED = 349,
+     KW_SIZEOF = 350,
+     KW_STATIC = 351,
+     KW_STATIC_ASSERT = 352,
+     KW_STATIC_CAST = 353,
+     KW_STRUCT = 354,
+     KW_TEMPLATE = 355,
+     KW_THROW = 356,
+     KW_TRUE = 357,
+     KW_TRY = 358,
+     KW_TYPEDEF = 359,
+     KW_TYPENAME = 360,
+     KW_UNION = 361,
+     KW_UNSIGNED = 362,
+     KW_USING = 363,
+     KW_VIRTUAL = 364,
+     KW_VOID = 365,
+     KW_VOLATILE = 366,
+     KW_WCHAR_T = 367,
+     KW_WHILE = 368,
+     START_CPP = 369,
+     START_CONST_EXPR = 370,
+     START_TYPE = 371
    };
    };
 #endif
 #endif
 /* Tokens.  */
 /* Tokens.  */
 #define REAL 258
 #define REAL 258
 #define INTEGER 259
 #define INTEGER 259
 #define CHAR_TOK 260
 #define CHAR_TOK 260
-#define STRING 261
+#define SIMPLE_STRING 261
 #define SIMPLE_IDENTIFIER 262
 #define SIMPLE_IDENTIFIER 262
-#define IDENTIFIER 263
-#define TYPENAME_IDENTIFIER 264
-#define SCOPING 265
-#define TYPEDEFNAME 266
-#define ELLIPSIS 267
-#define OROR 268
-#define ANDAND 269
-#define EQCOMPARE 270
-#define NECOMPARE 271
-#define LECOMPARE 272
-#define GECOMPARE 273
-#define LSHIFT 274
-#define RSHIFT 275
-#define POINTSAT_STAR 276
-#define DOT_STAR 277
-#define UNARY 278
-#define UNARY_NOT 279
-#define UNARY_NEGATE 280
-#define UNARY_MINUS 281
-#define UNARY_STAR 282
-#define UNARY_REF 283
-#define POINTSAT 284
-#define SCOPE 285
-#define PLUSPLUS 286
-#define MINUSMINUS 287
-#define TIMESEQUAL 288
-#define DIVIDEEQUAL 289
-#define MODEQUAL 290
-#define PLUSEQUAL 291
-#define MINUSEQUAL 292
-#define OREQUAL 293
-#define ANDEQUAL 294
-#define XOREQUAL 295
-#define LSHIFTEQUAL 296
-#define RSHIFTEQUAL 297
-#define KW_BEGIN_PUBLISH 298
-#define KW_BLOCKING 299
-#define KW_BOOL 300
-#define KW_CATCH 301
-#define KW_CHAR 302
-#define KW_CHAR16_T 303
-#define KW_CHAR32_T 304
-#define KW_CLASS 305
-#define KW_CONST 306
-#define KW_DELETE 307
-#define KW_DOUBLE 308
-#define KW_DYNAMIC_CAST 309
-#define KW_ELSE 310
-#define KW_END_PUBLISH 311
-#define KW_ENUM 312
-#define KW_EXTENSION 313
-#define KW_EXTERN 314
-#define KW_EXPLICIT 315
-#define KW_PUBLISHED 316
-#define KW_FALSE 317
-#define KW_FLOAT 318
-#define KW_FRIEND 319
-#define KW_FOR 320
-#define KW_GOTO 321
-#define KW_IF 322
-#define KW_INLINE 323
-#define KW_INT 324
-#define KW_LONG 325
-#define KW_LONGLONG 326
-#define KW_MAKE_PROPERTY 327
-#define KW_MAKE_SEQ 328
-#define KW_MUTABLE 329
-#define KW_NAMESPACE 330
-#define KW_NEW 331
-#define KW_NOEXCEPT 332
-#define KW_OPERATOR 333
-#define KW_PRIVATE 334
-#define KW_PROTECTED 335
-#define KW_PUBLIC 336
-#define KW_REGISTER 337
-#define KW_RETURN 338
-#define KW_SHORT 339
-#define KW_SIGNED 340
-#define KW_SIZEOF 341
-#define KW_STATIC 342
-#define KW_STATIC_CAST 343
-#define KW_STRUCT 344
-#define KW_TEMPLATE 345
-#define KW_THROW 346
-#define KW_TRUE 347
-#define KW_TRY 348
-#define KW_TYPEDEF 349
-#define KW_TYPENAME 350
-#define KW_UNION 351
-#define KW_UNSIGNED 352
-#define KW_USING 353
-#define KW_VIRTUAL 354
-#define KW_VOID 355
-#define KW_VOLATILE 356
-#define KW_WCHAR_T 357
-#define KW_WHILE 358
-#define START_CPP 359
-#define START_CONST_EXPR 360
-#define START_TYPE 361
+#define STRING_LITERAL 263
+#define CUSTOM_LITERAL 264
+#define IDENTIFIER 265
+#define TYPENAME_IDENTIFIER 266
+#define SCOPING 267
+#define TYPEDEFNAME 268
+#define ELLIPSIS 269
+#define OROR 270
+#define ANDAND 271
+#define EQCOMPARE 272
+#define NECOMPARE 273
+#define LECOMPARE 274
+#define GECOMPARE 275
+#define LSHIFT 276
+#define RSHIFT 277
+#define POINTSAT_STAR 278
+#define DOT_STAR 279
+#define UNARY 280
+#define UNARY_NOT 281
+#define UNARY_NEGATE 282
+#define UNARY_MINUS 283
+#define UNARY_STAR 284
+#define UNARY_REF 285
+#define POINTSAT 286
+#define SCOPE 287
+#define PLUSPLUS 288
+#define MINUSMINUS 289
+#define TIMESEQUAL 290
+#define DIVIDEEQUAL 291
+#define MODEQUAL 292
+#define PLUSEQUAL 293
+#define MINUSEQUAL 294
+#define OREQUAL 295
+#define ANDEQUAL 296
+#define XOREQUAL 297
+#define LSHIFTEQUAL 298
+#define RSHIFTEQUAL 299
+#define KW_ALIGNAS 300
+#define KW_ALIGNOF 301
+#define KW_AUTO 302
+#define KW_BEGIN_PUBLISH 303
+#define KW_BLOCKING 304
+#define KW_BOOL 305
+#define KW_CATCH 306
+#define KW_CHAR 307
+#define KW_CHAR16_T 308
+#define KW_CHAR32_T 309
+#define KW_CLASS 310
+#define KW_CONST 311
+#define KW_CONSTEXPR 312
+#define KW_DECLTYPE 313
+#define KW_DEFAULT 314
+#define KW_DELETE 315
+#define KW_DOUBLE 316
+#define KW_DYNAMIC_CAST 317
+#define KW_ELSE 318
+#define KW_END_PUBLISH 319
+#define KW_ENUM 320
+#define KW_EXTENSION 321
+#define KW_EXTERN 322
+#define KW_EXPLICIT 323
+#define KW_PUBLISHED 324
+#define KW_FALSE 325
+#define KW_FLOAT 326
+#define KW_FRIEND 327
+#define KW_FOR 328
+#define KW_GOTO 329
+#define KW_IF 330
+#define KW_INLINE 331
+#define KW_INT 332
+#define KW_LONG 333
+#define KW_LONGLONG 334
+#define KW_MAKE_PROPERTY 335
+#define KW_MAKE_SEQ 336
+#define KW_MUTABLE 337
+#define KW_NAMESPACE 338
+#define KW_NEW 339
+#define KW_NOEXCEPT 340
+#define KW_NULLPTR 341
+#define KW_OPERATOR 342
+#define KW_PRIVATE 343
+#define KW_PROTECTED 344
+#define KW_PUBLIC 345
+#define KW_REGISTER 346
+#define KW_RETURN 347
+#define KW_SHORT 348
+#define KW_SIGNED 349
+#define KW_SIZEOF 350
+#define KW_STATIC 351
+#define KW_STATIC_ASSERT 352
+#define KW_STATIC_CAST 353
+#define KW_STRUCT 354
+#define KW_TEMPLATE 355
+#define KW_THROW 356
+#define KW_TRUE 357
+#define KW_TRY 358
+#define KW_TYPEDEF 359
+#define KW_TYPENAME 360
+#define KW_UNION 361
+#define KW_UNSIGNED 362
+#define KW_USING 363
+#define KW_VIRTUAL 364
+#define KW_VOID 365
+#define KW_VOLATILE 366
+#define KW_WCHAR_T 367
+#define KW_WHILE 368
+#define START_CPP 369
+#define START_CONST_EXPR 370
+#define START_TYPE 371
 
 
 
 
 
 

+ 274 - 108
dtool/src/cppparser/cppBison.yxx

@@ -57,17 +57,22 @@ int yyparse();
 
 
 static void
 static void
 yyerror(const string &msg) {
 yyerror(const string &msg) {
-  current_lexer->error(msg);
+  current_lexer->error(msg, current_lexer->_last_token_loc);
+}
+
+static void
+yyerror(YYLTYPE *loc, const string &msg) {
+  current_lexer->error(msg, *loc);
 }
 }
 
 
 static void
 static void
 yyerror(const string &msg, YYLTYPE &loc) {
 yyerror(const string &msg, YYLTYPE &loc) {
-  current_lexer->error(msg, loc.first_line, loc.first_column);
+  current_lexer->error(msg, loc);
 }
 }
 
 
 static void
 static void
 yywarning(const string &msg, YYLTYPE &loc) {
 yywarning(const string &msg, YYLTYPE &loc) {
-  current_lexer->warning(msg, loc.first_line, loc.first_column);
+  current_lexer->warning(msg, loc);
 }
 }
 
 
 static int
 static int
@@ -194,13 +199,18 @@ pop_struct() {
 
 
 /* This is a bison-specific declaration to enable recursive calls to
 /* This is a bison-specific declaration to enable recursive calls to
    yyparse().  It changes the calling sequence to yylex(), passing
    yyparse().  It changes the calling sequence to yylex(), passing
-   pointers to the current yylval and yylloc. */
-%pure_parser
+   pointers to the current yylval and yylloc.  Bison 2.7 introduces a
+   different syntax that will also pass the current yylloc to yyerror,
+   but we have to support Bison versions as old as 2.5 for now. */
+/*%define api.pure full*/
+%pure-parser
+%locations
 
 
 %token <u.real> REAL
 %token <u.real> REAL
 %token <u.integer> INTEGER
 %token <u.integer> INTEGER
 %token <u.integer> CHAR_TOK
 %token <u.integer> CHAR_TOK
-%token <str> STRING SIMPLE_IDENTIFIER
+%token <str> SIMPLE_STRING SIMPLE_IDENTIFIER
+%token <u.expr> STRING_LITERAL CUSTOM_LITERAL
 %token <u.identifier> IDENTIFIER TYPENAME_IDENTIFIER SCOPING
 %token <u.identifier> IDENTIFIER TYPENAME_IDENTIFIER SCOPING
 %token <u.type> TYPEDEFNAME
 %token <u.type> TYPEDEFNAME
 
 
@@ -236,6 +246,9 @@ pop_struct() {
 %token LSHIFTEQUAL
 %token LSHIFTEQUAL
 %token RSHIFTEQUAL
 %token RSHIFTEQUAL
 
 
+%token KW_ALIGNAS
+%token KW_ALIGNOF
+%token KW_AUTO
 %token KW_BEGIN_PUBLISH
 %token KW_BEGIN_PUBLISH
 %token KW_BLOCKING
 %token KW_BLOCKING
 %token KW_BOOL
 %token KW_BOOL
@@ -245,6 +258,9 @@ pop_struct() {
 %token KW_CHAR32_T
 %token KW_CHAR32_T
 %token KW_CLASS
 %token KW_CLASS
 %token KW_CONST
 %token KW_CONST
+%token KW_CONSTEXPR
+%token KW_DECLTYPE
+%token KW_DEFAULT
 %token KW_DELETE
 %token KW_DELETE
 %token KW_DOUBLE
 %token KW_DOUBLE
 %token KW_DYNAMIC_CAST
 %token KW_DYNAMIC_CAST
@@ -271,6 +287,7 @@ pop_struct() {
 %token KW_NAMESPACE
 %token KW_NAMESPACE
 %token KW_NEW
 %token KW_NEW
 %token KW_NOEXCEPT
 %token KW_NOEXCEPT
+%token KW_NULLPTR
 %token KW_OPERATOR
 %token KW_OPERATOR
 %token KW_PRIVATE
 %token KW_PRIVATE
 %token KW_PROTECTED
 %token KW_PROTECTED
@@ -281,6 +298,7 @@ pop_struct() {
 %token KW_SIGNED
 %token KW_SIGNED
 %token KW_SIZEOF
 %token KW_SIZEOF
 %token KW_STATIC
 %token KW_STATIC
+%token KW_STATIC_ASSERT
 %token KW_STATIC_CAST
 %token KW_STATIC_CAST
 %token KW_STRUCT
 %token KW_STRUCT
 %token KW_TEMPLATE
 %token KW_TEMPLATE
@@ -338,7 +356,7 @@ pop_struct() {
 %type <u.type> enum_element_type
 %type <u.type> enum_element_type
 /*%type <u.type> typedefname*/
 /*%type <u.type> typedefname*/
 %type <u.identifier> name
 %type <u.identifier> name
-%type <str> string
+%type <u.expr> string_literal
 
 
 /* We need to treat KW_OPERATOR as a scopable keyword. */
 /* We need to treat KW_OPERATOR as a scopable keyword. */
 %type <u.identifier> KW_OPERATOR
 %type <u.identifier> KW_OPERATOR
@@ -511,10 +529,31 @@ declaration:
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
   }
   }
 }
 }
-         | KW_MAKE_SEQ '(' name ',' name ',' name ')' ';'
+        | KW_MAKE_SEQ '(' name ',' name ',' name ')' ';'
 {
 {
   CPPMakeSeq *make_seq = new CPPMakeSeq($3->get_simple_name(), $5->get_simple_name(), $7->get_simple_name(), @1.file);
   CPPMakeSeq *make_seq = new CPPMakeSeq($3->get_simple_name(), $5->get_simple_name(), $7->get_simple_name(), @1.file);
   current_scope->add_declaration(make_seq, global_scope, current_lexer, @1);
   current_scope->add_declaration(make_seq, global_scope, current_lexer, @1);
+}
+        | KW_STATIC_ASSERT '(' const_expr ',' string_literal ')'
+{
+  CPPExpression::Result result = $3->evaluate();
+  if (result._type == CPPExpression::RT_error) {
+    yywarning("static_assert requires a constant expression", @3);
+  } else if (!result.as_boolean()) {
+    stringstream str;
+    str << *$5;
+    yywarning("static_assert failed: " + str.str(), @3);
+  }
+}
+        | KW_STATIC_ASSERT '(' const_expr ')'
+{
+  // This alternative version of static_assert was introduced in C++17.
+  CPPExpression::Result result = $3->evaluate();
+  if (result._type == CPPExpression::RT_error) {
+    yywarning("static_assert requires a constant expression", @3);
+  } else if (!result.as_boolean()) {
+    yywarning("static_assert failed", @3);
+  }
 }
 }
         ;
         ;
 
 
@@ -538,56 +577,60 @@ storage_class:
 {
 {
   $$ = 0;
   $$ = 0;
 }
 }
-        | storage_class KW_EXTERN
+        | KW_EXTERN storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_extern;
+  $$ = $2 | (int)CPPInstance::SC_extern;
 }
 }
-        | storage_class KW_EXTERN string
+        | KW_EXTERN SIMPLE_STRING storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_extern;
-  if ($3 == "C") {
+  $$ = $3 | (int)CPPInstance::SC_extern;
+  if ($2 == "C") {
     $$ |= (int)CPPInstance::SC_c_binding;
     $$ |= (int)CPPInstance::SC_c_binding;
-  } else if ($3 == "C++") {
+  } else if ($2 == "C++") {
     $$ &= ~(int)CPPInstance::SC_c_binding;
     $$ &= ~(int)CPPInstance::SC_c_binding;
   } else {
   } else {
-    yywarning("Ignoring unknown linkage type \"" + $3 + "\"", @3);
+    yywarning("Ignoring unknown linkage type \"" + $2 + "\"", @2);
   }
   }
 }
 }
-        | storage_class KW_STATIC
+        | KW_STATIC storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_static;
+  $$ = $2 | (int)CPPInstance::SC_static;
 }
 }
-        | storage_class KW_INLINE
+        | KW_INLINE storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_inline;
+  $$ = $2 | (int)CPPInstance::SC_inline;
 }
 }
-        | storage_class KW_VIRTUAL
+        | KW_VIRTUAL storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_virtual;
+  $$ = $2 | (int)CPPInstance::SC_virtual;
 }
 }
-        | storage_class KW_EXPLICIT
+        | KW_EXPLICIT storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_explicit;
+  $$ = $2 | (int)CPPInstance::SC_explicit;
 }
 }
-        | storage_class KW_VOLATILE
+        | KW_REGISTER storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_volatile;
+  $$ = $2 | (int)CPPInstance::SC_register;
 }
 }
-        | storage_class KW_MUTABLE
+        | KW_VOLATILE storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_mutable;
+  $$ = $2 | (int)CPPInstance::SC_volatile;
 }
 }
-        | storage_class KW_REGISTER
+        | KW_MUTABLE storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_register;
+  $$ = $2 | (int)CPPInstance::SC_mutable;
 }
 }
-        | storage_class KW_BLOCKING
+        | KW_CONSTEXPR storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_blocking;
+  $$ = $2 | (int)CPPInstance::SC_constexpr;
 }
 }
-        | storage_class KW_EXTENSION
+        | KW_BLOCKING storage_class
 {
 {
-  $$ = $1 | (int)CPPInstance::SC_extension;
+  $$ = $2 | (int)CPPInstance::SC_blocking;
+}
+        | KW_EXTENSION storage_class
+{
+  $$ = $2 | (int)CPPInstance::SC_extension;
 }
 }
         ;
         ;
 
 
@@ -771,16 +814,18 @@ function_prototype:
 }
 }
         formal_parameter_list ')' function_post
         formal_parameter_list ')' function_post
 {
 {
-  pop_scope();
   CPPType *type;
   CPPType *type;
-  if ($1->get_simple_name() == current_scope->get_simple_name()) {
+  if ($1->get_simple_name() == current_scope->get_simple_name() ||
+      $1->get_simple_name() == string("~") + current_scope->get_simple_name()) {
     // This is a constructor, and has no return.
     // This is a constructor, and has no return.
     type = new CPPSimpleType(CPPSimpleType::T_void);
     type = new CPPSimpleType(CPPSimpleType::T_void);
   } else {
   } else {
     // This isn't a constructor, so it has an implicit return type of
     // This isn't a constructor, so it has an implicit return type of
     // int.
     // int.
+    yywarning("function has no return type, assuming int", @1);
     type = new CPPSimpleType(CPPSimpleType::T_int);
     type = new CPPSimpleType(CPPSimpleType::T_int);
   }
   }
+  pop_scope();
 
 
   CPPInstanceIdentifier *ii = new CPPInstanceIdentifier($1);
   CPPInstanceIdentifier *ii = new CPPInstanceIdentifier($1);
   ii->add_func_modifier($4, $6);
   ii->add_func_modifier($4, $6);
@@ -822,7 +867,7 @@ function_prototype:
     yyerror("Invalid destructor name: ~" + $2->get_fully_scoped_name(), @2);
     yyerror("Invalid destructor name: ~" + $2->get_fully_scoped_name(), @2);
   } else {
   } else {
     CPPIdentifier *ident =
     CPPIdentifier *ident =
-      new CPPIdentifier("~" + $2->get_simple_name(), @2.file);
+      new CPPIdentifier("~" + $2->get_simple_name(), @2);
     delete $2;
     delete $2;
 
 
     CPPType *type;
     CPPType *type;
@@ -849,6 +894,9 @@ function_prototype:
 {
 {
   pop_scope();
   pop_scope();
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if (type == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert(type != NULL);
   assert(type != NULL);
 
 
   CPPInstanceIdentifier *ii = $4;
   CPPInstanceIdentifier *ii = $4;
@@ -864,6 +912,9 @@ function_prototype:
 {
 {
   pop_scope();
   pop_scope();
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if (type == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert(type != NULL);
   assert(type != NULL);
 
 
   CPPInstanceIdentifier *ii = $5;
   CPPInstanceIdentifier *ii = $5;
@@ -897,7 +948,7 @@ function_prototype:
   string name = "operator typecast " + $2->get_simple_name();
   string name = "operator typecast " + $2->get_simple_name();
   CPPIdentifier *ident = $1;
   CPPIdentifier *ident = $1;
   if (ident == NULL) {
   if (ident == NULL) {
-    ident = new CPPIdentifier(name, @1.file);
+    ident = new CPPIdentifier(name, @2);
   } else {
   } else {
     ident->add_name(name);
     ident->add_name(name);
   }
   }
@@ -918,7 +969,7 @@ function_prototype:
 
 
   CPPIdentifier *ident = $1;
   CPPIdentifier *ident = $1;
   if (ident == NULL) {
   if (ident == NULL) {
-    ident = new CPPIdentifier("operator typecast", @1.file);
+    ident = new CPPIdentifier("operator typecast", @4);
   } else {
   } else {
     ident->add_name("operator typecast");
     ident->add_name("operator typecast");
   }
   }
@@ -1138,6 +1189,7 @@ template_declaration:
 {
 {
   pop_scope();
   pop_scope();
 }
 }
+	| KW_TEMPLATE type_like_declaration
         ;
         ;
 
 
 template_formal_parameters:
 template_formal_parameters:
@@ -1205,6 +1257,9 @@ template_formal_parameter_type:
         | TYPENAME_IDENTIFIER
         | TYPENAME_IDENTIFIER
 {
 {
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if ($$ == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert($$ != NULL);
   assert($$ != NULL);
 }
 }
         ;
         ;
@@ -1222,17 +1277,37 @@ instance_identifier:
   // like a regular function.
   // like a regular function.
   CPPIdentifier *ident = $1;
   CPPIdentifier *ident = $1;
   if (ident == NULL) {
   if (ident == NULL) {
-    ident = new CPPIdentifier("operator "+$2, @2.file);
+    ident = new CPPIdentifier("operator "+$2, @2);
   } else {
   } else {
     ident->_names.push_back("operator "+$2);
     ident->_names.push_back("operator "+$2);
   }
   }
 
 
+  $$ = new CPPInstanceIdentifier(ident);
+}
+        | KW_OPERATOR SIMPLE_STRING IDENTIFIER
+{
+  // A C++11 literal operator.
+  if (!$2.empty()) {
+    yyerror("expected empty string", @2);
+  }
+  CPPIdentifier *ident = $1;
+  if (ident == NULL) {
+    ident = new CPPIdentifier("operator \"\" "+$3->get_simple_name(), @3);
+  } else {
+    ident->_names.push_back("operator \"\" "+$3->get_simple_name());
+  }
+
   $$ = new CPPInstanceIdentifier(ident);
   $$ = new CPPInstanceIdentifier(ident);
 }
 }
         | KW_CONST instance_identifier  %prec UNARY
         | KW_CONST instance_identifier  %prec UNARY
 {
 {
   $$ = $2;
   $$ = $2;
   $$->add_modifier(IIT_const);
   $$->add_modifier(IIT_const);
+}
+        | KW_VOLATILE instance_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_volatile);
 }
 }
         | '*' instance_identifier  %prec UNARY
         | '*' instance_identifier  %prec UNARY
 {
 {
@@ -1394,20 +1469,22 @@ formal_parameter:
 {
 {
   $$ = new CPPInstance($1, $2, 0, @2.file);
   $$ = new CPPInstance($1, $2, 0, @2.file);
   $$->set_initializer($3);
   $$->set_initializer($3);
-}
-        | IDENTIFIER formal_parameter_identifier maybe_initialize
-{
-  yywarning("Not a type: " + $1->get_fully_scoped_name(), @1);
-  CPPType *type =
-    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_unknown));
-  $$ = new CPPInstance(type, $2, 0, @2.file);
-  $$->set_initializer($3);
 }
 }
         | KW_CONST type formal_parameter_identifier maybe_initialize
         | KW_CONST type formal_parameter_identifier maybe_initialize
 {
 {
   $3->add_modifier(IIT_const);
   $3->add_modifier(IIT_const);
   $$ = new CPPInstance($2, $3, 0, @3.file);
   $$ = new CPPInstance($2, $3, 0, @3.file);
   $$->set_initializer($4);
   $$->set_initializer($4);
+}
+        | KW_CONST KW_REGISTER type formal_parameter_identifier maybe_initialize
+{
+  $4->add_modifier(IIT_const);
+  $$ = new CPPInstance($3, $4, 0, @3.file);
+  $$->set_initializer($5);
+}
+        | KW_REGISTER formal_parameter
+{
+  $$ = $2;
 }
 }
         | formal_const_expr
         | formal_const_expr
 {
 {
@@ -1435,6 +1512,11 @@ not_paren_formal_parameter_identifier:
 {
 {
   $$ = $2;
   $$ = $2;
   $$->add_modifier(IIT_const);
   $$->add_modifier(IIT_const);
+}
+        | KW_VOLATILE not_paren_formal_parameter_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_volatile);
 }
 }
         | '*' not_paren_formal_parameter_identifier  %prec UNARY
         | '*' not_paren_formal_parameter_identifier  %prec UNARY
 {
 {
@@ -1480,6 +1562,11 @@ formal_parameter_identifier:
 {
 {
   $$ = $2;
   $$ = $2;
   $$->add_modifier(IIT_const);
   $$->add_modifier(IIT_const);
+}
+        | KW_VOLATILE formal_parameter_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_volatile);
 }
 }
         | '*' formal_parameter_identifier  %prec UNARY
         | '*' formal_parameter_identifier  %prec UNARY
 {
 {
@@ -1528,6 +1615,11 @@ empty_instance_identifier:
 {
 {
   $$ = $2;
   $$ = $2;
   $$->add_modifier(IIT_const);
   $$->add_modifier(IIT_const);
+}
+        | KW_VOLATILE empty_instance_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_volatile);
 }
 }
         | '*' empty_instance_identifier  %prec UNARY
         | '*' empty_instance_identifier  %prec UNARY
 {
 {
@@ -1570,6 +1662,9 @@ type:
         | TYPENAME_IDENTIFIER
         | TYPENAME_IDENTIFIER
 {
 {
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if ($$ == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert($$ != NULL);
   assert($$ != NULL);
 }
 }
         | KW_TYPENAME name
         | KW_TYPENAME name
@@ -1619,6 +1714,19 @@ type:
     }
     }
     $$ = et;
     $$ = et;
   }
   }
+}
+        | KW_DECLTYPE '(' const_expr ')'
+{
+  $$ = $3->determine_type();
+  if ($$ == (CPPType *)NULL) {
+    stringstream str;
+    str << *$3;
+    yyerror("could not determine type of " + str.str(), @3);
+  }
+}
+        | KW_AUTO
+{
+  $$ = new CPPSimpleType(CPPSimpleType::T_auto);
 }
 }
         ;
         ;
 
 
@@ -1630,6 +1738,9 @@ type_decl:
         | TYPENAME_IDENTIFIER
         | TYPENAME_IDENTIFIER
 {
 {
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if ($$ == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert($$ != NULL);
   assert($$ != NULL);
 }
 }
         | KW_TYPENAME name
         | KW_TYPENAME name
@@ -1697,6 +1808,19 @@ type_decl:
     }
     }
     $$ = et;
     $$ = et;
   }
   }
+}
+        | KW_DECLTYPE '(' const_expr ')'
+{
+  $$ = $3->determine_type();
+  if ($$ == (CPPType *)NULL) {
+    stringstream str;
+    str << *$3;
+    yyerror("could not determine type of " + str.str(), @3);
+  }
+}
+        | KW_AUTO
+{
+  $$ = new CPPSimpleType(CPPSimpleType::T_auto);
 }
 }
         ;
         ;
 
 
@@ -1708,6 +1832,9 @@ predefined_type:
         | TYPENAME_IDENTIFIER
         | TYPENAME_IDENTIFIER
 {
 {
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if ($$ == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert($$ != NULL);
   assert($$ != NULL);
 }
 }
         | KW_TYPENAME name
         | KW_TYPENAME name
@@ -1980,8 +2107,32 @@ namespace_declaration:
         cpp '}'
         cpp '}'
 {
 {
   pop_scope();
   pop_scope();
+}
+        | KW_INLINE KW_NAMESPACE name '{'
+{
+  CPPScope *scope = $3->find_scope(current_scope, global_scope, current_lexer);
+  if (scope == NULL) {
+    // This must be a new namespace declaration.
+    CPPScope *parent_scope =
+      $3->get_scope(current_scope, global_scope, current_lexer);
+    if (parent_scope == NULL) {
+      parent_scope = current_scope;
+    }
+    scope = new CPPScope(parent_scope, $3->_names.back(), V_public);
+  }
+
+  CPPNamespace *nspace = new CPPNamespace($3, scope, @2.file);
+  nspace->_is_inline = true;
+  current_scope->add_declaration(nspace, global_scope, current_lexer, @2);
+  current_scope->define_namespace(nspace);
+  push_scope(scope);
+}
+        cpp '}'
+{
+  pop_scope();
 }
 }
         | KW_NAMESPACE '{' cpp '}'
         | KW_NAMESPACE '{' cpp '}'
+        | KW_INLINE KW_NAMESPACE '{' cpp '}'
         ;
         ;
 
 
 using_declaration:
 using_declaration:
@@ -2134,48 +2285,34 @@ code_block:
 
 
 element:
 element:
         REAL
         REAL
-{
-}
         | INTEGER
         | INTEGER
-{
-}
-        | STRING
-{
-}
+        | SIMPLE_STRING
+        | STRING_LITERAL
+        | CUSTOM_LITERAL
         | CHAR_TOK
         | CHAR_TOK
-{
-}
         | IDENTIFIER
         | IDENTIFIER
-{
-}
         | TYPENAME_IDENTIFIER
         | TYPENAME_IDENTIFIER
-{
-}
         | SCOPING
         | SCOPING
-{
-}
         | SIMPLE_IDENTIFIER
         | SIMPLE_IDENTIFIER
-{
-}
         | ELLIPSIS | OROR | ANDAND
         | ELLIPSIS | OROR | ANDAND
         | EQCOMPARE | NECOMPARE | LECOMPARE | GECOMPARE
         | EQCOMPARE | NECOMPARE | LECOMPARE | GECOMPARE
         | LSHIFT | RSHIFT | POINTSAT_STAR | DOT_STAR | POINTSAT
         | LSHIFT | RSHIFT | POINTSAT_STAR | DOT_STAR | POINTSAT
         | SCOPE | PLUSPLUS | MINUSMINUS
         | SCOPE | PLUSPLUS | MINUSMINUS
         | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | PLUSEQUAL | MINUSEQUAL
         | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | PLUSEQUAL | MINUSEQUAL
         | OREQUAL | ANDEQUAL | XOREQUAL | LSHIFTEQUAL | RSHIFTEQUAL
         | OREQUAL | ANDEQUAL | XOREQUAL | LSHIFTEQUAL | RSHIFTEQUAL
-        | KW_BOOL | KW_CATCH | KW_CHAR | KW_CHAR16_T | KW_CHAR32_T
-        | KW_WCHAR_T | KW_CLASS | KW_CONST
+        | KW_ALIGNAS | KW_ALIGNOF | KW_AUTO | KW_BOOL | KW_CATCH
+        | KW_CHAR | KW_CHAR16_T | KW_CHAR32_T
+        | KW_CLASS | KW_CONST | KW_CONSTEXPR | KW_DECLTYPE | KW_DEFAULT
         | KW_DELETE | KW_DOUBLE | KW_DYNAMIC_CAST | KW_ELSE | KW_ENUM
         | KW_DELETE | KW_DOUBLE | KW_DYNAMIC_CAST | KW_ELSE | KW_ENUM
         | KW_EXTERN | KW_EXPLICIT | KW_FALSE
         | KW_EXTERN | KW_EXPLICIT | KW_FALSE
         | KW_FLOAT | KW_FRIEND | KW_FOR | KW_GOTO
         | KW_FLOAT | KW_FRIEND | KW_FOR | KW_GOTO
-        | KW_IF | KW_INLINE | KW_INT
-        | KW_LONG | KW_MUTABLE | KW_NEW | KW_PRIVATE | KW_PROTECTED
+        | KW_IF | KW_INLINE | KW_INT | KW_LONG | KW_MUTABLE
+        | KW_NEW | KW_NULLPTR | KW_OPERATOR | KW_PRIVATE | KW_PROTECTED
         | KW_PUBLIC | KW_PUBLISHED | KW_REGISTER | KW_RETURN
         | KW_PUBLIC | KW_PUBLISHED | KW_REGISTER | KW_RETURN
-        | KW_SHORT | KW_SIGNED | KW_SIZEOF | KW_STATIC | KW_STATIC_CAST
-        | KW_STRUCT | KW_THROW | KW_TRUE | KW_TRY | KW_TYPEDEF | KW_TYPENAME
-        | KW_UNION | KW_UNSIGNED | KW_VIRTUAL | KW_VOID | KW_VOLATILE
-        | KW_WHILE
-        | KW_OPERATOR
+        | KW_SHORT | KW_SIGNED | KW_SIZEOF | KW_STATIC | KW_STATIC_ASSERT
+        | KW_STATIC_CAST | KW_STRUCT | KW_THROW | KW_TRUE | KW_TRY
+        | KW_TYPEDEF | KW_TYPENAME | KW_UNION | KW_UNSIGNED | KW_VIRTUAL
+        | KW_VOID | KW_VOLATILE | KW_WCHAR_T | KW_WHILE
 {
 {
 }
 }
         | '+' | '-' | '*' | '/' | '&' | '|' | '^' | '!' | '~' | '=' | '%'
         | '+' | '-' | '*' | '/' | '&' | '|' | '^' | '!' | '~' | '=' | '%'
@@ -2236,6 +2373,10 @@ no_angle_bracket_const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_ALIGNOF '(' full_type ')' %prec UNARY
+{
+  $$ = new CPPExpression(CPPExpression::alignof_func($3));
 }
 }
         | '!' no_angle_bracket_const_expr    %prec UNARY
         | '!' no_angle_bracket_const_expr    %prec UNARY
 {
 {
@@ -2247,15 +2388,7 @@ no_angle_bracket_const_expr:
 }
 }
         | '-' no_angle_bracket_const_expr    %prec UNARY
         | '-' no_angle_bracket_const_expr    %prec UNARY
 {
 {
-  if ($2->_type == CPPExpression::T_integer) {
-    $$ = $2;
-    $$->_u._integer = -$$->_u._integer;
-  } else if ($2->_type == CPPExpression::T_real) {
-    $$ = $2;
-    $$->_u._real = -$$->_u._real;
-  } else {
-    $$ = new CPPExpression(UNARY_MINUS, $2);
-  }
+  $$ = new CPPExpression(UNARY_MINUS, $2);
 }
 }
         | '*' no_angle_bracket_const_expr    %prec UNARY
         | '*' no_angle_bracket_const_expr    %prec UNARY
 {
 {
@@ -2381,6 +2514,9 @@ const_expr:
 {
 {
   // A constructor call.
   // A constructor call.
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if (type == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert(type != NULL);
   assert(type != NULL);
   $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
   $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
 }
 }
@@ -2463,6 +2599,10 @@ const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_ALIGNOF '(' full_type ')' %prec UNARY
+{
+  $$ = new CPPExpression(CPPExpression::alignof_func($3));
 }
 }
         | KW_NEW predefined_type %prec UNARY
         | KW_NEW predefined_type %prec UNARY
 {
 {
@@ -2482,15 +2622,7 @@ const_expr:
 }
 }
         | '-' const_expr    %prec UNARY
         | '-' const_expr    %prec UNARY
 {
 {
-  if ($2->_type == CPPExpression::T_integer) {
-    $$ = $2;
-    $$->_u._integer = -$$->_u._integer;
-  } else if ($2->_type == CPPExpression::T_real) {
-    $$ = $2;
-    $$->_u._real = -$$->_u._real;
-  } else {
-    $$ = new CPPExpression(UNARY_MINUS, $2);
-  }
+  $$ = new CPPExpression(UNARY_MINUS, $2);
 }
 }
         | '*' const_expr    %prec UNARY
         | '*' const_expr    %prec UNARY
 {
 {
@@ -2623,13 +2755,21 @@ const_operand:
 {
 {
   $$ = new CPPExpression($1);
   $$ = new CPPExpression($1);
 }
 }
-        | string
+        | string_literal
 {
 {
-  $$ = new CPPExpression($1);
+  $$ = $1;
+}
+        | CUSTOM_LITERAL
+{
+  $$ = $1;
 }
 }
         | IDENTIFIER
         | IDENTIFIER
 {
 {
   $$ = new CPPExpression($1, current_scope, global_scope, current_lexer);
   $$ = new CPPExpression($1, current_scope, global_scope, current_lexer);
+}
+        | KW_NULLPTR
+{
+  $$ = new CPPExpression(CPPExpression::get_nullptr());
 }
 }
         ;
         ;
 
 
@@ -2660,6 +2800,10 @@ formal_const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_ALIGNOF '(' full_type ')' %prec UNARY
+{
+  $$ = new CPPExpression(CPPExpression::alignof_func($3));
 }
 }
         | KW_NEW predefined_type %prec UNARY
         | KW_NEW predefined_type %prec UNARY
 {
 {
@@ -2679,15 +2823,7 @@ formal_const_expr:
 }
 }
         | '-' const_expr    %prec UNARY
         | '-' const_expr    %prec UNARY
 {
 {
-  if ($2->_type == CPPExpression::T_integer) {
-    $$ = $2;
-    $$->_u._integer = -$$->_u._integer;
-  } else if ($2->_type == CPPExpression::T_real) {
-    $$ = $2;
-    $$->_u._real = -$$->_u._real;
-  } else {
-    $$ = new CPPExpression(UNARY_MINUS, $2);
-  }
+  $$ = new CPPExpression(UNARY_MINUS, $2);
 }
 }
         | '&' const_expr    %prec UNARY
         | '&' const_expr    %prec UNARY
 {
 {
@@ -2816,9 +2952,21 @@ formal_const_operand:
 {
 {
   $$ = new CPPExpression($1);
   $$ = new CPPExpression($1);
 }
 }
-        | string
+        | string_literal
 {
 {
-  $$ = new CPPExpression($1);
+  $$ = $1;
+}
+        | CUSTOM_LITERAL
+{
+  $$ = $1;
+}
+        | IDENTIFIER
+{
+  $$ = new CPPExpression($1, current_scope, global_scope, current_lexer);
+}
+        | KW_NULLPTR
+{
+  $$ = new CPPExpression(CPPExpression::get_nullptr());
 }
 }
         ;
         ;
 
 
@@ -2850,6 +2998,9 @@ typedefname:
         TYPENAME_IDENTIFIER
         TYPENAME_IDENTIFIER
 {
 {
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if (type == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert(type != NULL);
   assert(type != NULL);
   $$ = type;
   $$ = type;
 }
 }
@@ -2872,14 +3023,29 @@ name:
 }
 }
         ;
         ;
 
 
-string:
-        STRING
+string_literal:
+        SIMPLE_STRING
 {
 {
+  $$ = new CPPExpression($1);
+}
+        | STRING_LITERAL
+{
+  $$ = $1;
+}
+        | string_literal SIMPLE_STRING
+{
+  // The right string takes on the literal type of the left.
   $$ = $1;
   $$ = $1;
+  $$->_str += $2;
 }
 }
-        | string STRING
+        | string_literal STRING_LITERAL
 {
 {
-  $$ = $1 + $2;
+  // We have to check that the two literal types match up.
+  $$ = $1;
+  if ($2->_type != CPPExpression::T_string && $2->_type != $1->_type) {
+    yywarning("cannot concatenate two string literals of different types", @$);
+  }
+  $$->_str += $2->_str;
 }
 }
         ;
         ;
 
 

+ 2 - 2
dtool/src/cppparser/cppBisonDefs.h

@@ -68,8 +68,8 @@ class cppyystype {
 public:
 public:
   string str;
   string str;
   union {
   union {
-    int integer;
-    double real;
+    unsigned long long integer;
+    long double real;
     CPPScope *scope;
     CPPScope *scope;
     CPPDeclaration *decl;
     CPPDeclaration *decl;
     CPPInstance *instance;
     CPPInstance *instance;

+ 3 - 1
dtool/src/cppparser/cppEnumType.cxx

@@ -19,6 +19,7 @@
 #include "cppConstType.h"
 #include "cppConstType.h"
 #include "cppScope.h"
 #include "cppScope.h"
 #include "cppParser.h"
 #include "cppParser.h"
+#include "cppIdentifier.h"
 #include "indent.h"
 #include "indent.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -82,6 +83,7 @@ add_element(const string &name, CPPExpression *value) {
     inst = new CPPInstance(CPPType::new_type(new CPPConstType(_element_type)), ident);
     inst = new CPPInstance(CPPType::new_type(new CPPConstType(_element_type)), ident);
   }
   }
 
 
+  inst->_storage_class |= CPPInstance::SC_constexpr;
   _elements.push_back(inst);
   _elements.push_back(inst);
 
 
   if (value == (CPPExpression *)NULL) {
   if (value == (CPPExpression *)NULL) {
@@ -176,7 +178,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst,
 
 
   bool any_changed = false;
   bool any_changed = false;
 
 
-  for (int i = 0; i < _elements.size(); ++i) {
+  for (size_t i = 0; i < _elements.size(); ++i) {
     CPPInstance *elem_rep =
     CPPInstance *elem_rep =
       _elements[i]->substitute_decl(subst, current_scope, global_scope)
       _elements[i]->substitute_decl(subst, current_scope, global_scope)
       ->as_instance();
       ->as_instance();

+ 318 - 23
dtool/src/cppparser/cppExpression.cxx

@@ -201,6 +201,19 @@ output(ostream &out) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression::
+CPPExpression(unsigned long long value) :
+  CPPDeclaration(CPPFile())
+{
+  _type = T_integer;
+  _u._integer = value;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CPPExpression::Constructor
 //     Function: CPPExpression::Constructor
 //       Access: Public
 //       Access: Public
@@ -220,7 +233,7 @@ CPPExpression(int value) :
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPPExpression::
 CPPExpression::
-CPPExpression(double value) :
+CPPExpression(long double value) :
   CPPDeclaration(CPPFile())
   CPPDeclaration(CPPFile())
 {
 {
   _type = T_real;
   _type = T_real;
@@ -397,6 +410,89 @@ sizeof_func(CPPType *type) {
   return expr;
   return expr;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::named alignof_func constructor
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression CPPExpression::
+alignof_func(CPPType *type) {
+  CPPExpression expr(0);
+  expr._type = T_alignof;
+  expr._u._typecast._to = type;
+  expr._u._typecast._op1 = NULL;
+  return expr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::named literal constructor
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression CPPExpression::
+literal(unsigned long long value, CPPInstance *lit_op) {
+  CPPExpression expr(0);
+  expr._type = T_literal;
+  expr._u._literal._value = new CPPExpression(value);
+  expr._u._literal._operator = lit_op;
+  return expr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::named literal constructor
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression CPPExpression::
+literal(long double value, CPPInstance *lit_op) {
+  CPPExpression expr(0);
+  expr._type = T_literal;
+  expr._u._literal._value = new CPPExpression(value);
+  expr._u._literal._operator = lit_op;
+  return expr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::named literal constructor
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression CPPExpression::
+literal(CPPExpression *value, CPPInstance *lit_op) {
+  CPPExpression expr(0);
+  expr._type = T_literal;
+  expr._u._literal._value = value;
+  expr._u._literal._operator = lit_op;
+  return expr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::named raw_literal constructor
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression CPPExpression::
+raw_literal(const string &raw, CPPInstance *lit_op) {
+  CPPExpression expr(0);
+  expr._type = T_raw_literal;
+  expr._str = raw;
+  expr._u._literal._value = (CPPExpression *)NULL;
+  expr._u._literal._operator = lit_op;
+  return expr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::get_nullptr
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+const CPPExpression &CPPExpression::
+get_nullptr() {
+  static CPPExpression expr(0);
+  expr._type = T_nullptr;
+  return expr;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CPPExpression::Destructor
 //     Function: CPPExpression::Destructor
 //       Access: Public
 //       Access: Public
@@ -416,18 +512,29 @@ evaluate() const {
   Result r1, r2;
   Result r1, r2;
 
 
   switch (_type) {
   switch (_type) {
+  case T_nullptr:
+    return Result((void *)0);
+
   case T_integer:
   case T_integer:
-    return Result(_u._integer);
+    return Result((int)_u._integer);
 
 
   case T_real:
   case T_real:
-    return Result(_u._real);
+    return Result((double)_u._real);
 
 
   case T_string:
   case T_string:
+  case T_wstring:
+  case T_u8string:
+  case T_u16string:
+  case T_u32string:
     return Result();
     return Result();
 
 
   case T_variable:
   case T_variable:
     if (_u._variable->_type != NULL &&
     if (_u._variable->_type != NULL &&
         _u._variable->_initializer != NULL) {
         _u._variable->_initializer != NULL) {
+      // A constexpr variable, which is treated as const.
+      if (_u._variable->_storage_class & CPPInstance::SC_constexpr) {
+        return _u._variable->_initializer->evaluate();
+      }
       // A const variable.  Fetch its assigned value.
       // A const variable.  Fetch its assigned value.
       CPPConstType *const_type = _u._variable->_type->as_const_type();
       CPPConstType *const_type = _u._variable->_type->as_const_type();
       if (const_type != NULL) {
       if (const_type != NULL) {
@@ -468,6 +575,17 @@ evaluate() const {
   case T_sizeof:
   case T_sizeof:
     return Result();
     return Result();
 
 
+  case T_alignof:
+    if (_u._typecast._to != NULL) {
+      // Check if the type is defined with an alignas.  TODO: this should
+      // probably be moved to a virtual getter on CPPType.
+      CPPExtensionType *etype = _u._typecast._to->as_extension_type();
+      if (etype != NULL && etype->_alignment != NULL) {
+        return etype->_alignment->evaluate();
+      }
+    }
+    return Result();
+
   case T_binary_operation:
   case T_binary_operation:
     assert(_u._op._op2 != NULL);
     assert(_u._op._op2 != NULL);
     r2 = _u._op._op2->evaluate();
     r2 = _u._op._op2->evaluate();
@@ -503,23 +621,39 @@ evaluate() const {
       // long as we can evaluate the second one *and* that comes out
       // long as we can evaluate the second one *and* that comes out
       // to be true.
       // to be true.
       if (_u._op._operator == OROR && r2._type == RT_integer &&
       if (_u._op._operator == OROR && r2._type == RT_integer &&
-          r2.as_integer() != 0) {
+          r2.as_boolean()) {
         return r2;
         return r2;
       }
       }
 
 
       // Ditto for the operator being && and the second one coming out
       // Ditto for the operator being && and the second one coming out
       // false.
       // false.
       if (_u._op._operator == ANDAND && r2._type == RT_integer &&
       if (_u._op._operator == ANDAND && r2._type == RT_integer &&
-          r2.as_integer() == 0) {
+          !r2.as_boolean()) {
         return r2;
         return r2;
       }
       }
 
 
+      // Also for the operator being [] and the operand being a string.
+      if (_u._op._operator == '[' && r2._type == RT_integer &&
+          (_u._op._op1->_type == T_string ||
+           _u._op._op1->_type == T_u8string)) {
+
+        int index = (int)r2.as_integer();
+        if ((size_t)index == _u._op._op1->_str.size()) {
+          return Result(0);
+        } else if (index >= 0 && (size_t)index < _u._op._op1->_str.size()) {
+          return Result(_u._op._op1->_str[(size_t)index]);
+        } else {
+          cerr << "array index " << index << " out of bounds of string literal "
+               << *_u._op._op1 << "\n";
+        }
+      }
+
       return r1;
       return r1;
     }
     }
 
 
     switch (_u._op._operator) {
     switch (_u._op._operator) {
     case UNARY_NOT:
     case UNARY_NOT:
-      return Result(!r1.as_integer());
+      return Result(!r1.as_boolean());
 
 
     case UNARY_NEGATE:
     case UNARY_NEGATE:
       return Result(~r1.as_integer());
       return Result(~r1.as_integer());
@@ -569,14 +703,14 @@ evaluate() const {
       return Result(r1.as_integer() & r2.as_integer());
       return Result(r1.as_integer() & r2.as_integer());
 
 
     case OROR:
     case OROR:
-      if (r1.as_integer()) {
+      if (r1.as_boolean()) {
         return r1;
         return r1;
       } else {
       } else {
         return r2;
         return r2;
       }
       }
 
 
     case ANDAND:
     case ANDAND:
-      if (r1.as_integer()) {
+      if (r1.as_boolean()) {
         return r2;
         return r2;
       } else {
       } else {
         return r1;
         return r1;
@@ -652,6 +786,10 @@ evaluate() const {
       abort();
       abort();
     }
     }
 
 
+  case T_literal:
+  case T_raw_literal:
+    return Result();
+
   default:
   default:
     cerr << "**invalid operand**\n";
     cerr << "**invalid operand**\n";
     abort();
     abort();
@@ -671,25 +809,51 @@ determine_type() const {
   CPPType *t1 = (CPPType *)NULL;
   CPPType *t1 = (CPPType *)NULL;
   CPPType *t2 = (CPPType *)NULL;
   CPPType *t2 = (CPPType *)NULL;
 
 
-  CPPType *int_type =
+  static CPPType *nullptr_type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_nullptr));
+
+  static CPPType *int_type =
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int));
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int));
 
 
-  CPPType *bool_type =
+  static CPPType *unsigned_long_type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int,
+                                        CPPSimpleType::F_unsigned |
+                                        CPPSimpleType::F_long));
+
+  static CPPType *bool_type =
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_bool));
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_bool));
 
 
-  CPPType *float_type =
+  static CPPType *float_type =
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_double));
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_double));
 
 
-  CPPType *char_type =
+  static CPPType *char_type =
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char));
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char));
 
 
-  CPPType *const_char_type =
-    CPPType::new_type(new CPPConstType(char_type));
+  static CPPType *wchar_type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_wchar_t));
+
+  static CPPType *char16_type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char16_t));
+
+  static CPPType *char32_type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char32_t));
+
+  static CPPType *char_str_type = CPPType::new_type(
+    new CPPPointerType(CPPType::new_type(new CPPConstType(char_type))));
+
+  static CPPType *wchar_str_type = CPPType::new_type(
+    new CPPPointerType(CPPType::new_type(new CPPConstType(wchar_type))));
 
 
-  CPPType *char_star_type =
-    CPPType::new_type(new CPPPointerType(const_char_type));
+  static CPPType *char16_str_type = CPPType::new_type(
+    new CPPPointerType(CPPType::new_type(new CPPConstType(char16_type))));
+
+  static CPPType *char32_str_type = CPPType::new_type(
+    new CPPPointerType(CPPType::new_type(new CPPConstType(char32_type))));
 
 
   switch (_type) {
   switch (_type) {
+  case T_nullptr:
+    return nullptr_type;
+
   case T_integer:
   case T_integer:
     return int_type;
     return int_type;
 
 
@@ -697,7 +861,19 @@ determine_type() const {
     return float_type;
     return float_type;
 
 
   case T_string:
   case T_string:
-    return char_star_type;
+    return char_str_type;
+
+  case T_wstring:
+    return wchar_str_type;
+
+  case T_u8string:
+    return char_str_type;
+
+  case T_u16string:
+    return char16_str_type;
+
+  case T_u32string:
+    return char32_str_type;
 
 
   case T_variable:
   case T_variable:
     return _u._variable->_type;
     return _u._variable->_type;
@@ -725,7 +901,11 @@ determine_type() const {
     return CPPType::new_type(new CPPPointerType(_u._typecast._to));
     return CPPType::new_type(new CPPPointerType(_u._typecast._to));
 
 
   case T_sizeof:
   case T_sizeof:
-    return int_type;
+  case T_alignof:
+    // Note: this should actually be size_t, but that is defined as a
+    // typedef in parser-inc.  We could try to resolve it, but that's
+    // hacky.  Eh, it's probably not worth the effort to get this right.
+    return unsigned_long_type;
 
 
   case T_binary_operation:
   case T_binary_operation:
   case T_trinary_operation:
   case T_trinary_operation:
@@ -819,6 +999,18 @@ determine_type() const {
       abort();
       abort();
     }
     }
 
 
+  case T_literal:
+  case T_raw_literal:
+    if (_u._literal._operator != NULL) {
+      CPPType *type = _u._literal._operator->_type;
+
+      CPPFunctionType *ftype = type->as_function_type();
+      if (ftype != (CPPFunctionType *)NULL) {
+        return ftype->_return_type;
+      }
+    }
+    return NULL;
+
   default:
   default:
     cerr << "**invalid operand**\n";
     cerr << "**invalid operand**\n";
     abort();
     abort();
@@ -842,9 +1034,14 @@ is_fully_specified() const {
   }
   }
 
 
   switch (_type) {
   switch (_type) {
+  case T_nullptr:
   case T_integer:
   case T_integer:
   case T_real:
   case T_real:
   case T_string:
   case T_string:
+  case T_wstring:
+  case T_u8string:
+  case T_u16string:
+  case T_u32string:
     return false;
     return false;
 
 
   case T_variable:
   case T_variable:
@@ -865,6 +1062,7 @@ is_fully_specified() const {
   case T_default_construct:
   case T_default_construct:
   case T_default_new:
   case T_default_new:
   case T_sizeof:
   case T_sizeof:
+  case T_alignof:
     return _u._typecast._to->is_fully_specified();
     return _u._typecast._to->is_fully_specified();
 
 
   case T_trinary_operation:
   case T_trinary_operation:
@@ -882,6 +1080,13 @@ is_fully_specified() const {
   case T_unary_operation:
   case T_unary_operation:
     return _u._op._op1->is_fully_specified();
     return _u._op._op1->is_fully_specified();
 
 
+  case T_literal:
+    return _u._literal._value->is_fully_specified() &&
+      _u._literal._operator->is_fully_specified();
+
+  case T_raw_literal:
+    return _u._literal._value->is_fully_specified();
+
   default:
   default:
     return true;
     return true;
   }
   }
@@ -972,6 +1177,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst,
   case T_default_construct:
   case T_default_construct:
   case T_default_new:
   case T_default_new:
   case T_sizeof:
   case T_sizeof:
+  case T_alignof:
     rep->_u._typecast._to =
     rep->_u._typecast._to =
       _u._typecast._to->substitute_decl(subst, current_scope, global_scope)
       _u._typecast._to->substitute_decl(subst, current_scope, global_scope)
       ->as_type();
       ->as_type();
@@ -1025,6 +1231,9 @@ is_tbd() const {
   case T_variable:
   case T_variable:
     if (_u._variable->_type != NULL &&
     if (_u._variable->_type != NULL &&
         _u._variable->_initializer != NULL) {
         _u._variable->_initializer != NULL) {
+      if (_u._variable->_storage_class & CPPInstance::SC_constexpr) {
+        return false;
+      }
       CPPConstType *const_type = _u._variable->_type->as_const_type();
       CPPConstType *const_type = _u._variable->_type->as_const_type();
       if (const_type != NULL) {
       if (const_type != NULL) {
         return false;
         return false;
@@ -1042,6 +1251,7 @@ is_tbd() const {
   case T_default_construct:
   case T_default_construct:
   case T_default_new:
   case T_default_new:
   case T_sizeof:
   case T_sizeof:
+  case T_alignof:
     return _u._typecast._to->is_tbd();
     return _u._typecast._to->is_tbd();
 
 
   case T_trinary_operation:
   case T_trinary_operation:
@@ -1075,6 +1285,10 @@ is_tbd() const {
 void CPPExpression::
 void CPPExpression::
 output(ostream &out, int indent_level, CPPScope *scope, bool) const {
 output(ostream &out, int indent_level, CPPScope *scope, bool) const {
   switch (_type) {
   switch (_type) {
+  case T_nullptr:
+    out << "nullptr";
+    break;
+
   case T_integer:
   case T_integer:
     out << _u._integer;
     out << _u._integer;
     break;
     break;
@@ -1090,8 +1304,29 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     break;
     break;
 
 
   case T_string:
   case T_string:
-    out << '"';
+  case T_wstring:
+  case T_u8string:
+  case T_u16string:
+  case T_u32string:
     {
     {
+      switch (_type) {
+      case T_wstring:
+        out << 'L';
+        break;
+      case T_u8string:
+        out << "u8";
+        break;
+      case T_u16string:
+        out << "u";
+        break;
+      case T_u32string:
+        out << "U";
+        break;
+      default:
+        break;
+      }
+      // We don't really care about preserving the encoding for now.
+      out << '"';
       string::const_iterator si;
       string::const_iterator si;
       for (si = _str.begin(); si != _str.end(); ++si) {
       for (si = _str.begin(); si != _str.end(); ++si) {
         switch (*si) {
         switch (*si) {
@@ -1135,9 +1370,10 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     if (_u._variable->_type != NULL &&
     if (_u._variable->_type != NULL &&
         _u._variable->_initializer != NULL &&
         _u._variable->_initializer != NULL &&
         _u._variable->_vis > V_public) {
         _u._variable->_vis > V_public) {
-      // A const variable.  Fetch its assigned value.
+      // A constexpr or const variable.  Fetch its assigned value.
       CPPConstType *const_type = _u._variable->_type->as_const_type();
       CPPConstType *const_type = _u._variable->_type->as_const_type();
-      if (const_type != NULL) {
+      if ((_u._variable->_storage_class & CPPInstance::SC_constexpr) != 0 ||
+          const_type != NULL) {
         _u._variable->_initializer->output(out, indent_level, scope, false);
         _u._variable->_initializer->output(out, indent_level, scope, false);
         break;
         break;
       }
       }
@@ -1193,6 +1429,12 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     out << ")";
     out << ")";
     break;
     break;
 
 
+  case T_alignof:
+    out << "alignof(";
+    _u._typecast._to->output(out, indent_level, scope, false);
+    out << ")";
+    break;
+
   case T_unary_operation:
   case T_unary_operation:
     switch (_u._op._operator) {
     switch (_u._op._operator) {
     case UNARY_NOT:
     case UNARY_NOT:
@@ -1208,9 +1450,8 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
       break;
       break;
 
 
     case UNARY_MINUS:
     case UNARY_MINUS:
-      out << "(- ";
+      out << '-';
       _u._op._op1->output(out, indent_level, scope, false);
       _u._op._op1->output(out, indent_level, scope, false);
-      out << ")";
       break;
       break;
 
 
     case UNARY_STAR:
     case UNARY_STAR:
@@ -1362,6 +1603,24 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     out << ")";
     out << ")";
     break;
     break;
 
 
+  case T_literal:
+    _u._literal._value->output(out, indent_level, scope, false);
+    if (_u._literal._operator != NULL) {
+      string name = _u._literal._operator->get_simple_name();
+      assert(name.substr(0, 12) == "operator \"\" ");
+      out << name.substr(12);
+    }
+    break;
+
+  case T_raw_literal:
+    out << _str;
+    if (_u._literal._operator != NULL) {
+      string name = _u._literal._operator->get_simple_name();
+      assert(name.substr(0, 12) == "operator \"\" ");
+      out << name.substr(12);
+    }
+    break;
+
   default:
   default:
     out << "(** invalid operand type " << (int)_type << " **)";
     out << "(** invalid operand type " << (int)_type << " **)";
   }
   }
@@ -1456,6 +1715,9 @@ is_equal(const CPPDeclaration *other) const {
   }
   }
 
 
   switch (_type) {
   switch (_type) {
+  case T_nullptr:
+    return true;
+
   case T_integer:
   case T_integer:
     return _u._integer == ot->_u._integer;
     return _u._integer == ot->_u._integer;
 
 
@@ -1463,6 +1725,10 @@ is_equal(const CPPDeclaration *other) const {
     return _u._real == ot->_u._real;
     return _u._real == ot->_u._real;
 
 
   case T_string:
   case T_string:
+  case T_wstring:
+  case T_u8string:
+  case T_u16string:
+  case T_u32string:
     return _str == ot->_str;
     return _str == ot->_str;
 
 
   case T_variable:
   case T_variable:
@@ -1483,6 +1749,7 @@ is_equal(const CPPDeclaration *other) const {
   case T_default_construct:
   case T_default_construct:
   case T_default_new:
   case T_default_new:
   case T_sizeof:
   case T_sizeof:
+  case T_alignof:
     return _u._typecast._to == ot->_u._typecast._to;
     return _u._typecast._to == ot->_u._typecast._to;
 
 
   case T_unary_operation:
   case T_unary_operation:
@@ -1496,6 +1763,14 @@ is_equal(const CPPDeclaration *other) const {
     return *_u._op._op1 == *ot->_u._op._op1 &&
     return *_u._op._op1 == *ot->_u._op._op1 &&
       *_u._op._op2 == *ot->_u._op._op2;
       *_u._op._op2 == *ot->_u._op._op2;
 
 
+  case T_literal:
+    return *_u._literal._value == *ot->_u._literal._value &&
+      _u._literal._operator == ot->_u._literal._operator;
+
+  case T_raw_literal:
+    return _str == ot->_str &&
+      _u._literal._operator == ot->_u._literal._operator;
+
   default:
   default:
     cerr << "(** invalid operand type " << (int)_type << " **)";
     cerr << "(** invalid operand type " << (int)_type << " **)";
   }
   }
@@ -1520,6 +1795,9 @@ is_less(const CPPDeclaration *other) const {
   }
   }
 
 
   switch (_type) {
   switch (_type) {
+  case T_nullptr:
+    return false;
+
   case T_integer:
   case T_integer:
     return _u._integer < ot->_u._integer;
     return _u._integer < ot->_u._integer;
 
 
@@ -1527,6 +1805,10 @@ is_less(const CPPDeclaration *other) const {
     return _u._real < ot->_u._real;
     return _u._real < ot->_u._real;
 
 
   case T_string:
   case T_string:
+  case T_wstring:
+  case T_u8string:
+  case T_u16string:
+  case T_u32string:
     return _str < ot->_str;
     return _str < ot->_str;
 
 
   case T_variable:
   case T_variable:
@@ -1549,6 +1831,7 @@ is_less(const CPPDeclaration *other) const {
   case T_default_construct:
   case T_default_construct:
   case T_default_new:
   case T_default_new:
   case T_sizeof:
   case T_sizeof:
+  case T_alignof:
     return _u._typecast._to < ot->_u._typecast._to;
     return _u._typecast._to < ot->_u._typecast._to;
 
 
   case T_trinary_operation:
   case T_trinary_operation:
@@ -1566,6 +1849,18 @@ is_less(const CPPDeclaration *other) const {
   case T_unary_operation:
   case T_unary_operation:
     return *_u._op._op1 < *ot->_u._op._op1;
     return *_u._op._op1 < *ot->_u._op._op1;
 
 
+  case T_literal:
+    if (_u._literal._operator != ot->_u._literal._operator) {
+      return _u._literal._operator < ot->_u._literal._operator;
+    }
+    return *_u._literal._value < *ot->_u._literal._value;
+
+  case T_raw_literal:
+    if (_u._literal._operator != ot->_u._literal._operator) {
+      return _u._literal._operator < ot->_u._literal._operator;
+    }
+    return _str < ot->_str;
+
   default:
   default:
     cerr << "(** invalid operand type " << (int)_type << " **)";
     cerr << "(** invalid operand type " << (int)_type << " **)";
   }
   }

+ 25 - 5
dtool/src/cppparser/cppExpression.h

@@ -30,9 +30,10 @@ class CPPFunctionGroup;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class CPPExpression : public CPPDeclaration {
 class CPPExpression : public CPPDeclaration {
 public:
 public:
+  CPPExpression(unsigned long long value);
   CPPExpression(int value);
   CPPExpression(int value);
   CPPExpression(const string &value);
   CPPExpression(const string &value);
-  CPPExpression(double value);
+  CPPExpression(long double value);
   CPPExpression(CPPIdentifier *ident, CPPScope *current_scope,
   CPPExpression(CPPIdentifier *ident, CPPScope *current_scope,
                 CPPScope *global_scope, CPPPreprocessor *error_sink = NULL);
                 CPPScope *global_scope, CPPPreprocessor *error_sink = NULL);
   CPPExpression(int unary_operator, CPPExpression *op1);
   CPPExpression(int unary_operator, CPPExpression *op1);
@@ -43,6 +44,14 @@ public:
   static CPPExpression construct_op(CPPType *type, CPPExpression *op1);
   static CPPExpression construct_op(CPPType *type, CPPExpression *op1);
   static CPPExpression new_op(CPPType *type, CPPExpression *op1 = NULL);
   static CPPExpression new_op(CPPType *type, CPPExpression *op1 = NULL);
   static CPPExpression sizeof_func(CPPType *type);
   static CPPExpression sizeof_func(CPPType *type);
+  static CPPExpression alignof_func(CPPType *type);
+
+  static CPPExpression literal(unsigned long long value, CPPInstance *lit_op);
+  static CPPExpression literal(long double value, CPPInstance *lit_op);
+  static CPPExpression literal(CPPExpression *value, CPPInstance *lit_op);
+  static CPPExpression raw_literal(const string &raw, CPPInstance *lit_op);
+
+  static const CPPExpression &get_nullptr();
 
 
   ~CPPExpression();
   ~CPPExpression();
 
 
@@ -92,9 +101,14 @@ public:
 
 
 
 
   enum Type {
   enum Type {
+    T_nullptr,
     T_integer,
     T_integer,
     T_real,
     T_real,
     T_string,
     T_string,
+    T_wstring,
+    T_u8string,
+    T_u16string,
+    T_u32string,
     T_variable,
     T_variable,
     T_function,
     T_function,
     T_unknown_ident,
     T_unknown_ident,
@@ -104,16 +118,19 @@ public:
     T_new,
     T_new,
     T_default_new,
     T_default_new,
     T_sizeof,
     T_sizeof,
+    T_alignof,
     T_unary_operation,
     T_unary_operation,
     T_binary_operation,
     T_binary_operation,
     T_trinary_operation,
     T_trinary_operation,
+    T_literal,
+    T_raw_literal,
   };
   };
 
 
   Type _type;
   Type _type;
   string _str;
   string _str;
   union {
   union {
-    int _integer;
-    double _real;
+    unsigned long long _integer;
+    long double _real;
     CPPInstance *_variable;
     CPPInstance *_variable;
     CPPFunctionGroup *_fgroup;
     CPPFunctionGroup *_fgroup;
     CPPIdentifier *_ident;
     CPPIdentifier *_ident;
@@ -131,6 +148,11 @@ public:
       CPPExpression *_op2;
       CPPExpression *_op2;
       CPPExpression *_op3;
       CPPExpression *_op3;
     } _op;
     } _op;
+    class {
+    public:
+      CPPInstance *_operator;
+      CPPExpression *_value;
+    } _literal;
   } _u;
   } _u;
 
 
 protected:
 protected:
@@ -146,5 +168,3 @@ operator << (ostream &out, const CPPExpression::Result &result) {
 }
 }
 
 
 #endif
 #endif
-
-

+ 2 - 1
dtool/src/cppparser/cppExtensionType.cxx

@@ -29,7 +29,8 @@ CPPExtensionType(CPPExtensionType::Type type,
                  CPPIdentifier *ident, CPPScope *current_scope,
                  CPPIdentifier *ident, CPPScope *current_scope,
                  const CPPFile &file) :
                  const CPPFile &file) :
   CPPType(file),
   CPPType(file),
-  _type(type), _ident(ident)
+  _type(type), _ident(ident),
+  _alignment(NULL)
 {
 {
   if (_ident != NULL) {
   if (_ident != NULL) {
     _ident->_native_scope = current_scope;
     _ident->_native_scope = current_scope;

+ 1 - 1
dtool/src/cppparser/cppExtensionType.h

@@ -65,9 +65,9 @@ public:
 
 
   virtual CPPExtensionType *as_extension_type();
   virtual CPPExtensionType *as_extension_type();
 
 
-
   Type _type;
   Type _type;
   CPPIdentifier *_ident;
   CPPIdentifier *_ident;
+  CPPExpression *_alignment;
 };
 };
 
 
 ostream &operator << (ostream &out, CPPExtensionType::Type type);
 ostream &operator << (ostream &out, CPPExtensionType::Type type);

+ 56 - 6
dtool/src/cppparser/cppIdentifier.cxx

@@ -28,9 +28,14 @@
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPPIdentifier::
 CPPIdentifier::
-CPPIdentifier(const string &name, const CPPFile &file) : _file(file) {
+CPPIdentifier(const string &name, const CPPFile &file) {
   _names.push_back(CPPNameComponent(name));
   _names.push_back(CPPNameComponent(name));
   _native_scope = (CPPScope *)NULL;
   _native_scope = (CPPScope *)NULL;
+  _loc.first_line = 0;
+  _loc.first_column = 0;
+  _loc.last_line = 0;
+  _loc.last_column = 0;
+  _loc.file = file;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -39,7 +44,34 @@ CPPIdentifier(const string &name, const CPPFile &file) : _file(file) {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPPIdentifier::
 CPPIdentifier::
-CPPIdentifier(const CPPNameComponent &name, const CPPFile &file) : _file(file) {
+CPPIdentifier(const CPPNameComponent &name, const CPPFile &file) {
+  _names.push_back(name);
+  _native_scope = (CPPScope *)NULL;
+  _loc.first_line = 0;
+  _loc.first_column = 0;
+  _loc.last_line = 0;
+  _loc.last_column = 0;
+  _loc.file = file;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPIdentifier::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPIdentifier::
+CPPIdentifier(const string &name, const cppyyltype &loc) : _loc(loc) {
+  _names.push_back(CPPNameComponent(name));
+  _native_scope = (CPPScope *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPIdentifier::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPIdentifier::
+CPPIdentifier(const CPPNameComponent &name, const cppyyltype &loc) : _loc(loc) {
   _names.push_back(name);
   _names.push_back(name);
   _native_scope = (CPPScope *)NULL;
   _native_scope = (CPPScope *)NULL;
 }
 }
@@ -155,6 +187,12 @@ get_local_name(CPPScope *scope) const {
     // last name.
     // last name.
     CPPScope *my_scope = get_scope(scope, NULL);
     CPPScope *my_scope = get_scope(scope, NULL);
 
 
+    // Strip off template scopes, since they don't add anything
+    // particularly meaningful to the local name.
+    while (my_scope != NULL && my_scope->as_template_scope() != NULL) {
+      my_scope = my_scope->get_parent_scope();
+    }
+
     if (my_scope == NULL) {
     if (my_scope == NULL) {
       result = get_fully_scoped_name();
       result = get_fully_scoped_name();
     } else if (my_scope == scope) {
     } else if (my_scope == scope) {
@@ -256,7 +294,8 @@ get_scope(CPPScope *current_scope, CPPScope *global_scope,
       if (error_sink != NULL) {
       if (error_sink != NULL) {
         error_sink->error("Symbol " + _names[i].get_name() +
         error_sink->error("Symbol " + _names[i].get_name() +
                           " is not a known scope in " +
                           " is not a known scope in " +
-                          scope->get_fully_scoped_name());
+                          scope->get_fully_scoped_name(),
+                          _loc);
       }
       }
       return (CPPScope *)NULL;
       return (CPPScope *)NULL;
     }
     }
@@ -302,7 +341,8 @@ get_scope(CPPScope *current_scope, CPPScope *global_scope,
       if (error_sink != NULL) {
       if (error_sink != NULL) {
         error_sink->error("Symbol " + _names[i].get_name() +
         error_sink->error("Symbol " + _names[i].get_name() +
                           " is not a known scope in " +
                           " is not a known scope in " +
-                          scope->get_fully_scoped_name());
+                          scope->get_fully_scoped_name(),
+                          _loc);
       }
       }
       return (CPPScope *)NULL;
       return (CPPScope *)NULL;
     }
     }
@@ -429,7 +469,12 @@ find_symbol(CPPScope *current_scope, CPPScope *global_scope,
 
 
   CPPDeclaration *sym;
   CPPDeclaration *sym;
   if (!_names.back().has_templ()) {
   if (!_names.back().has_templ()) {
-    sym = scope->find_symbol(get_simple_name());
+    if (_names.size() > 1 && scope->get_simple_name() == get_simple_name()) {
+      // An identifier like Class::Class always refers to the class constructor.
+      sym = scope->get_struct_type()->get_constructor();
+    } else {
+      sym = scope->find_symbol(get_simple_name());
+    }
 
 
   } else {
   } else {
     sym = scope->find_template(get_simple_name());
     sym = scope->find_template(get_simple_name());
@@ -465,7 +510,12 @@ find_symbol(CPPScope *current_scope, CPPScope *global_scope,
 
 
   CPPDeclaration *sym;
   CPPDeclaration *sym;
   if (!_names.back().has_templ()) {
   if (!_names.back().has_templ()) {
-    sym = scope->find_symbol(get_simple_name());
+    if (_names.size() > 1 && scope->get_simple_name() == get_simple_name()) {
+      // An identifier like Class::Class always refers to the class constructor.
+      sym = scope->get_struct_type()->get_constructor();
+    } else {
+      sym = scope->find_symbol(get_simple_name());
+    }
 
 
   } else {
   } else {
     sym = scope->find_template(get_simple_name());
     sym = scope->find_template(get_simple_name());

+ 4 - 1
dtool/src/cppparser/cppIdentifier.h

@@ -20,6 +20,7 @@
 #include "cppDeclaration.h"
 #include "cppDeclaration.h"
 #include "cppNameComponent.h"
 #include "cppNameComponent.h"
 #include "cppFile.h"
 #include "cppFile.h"
+#include "cppBisonDefs.h"
 
 
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
@@ -37,6 +38,8 @@ class CPPIdentifier {
 public:
 public:
   CPPIdentifier(const string &name, const CPPFile &file = CPPFile());
   CPPIdentifier(const string &name, const CPPFile &file = CPPFile());
   CPPIdentifier(const CPPNameComponent &name, const CPPFile &file = CPPFile());
   CPPIdentifier(const CPPNameComponent &name, const CPPFile &file = CPPFile());
+  CPPIdentifier(const string &name, const cppyyltype &loc);
+  CPPIdentifier(const CPPNameComponent &name, const cppyyltype &loc);
   void add_name(const string &name);
   void add_name(const string &name);
   void add_name(const CPPNameComponent &name);
   void add_name(const CPPNameComponent &name);
 
 
@@ -91,7 +94,7 @@ public:
   typedef vector<CPPNameComponent> Names;
   typedef vector<CPPNameComponent> Names;
   Names _names;
   Names _names;
   CPPScope *_native_scope;
   CPPScope *_native_scope;
-  CPPFile _file;
+  cppyyltype _loc;
 };
 };
 
 
 inline ostream &operator << (ostream &out, const CPPIdentifier &identifier) {
 inline ostream &operator << (ostream &out, const CPPIdentifier &identifier) {

+ 45 - 4
dtool/src/cppparser/cppInstance.cxx

@@ -38,7 +38,8 @@ CPPInstance(CPPType *type, const string &name, int storage_class) :
   CPPDeclaration(CPPFile()),
   CPPDeclaration(CPPFile()),
   _type(type),
   _type(type),
   _ident(new CPPIdentifier(name)),
   _ident(new CPPIdentifier(name)),
-  _storage_class(storage_class)
+  _storage_class(storage_class),
+  _alignment(NULL)
 {
 {
   _initializer = NULL;
   _initializer = NULL;
 }
 }
@@ -53,7 +54,8 @@ CPPInstance(CPPType *type, CPPIdentifier *ident, int storage_class) :
   CPPDeclaration(CPPFile()),
   CPPDeclaration(CPPFile()),
   _type(type),
   _type(type),
   _ident(ident),
   _ident(ident),
-  _storage_class(storage_class)
+  _storage_class(storage_class),
+  _alignment(NULL)
 {
 {
   _initializer = NULL;
   _initializer = NULL;
 }
 }
@@ -69,7 +71,8 @@ CPPInstance(CPPType *type, CPPIdentifier *ident, int storage_class) :
 CPPInstance::
 CPPInstance::
 CPPInstance(CPPType *type, CPPInstanceIdentifier *ii, int storage_class,
 CPPInstance(CPPType *type, CPPInstanceIdentifier *ii, int storage_class,
             const CPPFile &file) :
             const CPPFile &file) :
-  CPPDeclaration(file)
+  CPPDeclaration(file),
+  _alignment(NULL)
 {
 {
   _type = ii->unroll_type(type);
   _type = ii->unroll_type(type);
   _ident = ii->_ident;
   _ident = ii->_ident;
@@ -102,7 +105,8 @@ CPPInstance(const CPPInstance &copy) :
   _type(copy._type),
   _type(copy._type),
   _ident(copy._ident),
   _ident(copy._ident),
   _initializer(copy._initializer),
   _initializer(copy._initializer),
-  _storage_class(copy._storage_class)
+  _storage_class(copy._storage_class),
+  _alignment(copy._alignment)
 {
 {
   assert(_type != NULL);
   assert(_type != NULL);
 }
 }
@@ -153,6 +157,9 @@ operator == (const CPPInstance &other) const {
   if (_storage_class != other._storage_class) {
   if (_storage_class != other._storage_class) {
     return false;
     return false;
   }
   }
+  if (_alignment != other._alignment) {
+    return false;
+  }
 
 
   // We *do* care about the identifier.  We need to differentiate
   // We *do* care about the identifier.  We need to differentiate
   // types of function variables, among possibly other things, based
   // types of function variables, among possibly other things, based
@@ -199,6 +206,9 @@ operator < (const CPPInstance &other) const {
   if (_storage_class != other._storage_class) {
   if (_storage_class != other._storage_class) {
     return _storage_class < other._storage_class;
     return _storage_class < other._storage_class;
   }
   }
+  if (_alignment != other._alignment) {
+    return _alignment < other._alignment;
+  }
 
 
   // We *do* care about the identifier.  We need to differentiate
   // We *do* care about the identifier.  We need to differentiate
   // types of function variables, among possibly other things, based
   // types of function variables, among possibly other things, based
@@ -252,6 +262,29 @@ set_initializer(CPPExpression *initializer) {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CPPInstance::set_alignment
+//       Access: Public
+//  Description: Sets the number of bytes to align this instance to.
+////////////////////////////////////////////////////////////////////
+void CPPInstance::
+set_alignment(int align) {
+  _alignment = new CPPExpression(align);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPInstance::set_alignment
+//       Access: Public
+//  Description: Sets the expression that is used to determine the
+//               required alignment for the variable.  This should
+//               be a constant expression, but we don't presently
+//               verify that it is.
+////////////////////////////////////////////////////////////////////
+void CPPInstance::
+set_alignment(CPPExpression *const_expr) {
+  _alignment = const_expr;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CPPInstance::is_scoped
 //     Function: CPPInstance::is_scoped
 //       Access: Public
 //       Access: Public
@@ -536,6 +569,11 @@ output(ostream &out, int indent_level, CPPScope *scope, bool complete,
     get_template_scope()->_parameters.write_formal(out, scope);
     get_template_scope()->_parameters.write_formal(out, scope);
     indent(out, indent_level);
     indent(out, indent_level);
   }
   }
+
+  if (_alignment != NULL) {
+    out << "alignas(" << *_alignment << ") ";
+  }
+
   if (_storage_class & SC_static) {
   if (_storage_class & SC_static) {
     out << "static ";
     out << "static ";
   }
   }
@@ -563,6 +601,9 @@ output(ostream &out, int indent_level, CPPScope *scope, bool complete,
   if (_storage_class & SC_mutable) {
   if (_storage_class & SC_mutable) {
     out << "mutable ";
     out << "mutable ";
   }
   }
+  if (_storage_class & SC_constexpr) {
+    out << "constexpr ";
+  }
 
 
   string name;
   string name;
   if (_ident != NULL) {
   if (_ident != NULL) {

+ 7 - 4
dtool/src/cppparser/cppInstance.h

@@ -19,7 +19,6 @@
 
 
 #include "cppDeclaration.h"
 #include "cppDeclaration.h"
 #include "cppType.h"
 #include "cppType.h"
-#include "cppIdentifier.h"
 #include "cppTemplateParameterList.h"
 #include "cppTemplateParameterList.h"
 
 
 class CPPInstanceIdentifier;
 class CPPInstanceIdentifier;
@@ -47,19 +46,20 @@ public:
     SC_pure_virtual = 0x0080,
     SC_pure_virtual = 0x0080,
     SC_volatile     = 0x0100,
     SC_volatile     = 0x0100,
     SC_mutable      = 0x0200,
     SC_mutable      = 0x0200,
+    SC_constexpr    = 0x0400,
 
 
     // This bit is only set by CPPStructType::check_virtual().
     // This bit is only set by CPPStructType::check_virtual().
-    SC_inherited_virtual = 0x0400,
+    SC_inherited_virtual = 0x0800,
 
 
     // This is a special "storage class" for methods tagged with the
     // This is a special "storage class" for methods tagged with the
     // BLOCKING macro (i.e. the special __blocking keyword).  These
     // BLOCKING macro (i.e. the special __blocking keyword).  These
     // are methods that might block and therefore need to release
     // are methods that might block and therefore need to release
     // Python threads for their duration.
     // Python threads for their duration.
-    SC_blocking     = 0x0800,
+    SC_blocking     = 0x1000,
 
 
     // And this is for methods tagged with __extension, which declares
     // And this is for methods tagged with __extension, which declares
     // extension methods defined separately from the source code.
     // extension methods defined separately from the source code.
-    SC_extension    = 0x1000,
+    SC_extension    = 0x2000,
   };
   };
 
 
   CPPInstance(CPPType *type, const string &name, int storage_class = 0);
   CPPInstance(CPPType *type, const string &name, int storage_class = 0);
@@ -78,6 +78,8 @@ public:
   bool operator < (const CPPInstance &other) const;
   bool operator < (const CPPInstance &other) const;
 
 
   void set_initializer(CPPExpression *initializer);
   void set_initializer(CPPExpression *initializer);
+  void set_alignment(int align);
+  void set_alignment(CPPExpression *const_expr);
 
 
   bool is_scoped() const;
   bool is_scoped() const;
   CPPScope *get_scope(CPPScope *current_scope, CPPScope *global_scope,
   CPPScope *get_scope(CPPScope *current_scope, CPPScope *global_scope,
@@ -112,6 +114,7 @@ public:
   CPPExpression *_initializer;
   CPPExpression *_initializer;
 
 
   int _storage_class;
   int _storage_class;
+  CPPExpression *_alignment;
 
 
 private:
 private:
   typedef map<const CPPTemplateParameterList *, CPPInstance *, CPPTPLCompare> Instantiations;
   typedef map<const CPPTemplateParameterList *, CPPInstance *, CPPTPLCompare> Instantiations;

+ 5 - 0
dtool/src/cppparser/cppInstanceIdentifier.cxx

@@ -286,6 +286,11 @@ r_unroll_type(CPPType *start_type,
     result = new CPPConstType(r_unroll_type(start_type, mi));
     result = new CPPConstType(r_unroll_type(start_type, mi));
     break;
     break;
 
 
+  case IIT_volatile:
+    // Just pass it through for now.
+    result = r_unroll_type(start_type, mi);
+    break;
+
   case IIT_paren:
   case IIT_paren:
     result = r_unroll_type(start_type, mi);
     result = r_unroll_type(start_type, mi);
     break;
     break;

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