Browse Source

Remove p3d/AppRunner/plugin system

Co-authored-by: rdb <[email protected]>

Closes #734
Mitchell Stokes 6 years ago
parent
commit
660249a5cc
100 changed files with 270 additions and 30965 deletions
  1. 0 99
      direct/src/directscripts/DetectPanda3D.js
  2. 0 132
      direct/src/directscripts/RunPanda3D.js
  3. 1 1
      direct/src/dist/commands.py
  4. 269 0
      direct/src/dist/icon.py
  5. 0 1245
      direct/src/p3d/AppRunner.py
  6. 0 96
      direct/src/p3d/DWBPackageInstaller.py
  7. 0 1496
      direct/src/p3d/DeploymentTools.py
  8. 0 246
      direct/src/p3d/FileSpec.py
  9. 0 751
      direct/src/p3d/HostInfo.py
  10. 0 24
      direct/src/p3d/InstalledHostData.py
  11. 0 30
      direct/src/p3d/InstalledPackageData.py
  12. 0 298
      direct/src/p3d/JavaScript.py
  13. 0 1237
      direct/src/p3d/PackageInfo.py
  14. 0 640
      direct/src/p3d/PackageInstaller.py
  15. 0 306
      direct/src/p3d/PackageMerger.py
  16. 0 3962
      direct/src/p3d/Packager.py
  17. 0 814
      direct/src/p3d/PatchMaker.py
  18. 0 95
      direct/src/p3d/ScanDirectoryNode.py
  19. 0 90
      direct/src/p3d/SeqValue.py
  20. 0 4
      direct/src/p3d/__init__.py
  21. 0 124
      direct/src/p3d/coreapi.pdef
  22. 0 127
      direct/src/p3d/p3dWrapper.c
  23. 0 241
      direct/src/p3d/packp3d.py
  24. 0 518
      direct/src/p3d/panda3d.pdef
  25. 0 359
      direct/src/p3d/pdeploy.py
  26. 0 106
      direct/src/p3d/pmerge.py
  27. 0 267
      direct/src/p3d/ppackage.py
  28. 0 101
      direct/src/p3d/ppatcher.py
  29. 0 79
      direct/src/p3d/runp3d.py
  30. 0 167
      direct/src/p3d/thirdparty.pdef
  31. 0 351
      direct/src/plugin/binaryXml.cxx
  32. 0 29
      direct/src/plugin/binaryXml.h
  33. BIN
      direct/src/plugin/failed.pgm
  34. BIN
      direct/src/plugin/failed.xcf
  35. 0 31
      direct/src/plugin/fhandle.h
  36. 0 102
      direct/src/plugin/fileSpec.I
  37. 0 486
      direct/src/plugin/fileSpec.cxx
  38. 0 78
      direct/src/plugin/fileSpec.h
  39. 0 311
      direct/src/plugin/find_root_dir.cxx
  40. 0 26
      direct/src/plugin/find_root_dir.h
  41. 0 87
      direct/src/plugin/find_root_dir_assist.mm
  42. 0 27
      direct/src/plugin/get_tinyxml.h
  43. 0 963
      direct/src/plugin/get_twirl_data.cxx
  44. 0 27
      direct/src/plugin/get_twirl_data.h
  45. 0 88
      direct/src/plugin/handleStream.I
  46. 0 14
      direct/src/plugin/handleStream.cxx
  47. 0 43
      direct/src/plugin/handleStream.h
  48. 0 31
      direct/src/plugin/handleStreamBuf.I
  49. 0 342
      direct/src/plugin/handleStreamBuf.cxx
  50. 0 60
      direct/src/plugin/handleStreamBuf.h
  51. 0 29
      direct/src/plugin/is_pathsep.I
  52. 0 22
      direct/src/plugin/is_pathsep.h
  53. 0 476
      direct/src/plugin/load_plugin.cxx
  54. 0 81
      direct/src/plugin/load_plugin.h
  55. 0 224
      direct/src/plugin/mkdir_complete.cxx
  56. 0 28
      direct/src/plugin/mkdir_complete.h
  57. 0 12
      direct/src/plugin/p3dAuthSession.I
  58. 0 456
      direct/src/plugin/p3dAuthSession.cxx
  59. 0 82
      direct/src/plugin/p3dAuthSession.h
  60. 0 68
      direct/src/plugin/p3dBoolObject.cxx
  61. 0 38
      direct/src/plugin/p3dBoolObject.h
  62. 0 20
      direct/src/plugin/p3dCInstance.I
  63. 0 32
      direct/src/plugin/p3dCInstance.cxx
  64. 0 50
      direct/src/plugin/p3dCInstance.h
  65. 0 742
      direct/src/plugin/p3dCert.cxx
  66. 0 117
      direct/src/plugin/p3dCert.h
  67. 0 567
      direct/src/plugin/p3dCert_strings.cxx
  68. 0 44
      direct/src/plugin/p3dCert_strings.h
  69. 0 618
      direct/src/plugin/p3dCert_wx.cxx
  70. 0 119
      direct/src/plugin/p3dCert_wx.h
  71. 0 213
      direct/src/plugin/p3dConcreteSequence.cxx
  72. 0 56
      direct/src/plugin/p3dConcreteSequence.h
  73. 0 178
      direct/src/plugin/p3dConcreteStruct.cxx
  74. 0 51
      direct/src/plugin/p3dConcreteStruct.h
  75. 0 12
      direct/src/plugin/p3dConditionVar.I
  76. 0 171
      direct/src/plugin/p3dConditionVar.cxx
  77. 0 48
      direct/src/plugin/p3dConditionVar.h
  78. 0 126
      direct/src/plugin/p3dDownload.I
  79. 0 192
      direct/src/plugin/p3dDownload.cxx
  80. 0 87
      direct/src/plugin/p3dDownload.h
  81. 0 20
      direct/src/plugin/p3dFileDownload.I
  82. 0 107
      direct/src/plugin/p3dFileDownload.cxx
  83. 0 50
      direct/src/plugin/p3dFileDownload.h
  84. 0 63
      direct/src/plugin/p3dFileParams.I
  85. 0 193
      direct/src/plugin/p3dFileParams.cxx
  86. 0 68
      direct/src/plugin/p3dFileParams.h
  87. 0 74
      direct/src/plugin/p3dFloatObject.cxx
  88. 0 39
      direct/src/plugin/p3dFloatObject.h
  89. 0 106
      direct/src/plugin/p3dHost.I
  90. 0 1056
      direct/src/plugin/p3dHost.cxx
  91. 0 115
      direct/src/plugin/p3dHost.h
  92. 0 147
      direct/src/plugin/p3dInstance.I
  93. 0 3936
      direct/src/plugin/p3dInstance.cxx
  94. 0 385
      direct/src/plugin/p3dInstance.h
  95. 0 322
      direct/src/plugin/p3dInstanceManager.I
  96. 0 1519
      direct/src/plugin/p3dInstanceManager.cxx
  97. 0 244
      direct/src/plugin/p3dInstanceManager.h
  98. 0 66
      direct/src/plugin/p3dIntObject.cxx
  99. 0 38
      direct/src/plugin/p3dIntObject.h
  100. 0 707
      direct/src/plugin/p3dMainObject.cxx

+ 0 - 99
direct/src/directscripts/DetectPanda3D.js

@@ -1,99 +0,0 @@
-// Based on Apple sample code at
-// http://developer.apple.com/internet/webcontent/examples/detectplugins_source.html
-
-
-// initialize global variables
-var detectableWithVB = false;
-var pluginFound = false;
-
-function goURL(daURL) {
-    // Assume we have Javascript 1.1 functionality.
-    window.location.replace(daURL);
-    return;
-}
-
-function redirectCheck(pluginFound, redirectURL, redirectIfFound) {
-    // check for redirection
-    if( redirectURL && ((pluginFound && redirectIfFound) || 
-	(!pluginFound && !redirectIfFound)) ) {
-	// go away
-	goURL(redirectURL);
-	return pluginFound;
-    } else {
-	// stay here and return result of plugin detection
-	return pluginFound;
-    }	
-}
-
-function canDetectPlugins() {
-    if( detectableWithVB || (navigator.plugins && navigator.plugins.length > 0) ) {
-	return true;
-    } else {
-	return false;
-    }
-}
-
-function detectPanda3D(redirectURL, redirectIfFound) {
-    pluginFound = detectPlugin('Panda3D'); 
-    // if not found, try to detect with VisualBasic
-    if(!pluginFound && detectableWithVB) {
-	pluginFound = detectActiveXControl('P3DACTIVEX.P3DActiveXCtrl.1');
-    }
-    // check for redirection
-    return redirectCheck(pluginFound, redirectURL, redirectIfFound);
-}
-
-function detectPlugin() {
-    // allow for multiple checks in a single pass
-    var daPlugins = detectPlugin.arguments;
-    // consider pluginFound to be false until proven true
-    var pluginFound = false;
-    // if plugins array is there and not fake
-    if (navigator.plugins && navigator.plugins.length > 0) {
-	var pluginsArrayLength = navigator.plugins.length;
-	// for each plugin...
-	for (pluginsArrayCounter=0; pluginsArrayCounter < pluginsArrayLength; pluginsArrayCounter++ ) {
-	    // loop through all desired names and check each against the current plugin name
-	    var numFound = 0;
-	    for(namesCounter=0; namesCounter < daPlugins.length; namesCounter++) {
-		// if desired plugin name is found in either plugin name or description
-		if( (navigator.plugins[pluginsArrayCounter].name.indexOf(daPlugins[namesCounter]) >= 0) || 
-		    (navigator.plugins[pluginsArrayCounter].description.indexOf(daPlugins[namesCounter]) >= 0) ) {
-		    // this name was found
-		    numFound++;
-		}   
-	    }
-	    // now that we have checked all the required names against this one plugin,
-	    // if the number we found matches the total number provided then we were successful
-	    if(numFound == daPlugins.length) {
-		pluginFound = true;
-		// if we've found the plugin, we can stop looking through at the rest of the plugins
-		break;
-	    }
-	}
-    }
-    return pluginFound;
-} // detectPlugin
-
-
-// Here we write out the VBScript block for MSIE Windows
-if ((navigator.userAgent.indexOf('MSIE') != -1) && (navigator.userAgent.indexOf('Win') != -1)) {
-    document.writeln('<script language="VBscript">');
-
-    document.writeln('\'do a one-time test for a version of VBScript that can handle this code');
-    document.writeln('detectableWithVB = False');
-    document.writeln('If ScriptEngineMajorVersion >= 2 then');
-    document.writeln('  detectableWithVB = True');
-    document.writeln('End If');
-
-    document.writeln('\'this next function will detect most plugins');
-    document.writeln('Function detectActiveXControl(activeXControlName)');
-    document.writeln('  on error resume next');
-    document.writeln('  detectActiveXControl = False');
-    document.writeln('  If detectableWithVB Then');
-    document.writeln('     detectActiveXControl = IsObject(CreateObject(activeXControlName))');
-    document.writeln('  End If');
-    document.writeln('End Function');
-
-    document.writeln('</script>');
-}

+ 0 - 132
direct/src/directscripts/RunPanda3D.js

@@ -1,132 +0,0 @@
-// This script injects the appropriate syntax into the document to
-// embed Panda3D, either for the ActiveX or Mozilla-based plugin.
-
-// It is also possible to write browser-independent code by nesting
-// <object> tags, but this causes problems when you need to reference
-// the particular object that is actually running (which object is
-// it?) for scripting purposes.
-
-// This script writes only a single <object> tag, and it can be
-// assigned the id you specify, avoiding this ambiguity.
-
-var isIE  = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
-var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
-var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;
-
-
-function P3D_Generateobj(objAttrs, params, embedAttrs, imageAttrs) 
-{ 
-  var str = '';
-
-  if (isIE && isWin && !isOpera)
-  {
-    str += '<object ';
-    for (var i in objAttrs)
-    {
-      str += i + '="' + objAttrs[i] + '" ';
-    }
-    str += '>';
-    for (var i in params)
-    {
-      str += '<param name="' + i + '" value="' + params[i] + '" /> ';
-    }
-  }
-  else
-  {
-    str += '<object ';
-    for (var i in embedAttrs)
-    {
-      str += i + '="' + embedAttrs[i] + '" ';
-    }
-    str += '> ';
-  }
-  if (imageAttrs["src"]) {
-    if (imageAttrs["href"]) {
-      str += '<a href="' + imageAttrs["href"] + '">';
-    }
-    str += '<img ';
-    for (var i in imageAttrs) {
-      if (i != "href") {
-        str += i + '="' + imageAttrs[i] + '" ';
-      }
-    }
-    str += '>';
-    if (imageAttrs["href"]) {
-      str += '</a>';
-    }
-  }
-  str += '</object>';
-
-  document.write(str);
-}
-
-function P3D_RunContent() {
-  var ret = 
-    P3D_GetArgs
-      (arguments, "clsid:924b4927-d3ba-41ea-9f7e-8a89194ab3ac",
-       "application/x-panda3d");
-  P3D_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs, ret.imageAttrs);
-}
-
-function P3D_GetArgs(args, classid, mimeType){
-  var ret = new Object();
-  ret.embedAttrs = new Object();
-  ret.params = new Object();
-  ret.objAttrs = new Object();
-  ret.imageAttrs = new Object();
-
-  for (var i = 0; i < args.length; i = i + 2){
-    var currArg = args[i].toLowerCase();    
-
-    switch (currArg){	
-    case "src":
-    case "data":
-        ret.embedAttrs['data'] = args[i+1];
-        ret.params['data'] = args[i+1];
-        break;
-
-    case "codebase":
-        ret.objAttrs['codebase'] = args[i+1];
-        break;
-
-    case "noplugin_img":
-        ret.imageAttrs["src"] = args[i+1];
-        ret.imageAttrs["border"] = '0';
-        break;
-
-    case "noplugin_href":
-        ret.imageAttrs["href"] = args[i+1];
-        break;
-
-    case "splash_img":
-        ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
-        if (!ret.imageAttrs["src"]) {
-          ret.imageAttrs["src"] = args[i+1];
-        }
-        break;
-
-    case "width":
-    case "height":
-        ret.imageAttrs[args[i]] = ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
-        break;
-
-    case "id":
-    case "align":
-    case "vspace": 
-    case "hspace":
-    case "class":
-    case "title":
-    case "accesskey":
-    case "name":
-    case "tabindex":
-        ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
-        break;
-
-    default:
-        ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
-    }
-  }
-  ret.objAttrs["classid"] = classid;
-  if (mimeType) ret.embedAttrs["type"] = mimeType;
-  return ret;
-}

+ 1 - 1
direct/src/dist/commands.py

@@ -21,7 +21,7 @@ import distutils.log
 
 from . import FreezeTool
 from . import pefile
-from direct.p3d.DeploymentTools import Icon
+from .icon import Icon
 import panda3d.core as p3d
 
 

+ 269 - 0
direct/src/dist/icon.py

@@ -0,0 +1,269 @@
+from direct.directnotify.DirectNotifyGlobal import *
+from panda3d.core import PNMImage, Filename
+
+class Icon:
+    """ This class is used to create an icon for various platforms. """
+    notify = directNotify.newCategory("Icon")
+
+    def __init__(self):
+        self.images = {}
+
+    def addImage(self, image):
+        """ Adds an image to the icon.  Returns False on failure, True on success.
+        Only one image per size can be loaded, and the image size must be square. """
+
+        if not isinstance(image, PNMImage):
+            fn = image
+            if not isinstance(fn, Filename):
+                fn = Filename.fromOsSpecific(fn)
+
+            image = PNMImage()
+            if not image.read(fn):
+                Icon.notify.warning("Image '%s' could not be read" % fn.getBasename())
+                return False
+
+        if image.getXSize() != image.getYSize():
+            Icon.notify.warning("Ignoring image without square size")
+            return False
+
+        self.images[image.getXSize()] = image
+
+        return True
+
+    def generateMissingImages(self):
+        """ Generates image sizes that should be present but aren't by scaling
+        from the next higher size. """
+
+        for required_size in (256, 128, 48, 32, 16):
+            if required_size in self.images:
+                continue
+
+            sizes = sorted(self.images.keys())
+            if required_size * 2 in sizes:
+                from_size = required_size * 2
+            else:
+                from_size = 0
+                for from_size in sizes:
+                    if from_size > required_size:
+                        break
+
+            if from_size > required_size:
+                Icon.notify.warning("Generating %dx%d icon by scaling down %dx%d image" % (required_size, required_size, from_size, from_size))
+
+                image = PNMImage(required_size, required_size)
+                if self.images[from_size].hasAlpha():
+                    image.addAlpha()
+                image.quickFilterFrom(self.images[from_size])
+                self.images[required_size] = image
+            else:
+                Icon.notify.warning("Cannot generate %dx%d icon; no higher resolution image available" % (required_size, required_size))
+
+    def _write_bitmap(self, fp, image, size, bpp):
+        """ Writes the bitmap header and data of an .ico file. """
+
+        fp.write(struct.pack('<IiiHHIIiiII', 40, size, size * 2, 1, bpp, 0, 0, 0, 0, 0, 0))
+
+        # XOR mask
+        if bpp == 24:
+            # Align rows to 4-byte boundary
+            rowalign = b'\0' * (-(size * 3) & 3)
+            for y in xrange(size):
+                for x in xrange(size):
+                    r, g, b = image.getXel(x, size - y - 1)
+                    fp.write(struct.pack('<BBB', int(b * 255), int(g * 255), int(r * 255)))
+                fp.write(rowalign)
+
+        elif bpp == 32:
+            for y in xrange(size):
+                for x in xrange(size):
+                    r, g, b, a = image.getXelA(x, size - y - 1)
+                    fp.write(struct.pack('<BBBB', int(b * 255), int(g * 255), int(r * 255), int(a * 255)))
+
+        elif bpp == 8:
+            # We'll have to generate a palette of 256 colors.
+            hist = PNMImage.Histogram()
+            image2 = PNMImage(image)
+            if image2.hasAlpha():
+                image2.premultiplyAlpha()
+                image2.removeAlpha()
+            image2.quantize(256)
+            image2.make_histogram(hist)
+            colors = list(hist.get_pixels())
+            assert len(colors) <= 256
+
+            # Write the palette.
+            i = 0
+            while i < 256 and i < len(colors):
+                r, g, b, a = colors[i]
+                fp.write(struct.pack('<BBBB', b, g, r, 0))
+                i += 1
+            if i < 256:
+                # Fill the rest with zeroes.
+                fp.write(b'\x00' * (4 * (256 - i)))
+
+            # Write indices.  Align rows to 4-byte boundary.
+            rowalign = b'\0' * (-size & 3)
+            for y in xrange(size):
+                for x in xrange(size):
+                    pixel = image2.get_pixel(x, size - y - 1)
+                    index = colors.index(pixel)
+                    if index >= 256:
+                        # Find closest pixel instead.
+                        index = closest_indices[index - 256]
+                    fp.write(struct.pack('<B', index))
+                fp.write(rowalign)
+        else:
+            raise ValueError("Invalid bpp %d" % (bpp))
+
+        # Create an AND mask, aligned to 4-byte boundary
+        if image.hasAlpha() and bpp <= 8:
+            rowalign = b'\0' * (-((size + 7) >> 3) & 3)
+            for y in xrange(size):
+                mask = 0
+                num_bits = 7
+                for x in xrange(size):
+                    a = image.get_alpha_val(x, size - y - 1)
+                    if a <= 1:
+                        mask |= (1 << num_bits)
+                    num_bits -= 1
+                    if num_bits < 0:
+                        fp.write(struct.pack('<B', mask))
+                        mask = 0
+                        num_bits = 7
+                if num_bits < 7:
+                    fp.write(struct.pack('<B', mask))
+                fp.write(rowalign)
+        else:
+            andsize = (size + 7) >> 3
+            if andsize % 4 != 0:
+                andsize += 4 - (andsize % 4)
+            fp.write(b'\x00' * (andsize * size))
+
+    def makeICO(self, fn):
+        """ Writes the images to a Windows ICO file.  Returns True on success. """
+
+        if not isinstance(fn, Filename):
+            fn = Filename.fromOsSpecific(fn)
+        fn.setBinary()
+
+        # ICO files only support resolutions up to 256x256.
+        count = 0
+        for size in self.images.keys():
+            if size < 256:
+                count += 1
+            if size <= 256:
+                count += 1
+        dataoffs = 6 + count * 16
+
+        ico = open(fn, 'wb')
+        ico.write(struct.pack('<HHH', 0, 1, count))
+
+        # Write 8-bpp image headers for sizes under 256x256.
+        for size, image in self.images.items():
+            if size >= 256:
+                continue
+            ico.write(struct.pack('<BB', size, size))
+
+            # Calculate row sizes
+            xorsize = size
+            if xorsize % 4 != 0:
+                xorsize += 4 - (xorsize % 4)
+            andsize = (size + 7) >> 3
+            if andsize % 4 != 0:
+                andsize += 4 - (andsize % 4)
+            datasize = 40 + 256 * 4 + (xorsize + andsize) * size
+
+            ico.write(struct.pack('<BBHHII', 0, 0, 1, 8, datasize, dataoffs))
+            dataoffs += datasize
+
+        # Write 24/32-bpp image headers.
+        for size, image in self.images.items():
+            if size > 256:
+                continue
+            elif size == 256:
+                ico.write(b'\0\0')
+            else:
+                ico.write(struct.pack('<BB', size, size))
+
+            # Calculate the size so we can write the offset within the file.
+            if image.hasAlpha():
+                bpp = 32
+                xorsize = size * 4
+            else:
+                bpp = 24
+                xorsize = size * 3 + (-(size * 3) & 3)
+            andsize = (size + 7) >> 3
+            if andsize % 4 != 0:
+                andsize += 4 - (andsize % 4)
+            datasize = 40 + (xorsize + andsize) * size
+
+            ico.write(struct.pack('<BBHHII', 0, 0, 1, bpp, datasize, dataoffs))
+            dataoffs += datasize
+
+        # Now write the actual icon bitmap data.
+        for size, image in self.images.items():
+            if size < 256:
+                self._write_bitmap(ico, image, size, 8)
+
+        for size, image in self.images.items():
+            if size <= 256:
+                bpp = 32 if image.hasAlpha() else 24
+                self._write_bitmap(ico, image, size, bpp)
+
+        assert ico.tell() == dataoffs
+        ico.close()
+
+        return True
+
+    def makeICNS(self, fn):
+        """ Writes the images to an Apple ICNS file.  Returns True on success. """
+
+        if not isinstance(fn, Filename):
+            fn = Filename.fromOsSpecific(fn)
+        fn.setBinary()
+
+        icns = open(fn, 'wb')
+        icns.write(b'icns\0\0\0\0')
+
+        icon_types = {16: b'is32', 32: b'il32', 48: b'ih32', 128: b'it32'}
+        mask_types = {16: b's8mk', 32: b'l8mk', 48: b'h8mk', 128: b't8mk'}
+        png_types = {256: b'ic08', 512: b'ic09', 1024: b'ic10'}
+
+        pngtype = PNMFileTypeRegistry.getGlobalPtr().getTypeFromExtension("png")
+
+        for size, image in sorted(self.images.items(), key=lambda item:item[0]):
+            if size in png_types and pngtype is not None:
+                stream = StringStream()
+                image.write(stream, "", pngtype)
+                pngdata = stream.data
+
+                icns.write(png_types[size])
+                icns.write(struct.pack('>I', len(pngdata)))
+                icns.write(pngdata)
+
+            elif size in icon_types:
+                # If it has an alpha channel, we write out a mask too.
+                if image.hasAlpha():
+                    icns.write(mask_types[size])
+                    icns.write(struct.pack('>I', size * size + 8))
+
+                    for y in xrange(size):
+                        for x in xrange(size):
+                            icns.write(struct.pack('<B', int(image.getAlpha(x, y) * 255)))
+
+                icns.write(icon_types[size])
+                icns.write(struct.pack('>I', size * size * 4 + 8))
+
+                for y in xrange(size):
+                    for x in xrange(size):
+                        r, g, b = image.getXel(x, y)
+                        icns.write(struct.pack('>BBBB', 0, int(r * 255), int(g * 255), int(b * 255)))
+
+        length = icns.tell()
+        icns.seek(4)
+        icns.write(struct.pack('>I', length))
+        icns.close()
+
+        return True
+
+

+ 0 - 1245
direct/src/p3d/AppRunner.py

@@ -1,1245 +0,0 @@
-"""
-This module is intended to be compiled into the Panda3D runtime
-distributable, to execute a packaged p3d application, but it can also
-be run directly via the Python interpreter (if the current Panda3D and
-Python versions match the version expected by the application).  See
-runp3d.py for a command-line tool to invoke this module.
-
-The global AppRunner instance may be imported as follows::
-
-   from direct.showbase.AppRunnerGlobal import appRunner
-
-This will be None if Panda was not run from the runtime environment.
-"""
-
-__all__ = ["AppRunner", "dummyAppRunner", "ArgumentError"]
-
-import sys
-import os
-
-if sys.version_info >= (3, 0):
-    import builtins
-else:
-    import __builtin__ as builtins
-
-from direct.showbase import VFSImporter
-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.direct import init_app_for_gui
-from panda3d import core
-from direct.stdpy import file, glob
-from direct.task.TaskManagerGlobal import taskMgr
-from direct.showbase.MessengerGlobal import messenger
-from direct.showbase import AppRunnerGlobal
-from direct.directnotify.DirectNotifyGlobal import directNotify
-from direct.p3d.HostInfo import HostInfo
-from direct.p3d.ScanDirectoryNode import ScanDirectoryNode
-from direct.p3d.InstalledHostData import InstalledHostData
-from direct.p3d.InstalledPackageData import InstalledPackageData
-
-# These imports are read by the C++ wrapper in p3dPythonRun.cxx.
-from direct.p3d.JavaScript import Undefined, ConcreteStruct
-
-class ArgumentError(AttributeError):
-    pass
-
-class ScriptAttributes:
-    """ This dummy class serves as the root object for the scripting
-    interface.  The Python code can store objects and functions here
-    for direct inspection by the browser's JavaScript code. """
-    pass
-
-class AppRunner(DirectObject):
-
-    """ This class is intended to be compiled into the Panda3D runtime
-    distributable, to execute a packaged p3d application.  It also
-    provides some useful runtime services while running in that
-    packaged environment.
-
-    It does not usually exist while running Python directly, but you
-    can use dummyAppRunner() to create one at startup for testing or
-    development purposes.  """
-
-    notify = directNotify.newCategory("AppRunner")
-
-    ConfigBasename = 'config.xml'
-
-    # Default values for parameters that are absent from the config file:
-    maxDiskUsage = 2048 * 1048576  # 2 GB
-
-    # Values for verifyContents, from p3d_plugin.h
-    P3DVCNone = 0
-    P3DVCNormal = 1
-    P3DVCForce = 2
-    P3DVCNever = 3
-
-    # Also from p3d_plugin.h
-    P3D_CONTENTS_DEFAULT_MAX_AGE = 5
-
-    def __init__(self):
-        DirectObject.__init__(self)
-
-        # We direct both our stdout and stderr objects onto Panda's
-        # Notify stream.  This ensures that unadorned print statements
-        # made within Python will get routed into the log properly.
-        stream = StreamWriter(Notify.out(), False)
-        sys.stdout = stream
-        sys.stderr = stream
-
-        # This is set true by dummyAppRunner(), below.
-        self.dummy = False
-
-        # These will be set from the application flags when
-        # setP3DFilename() is called.
-        self.allowPythonDev = False
-        self.guiApp = False
-        self.interactiveConsole = False
-        self.initialAppImport = False
-        self.trueFileIO = False
-        self.respectPerPlatform = None
-
-        self.verifyContents = self.P3DVCNone
-
-        self.sessionId = 0
-        self.packedAppEnvironmentInitialized = False
-        self.gotWindow = False
-        self.gotP3DFilename = False
-        self.p3dFilename = None
-        self.p3dUrl = None
-        self.started = False
-        self.windowOpened = False
-        self.windowPrc = None
-
-        self.http = None
-        if hasattr(core, 'HTTPClient'):
-            self.http = core.HTTPClient.getGlobalPtr()
-
-        self.Undefined = Undefined
-        self.ConcreteStruct = ConcreteStruct
-
-        # This is per session.
-        self.nextScriptId = 0
-
-        # TODO: we need one of these per instance, not per session.
-        self.instanceId = None
-
-        # The root Panda3D install directory.  This is filled in when
-        # the instance starts up.
-        self.rootDir = None
-
-        # The log directory.  Also filled in when the instance starts.
-        self.logDirectory = None
-
-        # self.superMirrorUrl, if nonempty, is the "super mirror" URL
-        # that should be contacted first before trying the actual
-        # host.  This is primarily used for "downloading" from a
-        # locally-stored Panda3D installation.  This is also filled in
-        # when the instance starts up.
-        self.superMirrorUrl = None
-
-        # A list of the Panda3D packages that have been loaded.
-        self.installedPackages = []
-
-        # A list of the Panda3D packages that in the queue to be
-        # downloaded.
-        self.downloadingPackages = []
-
-        # A dictionary of HostInfo objects for the various download
-        # hosts we have imported packages from.
-        self.hosts = {}
-
-        # The altHost string that is in effect from the HTML tokens,
-        # if any, and the dictionary of URL remapping: orig host url
-        # -> alt host url.
-        self.altHost = None
-        self.altHostMap = {}
-
-        # The URL from which Panda itself should be downloaded.
-        self.pandaHostUrl = PandaSystem.getPackageHostUrl()
-
-        # Application code can assign a callable object here; if so,
-        # it will be invoked when an uncaught exception propagates to
-        # the top of the TaskMgr.run() loop.
-        self.exceptionHandler = None
-
-        # Managing packages for runtime download.
-        self.downloadingPackages = []
-        self.downloadTask = None
-
-        # The mount point for the multifile.  For now, this is always
-        # the current working directory, for convenience; but when we
-        # move to multiple-instance sessions, it may have to be
-        # different for each instance.
-        self.multifileRoot = str(ExecutionEnvironment.getCwd())
-
-        # The "main" object will be exposed to the DOM as a property
-        # of the plugin object; that is, document.pluginobject.main in
-        # JavaScript will be appRunner.main here.  This may be
-        # replaced with a direct reference to the JavaScript object
-        # later, in setInstanceInfo().
-        self.main = ScriptAttributes()
-
-        # By default, we publish a stop() method so the browser can
-        # easy stop the plugin.  A particular application can remove
-        # this if it chooses.
-        self.main.stop = self.stop
-
-        # This will be the browser's toplevel window DOM object;
-        # e.g. self.dom.document will be the document.
-        self.dom = None
-
-        # This is the list of expressions we will evaluate when
-        # self.dom gets assigned.
-        self.deferredEvals = []
-
-        # This is the default requestFunc that is installed if we
-        # never call setRequestFunc().
-        def defaultRequestFunc(*args):
-            if args[1] == 'notify':
-                # Quietly ignore notifies.
-                return
-            self.notify.info("Ignoring request: %s" % (args,))
-        self.requestFunc = defaultRequestFunc
-
-        # This will be filled in with the default WindowProperties for
-        # this instance, e.g. the WindowProperties necessary to
-        # re-embed a window in the browser frame.
-        self.windowProperties = None
-
-        # Store our pointer so DirectStart-based apps can find us.
-        if AppRunnerGlobal.appRunner is None:
-            AppRunnerGlobal.appRunner = self
-
-        # We use this messenger hook to dispatch this __startIfReady()
-        # call back to the main thread.
-        self.accept('AppRunner_startIfReady', self.__startIfReady)
-
-    def getToken(self, tokenName):
-        """ Returns the value of the indicated web token as a string,
-        if it was set, or None if it was not. """
-
-        return self.tokenDict.get(tokenName.lower(), None)
-
-    def getTokenInt(self, tokenName):
-        """ Returns the value of the indicated web token as an integer
-        value, if it was set, or None if it was not, or not an
-        integer. """
-
-        value = self.getToken(tokenName)
-        if value is not None:
-            try:
-                value = int(value)
-            except ValueError:
-                value = None
-        return value
-
-    def getTokenFloat(self, tokenName):
-        """ Returns the value of the indicated web token as a
-        floating-point value value, if it was set, or None if it was
-        not, or not a number. """
-
-        value = self.getToken(tokenName)
-        if value is not None:
-            try:
-                value = float(value)
-            except ValueError:
-                value = None
-        return value
-
-    def getTokenBool(self, tokenName):
-        """ Returns the value of the indicated web token as a boolean
-        value, if it was set, or None if it was not. """
-
-        value = self.getTokenInt(tokenName)
-        if value is not None:
-            value = bool(value)
-        return value
-
-
-
-    def installPackage(self, packageName, version = None, hostUrl = None):
-
-        """ Installs the named package, downloading it first if
-        necessary.  Returns true on success, false on failure.  This
-        method runs synchronously, and will block until it is
-        finished; see the PackageInstaller class if you want this to
-        happen asynchronously instead. """
-
-        host = self.getHostWithAlt(hostUrl)
-        if not host.downloadContentsFile(self.http):
-            return False
-
-        # All right, get the package info now.
-        package = host.getPackage(packageName, version)
-        if not package:
-            self.notify.warning("Package %s %s not known on %s" % (
-                packageName, version, hostUrl))
-            return False
-
-        return self.__rInstallPackage(package, [])
-
-    def __rInstallPackage(self, package, nested):
-        """ The recursive implementation of installPackage().  The new
-        parameter, nested, is a list of packages that we are
-        recursively calling this from, to avoid recursive loops. """
-
-        package.checkStatus()
-        if not package.downloadDescFile(self.http):
-            return False
-
-        # Now that we've downloaded and read the desc file, we can
-        # install all of the required packages first.
-        nested = nested[:] + [self]
-        for packageName, version, host in package.requires:
-            if host.downloadContentsFile(self.http):
-                p2 = host.getPackage(packageName, version)
-                if not p2:
-                    self.notify.warning("Couldn't find %s %s on %s" % (packageName, version, host.hostUrl))
-                else:
-                    if p2 not in nested:
-                        self.__rInstallPackage(p2, nested)
-
-        # Now that all of the required packages are installed, carry
-        # on to download and install this package.
-        if not package.downloadPackage(self.http):
-            return False
-
-        if not package.installPackage(self):
-            return False
-
-        self.notify.info("Package %s %s installed." % (
-            package.packageName, package.packageVersion))
-        return True
-
-    def getHostWithAlt(self, hostUrl):
-        """ Returns a suitable HostInfo object for downloading
-        contents from the indicated URL.  This is almost always the
-        same thing as getHost(), except in the rare case when we have
-        an alt_host specified in the HTML tokens; in this case, we may
-        actually want to download the contents from a different URL
-        than the one given, for instance to download a version in
-        testing. """
-
-        if hostUrl is None:
-            hostUrl = self.pandaHostUrl
-
-        altUrl = self.altHostMap.get(hostUrl, None)
-        if altUrl:
-            # We got an alternate host.  Use it.
-            return self.getHost(altUrl)
-
-        # We didn't get an aternate host, use the original.
-        host = self.getHost(hostUrl)
-
-        # But we might need to consult the host itself to see if *it*
-        # recommends an altHost.
-        if self.altHost:
-            # This means forcing the host to download its contents
-            # file on the spot, a blocking operation.  This is a
-            # little unfortunate, but since alt_host is so rarely
-            # used, probably not really a problem.
-            host.downloadContentsFile(self.http)
-            altUrl = host.altHosts.get(self.altHost, None)
-            if altUrl:
-                return self.getHost(altUrl)
-
-        # No shenanigans, just return the requested host.
-        return host
-
-    def getHost(self, hostUrl, hostDir = None):
-        """ Returns a new HostInfo object corresponding to the
-        indicated host URL.  If we have already seen this URL
-        previously, returns the same object.
-
-        This returns the literal referenced host.  To return the
-        mapped host, which is the one we should actually download
-        from, see getHostWithAlt().  """
-
-        if not hostUrl:
-            hostUrl = self.pandaHostUrl
-
-        host = self.hosts.get(hostUrl, None)
-        if not host:
-            host = HostInfo(hostUrl, appRunner = self, hostDir = hostDir)
-            self.hosts[hostUrl] = host
-        return host
-
-    def getHostWithDir(self, hostDir):
-        """ Returns the HostInfo object that corresponds to the
-        indicated on-disk host directory.  This would be used when
-        reading a host directory from disk, instead of downloading it
-        from a server.  Supply the full path to the host directory, as
-        a Filename.  Returns None if the contents.xml in the indicated
-        host directory cannot be read or doesn't seem consistent. """
-
-        host = HostInfo(None, hostDir = hostDir, appRunner = self)
-        if not host.hasContentsFile:
-            if not host.readContentsFile():
-                # Couldn't read the contents.xml file
-                return None
-
-        if not host.hostUrl:
-            # The contents.xml file there didn't seem to indicate the
-            # same host directory.
-            return None
-
-        host2 = self.hosts.get(host.hostUrl)
-        if host2 is None:
-            # No such host already; store this one.
-            self.hosts[host.hostUrl] = host
-            return host
-
-        if host2.hostDir != host.hostDir:
-            # Hmm, we already have that host somewhere else.
-            return None
-
-        # We already have that host, and it's consistent.
-        return host2
-
-    def deletePackages(self, packages):
-        """ Removes all of the indicated packages from the disk,
-        uninstalling them and deleting all of their files.  The
-        packages parameter must be a list of one or more PackageInfo
-        objects, for instance as returned by getHost().getPackage().
-        Returns the list of packages that were NOT found. """
-
-        for hostUrl, host in self.hosts.items():
-            packages = host.deletePackages(packages)
-
-            if not host.packages:
-                # If that's all of the packages for this host, delete
-                # the host directory too.
-                del self.hosts[hostUrl]
-                self.__deleteHostFiles(host)
-
-        return packages
-
-    def __deleteHostFiles(self, host):
-        """ Called by deletePackages(), this removes all the files for
-        the indicated host (for which we have presumably already
-        removed all of the packages). """
-
-        self.notify.info("Deleting host %s: %s" % (host.hostUrl, host.hostDir))
-        self.rmtree(host.hostDir)
-
-        self.sendRequest('forget_package', host.hostUrl, '', '')
-
-
-    def freshenFile(self, host, fileSpec, localPathname):
-        """ Ensures that the localPathname is the most current version
-        of the file defined by fileSpec, as offered by host.  If not,
-        it downloads a new version on-the-spot.  Returns true on
-        success, false on failure. """
-
-        assert self.http
-        return host.freshenFile(self.http, fileSpec, localPathname)
-
-    def scanInstalledPackages(self):
-        """ Scans the hosts and packages already installed locally on
-        the system.  Returns a list of InstalledHostData objects, each
-        of which contains a list of InstalledPackageData objects. """
-
-        result = []
-        hostsFilename = Filename(self.rootDir, 'hosts')
-        hostsDir = ScanDirectoryNode(hostsFilename)
-        for dirnode in hostsDir.nested:
-            host = self.getHostWithDir(dirnode.pathname)
-            hostData = InstalledHostData(host, dirnode)
-
-            if host:
-                for package in host.getAllPackages(includeAllPlatforms = True):
-                    packageDir = package.getPackageDir()
-                    if not packageDir.exists():
-                        continue
-
-                    subdir = dirnode.extractSubdir(packageDir)
-                    if not subdir:
-                        # This package, while defined by the host, isn't installed
-                        # locally; ignore it.
-                        continue
-
-                    packageData = InstalledPackageData(package, subdir)
-                    hostData.packages.append(packageData)
-
-            # Now that we've examined all of the packages for the host,
-            # anything left over is junk.
-            for subdir in dirnode.nested:
-                packageData = InstalledPackageData(None, subdir)
-                hostData.packages.append(packageData)
-
-            result.append(hostData)
-
-        return result
-
-    def readConfigXml(self):
-        """ Reads the config.xml file that may be present in the root
-        directory. """
-
-        if not hasattr(core, 'TiXmlDocument'):
-            return
-
-        filename = Filename(self.rootDir, self.ConfigBasename)
-        doc = core.TiXmlDocument(filename.toOsSpecific())
-        if not doc.LoadFile():
-            return
-
-        xconfig = doc.FirstChildElement('config')
-        if xconfig:
-            maxDiskUsage = xconfig.Attribute('max_disk_usage')
-            try:
-                self.maxDiskUsage = int(maxDiskUsage or '')
-            except ValueError:
-                pass
-
-    def writeConfigXml(self):
-        """ Rewrites the config.xml to the root directory.  This isn't
-        called automatically; an application may call this after
-        adjusting some parameters (such as self.maxDiskUsage). """
-
-        from panda3d.core import TiXmlDocument, TiXmlDeclaration, TiXmlElement
-
-        filename = Filename(self.rootDir, self.ConfigBasename)
-        doc = TiXmlDocument(filename.toOsSpecific())
-        decl = TiXmlDeclaration("1.0", "utf-8", "")
-        doc.InsertEndChild(decl)
-
-        xconfig = TiXmlElement('config')
-        xconfig.SetAttribute('max_disk_usage', str(self.maxDiskUsage))
-        doc.InsertEndChild(xconfig)
-
-        # Write the file to a temporary filename, then atomically move
-        # it to its actual filename, to avoid race conditions when
-        # updating this file.
-        tfile = Filename.temporary(str(self.rootDir), '.xml')
-        if doc.SaveFile(tfile.toOsSpecific()):
-            tfile.renameTo(filename)
-
-
-    def checkDiskUsage(self):
-        """ Checks the total disk space used by all packages, and
-        removes old packages if necessary. """
-
-        totalSize = 0
-        hosts = self.scanInstalledPackages()
-        for hostData in hosts:
-            for packageData in hostData.packages:
-                totalSize += packageData.totalSize
-        self.notify.info("Total Panda3D disk space used: %s MB" % (
-            (totalSize + 524288) // 1048576))
-
-        if self.verifyContents == self.P3DVCNever:
-            # We're not allowed to delete anything anyway.
-            return
-
-        self.notify.info("Configured max usage is: %s MB" % (
-            (self.maxDiskUsage + 524288) // 1048576))
-        if totalSize <= self.maxDiskUsage:
-            # Still within budget; no need to clean up anything.
-            return
-
-        # OK, we're over budget.  Now we have to remove old packages.
-        usedPackages = []
-        for hostData in hosts:
-            for packageData in hostData.packages:
-                if packageData.package and packageData.package.installed:
-                    # Don't uninstall any packages we're currently using.
-                    continue
-
-                usedPackages.append((packageData.lastUse, packageData))
-
-        # Sort the packages into oldest-first order.
-        usedPackages.sort()
-
-        # Delete packages until we free up enough space.
-        packages = []
-        for lastUse, packageData in usedPackages:
-            if totalSize <= self.maxDiskUsage:
-                break
-            totalSize -= packageData.totalSize
-
-            if packageData.package:
-                packages.append(packageData.package)
-            else:
-                # If it's an unknown package, just delete it directly.
-                print("Deleting unknown package %s" % (packageData.pathname))
-                self.rmtree(packageData.pathname)
-
-        packages = self.deletePackages(packages)
-        if packages:
-            print("Unable to delete %s packages" % (len(packages)))
-
-        return
-
-    def stop(self):
-        """ This method can be called by JavaScript to stop the
-        application. """
-
-        # We defer the actual exit for a few frames, so we don't raise
-        # an exception and invalidate the JavaScript call; and also to
-        # help protect against race conditions as the application
-        # shuts down.
-        taskMgr.doMethodLater(0.5, sys.exit, 'exit')
-
-    def run(self):
-        """ This method calls taskMgr.run(), with an optional
-        exception handler.  This is generally the program's main loop
-        when running in a p3d environment (except on unusual platforms
-        like the iPhone, which have to hand the main loop off to the
-        OS, and don't use this interface). """
-
-        try:
-            taskMgr.run()
-
-        except SystemExit as err:
-            # Presumably the window has already been shut down here, but shut
-            # it down again for good measure.
-            if hasattr(builtins, "base"):
-                base.destroy()
-
-            self.notify.info("Normal exit with status %s." % repr(err.code))
-            raise
-
-        except:
-            # Some unexpected Python exception; pass it to the
-            # optional handler, if it is defined.
-            if self.exceptionHandler and not self.interactiveConsole:
-                self.exceptionHandler()
-            else:
-                raise
-
-    def rmtree(self, filename):
-        """ This is like shutil.rmtree(), but it can remove read-only
-        files on Windows.  It receives a Filename, the root directory
-        to delete. """
-        if filename.isDirectory():
-            for child in filename.scanDirectory():
-                self.rmtree(Filename(filename, child))
-            if not filename.rmdir():
-                print("could not remove directory %s" % (filename))
-        else:
-            if not filename.unlink():
-                print("could not delete %s" % (filename))
-
-    def setSessionId(self, sessionId):
-        """ This message should come in at startup. """
-        self.sessionId = sessionId
-        self.nextScriptId = self.sessionId * 1000 + 10000
-
-    def initPackedAppEnvironment(self):
-        """ This function sets up the Python environment suitably for
-        running a packed app.  It should only run once in any given
-        session (and it includes logic to ensure this). """
-
-        if self.packedAppEnvironmentInitialized:
-            return
-
-        self.packedAppEnvironmentInitialized = True
-
-        vfs = VirtualFileSystem.getGlobalPtr()
-
-        # Now set up Python to import this stuff.
-        VFSImporter.register()
-        sys.path.append(self.multifileRoot)
-
-        # Make sure that $MAIN_DIR is set to the p3d root before we
-        # start executing the code in this file.
-        ExecutionEnvironment.setEnvironmentVariable("MAIN_DIR", Filename(self.multifileRoot).toOsSpecific())
-
-        # Put our root directory on the model-path, too.
-        getModelPath().appendDirectory(self.multifileRoot)
-
-        if not self.trueFileIO:
-            # Replace the builtin open and file symbols so user code will get
-            # our versions by default, which can open and read files out of
-            # the multifile.
-            builtins.open = file.open
-            if sys.version_info < (3, 0):
-                builtins.file = file.open
-                builtins.execfile = file.execfile
-            os.listdir = file.listdir
-            os.walk = file.walk
-            os.path.join = file.join
-            os.path.isfile = file.isfile
-            os.path.isdir = file.isdir
-            os.path.exists = file.exists
-            os.path.lexists = file.lexists
-            os.path.getmtime = file.getmtime
-            os.path.getsize = file.getsize
-            sys.modules['glob'] = glob
-
-        self.checkDiskUsage()
-
-    def __startIfReady(self):
-        """ Called internally to start the application. """
-        if self.started:
-            return
-
-        if self.gotWindow and self.gotP3DFilename:
-            self.started = True
-
-            # Now we can ignore future calls to startIfReady().
-            self.ignore('AppRunner_startIfReady')
-
-            # Hang a hook so we know when the window is actually opened.
-            self.acceptOnce('window-event', self.__windowEvent)
-
-            # Look for the startup Python file.  This might be a magic
-            # filename (like "__main__", or any filename that contains
-            # invalid module characters), so we can't just import it
-            # directly; instead, we go through the low-level importer.
-
-            # If there's no p3d_info.xml file, we look for "main".
-            moduleName = 'main'
-            if self.p3dPackage:
-                mainName = self.p3dPackage.Attribute('main_module')
-                if mainName:
-                    moduleName = mainName
-
-            # Temporarily set this flag while we import the app, so
-            # that if the app calls run() within its own main.py, it
-            # will properly get ignored by ShowBase.
-            self.initialAppImport = True
-
-            # Python won't let us import a module named __main__.  So,
-            # we have to do that manually, via the VFSImporter.
-            if moduleName == '__main__':
-                dirName = Filename(self.multifileRoot).toOsSpecific()
-                importer = VFSImporter.VFSImporter(dirName)
-                loader = importer.find_module('__main__')
-                if loader is None:
-                    raise ImportError('No module named __main__')
-
-                mainModule = loader.load_module('__main__')
-            else:
-                __import__(moduleName)
-                mainModule = sys.modules[moduleName]
-
-            # Check if it has a main() function.  If so, call it.
-            if hasattr(mainModule, 'main') and hasattr(mainModule.main, '__call__'):
-                mainModule.main(self)
-
-            # Now clear this flag.
-            self.initialAppImport = False
-
-            if self.interactiveConsole:
-                # At this point, we have successfully loaded the app.
-                # If the interactive_console flag is enabled, stop the
-                # main loop now and give the user a Python prompt.
-                taskMgr.stop()
-
-    def getPandaScriptObject(self):
-        """ Called by the browser to query the Panda instance's
-        toplevel scripting object, for querying properties in the
-        Panda instance.  The attributes on this object are mapped to
-        document.pluginobject.main within the DOM. """
-
-        return self.main
-
-    def setBrowserScriptObject(self, dom):
-        """ Called by the browser to supply the browser's toplevel DOM
-        object, for controlling the JavaScript and the document in the
-        same page with the Panda3D plugin. """
-
-        self.dom = dom
-
-        # Now evaluate any deferred expressions.
-        for expression in self.deferredEvals:
-            self.scriptRequest('eval', self.dom, value = expression,
-                               needsResponse = False)
-        self.deferredEvals = []
-
-    def setInstanceInfo(self, rootDir, logDirectory, superMirrorUrl,
-                        verifyContents, main, respectPerPlatform):
-        """ Called by the browser to set some global information about
-        the instance. """
-
-        # rootDir is the root Panda3D install directory on the local
-        # machine.
-        self.rootDir = Filename.fromOsSpecific(rootDir)
-
-        # logDirectory is the directory name where all log files end
-        # up.
-        if logDirectory:
-            self.logDirectory = Filename.fromOsSpecific(logDirectory)
-        else:
-            self.logDirectory = Filename(rootDir, 'log')
-
-        # The "super mirror" URL, generally used only by panda3d.exe.
-        self.superMirrorUrl = superMirrorUrl
-
-        # How anxious should we be about contacting the server for
-        # the latest code?
-        self.verifyContents = verifyContents
-
-        # The initial "main" object, if specified.
-        if main is not None:
-            self.main = main
-
-        self.respectPerPlatform = respectPerPlatform
-        #self.notify.info("respectPerPlatform = %s" % (self.respectPerPlatform))
-
-        # Now that we have rootDir, we can read the config file.
-        self.readConfigXml()
-
-
-    def addPackageInfo(self, name, platform, version, hostUrl, hostDir = None,
-                       recurse = False):
-        """ Called by the browser for each one of the "required"
-        packages that were preloaded before starting the application.
-        If for some reason the package isn't already downloaded, this
-        will download it on the spot.  Raises OSError on failure. """
-
-        host = self.getHost(hostUrl, hostDir = hostDir)
-
-        if not host.hasContentsFile:
-            # Always pre-read these hosts' contents.xml files, even if
-            # we have P3DVCForce in effect, since presumably we've
-            # already forced them on the plugin side.
-            host.readContentsFile()
-
-        if not host.downloadContentsFile(self.http):
-            # Couldn't download?  Must have failed to download in the
-            # plugin as well.  But since we launched, we probably have
-            # a copy already local; let's use it.
-            message = "Host %s cannot be downloaded, cannot preload %s." % (hostUrl, name)
-            if not host.hasContentsFile:
-                # This is weird.  How did we launch without having
-                # this file at all?
-                raise OSError(message)
-
-            # Just make it a warning and continue.
-            self.notify.warning(message)
-
-        if name == 'panda3d' and not self.pandaHostUrl:
-            # A special case: in case we don't have the PackageHostUrl
-            # compiled in, infer it from the first package we
-            # installed named "panda3d".
-            self.pandaHostUrl = hostUrl
-
-        if not platform:
-            platform = None
-        package = host.getPackage(name, version, platform = platform)
-        if not package:
-            if not recurse:
-                # Maybe the contents.xml file isn't current.  Re-fetch it.
-                if host.redownloadContentsFile(self.http):
-                    return self.addPackageInfo(name, platform, version, hostUrl, hostDir = hostDir, recurse = True)
-
-            message = "Couldn't find %s %s on %s" % (name, version, hostUrl)
-            raise OSError(message)
-
-        package.checkStatus()
-        if not package.downloadDescFile(self.http):
-            message = "Couldn't get desc file for %s" % (name)
-            raise OSError(message)
-
-        if not package.downloadPackage(self.http):
-            message = "Couldn't download %s" % (name)
-            raise OSError(message)
-
-        if not package.installPackage(self):
-            message = "Couldn't install %s" % (name)
-            raise OSError(message)
-
-        if package.guiApp:
-            self.guiApp = True
-            init_app_for_gui()
-
-    def setP3DFilename(self, p3dFilename, tokens, argv, instanceId,
-                       interactiveConsole, p3dOffset = 0, p3dUrl = None):
-        """ Called by the browser to specify the p3d file that
-        contains the application itself, along with the web tokens
-        and/or command-line arguments.  Once this method has been
-        called, the application is effectively started. """
-
-        # One day we will have support for multiple instances within a
-        # Python session.  Against that day, we save the instance ID
-        # for this instance.
-        self.instanceId = instanceId
-
-        self.tokens = tokens
-        self.argv = argv
-
-        # We build up a token dictionary with care, so that if a given
-        # token appears twice in the token list, we record only the
-        # first value, not the second or later.  This is consistent
-        # with the internal behavior of the core API.
-        self.tokenDict = {}
-        for token, keyword in tokens:
-            self.tokenDict.setdefault(token, keyword)
-
-        # Also store the arguments on sys, for applications that
-        # aren't instance-ready.
-        sys.argv = argv
-
-        # That means we now know the altHost in effect.
-        self.altHost = self.tokenDict.get('alt_host', None)
-
-        # Tell the browser that Python is up and running, and ready to
-        # respond to queries.
-        self.notifyRequest('onpythonload')
-
-        # Now go load the applet.
-        fname = Filename.fromOsSpecific(p3dFilename)
-        vfs = VirtualFileSystem.getGlobalPtr()
-
-        if not vfs.exists(fname):
-            raise ArgumentError("No such file: %s" % (p3dFilename))
-
-        fname.makeAbsolute()
-        fname.setBinary()
-        mf = Multifile()
-        if p3dOffset == 0:
-            if not mf.openRead(fname):
-                raise ArgumentError("Not a Panda3D application: %s" % (p3dFilename))
-        else:
-            if not mf.openRead(fname, p3dOffset):
-                raise ArgumentError("Not a Panda3D application: %s at offset: %s" % (p3dFilename, p3dOffset))
-
-        # Now load the p3dInfo file.
-        self.p3dInfo = None
-        self.p3dPackage = None
-        self.p3dConfig = None
-        self.allowPythonDev = False
-
-        i = mf.findSubfile('p3d_info.xml')
-        if i >= 0 and hasattr(core, 'readXmlStream'):
-            stream = mf.openReadSubfile(i)
-            self.p3dInfo = core.readXmlStream(stream)
-            mf.closeReadSubfile(stream)
-        if self.p3dInfo:
-            self.p3dPackage = self.p3dInfo.FirstChildElement('package')
-        if self.p3dPackage:
-            self.p3dConfig = self.p3dPackage.FirstChildElement('config')
-
-            xhost = self.p3dPackage.FirstChildElement('host')
-            while xhost:
-                self.__readHostXml(xhost)
-                xhost = xhost.NextSiblingElement('host')
-
-        if self.p3dConfig:
-            allowPythonDev = self.p3dConfig.Attribute('allow_python_dev')
-            if allowPythonDev:
-                self.allowPythonDev = int(allowPythonDev)
-            guiApp = self.p3dConfig.Attribute('gui_app')
-            if guiApp:
-                self.guiApp = int(guiApp)
-
-            trueFileIO = self.p3dConfig.Attribute('true_file_io')
-            if trueFileIO:
-                self.trueFileIO = int(trueFileIO)
-
-        # The interactiveConsole flag can only be set true if the
-        # application has allow_python_dev set.
-        if not self.allowPythonDev and interactiveConsole:
-            raise Exception("Impossible, interactive_console set without allow_python_dev.")
-        self.interactiveConsole = interactiveConsole
-
-        if self.allowPythonDev:
-            # Set the fps text to remind the user that
-            # allow_python_dev is enabled.
-            ConfigVariableString('frame-rate-meter-text-pattern').setValue('allow_python_dev %0.1f fps')
-
-        if self.guiApp:
-            init_app_for_gui()
-
-        self.initPackedAppEnvironment()
-
-        # Mount the Multifile under self.multifileRoot.
-        vfs.mount(mf, self.multifileRoot, vfs.MFReadOnly)
-        self.p3dMultifile = mf
-        VFSImporter.reloadSharedPackages()
-
-        self.loadMultifilePrcFiles(mf, self.multifileRoot)
-        self.gotP3DFilename = True
-        self.p3dFilename = fname
-        if p3dUrl:
-            # The url from which the p3d file was downloaded is
-            # provided if available.  It is only for documentation
-            # purposes; the actual p3d file has already been
-            # downloaded to p3dFilename.
-            self.p3dUrl = core.URLSpec(p3dUrl)
-
-        # Send this call to the main thread; don't call it directly.
-        messenger.send('AppRunner_startIfReady', taskChain = 'default')
-
-    def __readHostXml(self, xhost):
-        """ Reads the data in the indicated <host> entry. """
-
-        url = xhost.Attribute('url')
-        host = self.getHost(url)
-        host.readHostXml(xhost)
-
-        # Scan for a matching <alt_host>.  If found, it means we
-        # should use the alternate URL instead of the original URL.
-        if self.altHost:
-            xalthost = xhost.FirstChildElement('alt_host')
-            while xalthost:
-                keyword = xalthost.Attribute('keyword')
-                if keyword == self.altHost:
-                    origUrl = xhost.Attribute('url')
-                    newUrl = xalthost.Attribute('url')
-                    self.altHostMap[origUrl] = newUrl
-                    break
-
-                xalthost = xalthost.NextSiblingElement('alt_host')
-
-    def loadMultifilePrcFiles(self, mf, root):
-        """ Loads any prc files in the root of the indicated
-        Multifile, which is presumed to have been mounted already
-        under root. """
-
-        # We have to load these prc files explicitly, since the
-        # ConfigPageManager can't directly look inside the vfs.  Use
-        # the Multifile interface to find the prc files, rather than
-        # vfs.scanDirectory(), so we only pick up the files in this
-        # particular multifile.
-        cpMgr = ConfigPageManager.getGlobalPtr()
-        for f in mf.getSubfileNames():
-            fn = Filename(f)
-            if fn.getDirname() == '' and fn.getExtension() == 'prc':
-                pathname = '%s/%s' % (root, f)
-
-                alreadyLoaded = False
-                for cpi in range(cpMgr.getNumImplicitPages()):
-                    if cpMgr.getImplicitPage(cpi).getName() == pathname:
-                        # No need to load this file twice.
-                        alreadyLoaded = True
-                        break
-
-                if not alreadyLoaded:
-                    data = file.open(Filename(pathname), 'r').read()
-                    cp = loadPrcFileData(pathname, data)
-                    # Set it to sort value 20, behind the implicit pages.
-                    cp.setSort(20)
-
-
-    def __clearWindowProperties(self):
-        """ Clears the windowPrc file that was created in a previous
-        call to setupWindow(), if any. """
-
-        if self.windowPrc:
-            unloadPrcFile(self.windowPrc)
-            self.windowPrc = None
-        WindowProperties.clearDefault()
-
-        # However, we keep the self.windowProperties object around, in
-        # case an application wants to return the window to the
-        # browser frame.
-
-    def setupWindow(self, windowType, x, y, width, height,
-                    parent):
-        """ Applies the indicated window parameters to the prc
-        settings, for future windows; or applies them directly to the
-        main window if the window has already been opened.  This is
-        called by the browser. """
-
-        if self.started and base.win:
-            # If we've already got a window, this must be a
-            # resize/reposition request.
-            wp = WindowProperties()
-            if x or y or windowType == 'embedded':
-                wp.setOrigin(x, y)
-            if width or height:
-                wp.setSize(width, height)
-            if windowType == 'embedded':
-                wp.setParentWindow(parent)
-            wp.setFullscreen(False)
-            base.win.requestProperties(wp)
-            self.windowProperties = wp
-            return
-
-        # If we haven't got a window already, start 'er up.  Apply the
-        # requested setting to the prc file, and to the default
-        # WindowProperties structure.
-
-        self.__clearWindowProperties()
-
-        if windowType == 'hidden':
-            data = 'window-type none\n'
-        else:
-            data = 'window-type onscreen\n'
-
-        wp = WindowProperties.getDefault()
-
-        wp.clearParentWindow()
-        wp.clearOrigin()
-        wp.clearSize()
-
-        wp.setFullscreen(False)
-        if windowType == 'fullscreen':
-            wp.setFullscreen(True)
-
-        if windowType == 'embedded':
-            wp.setParentWindow(parent)
-
-        if x or y or windowType == 'embedded':
-            wp.setOrigin(x, y)
-
-        if width or height:
-            wp.setSize(width, height)
-
-        self.windowProperties = wp
-        self.windowPrc = loadPrcFileData("setupWindow", data)
-        WindowProperties.setDefault(wp)
-
-        self.gotWindow = True
-
-        # Send this call to the main thread; don't call it directly.
-        messenger.send('AppRunner_startIfReady', taskChain = 'default')
-
-    def setRequestFunc(self, func):
-        """ This method is called by the browser at startup to supply a
-        function that can be used to deliver requests upstream, to the
-        core API, and thereby to the browser. """
-        self.requestFunc = func
-
-    def sendRequest(self, request, *args):
-        """ Delivers a request to the browser via self.requestFunc.
-        This low-level function is not intended to be called directly
-        by user code. """
-
-        assert self.requestFunc
-        return self.requestFunc(self.instanceId, request, args)
-
-    def __windowEvent(self, win):
-        """ This method is called when we get a window event.  We
-        listen for this to detect when the window has been
-        successfully opened. """
-
-        if not self.windowOpened:
-            self.windowOpened = True
-
-            # Now that the window is open, we don't need to keep those
-            # prc settings around any more.
-            self.__clearWindowProperties()
-
-            # Inform the plugin and browser.
-            self.notifyRequest('onwindowopen')
-
-    def notifyRequest(self, message):
-        """ Delivers a notify request to the browser.  This is a "this
-        happened" type notification; it also triggers some JavaScript
-        code execution, if indicated in the HTML tags, and may also
-        trigger some internal automatic actions.  (For instance, the
-        plugin takes down the splash window when it sees the
-        onwindowopen notification. """
-
-        self.sendRequest('notify', message.lower())
-
-    def evalScript(self, expression, needsResponse = False):
-        """ Evaluates an arbitrary JavaScript expression in the global
-        DOM space.  This may be deferred if necessary if needsResponse
-        is False and self.dom has not yet been assigned.  If
-        needsResponse is true, this waits for the value and returns
-        it, which means it cannot be deferred. """
-
-        if not self.dom:
-            # Defer the expression.
-            assert not needsResponse
-            self.deferredEvals.append(expression)
-        else:
-            # Evaluate it now.
-            return self.scriptRequest('eval', self.dom, value = expression,
-                                      needsResponse = needsResponse)
-
-    def scriptRequest(self, operation, object, propertyName = '',
-                      value = None, needsResponse = True):
-        """ Issues a new script request to the browser.  This queries
-        or modifies one of the browser's DOM properties.  This is a
-        low-level method that user code should not call directly;
-        instead, just operate on the Python wrapper objects that
-        shadow the DOM objects, beginning with appRunner.dom.
-
-        operation may be one of [ 'get_property', 'set_property',
-        'call', 'evaluate' ].
-
-        object is the browser object to manipulate, or the scope in
-        which to evaluate the expression.
-
-        propertyName is the name of the property to manipulate, if
-        relevant (set to None for the default method name).
-
-        value is the new value to assign to the property for
-        set_property, or the parameter list for call, or the string
-        expression for evaluate.
-
-        If needsResponse is true, this method will block until the
-        return value is received from the browser, and then it returns
-        that value.  Otherwise, it returns None immediately, without
-        waiting for the browser to process the request.
-        """
-        uniqueId = self.nextScriptId
-        self.nextScriptId = (self.nextScriptId + 1) % 0xffffffff
-        self.sendRequest('script', operation, object,
-                         propertyName, value, needsResponse, uniqueId)
-
-        if needsResponse:
-            # Now wait for the response to come in.
-            result = self.sendRequest('wait_script_response', uniqueId)
-            return result
-
-    def dropObject(self, objectId):
-        """ Inform the parent process that we no longer have an
-        interest in the P3D_object corresponding to the indicated
-        objectId.  Not intended to be called by user code. """
-
-        self.sendRequest('drop_p3dobj', objectId)
-
-def dummyAppRunner(tokens = [], argv = None):
-    """ This function creates a dummy global AppRunner object, which
-    is useful for testing running in a packaged environment without
-    actually bothering to package up the application.  Call this at
-    the start of your application to enable it.
-
-    It places the current working directory under /mf, as if it were
-    mounted from a packed multifile.  It doesn't convert egg files to
-    bam files, of course; and there are other minor differences from
-    running in an actual packaged environment.  But it can be a useful
-    first-look sanity check. """
-
-    if AppRunnerGlobal.appRunner:
-        print("Already have AppRunner, not creating a new one.")
-        return AppRunnerGlobal.appRunner
-
-    appRunner = AppRunner()
-    appRunner.dummy = True
-    AppRunnerGlobal.appRunner = appRunner
-
-    platform = PandaSystem.getPlatform()
-    version = PandaSystem.getPackageVersionString()
-    hostUrl = PandaSystem.getPackageHostUrl()
-
-    if platform.startswith('win'):
-        rootDir = Filename(Filename.getUserAppdataDirectory(), 'Panda3D')
-    elif platform.startswith('osx'):
-        rootDir = Filename(Filename.getHomeDirectory(), 'Library/Caches/Panda3D')
-    else:
-        rootDir = Filename(Filename.getHomeDirectory(), '.panda3d')
-
-    appRunner.rootDir = rootDir
-    appRunner.logDirectory = Filename(rootDir, 'log')
-
-    # Of course we will have the panda3d application loaded.
-    appRunner.addPackageInfo('panda3d', platform, version, hostUrl)
-
-    appRunner.tokens = tokens
-    appRunner.tokenDict = dict(tokens)
-    if argv is None:
-        argv = sys.argv
-    appRunner.argv = argv
-    appRunner.altHost = appRunner.tokenDict.get('alt_host', None)
-
-    appRunner.p3dInfo = None
-    appRunner.p3dPackage = None
-
-    # Mount the current directory under the multifileRoot, as if it
-    # were coming from a multifile.
-    cwd = ExecutionEnvironment.getCwd()
-    vfs = VirtualFileSystem.getGlobalPtr()
-    vfs.mount(cwd, appRunner.multifileRoot, vfs.MFReadOnly)
-
-    appRunner.initPackedAppEnvironment()
-
-    return appRunner
-

+ 0 - 96
direct/src/p3d/DWBPackageInstaller.py

@@ -1,96 +0,0 @@
-__all__ = ["DWBPackageInstaller"]
-
-from direct.p3d.PackageInstaller import PackageInstaller
-from direct.gui.DirectWaitBar import DirectWaitBar
-from direct.gui import DirectGuiGlobals as DGG
-
-class DWBPackageInstaller(DirectWaitBar, PackageInstaller):
-    """ This class presents a PackageInstaller that also inherits from
-    DirectWaitBar, so it updates its own GUI as it downloads.
-
-    Specify perPackage = True to make the progress bar reset for each
-    package, or False (the default) to show one continuous progress
-    bar for all packages.
-
-    Specify updateText = True (the default) to update the text label
-    with the name of the package or False to leave it up to you to set
-    it.
-
-    You can specify a callback function with finished = func; this
-    function will be called, with one boolean parameter, when the
-    download has completed.  The parameter will be true on success, or
-    false on failure.
-    """
-
-    def __init__(self, appRunner, parent = None, **kw):
-        PackageInstaller.__init__(self, appRunner)
-
-        optiondefs = (
-            ('borderWidth',    (0.01, 0.01),       None),
-            ('relief',         DGG.SUNKEN,         self.setRelief),
-            ('range',          1,                  self.setRange),
-            ('barBorderWidth', (0.01, 0.01),       self.setBarBorderWidth),
-            ('barColor',       (0.424, 0.647, 0.878, 1),  self.setBarColor),
-            ('barRelief',      DGG.RAISED,         self.setBarRelief),
-            ('text',           'Starting',         self.setText),
-            ('text_pos',       (0, -0.025),        None),
-            ('text_scale',     0.1,                None),
-            ('perPackage',     False,              None),
-            ('updateText',     True,               None),
-            ('finished',       None,               None),
-            )
-        self.defineoptions(kw, optiondefs)
-        DirectWaitBar.__init__(self, parent, **kw)
-        self.initialiseoptions(DWBPackageInstaller)
-        self.updateBarStyle()
-
-        # Hidden by default until the download begins.
-        self.hide()
-
-    def cleanup(self):
-        PackageInstaller.cleanup(self)
-        DirectWaitBar.destroy(self)
-
-    def destroy(self):
-        PackageInstaller.cleanup(self)
-        DirectWaitBar.destroy(self)
-
-    def packageStarted(self, package):
-        """ This callback is made for each package between
-        downloadStarted() and downloadFinished() to indicate the start
-        of a new package. """
-        if self['updateText']:
-            self['text'] = 'Installing %s' % (package.getFormattedName())
-        self.show()
-
-    def packageProgress(self, package, progress):
-        """ This callback is made repeatedly between packageStarted()
-        and packageFinished() to update the current progress on the
-        indicated package only.  The progress value ranges from 0
-        (beginning) to 1 (complete). """
-
-        if self['perPackage']:
-            self['value'] = progress * self['range']
-
-    def downloadProgress(self, overallProgress):
-        """ This callback is made repeatedly between downloadStarted()
-        and downloadFinished() to update the current progress through
-        all packages.  The progress value ranges from 0 (beginning) to
-        1 (complete). """
-
-        if not self['perPackage']:
-            self['value'] = overallProgress * self['range']
-
-    def downloadFinished(self, success):
-        """ This callback is made when all of the packages have been
-        downloaded and installed (or there has been some failure).  If
-        all packages where successfully installed, success is True.
-
-        If there were no packages that required downloading, this
-        callback will be made immediately, *without* a corresponding
-        call to downloadStarted(). """
-
-        self.hide()
-
-        if self['finished']:
-            self['finished'](success)

+ 0 - 1496
direct/src/p3d/DeploymentTools.py

@@ -1,1496 +0,0 @@
-""" This module is used to build a graphical installer
-or a standalone executable from a p3d file. It will try
-to build for as many platforms as possible. """
-
-__all__ = ["Standalone", "Installer"]
-
-import os, sys, subprocess, tarfile, shutil, time, zipfile, socket, getpass, struct
-import gzip, plistlib
-from direct.directnotify.DirectNotifyGlobal import *
-from direct.showbase.AppRunnerGlobal import appRunner
-from panda3d.core import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile
-from panda3d.core import TiXmlDocument, TiXmlDeclaration, TiXmlElement, readXmlStream
-from panda3d.core import PNMImage, PNMFileTypeRegistry, StringStream
-from direct.stdpy.file import *
-from direct.p3d.HostInfo import HostInfo
-# This is important for some reason
-import encodings
-
-try:
-    import pwd
-except ImportError:
-    pwd = None
-
-if sys.version_info >= (3, 0):
-    xrange = range
-    from io import BytesIO, TextIOWrapper
-else:
-    from io import BytesIO
-    from StringIO import StringIO
-
-# Make sure this matches with the magic in p3dEmbedMain.cxx.
-P3DEMBED_MAGIC = 0xFF3D3D00
-
-# This filter function is used when creating
-# an archive that should be owned by root.
-def archiveFilter(info):
-    basename = os.path.basename(info.name)
-    if basename in [".DS_Store", "Thumbs.db"]:
-        return None
-
-    info.uid = info.gid = 0
-    info.uname = info.gname = "root"
-
-    # All files without an extension get mode 0755,
-    # all other files are chmodded to 0644.
-    # Somewhat hacky, but it's the only way
-    # permissions can work on a Windows box.
-    if info.type != tarfile.DIRTYPE and '.' in info.name.rsplit('/', 1)[-1]:
-        info.mode = 0o644
-    else:
-        info.mode = 0o755
-
-    return info
-
-# Hack to make all files in a tar file owned by root.
-# The tarfile module doesn't have filter functionality until Python 2.7.
-# Yay duck typing!
-class TarInfoRoot(tarfile.TarInfo):
-    uid = property(lambda self: 0, lambda self, x: None)
-    gid = property(lambda self: 0, lambda self, x: None)
-    uname = property(lambda self: "root", lambda self, x: None)
-    gname = property(lambda self: "root", lambda self, x: None)
-    mode = property(lambda self: 0o644 if self.type != tarfile.DIRTYPE and \
-                    '.' in self.name.rsplit('/', 1)[-1] else 0o755,
-                    lambda self, x: None)
-
-# On OSX, the root group is named "wheel".
-class TarInfoRootOSX(TarInfoRoot):
-    gname = property(lambda self: "wheel", lambda self, x: None)
-
-
-class Standalone:
-    """ This class creates a standalone executable from a given .p3d file. """
-    notify = directNotify.newCategory("Standalone")
-
-    def __init__(self, p3dfile, tokens = {}):
-        self.p3dfile = Filename(p3dfile)
-        self.basename = self.p3dfile.getBasenameWoExtension()
-        self.tokens = tokens
-
-        self.tempDir = Filename.temporary("", self.basename, "") + "/"
-        self.tempDir.makeDir()
-        self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = appRunner, hostDir = self.tempDir, asMirror = False, perPlatform = True)
-
-        self.http = HTTPClient.getGlobalPtr()
-        if not self.host.hasContentsFile:
-            if not self.host.readContentsFile():
-                if not self.host.downloadContentsFile(self.http):
-                    Standalone.notify.error("couldn't read host")
-                    return
-
-    def __del__(self):
-        try:
-            appRunner.rmtree(self.tempDir)
-        except:
-            try: shutil.rmtree(self.tempDir.toOsSpecific())
-            except: pass
-
-    def buildAll(self, outputDir = "."):
-        """ Builds standalone executables for every known platform,
-        into the specified output directory. """
-
-        platforms = set()
-        for package in self.host.getPackages(name = "p3dembed"):
-            platforms.add(package.platform)
-        if len(platforms) == 0:
-            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.makeDir()
-        for platform in platforms:
-            if platform.startswith("win"):
-                self.build(Filename(outputDir, platform + "/" + self.basename + ".exe"), platform)
-            else:
-                self.build(Filename(outputDir, platform + "/" + self.basename), platform)
-
-    def build(self, output, platform = None, extraTokens = {}):
-        """ Builds a standalone executable and stores it into the path
-        indicated by the 'output' argument. You can specify to build for
-        a different platform by altering the 'platform' argument. """
-
-        if platform == None:
-            platform = PandaSystem.getPlatform()
-
-        vfs = VirtualFileSystem.getGlobalPtr()
-
-        for package in self.host.getPackages(name = "p3dembed", platform = platform):
-            if not package.downloadDescFile(self.http):
-                Standalone.notify.warning("  -> %s failed for platform %s" % (package.packageName, package.platform))
-                continue
-            if not package.downloadPackage(self.http):
-                Standalone.notify.warning("  -> %s failed for platform %s" % (package.packageName, package.platform))
-                continue
-
-            # Figure out where p3dembed might be now.
-            if package.platform.startswith("win"):
-                # Use p3dembedw unless console_environment was set.
-                cEnv = extraTokens.get("console_environment", self.tokens.get("console_environment", 0))
-                if cEnv != "" and int(cEnv) != 0:
-                    p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed.exe" % package.platform)
-                else:
-                    p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembedw.exe" % package.platform)
-                    # Fallback for older p3dembed versions
-                    if not vfs.exists(p3dembed):
-                        Filename(self.host.hostDir, "p3dembed/%s/p3dembed.exe" % package.platform)
-            else:
-                p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed" % package.platform)
-
-            if not vfs.exists(p3dembed):
-                Standalone.notify.warning("  -> %s failed for platform %s" % (package.packageName, package.platform))
-                continue
-
-            return self.embed(output, p3dembed, extraTokens)
-
-        Standalone.notify.error("Failed to build standalone for platform %s" % platform)
-
-    def embed(self, output, p3dembed, extraTokens = {}):
-        """ Embeds the p3d file into the provided p3dembed executable.
-        This function is not really useful - use build() or buildAll() instead. """
-
-        # Load the p3dembed data into memory
-        size = p3dembed.getFileSize()
-        p3dembed_data = VirtualFileSystem.getGlobalPtr().readFile(p3dembed, True)
-        assert len(p3dembed_data) == size
-
-        # Find the magic size string and replace it with the real size,
-        # regardless of the endianness of the p3dembed executable.
-        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
-        Standalone.notify.info("Creating %s..." % output)
-        output.makeDir()
-        ohandle = open(output.toOsSpecific(), "wb")
-        ohandle.write(p3dembed_data)
-
-        # Write out the tokens. Set log_basename to the basename by default
-        tokens = {"log_basename": self.basename}
-        tokens.update(self.tokens)
-        tokens.update(extraTokens)
-        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.
-        phandle = open(self.p3dfile.toOsSpecific(), "rb")
-        buf = phandle.read(1024 * 1024)
-        while len(buf) != 0:
-            ohandle.write(buf)
-            buf = phandle.read(1024 * 1024)
-        ohandle.close()
-        phandle.close()
-
-        os.chmod(output.toOsSpecific(), 0o755)
-
-    def getExtraFiles(self, platform):
-        """ Returns a list of extra files that will need to be included
-        with the standalone executable in order for it to run, such as
-        dependent libraries. The returned paths are full absolute paths. """
-
-        package = self.host.getPackages(name = "p3dembed", platform = platform)[0]
-
-        if not package.downloadDescFile(self.http):
-            Standalone.notify.warning("  -> %s failed for platform %s" % (package.packageName, package.platform))
-            return []
-        if not package.downloadPackage(self.http):
-            Standalone.notify.warning("  -> %s failed for platform %s" % (package.packageName, package.platform))
-            return []
-
-        filenames = []
-        vfs = VirtualFileSystem.getGlobalPtr()
-        for e in package.extracts:
-            if e.basename not in ["p3dembed", "p3dembed.exe", "p3dembed.exe.manifest", "p3dembedw.exe", "p3dembedw.exe.manifest"]:
-                filename = Filename(package.getPackageDir(), e.filename)
-                filename.makeAbsolute()
-                if vfs.exists(filename):
-                    filenames.append(filename)
-                else:
-                    Standalone.notify.error("%s mentioned in xml, but does not exist" % e.filename)
-
-        return filenames
-
-
-class PackageTree:
-    """ A class used internally to build a temporary package
-    tree for inclusion into an installer. """
-
-    def __init__(self, platform, hostDir, hostUrl):
-        self.platform = platform
-        self.hosts = {}
-        self.packages = {}
-        self.hostUrl = hostUrl
-        self.hostDir = Filename(hostDir)
-        self.hostDir.makeDir()
-        self.http = HTTPClient.getGlobalPtr()
-
-    def getHost(self, hostUrl):
-        if hostUrl in self.hosts:
-            return self.hosts[hostUrl]
-
-        host = HostInfo(hostUrl, appRunner = appRunner, hostDir = self.hostDir, asMirror = False, perPlatform = True)
-        if not host.hasContentsFile:
-            if not host.readContentsFile():
-                if not host.downloadContentsFile(self.http):
-                    Installer.notify.error("couldn't read host %s" % host.hostUrl)
-                    return None
-        self.hosts[hostUrl] = host
-        return host
-
-    def installPackage(self, name, version, hostUrl = None):
-        """ Installs the named package into the tree. """
-
-        if hostUrl is None:
-            hostUrl = self.hostUrl
-
-        pkgIdent = (name, version)
-        if pkgIdent in self.packages:
-            return self.packages[pkgIdent]
-
-        package = None
-        # Always try the super host first, if any.
-        if appRunner and appRunner.superMirrorUrl:
-            superHost = self.getHost(appRunner.superMirrorUrl)
-            if self.platform:
-                package = superHost.getPackage(name, version, self.platform)
-            if not package:
-                package = superHost.getPackage(name, version)
-
-        if not package:
-            host = self.getHost(hostUrl)
-            if self.platform:
-                package = host.getPackage(name, version, self.platform)
-            if not package:
-                package = host.getPackage(name, version)
-
-        if not package:
-            Installer.notify.error("Package %s %s for %s not known on %s" % (
-                name, version, self.platform, hostUrl))
-            return
-
-        package.installed = True # Hack not to let it unnecessarily install itself
-        if not package.downloadDescFile(self.http):
-            Installer.notify.error("  -> %s failed for platform %s" % (package.packageName, package.platform))
-            return
-        if not package.downloadPackage(self.http):
-            Installer.notify.error("  -> %s failed for platform %s" % (package.packageName, package.platform))
-            return
-
-        self.packages[pkgIdent] = package
-
-        # Check for any dependencies.
-        for rname, rversion, rhost in package.requires:
-            self.installPackage(rname, rversion, rhost.hostUrl)
-
-        return package
-
-
-class Icon:
-    """ This class is used to create an icon for various platforms. """
-    notify = directNotify.newCategory("Icon")
-
-    def __init__(self):
-        self.images = {}
-
-    def addImage(self, image):
-        """ Adds an image to the icon.  Returns False on failure, True on success.
-        Only one image per size can be loaded, and the image size must be square. """
-
-        if not isinstance(image, PNMImage):
-            fn = image
-            if not isinstance(fn, Filename):
-                fn = Filename.fromOsSpecific(fn)
-
-            image = PNMImage()
-            if not image.read(fn):
-                Icon.notify.warning("Image '%s' could not be read" % fn.getBasename())
-                return False
-
-        if image.getXSize() != image.getYSize():
-            Icon.notify.warning("Ignoring image without square size")
-            return False
-
-        self.images[image.getXSize()] = image
-
-        return True
-
-    def generateMissingImages(self):
-        """ Generates image sizes that should be present but aren't by scaling
-        from the next higher size. """
-
-        for required_size in (256, 128, 48, 32, 16):
-            if required_size in self.images:
-                continue
-
-            sizes = sorted(self.images.keys())
-            if required_size * 2 in sizes:
-                from_size = required_size * 2
-            else:
-                from_size = 0
-                for from_size in sizes:
-                    if from_size > required_size:
-                        break
-
-            if from_size > required_size:
-                Icon.notify.warning("Generating %dx%d icon by scaling down %dx%d image" % (required_size, required_size, from_size, from_size))
-
-                image = PNMImage(required_size, required_size)
-                if self.images[from_size].hasAlpha():
-                    image.addAlpha()
-                image.quickFilterFrom(self.images[from_size])
-                self.images[required_size] = image
-            else:
-                Icon.notify.warning("Cannot generate %dx%d icon; no higher resolution image available" % (required_size, required_size))
-
-    def _write_bitmap(self, fp, image, size, bpp):
-        """ Writes the bitmap header and data of an .ico file. """
-
-        fp.write(struct.pack('<IiiHHIIiiII', 40, size, size * 2, 1, bpp, 0, 0, 0, 0, 0, 0))
-
-        # XOR mask
-        if bpp == 24:
-            # Align rows to 4-byte boundary
-            rowalign = b'\0' * (-(size * 3) & 3)
-            for y in xrange(size):
-                for x in xrange(size):
-                    r, g, b = image.getXel(x, size - y - 1)
-                    fp.write(struct.pack('<BBB', int(b * 255), int(g * 255), int(r * 255)))
-                fp.write(rowalign)
-
-        elif bpp == 32:
-            for y in xrange(size):
-                for x in xrange(size):
-                    r, g, b, a = image.getXelA(x, size - y - 1)
-                    fp.write(struct.pack('<BBBB', int(b * 255), int(g * 255), int(r * 255), int(a * 255)))
-
-        elif bpp == 8:
-            # We'll have to generate a palette of 256 colors.
-            hist = PNMImage.Histogram()
-            image2 = PNMImage(image)
-            if image2.hasAlpha():
-                image2.premultiplyAlpha()
-                image2.removeAlpha()
-            image2.quantize(256)
-            image2.make_histogram(hist)
-            colors = list(hist.get_pixels())
-            assert len(colors) <= 256
-
-            # Write the palette.
-            i = 0
-            while i < 256 and i < len(colors):
-                r, g, b, a = colors[i]
-                fp.write(struct.pack('<BBBB', b, g, r, 0))
-                i += 1
-            if i < 256:
-                # Fill the rest with zeroes.
-                fp.write(b'\x00' * (4 * (256 - i)))
-
-            # Write indices.  Align rows to 4-byte boundary.
-            rowalign = b'\0' * (-size & 3)
-            for y in xrange(size):
-                for x in xrange(size):
-                    pixel = image2.get_pixel(x, size - y - 1)
-                    index = colors.index(pixel)
-                    if index >= 256:
-                        # Find closest pixel instead.
-                        index = closest_indices[index - 256]
-                    fp.write(struct.pack('<B', index))
-                fp.write(rowalign)
-        else:
-            raise ValueError("Invalid bpp %d" % (bpp))
-
-        # Create an AND mask, aligned to 4-byte boundary
-        if image.hasAlpha() and bpp <= 8:
-            rowalign = b'\0' * (-((size + 7) >> 3) & 3)
-            for y in xrange(size):
-                mask = 0
-                num_bits = 7
-                for x in xrange(size):
-                    a = image.get_alpha_val(x, size - y - 1)
-                    if a <= 1:
-                        mask |= (1 << num_bits)
-                    num_bits -= 1
-                    if num_bits < 0:
-                        fp.write(struct.pack('<B', mask))
-                        mask = 0
-                        num_bits = 7
-                if num_bits < 7:
-                    fp.write(struct.pack('<B', mask))
-                fp.write(rowalign)
-        else:
-            andsize = (size + 7) >> 3
-            if andsize % 4 != 0:
-                andsize += 4 - (andsize % 4)
-            fp.write(b'\x00' * (andsize * size))
-
-    def makeICO(self, fn):
-        """ Writes the images to a Windows ICO file.  Returns True on success. """
-
-        if not isinstance(fn, Filename):
-            fn = Filename.fromOsSpecific(fn)
-        fn.setBinary()
-
-        # ICO files only support resolutions up to 256x256.
-        count = 0
-        for size in self.images.keys():
-            if size < 256:
-                count += 1
-            if size <= 256:
-                count += 1
-        dataoffs = 6 + count * 16
-
-        ico = open(fn, 'wb')
-        ico.write(struct.pack('<HHH', 0, 1, count))
-
-        # Write 8-bpp image headers for sizes under 256x256.
-        for size, image in self.images.items():
-            if size >= 256:
-                continue
-            ico.write(struct.pack('<BB', size, size))
-
-            # Calculate row sizes
-            xorsize = size
-            if xorsize % 4 != 0:
-                xorsize += 4 - (xorsize % 4)
-            andsize = (size + 7) >> 3
-            if andsize % 4 != 0:
-                andsize += 4 - (andsize % 4)
-            datasize = 40 + 256 * 4 + (xorsize + andsize) * size
-
-            ico.write(struct.pack('<BBHHII', 0, 0, 1, 8, datasize, dataoffs))
-            dataoffs += datasize
-
-        # Write 24/32-bpp image headers.
-        for size, image in self.images.items():
-            if size > 256:
-                continue
-            elif size == 256:
-                ico.write(b'\0\0')
-            else:
-                ico.write(struct.pack('<BB', size, size))
-
-            # Calculate the size so we can write the offset within the file.
-            if image.hasAlpha():
-                bpp = 32
-                xorsize = size * 4
-            else:
-                bpp = 24
-                xorsize = size * 3 + (-(size * 3) & 3)
-            andsize = (size + 7) >> 3
-            if andsize % 4 != 0:
-                andsize += 4 - (andsize % 4)
-            datasize = 40 + (xorsize + andsize) * size
-
-            ico.write(struct.pack('<BBHHII', 0, 0, 1, bpp, datasize, dataoffs))
-            dataoffs += datasize
-
-        # Now write the actual icon bitmap data.
-        for size, image in self.images.items():
-            if size < 256:
-                self._write_bitmap(ico, image, size, 8)
-
-        for size, image in self.images.items():
-            if size <= 256:
-                bpp = 32 if image.hasAlpha() else 24
-                self._write_bitmap(ico, image, size, bpp)
-
-        assert ico.tell() == dataoffs
-        ico.close()
-
-        return True
-
-    def makeICNS(self, fn):
-        """ Writes the images to an Apple ICNS file.  Returns True on success. """
-
-        if not isinstance(fn, Filename):
-            fn = Filename.fromOsSpecific(fn)
-        fn.setBinary()
-
-        icns = open(fn, 'wb')
-        icns.write(b'icns\0\0\0\0')
-
-        icon_types = {16: b'is32', 32: b'il32', 48: b'ih32', 128: b'it32'}
-        mask_types = {16: b's8mk', 32: b'l8mk', 48: b'h8mk', 128: b't8mk'}
-        png_types = {256: b'ic08', 512: b'ic09', 1024: b'ic10'}
-
-        pngtype = PNMFileTypeRegistry.getGlobalPtr().getTypeFromExtension("png")
-
-        for size, image in sorted(self.images.items(), key=lambda item:item[0]):
-            if size in png_types and pngtype is not None:
-                stream = StringStream()
-                image.write(stream, "", pngtype)
-                pngdata = stream.data
-
-                icns.write(png_types[size])
-                icns.write(struct.pack('>I', len(pngdata)))
-                icns.write(pngdata)
-
-            elif size in icon_types:
-                # If it has an alpha channel, we write out a mask too.
-                if image.hasAlpha():
-                    icns.write(mask_types[size])
-                    icns.write(struct.pack('>I', size * size + 8))
-
-                    for y in xrange(size):
-                        for x in xrange(size):
-                            icns.write(struct.pack('<B', int(image.getAlpha(x, y) * 255)))
-
-                icns.write(icon_types[size])
-                icns.write(struct.pack('>I', size * size * 4 + 8))
-
-                for y in xrange(size):
-                    for x in xrange(size):
-                        r, g, b = image.getXel(x, y)
-                        icns.write(struct.pack('>BBBB', 0, int(r * 255), int(g * 255), int(b * 255)))
-
-        length = icns.tell()
-        icns.seek(4)
-        icns.write(struct.pack('>I', length))
-        icns.close()
-
-        return True
-
-
-class Installer:
-    """ This class creates a (graphical) installer from a given .p3d file. """
-    notify = directNotify.newCategory("Installer")
-
-    def __init__(self, p3dfile, shortname, fullname, version, tokens = {}):
-        self.p3dFilename = p3dfile
-        if not shortname:
-            shortname = p3dfile.getBasenameWoExtension()
-        self.shortname = shortname
-        self.fullname = fullname
-        self.version = str(version)
-        self.includeRequires = False
-        self.offerRun = True
-        self.offerDesktopShortcut = True
-        self.licensename = ""
-        self.licensefile = Filename()
-        self.authorid = "org.panda3d"
-        self.authorname = os.environ.get("DEBFULLNAME", "")
-        self.authoremail = os.environ.get("DEBEMAIL", "")
-        self.icon = None
-
-        # Try to determine a default author name ourselves.
-        uname = None
-        if pwd is not None and hasattr(os, 'getuid'):
-            uinfo = pwd.getpwuid(os.getuid())
-            if uinfo:
-                uname = uinfo.pw_name
-                if not self.authorname:
-                    self.authorname = \
-                        uinfo.pw_gecos.split(',', 1)[0]
-
-        # Fallbacks in case that didn't work or wasn't supported.
-        if not uname:
-            uname = getpass.getuser()
-        if not self.authorname:
-            self.authorname = uname
-        if not self.authoremail and ' ' not in uname:
-            self.authoremail = "%s@%s" % (uname, socket.gethostname())
-
-        # Load the p3d file to read out the required packages
-        mf = Multifile()
-        if not mf.openRead(self.p3dFilename):
-            Installer.notify.error("Not a Panda3D application: %s" % (p3dfile))
-            return
-
-        # Now load the p3dInfo file.
-        self.hostUrl = None
-        self.requires = []
-        self.extracts = []
-        i = mf.findSubfile('p3d_info.xml')
-        if i >= 0:
-            stream = mf.openReadSubfile(i)
-            p3dInfo = readXmlStream(stream)
-            mf.closeReadSubfile(stream)
-            if p3dInfo:
-                p3dPackage = p3dInfo.FirstChildElement('package')
-                p3dHost = p3dPackage.FirstChildElement('host')
-                if p3dHost.Attribute('url'):
-                    self.hostUrl = p3dHost.Attribute('url')
-                p3dRequires = p3dPackage.FirstChildElement('requires')
-                while p3dRequires:
-                    self.requires.append((
-                        p3dRequires.Attribute('name'),
-                        p3dRequires.Attribute('version'),
-                        p3dRequires.Attribute('host')))
-                    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:
-                    p3dConfig = p3dPackage.FirstChildElement('config')
-                    if p3dConfig:
-                        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:
-            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):
-        try:
-            appRunner.rmtree(self.tempDir)
-        except:
-            try: shutil.rmtree(self.tempDir.toOsSpecific())
-            except: pass
-
-    def installPackagesInto(self, hostDir, platform):
-        """ Installs the packages required by the .p3d file into
-        the specified directory, for the given platform. """
-
-        if not self.includeRequires:
-            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.installPackage("images", None, self.standalone.host.hostUrl)
-
-        for name, version, hostUrl in self.requires:
-            pkgTree.installPackage(name, version, hostUrl)
-
-        # Remove the extracted files from the compressed archive, to save space.
-        vfs = VirtualFileSystem.getGlobalPtr()
-        for package in pkgTree.packages.values():
-            if package.uncompressedArchive:
-                archive = Filename(package.getPackageDir(), package.uncompressedArchive.filename)
-                if not archive.exists():
-                    continue
-
-                mf = Multifile()
-                # Make sure that it isn't mounted before altering it, just to be safe
-                vfs.unmount(archive)
-                os.chmod(archive.toOsSpecific(), 0o644)
-                if not mf.openReadWrite(archive):
-                    Installer.notify.warning("Failed to open archive %s" % (archive))
-                    continue
-
-                # We don't iterate over getNumSubfiles because we're
-                # removing subfiles while we're iterating over them.
-                subfiles = mf.getSubfileNames()
-                for subfile in subfiles:
-                    # We do *NOT* call vfs.exists here in case the package is mounted.
-                    if Filename(package.getPackageDir(), subfile).exists():
-                        Installer.notify.debug("Removing already-extracted %s from multifile" % (subfile))
-                        mf.removeSubfile(subfile)
-
-                # This seems essential for mf.close() not to crash later.
-                mf.repack()
-
-                # If we have no subfiles left, we can just remove the multifile.
-                #XXX rdb: it seems that removing it causes trouble, so let's not.
-                #if mf.getNumSubfiles() == 0:
-                #    Installer.notify.info("Removing empty archive %s" % (package.uncompressedArchive.filename))
-                #    mf.close()
-                #    archive.unlink()
-                #else:
-                mf.close()
-                try: os.chmod(archive.toOsSpecific(), 0o444)
-                except: pass
-
-        # Write out our own contents.xml file.
-        doc = TiXmlDocument()
-        decl = TiXmlDeclaration("1.0", "utf-8", "")
-        doc.InsertEndChild(decl)
-
-        xcontents = TiXmlElement("contents")
-        for package in pkgTree.packages.values():
-            xpackage = TiXmlElement('package')
-            xpackage.SetAttribute('name', package.packageName)
-
-            filename = package.packageName + "/"
-
-            if package.packageVersion:
-                xpackage.SetAttribute('version', package.packageVersion)
-                filename += package.packageVersion + "/"
-
-            if package.platform:
-                xpackage.SetAttribute('platform', package.platform)
-                filename += package.platform + "/"
-                assert package.platform == platform
-
-            xpackage.SetAttribute('per_platform', '1')
-
-            filename += package.descFileBasename
-            xpackage.SetAttribute('filename', filename)
-            xcontents.InsertEndChild(xpackage)
-
-        doc.InsertEndChild(xcontents)
-        doc.SaveFile(Filename(hostDir, "contents.xml").toOsSpecific())
-
-    def buildAll(self, outputDir = "."):
-        """ Creates a (graphical) installer for every known platform.
-        Call this after you have set the desired parameters. """
-
-        platforms = set()
-        for package in self.standalone.host.getPackages(name = "p3dembed"):
-            platforms.add(package.platform)
-        if len(platforms) == 0:
-            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.makeDir()
-        for platform in platforms:
-            output = Filename(outputDir, platform + "/")
-            output.makeDir()
-            self.build(output, platform)
-
-    def build(self, output, platform = None):
-        """ Builds (graphical) installers and stores it into the path
-        indicated by the 'output' argument. You can specify to build for
-        a different platform by altering the 'platform' argument.
-        If 'output' is a directory, the installer will be stored in it. """
-
-        if platform == None:
-            platform = PandaSystem.getPlatform()
-
-        if platform.startswith("win"):
-            self.buildNSIS(output, platform)
-            return
-        elif "_" in platform:
-            osname, arch = platform.split("_", 1)
-            if osname == "linux":
-                self.buildDEB(output, platform)
-                self.buildArch(output, platform)
-                return
-            elif osname == "osx":
-                self.buildPKG(output, platform)
-                return
-        Installer.notify.info("Ignoring unknown platform " + platform)
-
-    def __buildTempLinux(self, platform):
-        """ Builds a filesystem for Linux.  Used so that buildDEB,
-        buildRPM and buildArch can share the same temp directory. """
-
-        if platform in self.__tempRoots:
-            return self.__tempRoots[platform]
-
-        tempdir = Filename(self.tempDir, platform)
-        tempdir.makeDir()
-
-        Filename(tempdir, "usr/bin/").makeDir()
-        if self.includeRequires:
-            extraTokens = {"host_dir" : "/usr/lib/" + self.shortname.lower(),
-                           "start_dir" : "/usr/lib/" + self.shortname.lower()}
-        else:
-            extraTokens = {}
-        self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform, extraTokens)
-        if not self.licensefile.empty():
-            Filename(tempdir, "usr/share/doc/%s/" % self.shortname.lower()).makeDir()
-            shutil.copyfile(self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/copyright" % self.shortname.lower()).toOsSpecific())
-            shutil.copyfile(self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/LICENSE" % self.shortname.lower()).toOsSpecific())
-
-        # Add an image file to /usr/share/pixmaps/
-        iconFile = None
-        if self.icon is not None:
-            iconImage = None
-            if 48 in self.icon.images:
-                iconImage = self.icon.images[48]
-            elif 64 in self.icon.images:
-                iconImage = self.icon.images[64]
-            elif 32 in self.icon.images:
-                iconImage = self.icon.images[32]
-            else:
-                Installer.notify.warning("No suitable icon image for Linux provided, should preferably be 48x48 in size")
-
-            if iconImage is not None:
-                iconFile = Filename(tempdir, "usr/share/pixmaps/%s.png" % self.shortname)
-                iconFile.setBinary()
-                iconFile.makeDir()
-                if not iconImage.write(iconFile):
-                    Installer.notify.warning("Failed to write icon file for Linux")
-                    iconFile.unlink()
-                    iconFile = None
-
-        # Write a .desktop file to /usr/share/applications/
-        desktopFile = Filename(tempdir, "usr/share/applications/%s.desktop" % self.shortname.lower())
-        desktopFile.setText()
-        desktopFile.makeDir()
-        desktop = open(desktopFile.toOsSpecific(), 'w')
-        desktop.write("[Desktop Entry]\n")
-        desktop.write("Name=%s\n" % self.fullname)
-        desktop.write("Exec=%s\n" % self.shortname.lower())
-        if iconFile is not None:
-            desktop.write("Icon=%s\n" % iconFile.getBasename())
-
-        # Set the "Terminal" option based on whether or not a console env is requested
-        cEnv = self.standalone.tokens.get("console_environment", "")
-        if cEnv == "" or int(cEnv) == 0:
-            desktop.write("Terminal=false\n")
-        else:
-            desktop.write("Terminal=true\n")
-
-        desktop.write("Type=Application\n")
-        desktop.close()
-
-        if self.includeRequires or self.extracts:
-            hostDir = Filename(tempdir, "usr/lib/%s/" % self.shortname.lower())
-            hostDir.makeDir()
-            self.installPackagesInto(hostDir, platform)
-
-        totsize = 0
-        for root, dirs, files in self.os_walk(tempdir.toOsSpecific()):
-            for name in files:
-                totsize += os.path.getsize(os.path.join(root, name))
-
-        self.__tempRoots[platform] = (tempdir, totsize)
-        return self.__tempRoots[platform]
-
-    def buildDEB(self, output, platform):
-        """ Builds a .deb archive and stores it in the path indicated
-        by the 'output' argument. It will be built for the architecture
-        specified by the 'arch' argument.
-        If 'output' is a directory, the deb file will be stored in it. """
-
-        arch = platform.rsplit("_", 1)[-1]
-        output = Filename(output)
-        if output.isDirectory():
-            output = Filename(output, "%s_%s_%s.deb" % (self.shortname.lower(), self.version, arch))
-        Installer.notify.info("Creating %s..." % output)
-        modtime = int(time.time())
-
-        # Create a temporary directory and write the launcher and dependencies to it.
-        tempdir, totsize = self.__buildTempLinux(platform)
-
-        # Create a control file in memory.
-        controlfile = BytesIO()
-        if sys.version_info >= (3, 0):
-            cout = TextIOWrapper(controlfile, encoding='utf-8', newline='')
-        else:
-            cout = StringIO()
-
-        cout.write("Package: %s\n" % self.shortname.lower())
-        cout.write("Version: %s\n" % self.version)
-        cout.write("Maintainer: %s <%s>\n" % (self.authorname, self.authoremail))
-        cout.write("Section: games\n")
-        cout.write("Priority: optional\n")
-        cout.write("Architecture: %s\n" % arch)
-        cout.write("Installed-Size: %d\n" % -(-totsize // 1024))
-        cout.write("Description: %s\n" % self.fullname)
-        cout.write("Depends: libc6, libgcc1, libstdc++6, libx11-6\n")
-        cout.flush()
-        if sys.version_info < (3, 0):
-            controlfile.write(cout.getvalue().encode('utf-8'))
-
-        controlinfo = TarInfoRoot("control")
-        controlinfo.mtime = modtime
-        controlinfo.size = controlfile.tell()
-        controlfile.seek(0)
-
-        # Open the deb file and write to it. It's actually
-        # just an AR file, which is very easy to make.
-        if output.exists():
-            output.unlink()
-        debfile = open(output.toOsSpecific(), "wb")
-        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()
-        ctarfile = tarfile.open("control.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
-        ctarfile.addfile(controlinfo, controlfile)
-        ctarfile.close()
-        ctarsize = debfile.tell() - ctaroffs
-        if (ctarsize & 1): debfile.write(b"\x0A")
-
-        # 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()
-        dtarfile = tarfile.open("data.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
-        dtarfile.add(Filename(tempdir, "usr").toOsSpecific(), "/usr")
-        dtarfile.close()
-        dtarsize = debfile.tell() - dtaroffs
-        if (dtarsize & 1): debfile.write(b"\x0A")
-
-        # Write the correct sizes of the archives.
-        debfile.seek(ctaroffs - 12)
-        debfile.write(str(ctarsize).encode().ljust(10, b' '))
-        debfile.seek(dtaroffs - 12)
-        debfile.write(str(dtarsize).encode().ljust(10, b' '))
-
-        debfile.close()
-
-        return output
-
-    def buildArch(self, output, platform):
-        """ Builds an ArchLinux package and stores it in the path
-        indicated by the 'output' argument. It will be built for the
-        architecture specified by the 'arch' argument.
-        If 'output' is a directory, the deb file will be stored in it. """
-
-        arch = platform.rsplit("_", 1)[-1]
-        assert arch in ("i386", "amd64")
-        arch = {"i386" : "i686", "amd64" : "x86_64"}[arch]
-        pkgver = self.version + "-1"
-
-        output = Filename(output)
-        if output.isDirectory():
-            output = Filename(output, "%s-%s-%s.pkg.tar.gz" % (self.shortname.lower(), pkgver, arch))
-        Installer.notify.info("Creating %s..." % output)
-        modtime = int(time.time())
-
-        # Create a temporary directory and write the launcher and dependencies to it.
-        tempdir, totsize = self.__buildTempLinux(platform)
-
-        # Create a pkginfo file in memory.
-        pkginfo = BytesIO()
-        if sys.version_info >= (3, 0):
-            pout = TextIOWrapper(pkginfo, encoding='utf-8', newline='')
-        else:
-            pout = StringIO()
-
-        pout.write("# Generated using pdeploy\n")
-        pout.write("# %s\n" % time.ctime(modtime))
-        pout.write("pkgname = %s\n" % self.shortname.lower())
-        pout.write("pkgver = %s\n" % pkgver)
-        pout.write("pkgdesc = %s\n" % self.fullname)
-        pout.write("builddate = %s\n" % modtime)
-        pout.write("packager = %s <%s>\n" % (self.authorname, self.authoremail))
-        pout.write("size = %d\n" % totsize)
-        pout.write("arch = %s\n" % arch)
-        if self.licensename != "":
-            pout.write("license = %s\n" % self.licensename)
-        pout.flush()
-        if sys.version_info < (3, 0):
-            pkginfo.write(pout.getvalue().encode('utf-8'))
-
-        pkginfoinfo = TarInfoRoot(".PKGINFO")
-        pkginfoinfo.mtime = modtime
-        pkginfoinfo.size = pkginfo.tell()
-        pkginfo.seek(0)
-
-        # Create the actual package now.
-        pkgfile = tarfile.open(output.toOsSpecific(), "w:gz", tarinfo = TarInfoRoot)
-        pkgfile.addfile(pkginfoinfo, pkginfo)
-        pkgfile.add(tempdir.toOsSpecific(), "/")
-        if not self.licensefile.empty():
-            pkgfile.add(self.licensefile.toOsSpecific(), "/usr/share/licenses/%s/LICENSE" % self.shortname.lower())
-        pkgfile.close()
-
-        return output
-
-    def buildAPP(self, output, platform):
-
-        output = Filename(output)
-        if output.isDirectory() and output.getExtension() != 'app':
-            output = Filename(output, "%s.app" % self.fullname)
-        Installer.notify.info("Creating %s..." % output)
-
-        # Create the executable for the application bundle
-        exefile = Filename(output, "Contents/MacOS/" + self.shortname)
-        exefile.makeDir()
-        if self.includeRequires:
-            extraTokens = {"host_dir": "../Resources", "start_dir": "../Resources"}
-        else:
-            extraTokens = {}
-        self.standalone.build(exefile, platform, extraTokens)
-        hostDir = Filename(output, "Contents/Resources/")
-        hostDir.makeDir()
-        self.installPackagesInto(hostDir, platform)
-
-        hasIcon = False
-        if self.icon is not None:
-            Installer.notify.info("Generating %s.icns..." % self.shortname)
-            hasIcon = self.icon.makeICNS(Filename(hostDir, "%s.icns" % self.shortname))
-
-        # Create the application plist file using Python's plistlib module.
-        plist = {
-            'CFBundleDevelopmentRegion': 'English',
-            'CFBundleDisplayName': self.fullname,
-            'CFBundleExecutable': exefile.getBasename(),
-            'CFBundleIdentifier': '%s.%s' % (self.author, self.shortname),
-            'CFBundleInfoDictionaryVersion': '6.0',
-            'CFBundleName': self.shortname,
-            'CFBundlePackageType': 'APPL',
-            'CFBundleShortVersionString': self.version,
-            'CFBundleVersion': self.version,
-            'LSHasLocalizedDisplayName': False,
-            'NSAppleScriptEnabled': False,
-            'NSPrincipalClass': 'NSApplication',
-        }
-        if hasIcon:
-            plist['CFBundleIconFile'] = self.shortname + '.icns'
-
-        plistlib.writePlist(plist, Filename(output, "Contents/Info.plist").toOsSpecific())
-        return output
-
-    def buildPKG(self, output, platform):
-        appfn = self.buildAPP(output, platform)
-        appname = "/Applications/" + appfn.getBasename()
-        output = Filename(output)
-        if output.isDirectory():
-            output = Filename(output, "%s %s.pkg" % (self.fullname, self.version))
-        Installer.notify.info("Creating %s..." % output)
-
-        Filename(output, "Contents/Resources/en.lproj/").makeDir()
-        if self.licensefile:
-            shutil.copyfile(self.licensefile.toOsSpecific(), Filename(output, "Contents/Resources/License.txt").toOsSpecific())
-        pkginfo = open(Filename(output, "Contents/PkgInfo").toOsSpecific(), "w")
-        pkginfo.write("pmkrpkg1")
-        pkginfo.close()
-        pkginfo = open(Filename(output, "Contents/Resources/package_version").toOsSpecific(), "w")
-        pkginfo.write("major: 1\nminor: 9")
-        pkginfo.close()
-
-        # Although it might make more sense to use Python's plistlib here,
-        # it is not available on non-OSX systems before Python 2.6.
-        plist = open(Filename(output, "Contents/Info.plist").toOsSpecific(), "w")
-        plist.write('<?xml version="1.0" encoding="UTF-8"?>\n')
-        plist.write('<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n')
-        plist.write('<plist version="1.0">\n')
-        plist.write('<dict>\n')
-        plist.write('\t<key>CFBundleIdentifier</key>\n')
-        plist.write('\t<string>%s.pkg.%s</string>\n' % (self.authorid, self.shortname))
-        plist.write('\t<key>CFBundleShortVersionString</key>\n')
-        plist.write('\t<string>%s</string>\n' % self.version)
-        plist.write('\t<key>IFMajorVersion</key>\n')
-        plist.write('\t<integer>1</integer>\n')
-        plist.write('\t<key>IFMinorVersion</key>\n')
-        plist.write('\t<integer>9</integer>\n')
-        plist.write('\t<key>IFPkgFlagAllowBackRev</key>\n')
-        plist.write('\t<false/>\n')
-        plist.write('\t<key>IFPkgFlagAuthorizationAction</key>\n')
-        plist.write('\t<string>RootAuthorization</string>\n')
-        plist.write('\t<key>IFPkgFlagDefaultLocation</key>\n')
-        plist.write('\t<string>/</string>\n')
-        plist.write('\t<key>IFPkgFlagFollowLinks</key>\n')
-        plist.write('\t<true/>\n')
-        plist.write('\t<key>IFPkgFlagIsRequired</key>\n')
-        plist.write('\t<false/>\n')
-        plist.write('\t<key>IFPkgFlagOverwritePermissions</key>\n')
-        plist.write('\t<false/>\n')
-        plist.write('\t<key>IFPkgFlagRelocatable</key>\n')
-        plist.write('\t<false/>\n')
-        plist.write('\t<key>IFPkgFlagRestartAction</key>\n')
-        plist.write('\t<string>None</string>\n')
-        plist.write('\t<key>IFPkgFlagRootVolumeOnly</key>\n')
-        plist.write('\t<true/>\n')
-        plist.write('\t<key>IFPkgFlagUpdateInstalledLanguages</key>\n')
-        plist.write('\t<false/>\n')
-        plist.write('\t<key>IFPkgFormatVersion</key>\n')
-        plist.write('\t<real>0.10000000149011612</real>\n')
-        plist.write('\t<key>IFPkgPathMappings</key>\n')
-        plist.write('\t<dict>\n')
-        plist.write('\t\t<key>%s</key>\n' % appname)
-        plist.write('\t\t<string>{pkmk-token-2}</string>\n')
-        plist.write('\t</dict>\n')
-        plist.write('</dict>\n')
-        plist.write('</plist>\n')
-        plist.close()
-
-        plist = open(Filename(output, "Contents/Resources/TokenDefinitions.plist").toOsSpecific(), "w")
-        plist.write('<?xml version="1.0" encoding="UTF-8"?>\n')
-        plist.write('<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n')
-        plist.write('<plist version="1.0">\n')
-        plist.write('<dict>\n')
-        plist.write('\t<key>pkmk-token-2</key>\n')
-        plist.write('\t<array>\n')
-        plist.write('\t\t<dict>\n')
-        plist.write('\t\t\t<key>identifier</key>\n')
-        plist.write('\t\t\t<string>%s.%s</string>\n' % (self.authorid, self.shortname))
-        plist.write('\t\t\t<key>path</key>\n')
-        plist.write('\t\t\t<string>%s</string>\n' % appname)
-        plist.write('\t\t\t<key>searchPlugin</key>\n')
-        plist.write('\t\t\t<string>CommonAppSearch</string>\n')
-        plist.write('\t\t</dict>\n')
-        plist.write('\t</array>\n')
-        plist.write('</dict>\n')
-        plist.write('</plist>\n')
-        plist.close()
-
-        plist = open(Filename(output, "Contents/Resources/en.lproj/Description.plist").toOsSpecific(), "w")
-        plist.write('<?xml version="1.0" encoding="UTF-8"?>\n')
-        plist.write('<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n')
-        plist.write('<plist version="1.0">\n')
-        plist.write('<dict>\n')
-        plist.write('\t<key>IFPkgDescriptionDescription</key>\n')
-        plist.write('\t<string></string>\n')
-        plist.write('\t<key>IFPkgDescriptionTitle</key>\n')
-        plist.write('\t<string>%s</string>\n' % self.fullname)
-        plist.write('</dict>\n')
-        plist.write('</plist>\n')
-        plist.close()
-
-        # OS X El Capitan no longer accepts .pax archives - it must be a CPIO archive named .pax.
-        archive = gzip.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), 'wb')
-        self.__ino = 0
-        self.__writeCPIO(archive, appfn, appname)
-        archive.write(b"0707070000000000000000000000000000000000010000000000000000000001300000000000TRAILER!!!\0")
-        archive.close()
-
-        # Put the .pkg into a zipfile
-        zip_fn = Filename(output.getDirname(), "%s %s.pkg.zip" % (self.fullname, self.version))
-        dir = Filename(output.getDirname())
-        dir.makeAbsolute()
-        zip = zipfile.ZipFile(zip_fn.toOsSpecific(), 'w')
-        for root, dirs, files in self.os_walk(output.toOsSpecific()):
-            for name in files:
-                file = Filename.fromOsSpecific(os.path.join(root, name))
-                file.makeAbsolute()
-                file.makeRelativeTo(dir)
-                zip.write(os.path.join(root, name), str(file))
-        zip.close()
-
-        return output
-
-    def __writeCPIO(self, archive, fn, name):
-        """ Adds the given fn under the given name to the CPIO archive. """
-
-        st = os.lstat(fn.toOsSpecific())
-
-        archive.write(b"070707") # magic
-        archive.write(b"000000") # dev
-
-        # Synthesize an inode number, different for each entry.
-        self.__ino += 1
-        archive.write("%06o" % (self.__ino))
-
-        # Determine based on the type which mode to write.
-        if os.path.islink(fn.toOsSpecific()):
-            archive.write("%06o" % (st.st_mode))
-            target = os.path.readlink(fn.toOsSpecific()).encode('utf-8')
-            size = len(target)
-        elif os.path.isdir(fn.toOsSpecific()):
-            archive.write(b"040755")
-            size = 0
-        elif not fn.getExtension():  # Binary file?
-            archive.write(b"100755")
-            size = st.st_size
-        else:
-            archive.write(b"100644")
-            size = st.st_size
-
-        archive.write("000000") # uid (root)
-        archive.write("000000") # gid (wheel)
-        archive.write("%06o" % (st.st_nlink))
-        archive.write("000000") # rdev
-        archive.write("%011o" % (st.st_mtime))
-        archive.write("%06o" % (len(name) + 1))
-        archive.write("%011o" % (size))
-
-        # Write the filename, plus terminating NUL byte.
-        archive.write(name.encode('utf-8'))
-        archive.write(b"\0")
-
-        # Copy the file data to the archive.
-        if os.path.islink(fn.toOsSpecific()):
-            archive.write(target)
-        elif size:
-            handle = open(fn.toOsSpecific(), 'rb')
-            data = handle.read(1024 * 1024)
-            while data:
-                archive.write(data)
-                data = handle.read(1024 * 1024)
-            handle.close()
-
-        # If this is a directory, recurse.
-        if os.path.isdir(fn.toOsSpecific()):
-            for child in os.listdir(fn.toOsSpecific()):
-                self.__writeCPIO(archive, Filename(fn, child), name + "/" + child)
-
-    def buildNSIS(self, output, platform):
-        # Check if we have makensis first
-        makensis = None
-        if (sys.platform.startswith("win")):
-            syspath = os.defpath.split(";") + os.environ["PATH"].split(";")
-            for p in set(syspath):
-                p1 = os.path.join(p, "makensis.exe")
-                p2 = os.path.join(os.path.dirname(p), "nsis", "makensis.exe")
-                if os.path.isfile(p1):
-                    makensis = p1
-                    break
-                elif os.path.isfile(p2):
-                    makensis = p2
-                    break
-            if not makensis:
-                import pandac
-                makensis = os.path.dirname(os.path.dirname(pandac.__file__))
-                makensis = os.path.join(makensis, "nsis", "makensis.exe")
-                if not os.path.isfile(makensis): makensis = None
-        else:
-            for p in os.defpath.split(":") + os.environ["PATH"].split(":"):
-                if os.path.isfile(os.path.join(p, "makensis")):
-                    makensis = os.path.join(p, "makensis")
-
-        if makensis == None:
-            Installer.notify.warning("Makensis utility not found, no Windows installer will be built!")
-            return None
-
-        output = Filename(output)
-        if output.isDirectory():
-            output = Filename(output, "%s %s.exe" % (self.fullname, self.version))
-        Installer.notify.info("Creating %s..." % output)
-        output.makeAbsolute()
-        extrafiles = self.standalone.getExtraFiles(platform)
-
-        exefile = Filename(Filename.getTempDirectory(), self.shortname + ".exe")
-        exefile.unlink()
-        if self.includeRequires:
-            extraTokens = {"host_dir": ".", "start_dir": "."}
-        else:
-            extraTokens = {}
-        self.standalone.build(exefile, platform, extraTokens)
-
-        # Temporary directory to store the hostdir in
-        hostDir = Filename(self.tempDir, platform + "/")
-        if not hostDir.exists():
-            hostDir.makeDir()
-            self.installPackagesInto(hostDir, platform)
-
-        # See if we can generate an icon
-        icofile = None
-        if self.icon is not None:
-            icofile = Filename(Filename.getTempDirectory(), self.shortname + ".ico")
-            icofile.unlink()
-            Installer.notify.info("Generating %s.ico..." % self.shortname)
-            if not self.icon.makeICO(icofile):
-                icofile = None
-
-        # Create the .nsi installer script
-        nsifile = Filename(Filename.getTempDirectory(), self.shortname + ".nsi")
-        nsifile.unlink()
-        nsi = open(nsifile.toOsSpecific(), "w")
-
-        # Some global info
-        nsi.write('Name "%s"\n' % self.fullname)
-        nsi.write('OutFile "%s"\n' % output.toOsSpecific())
-        if platform == 'win_amd64':
-            nsi.write('InstallDir "$PROGRAMFILES64\\%s"\n' % self.fullname)
-        else:
-            nsi.write('InstallDir "$PROGRAMFILES\\%s"\n' % self.fullname)
-        nsi.write('SetCompress auto\n')
-        nsi.write('SetCompressor lzma\n')
-        nsi.write('ShowInstDetails nevershow\n')
-        nsi.write('ShowUninstDetails nevershow\n')
-        nsi.write('InstType "Typical"\n')
-
-        # Tell Vista that we require admin rights
-        nsi.write('RequestExecutionLevel admin\n')
-        nsi.write('\n')
-        if self.offerRun:
-            nsi.write('Function launch\n')
-            nsi.write('  ExecShell "open" "$INSTDIR\\%s.exe"\n' % self.shortname)
-            nsi.write('FunctionEnd\n')
-            nsi.write('\n')
-
-        if self.offerDesktopShortcut:
-            nsi.write('Function desktopshortcut\n')
-            if icofile is None:
-                nsi.write('  CreateShortcut "$DESKTOP\\%s.lnk" "$INSTDIR\\%s.exe"\n' % (self.fullname, self.shortname))
-            else:
-                nsi.write('  CreateShortcut "$DESKTOP\\%s.lnk" "$INSTDIR\\%s.exe" "" "$INSTDIR\\%s.ico"\n' % (self.fullname, self.shortname, self.shortname))
-            nsi.write('FunctionEnd\n')
-            nsi.write('\n')
-
-        nsi.write('!include "MUI2.nsh"\n')
-        nsi.write('!define MUI_ABORTWARNING\n')
-        if self.offerRun:
-            nsi.write('!define MUI_FINISHPAGE_RUN\n')
-            nsi.write('!define MUI_FINISHPAGE_RUN_NOTCHECKED\n')
-            nsi.write('!define MUI_FINISHPAGE_RUN_FUNCTION launch\n')
-            nsi.write('!define MUI_FINISHPAGE_RUN_TEXT "Run %s"\n' % self.fullname)
-        if self.offerDesktopShortcut:
-            nsi.write('!define MUI_FINISHPAGE_SHOWREADME ""\n')
-            nsi.write('!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED\n')
-            nsi.write('!define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut"\n')
-            nsi.write('!define MUI_FINISHPAGE_SHOWREADME_FUNCTION desktopshortcut\n')
-        nsi.write('\n')
-        nsi.write('Var StartMenuFolder\n')
-        nsi.write('!insertmacro MUI_PAGE_WELCOME\n')
-        if not self.licensefile.empty():
-            abs = Filename(self.licensefile)
-            abs.makeAbsolute()
-            nsi.write('!insertmacro MUI_PAGE_LICENSE "%s"\n' % abs.toOsSpecific())
-        nsi.write('!insertmacro MUI_PAGE_DIRECTORY\n')
-        nsi.write('!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder\n')
-        nsi.write('!insertmacro MUI_PAGE_INSTFILES\n')
-        nsi.write('!insertmacro MUI_PAGE_FINISH\n')
-        nsi.write('!insertmacro MUI_UNPAGE_WELCOME\n')
-        nsi.write('!insertmacro MUI_UNPAGE_CONFIRM\n')
-        nsi.write('!insertmacro MUI_UNPAGE_INSTFILES\n')
-        nsi.write('!insertmacro MUI_UNPAGE_FINISH\n')
-        nsi.write('!insertmacro MUI_LANGUAGE "English"\n')
-
-        # This section defines the installer.
-        nsi.write('Section "" SecCore\n')
-        nsi.write('  SetOutPath "$INSTDIR"\n')
-        nsi.write('  File "%s"\n' % exefile.toOsSpecific())
-        if icofile is not None:
-            nsi.write('  File "%s"\n' % icofile.toOsSpecific())
-        for f in extrafiles:
-            nsi.write('  File "%s"\n' % f.toOsSpecific())
-        curdir = ""
-        for root, dirs, files in self.os_walk(hostDir.toOsSpecific()):
-            for name in files:
-                basefile = Filename.fromOsSpecific(os.path.join(root, name))
-                file = Filename(basefile)
-                file.makeAbsolute()
-                file.makeRelativeTo(hostDir)
-                outdir = file.getDirname().replace('/', '\\')
-                if curdir != outdir:
-                    nsi.write('  SetOutPath "$INSTDIR\\%s"\n' % outdir)
-                    curdir = outdir
-                nsi.write('  File "%s"\n' % (basefile.toOsSpecific()))
-        nsi.write('  SetOutPath "$INSTDIR"\n')
-        nsi.write('  WriteUninstaller "$INSTDIR\\Uninstall.exe"\n')
-        nsi.write('  ; Start menu items\n')
-        nsi.write('  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n')
-        nsi.write('    CreateDirectory "$SMPROGRAMS\\$StartMenuFolder"\n')
-        if icofile is None:
-            nsi.write('    CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk" "$INSTDIR\\%s.exe"\n' % (self.fullname, self.shortname))
-        else:
-            nsi.write('    CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk" "$INSTDIR\\%s.exe" "" "$INSTDIR\\%s.ico"\n' % (self.fullname, self.shortname, self.shortname))
-        nsi.write('    CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\Uninstall.lnk" "$INSTDIR\\Uninstall.exe"\n')
-        nsi.write('  !insertmacro MUI_STARTMENU_WRITE_END\n')
-        nsi.write('SectionEnd\n')
-
-        # This section defines the uninstaller.
-        nsi.write('Section Uninstall\n')
-        nsi.write('  Delete "$INSTDIR\\%s.exe"\n' % self.shortname)
-        if icofile is not None:
-            nsi.write('  Delete "$INSTDIR\\%s.ico"\n' % self.shortname)
-        for f in extrafiles:
-            nsi.write('  Delete "%s"\n' % f.getBasename())
-        nsi.write('  Delete "$INSTDIR\\Uninstall.exe"\n')
-        nsi.write('  RMDir /r "$INSTDIR"\n')
-        nsi.write('  ; Desktop icon\n')
-        nsi.write('  Delete "$DESKTOP\\%s.lnk"\n' % self.fullname)
-        nsi.write('  ; Start menu items\n')
-        nsi.write('  !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder\n')
-        nsi.write('  Delete "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk"\n' % self.fullname)
-        nsi.write('  Delete "$SMPROGRAMS\\$StartMenuFolder\\Uninstall.lnk"\n')
-        nsi.write('  RMDir "$SMPROGRAMS\\$StartMenuFolder"\n')
-        nsi.write('SectionEnd\n')
-        nsi.close()
-
-        cmd = [makensis]
-        for o in ["V2"]:
-            if sys.platform.startswith("win"):
-                cmd.append("/" + o)
-            else:
-                cmd.append("-" + o)
-        cmd.append(nsifile.toOsSpecific())
-        print(cmd)
-        try:
-            retcode = subprocess.call(cmd, shell = False)
-            if retcode != 0:
-                self.notify.warning("Failure invoking NSIS command.")
-            else:
-                nsifile.unlink()
-        except OSError:
-            self.notify.warning("Unable to invoke NSIS command.")
-
-        if icofile is not None:
-            icofile.unlink()
-
-        return output
-
-    def os_walk(self, top):
-        """ Re-implements os.walk().  For some reason the built-in
-        definition is failing on Windows when this is run within a p3d
-        environment!? """
-
-        dirnames = []
-        filenames = []
-
-        dirlist = os.listdir(top)
-        if dirlist:
-            for file in dirlist:
-                path = os.path.join(top, file)
-                if os.path.isdir(path):
-                    dirnames.append(file)
-                else:
-                    filenames.append(file)
-
-        yield (top, dirnames, filenames)
-
-        for dir in dirnames:
-            next = os.path.join(top, dir)
-            for tuple in self.os_walk(next):
-                yield tuple

+ 0 - 246
direct/src/p3d/FileSpec.py

@@ -1,246 +0,0 @@
-__all__ = ["FileSpec"]
-
-import os
-import time
-from panda3d.core import Filename, HashVal, VirtualFileSystem
-
-class FileSpec:
-    """ This class represents a disk file whose hash and size
-    etc. were read from an xml file.  This class provides methods to
-    verify whether the file on disk matches the version demanded by
-    the xml. """
-
-    def __init__(self):
-        self.actualFile = None
-        self.filename = None
-        self.size = 0
-        self.timestamp = 0
-        self.hash = None
-
-    def fromFile(self, packageDir, filename, pathname = None, st = None):
-        """ Reads the file information from the indicated file.  If st
-        is supplied, it is the result of os.stat on the filename. """
-
-        vfs = VirtualFileSystem.getGlobalPtr()
-
-        filename = Filename(filename)
-        if pathname is None:
-            pathname = Filename(packageDir, filename)
-
-        self.filename = str(filename)
-        self.basename = filename.getBasename()
-
-        if st is None:
-            st = os.stat(pathname.toOsSpecific())
-        self.size = st.st_size
-        self.timestamp = int(st.st_mtime)
-
-        self.readHash(pathname)
-
-    def readHash(self, pathname):
-        """ Reads the hash only from the indicated pathname. """
-
-        hv = HashVal()
-        hv.hashFile(pathname)
-        self.hash = hv.asHex()
-
-
-    def loadXml(self, xelement):
-        """ Reads the file information from the indicated XML
-        element. """
-
-        self.filename = xelement.Attribute('filename')
-        self.basename = None
-        if self.filename:
-            self.basename = Filename(self.filename).getBasename()
-
-        size = xelement.Attribute('size')
-        try:
-            self.size = int(size)
-        except:
-            self.size = 0
-
-        timestamp = xelement.Attribute('timestamp')
-        try:
-            self.timestamp = int(timestamp)
-        except:
-            self.timestamp = 0
-
-        self.hash = xelement.Attribute('hash')
-
-    def storeXml(self, xelement):
-        """ Adds the file information to the indicated XML
-        element. """
-
-        if self.filename:
-            xelement.SetAttribute('filename', self.filename)
-        if self.size:
-            xelement.SetAttribute('size', str(self.size))
-        if self.timestamp:
-            xelement.SetAttribute('timestamp', str(int(self.timestamp)))
-        if self.hash:
-            xelement.SetAttribute('hash', self.hash)
-
-    def storeMiniXml(self, xelement):
-        """ Adds the just the "mini" file information--size and
-        hash--to the indicated XML element. """
-
-        if self.size:
-            xelement.SetAttribute('size', str(self.size))
-        if self.hash:
-            xelement.SetAttribute('hash', self.hash)
-
-    def quickVerify(self, packageDir = None, pathname = None,
-                    notify = None, correctSelf = False):
-        """ Performs a quick test to ensure the file has not been
-        modified.  This test is vulnerable to people maliciously
-        attempting to fool the program (by setting datestamps etc.).
-
-        if correctSelf is True, then any discrepency is corrected by
-        updating the appropriate fields internally, making the
-        assumption that the file on disk is the authoritative version.
-
-        Returns true if it is intact, false if it is incorrect.  If
-        correctSelf is true, raises OSError if the self-update is
-        impossible (for instance, because the file does not exist)."""
-
-        if not pathname:
-            pathname = Filename(packageDir, self.filename)
-        try:
-            st = os.stat(pathname.toOsSpecific())
-        except OSError:
-            # If the file is missing, the file fails.
-            if notify:
-                notify.debug("file not found: %s" % (pathname))
-                if correctSelf:
-                    raise
-            return False
-
-        if st.st_size != self.size:
-            # If the size is wrong, the file fails.
-            if notify:
-                notify.debug("size wrong: %s" % (pathname))
-            if correctSelf:
-                self.__correctHash(packageDir, pathname, st, notify)
-            return False
-
-        if int(st.st_mtime) == self.timestamp:
-            # If the size is right and the timestamp is right, the
-            # file passes.
-            if notify:
-                notify.debug("file ok: %s" % (pathname))
-            return True
-
-        if notify:
-            notify.debug("modification time wrong: %s" % (pathname))
-
-        # If the size is right but the timestamp is wrong, the file
-        # soft-fails.  We follow this up with a hash check.
-        if not self.checkHash(packageDir, pathname, st):
-            # Hard fail, the hash is wrong.
-            if notify:
-                notify.debug("hash check wrong: %s" % (pathname))
-                notify.debug("  found %s, expected %s" % (self.actualFile.hash, self.hash))
-            if correctSelf:
-                self.__correctHash(packageDir, pathname, st, notify)
-            return False
-
-        if notify:
-            notify.debug("hash check ok: %s" % (pathname))
-
-        # The hash is OK after all.  Change the file's timestamp back
-        # to what we expect it to be, so we can quick-verify it
-        # successfully next time.
-        if correctSelf:
-            # Or update our own timestamp.
-            self.__correctTimestamp(pathname, st, notify)
-            return False
-        else:
-            self.__updateTimestamp(pathname, st)
-
-        return True
-
-
-    def fullVerify(self, packageDir = None, pathname = None, notify = None):
-        """ Performs a more thorough test to ensure the file has not
-        been modified.  This test is less vulnerable to malicious
-        attacks, since it reads and verifies the entire file.
-
-        Returns true if it is intact, false if it needs to be
-        redownloaded. """
-
-        if not pathname:
-            pathname = Filename(packageDir, self.filename)
-        try:
-            st = os.stat(pathname.toOsSpecific())
-        except OSError:
-            # If the file is missing, the file fails.
-            if notify:
-                notify.debug("file not found: %s" % (pathname))
-            return False
-
-        if st.st_size != self.size:
-            # If the size is wrong, the file fails;
-            if notify:
-                notify.debug("size wrong: %s" % (pathname))
-            return False
-
-        if not self.checkHash(packageDir, pathname, st):
-            # Hard fail, the hash is wrong.
-            if notify:
-                notify.debug("hash check wrong: %s" % (pathname))
-                notify.debug("  found %s, expected %s" % (self.actualFile.hash, self.hash))
-            return False
-
-        if notify:
-            notify.debug("hash check ok: %s" % (pathname))
-
-        # The hash is OK.  If the timestamp is wrong, change it back
-        # to what we expect it to be, so we can quick-verify it
-        # successfully next time.
-        if int(st.st_mtime) != self.timestamp:
-            self.__updateTimestamp(pathname, st)
-
-        return True
-
-    def __updateTimestamp(self, pathname, st):
-        # On Windows, we have to change the file to read-write before
-        # we can successfully update its timestamp.
-        try:
-            os.chmod(pathname.toOsSpecific(), 0o755)
-            os.utime(pathname.toOsSpecific(), (st.st_atime, self.timestamp))
-            os.chmod(pathname.toOsSpecific(), 0o555)
-        except OSError:
-            pass
-
-    def __correctTimestamp(self, pathname, st, notify):
-        """ Corrects the internal timestamp to match the one on
-        disk. """
-        if notify:
-            notify.info("Correcting timestamp of %s to %d (%s)" % (
-                self.filename, st.st_mtime, time.asctime(time.localtime(st.st_mtime))))
-        self.timestamp = int(st.st_mtime)
-
-    def checkHash(self, packageDir, pathname, st):
-        """ Returns true if the file has the expected md5 hash, false
-        otherwise.  As a side effect, stores a FileSpec corresponding
-        to the on-disk file in self.actualFile. """
-
-        fileSpec = FileSpec()
-        fileSpec.fromFile(packageDir, self.filename,
-                          pathname = pathname, st = st)
-        self.actualFile = fileSpec
-
-        return (fileSpec.hash == self.hash)
-
-    def __correctHash(self, packageDir, pathname, st, notify):
-        """ Corrects the internal hash to match the one on disk. """
-        if not self.actualFile:
-            self.checkHash(packageDir, pathname, st)
-
-        if notify:
-            notify.info("Correcting hash %s to %s" % (
-                self.filename, self.actualFile.hash))
-        self.hash = self.actualFile.hash
-        self.size = self.actualFile.size
-        self.timestamp = self.actualFile.timestamp

+ 0 - 751
direct/src/p3d/HostInfo.py

@@ -1,751 +0,0 @@
-__all__ = ["HostInfo"]
-
-from panda3d.core import HashVal, Filename, PandaSystem, DocumentSpec, Ramfile
-from panda3d.core import HTTPChannel, ConfigVariableInt
-from panda3d import core
-from direct.p3d.PackageInfo import PackageInfo
-from direct.p3d.FileSpec import FileSpec
-from direct.directnotify.DirectNotifyGlobal import directNotify
-import time
-
-class HostInfo:
-    """ This class represents a particular download host serving up
-    Panda3D packages.  It is the Python equivalent of the P3DHost
-    class in the core API. """
-
-    notify = directNotify.newCategory("HostInfo")
-
-    def __init__(self, hostUrl, appRunner = None, hostDir = None,
-                 rootDir = None, asMirror = False, perPlatform = None):
-
-        """ You must specify either an appRunner or a hostDir to the
-        HostInfo constructor.
-
-        If you pass asMirror = True, it means that this HostInfo
-        object is to be used to populate a "mirror" folder, a
-        duplicate (or subset) of the contents hosted by a server.
-        This means when you use this HostInfo to download packages, it
-        will only download the compressed archive file and leave it
-        there.  At the moment, mirror folders do not download old
-        patch files from the server.
-
-        If you pass perPlatform = True, then files are unpacked into a
-        platform-specific directory, which is appropriate when you
-        might be downloading multiple platforms.  The default is
-        perPlatform = False, which means all files are unpacked into
-        the host directory directly, without an intervening
-        platform-specific directory name.  If asMirror is True, then
-        the default is perPlatform = True.
-
-        Note that perPlatform is also restricted by the individual
-        package's specification.  """
-
-        self.__setHostUrl(hostUrl)
-        self.appRunner = appRunner
-        self.rootDir = rootDir
-        if rootDir is None and appRunner:
-            self.rootDir = appRunner.rootDir
-
-        if hostDir and not isinstance(hostDir, Filename):
-            hostDir = Filename.fromOsSpecific(hostDir)
-
-        self.hostDir = hostDir
-        self.asMirror = asMirror
-        self.perPlatform = perPlatform
-        if perPlatform is None:
-            self.perPlatform = asMirror
-
-        # Initially false, this is set true when the contents file is
-        # successfully read.
-        self.hasContentsFile = False
-
-        # This is the time value at which the current contents file is
-        # no longer valid.
-        self.contentsExpiration = 0
-
-        # Contains the md5 hash of the original contents.xml file.
-        self.contentsSpec = FileSpec()
-
-        # descriptiveName will be filled in later, when the
-        # contents file is read.
-        self.descriptiveName = None
-
-        # A list of known mirrors for this host, all URL's guaranteed
-        # to end with a slash.
-        self.mirrors = []
-
-        # A map of keyword -> altHost URL's.  An altHost is different
-        # than a mirror; an altHost is an alternate URL to download a
-        # different (e.g. testing) version of this host's contents.
-        # It is rarely used.
-        self.altHosts = {}
-
-        # This is a dictionary of packages by (name, version).  It
-        # will be filled in when the contents file is read.
-        self.packages = {}
-
-        if self.appRunner and self.appRunner.verifyContents != self.appRunner.P3DVCForce:
-            # Attempt to pre-read the existing contents.xml; maybe it
-            # will be current enough for our purposes.
-            self.readContentsFile()
-
-    def __setHostUrl(self, hostUrl):
-        """ Assigns self.hostUrl, and related values. """
-        self.hostUrl = hostUrl
-
-        if not self.hostUrl:
-            # A special case: the URL will be set later.
-            self.hostUrlPrefix = None
-            self.downloadUrlPrefix = None
-        else:
-            # hostUrlPrefix is the host URL, but it is guaranteed to end
-            # with a slash.
-            self.hostUrlPrefix = hostUrl
-            if self.hostUrlPrefix[-1] != '/':
-                self.hostUrlPrefix += '/'
-
-            # downloadUrlPrefix is the URL prefix that should be used for
-            # everything other than the contents.xml file.  It might be
-            # the same as hostUrlPrefix, but in the case of an
-            # https-protected hostUrl, it will be the cleartext channel.
-            self.downloadUrlPrefix = self.hostUrlPrefix
-
-    def freshenFile(self, http, fileSpec, localPathname):
-        """ Ensures that the localPathname is the most current version
-        of the file defined by fileSpec, as offered by host.  If not,
-        it downloads a new version on-the-spot.  Returns true on
-        success, false on failure. """
-
-        if fileSpec.quickVerify(pathname = localPathname):
-            # It's good, keep it.
-            return True
-
-        # It's stale, get a new one.
-        doc = None
-        if self.appRunner and self.appRunner.superMirrorUrl:
-            # Use the "super mirror" first.
-            url = core.URLSpec(self.appRunner.superMirrorUrl + fileSpec.filename)
-            self.notify.info("Freshening %s" % (url))
-            doc = http.getDocument(url)
-
-        if not doc or not doc.isValid():
-            # Failing the super mirror, contact the actual host.
-            url = core.URLSpec(self.hostUrlPrefix + fileSpec.filename)
-            self.notify.info("Freshening %s" % (url))
-            doc = http.getDocument(url)
-            if not doc.isValid():
-                return False
-
-        file = Filename.temporary('', 'p3d_')
-        if not doc.downloadToFile(file):
-            # Failed to download.
-            file.unlink()
-            return False
-
-        # Successfully downloaded!
-        localPathname.makeDir()
-        if not file.renameTo(localPathname):
-            # Couldn't move it into place.
-            file.unlink()
-            return False
-
-        if not fileSpec.fullVerify(pathname = localPathname, notify = self.notify):
-            # No good after download.
-            self.notify.info("%s is still no good after downloading." % (url))
-            return False
-
-        return True
-
-    def downloadContentsFile(self, http, redownload = False,
-                             hashVal = None):
-        """ Downloads the contents.xml file for this particular host,
-        synchronously, and then reads it.  Returns true on success,
-        false on failure.  If hashVal is not None, it should be a
-        HashVal object, which will be filled with the hash from the
-        new contents.xml file."""
-
-        if self.hasCurrentContentsFile():
-            # We've already got one.
-            return True
-
-        if self.appRunner and self.appRunner.verifyContents == self.appRunner.P3DVCNever:
-            # Not allowed to.
-            return False
-
-        rf = None
-        if http:
-            if not redownload and self.appRunner and self.appRunner.superMirrorUrl:
-                # We start with the "super mirror", if it's defined.
-                url = self.appRunner.superMirrorUrl + 'contents.xml'
-                request = DocumentSpec(url)
-                self.notify.info("Downloading contents file %s" % (request))
-
-                rf = Ramfile()
-                channel = http.makeChannel(False)
-                channel.getDocument(request)
-                if not channel.downloadToRam(rf):
-                    self.notify.warning("Unable to download %s" % (url))
-                    rf = None
-
-            if not rf:
-                # Then go to the main host, if our super mirror let us
-                # down.
-
-                url = self.hostUrlPrefix + '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.
-                url += '?' + str(int(time.time()))
-
-                # We might as well explicitly request the cache to be disabled
-                # too, since we have an interface for that via HTTPChannel.
-                request = DocumentSpec(url)
-                request.setCacheControl(DocumentSpec.CCNoCache)
-
-                self.notify.info("Downloading contents file %s" % (request))
-                statusCode = None
-                statusString = ''
-                for attempt in range(int(ConfigVariableInt('contents-xml-dl-attempts', 3))):
-                    if attempt > 0:
-                        self.notify.info("Retrying (%s)..."%(attempt,))
-                    rf = Ramfile()
-                    channel = http.makeChannel(False)
-                    channel.getDocument(request)
-                    if channel.downloadToRam(rf):
-                        self.notify.info("Successfully downloaded %s" % (url,))
-                        break
-                    else:
-                        rf = None
-                        statusCode = channel.getStatusCode()
-                        statusString = channel.getStatusString()
-                        self.notify.warning("Could not contact download server at %s" % (url,))
-                        self.notify.warning("Status code = %s %s" % (statusCode, statusString))
-
-                if not rf:
-                    self.notify.warning("Unable to download %s" % (url,))
-                    try:
-                        # Something screwed up.
-                        if statusCode == HTTPChannel.SCDownloadOpenError or \
-                           statusCode == HTTPChannel.SCDownloadWriteError:
-                            launcher.setPandaErrorCode(2)
-                        elif statusCode == 404:
-                            # 404 not found
-                            launcher.setPandaErrorCode(5)
-                        elif statusCode < 100:
-                            # statusCode < 100 implies the connection attempt itself
-                            # failed.  This is usually due to firewall software
-                            # interfering.  Apparently some firewall software might
-                            # allow the first connection and disallow subsequent
-                            # connections; how strange.
-                            launcher.setPandaErrorCode(4)
-                        else:
-                            # There are other kinds of failures, but these will
-                            # generally have been caught already by the first test; so
-                            # if we get here there may be some bigger problem.  Just
-                            # give the generic "big problem" message.
-                            launcher.setPandaErrorCode(6)
-                    except NameError as e:
-                        # no launcher
-                        pass
-                    except AttributeError as e:
-                        self.notify.warning("%s" % (str(e),))
-                        pass
-                    return False
-
-        tempFilename = Filename.temporary('', 'p3d_', '.xml')
-        if rf:
-            f = open(tempFilename.toOsSpecific(), 'wb')
-            f.write(rf.getData())
-            f.close()
-            if hashVal:
-                hashVal.hashString(rf.getData())
-
-            if not self.readContentsFile(tempFilename, freshDownload = True):
-                self.notify.warning("Failure reading %s" % (url))
-                tempFilename.unlink()
-                return False
-
-            tempFilename.unlink()
-            return True
-
-        # Couldn't download the file.  Maybe we should look for a
-        # previously-downloaded copy already on disk?
-        return False
-
-    def redownloadContentsFile(self, http):
-        """ Downloads a new contents.xml file in case it has changed.
-        Returns true if the file has indeed changed, false if it has
-        not. """
-        assert self.hasContentsFile
-
-        if self.appRunner and self.appRunner.verifyContents == self.appRunner.P3DVCNever:
-            # Not allowed to.
-            return False
-
-        url = self.hostUrlPrefix + 'contents.xml'
-        self.notify.info("Redownloading %s" % (url))
-
-        # Get the hash of the original file.
-        assert self.hostDir
-        hv1 = HashVal()
-        if self.contentsSpec.hash:
-            hv1.setFromHex(self.contentsSpec.hash)
-        else:
-            filename = Filename(self.hostDir, 'contents.xml')
-            hv1.hashFile(filename)
-
-        # Now download it again.
-        self.hasContentsFile = False
-        hv2 = HashVal()
-        if not self.downloadContentsFile(http, redownload = True,
-                                         hashVal = hv2):
-            return False
-
-        if hv2 == HashVal():
-            self.notify.info("%s didn't actually redownload." % (url))
-            return False
-        elif hv1 != hv2:
-            self.notify.info("%s has changed." % (url))
-            return True
-        else:
-            self.notify.info("%s has not changed." % (url))
-            return False
-
-    def hasCurrentContentsFile(self):
-        """ Returns true if a contents.xml file has been successfully
-        read for this host and is still current, false otherwise. """
-        if not self.appRunner \
-            or self.appRunner.verifyContents == self.appRunner.P3DVCNone \
-            or self.appRunner.verifyContents == self.appRunner.P3DVCNever:
-            # If we're not asking to verify contents, then
-            # contents.xml files never expires.
-            return self.hasContentsFile
-
-        now = int(time.time())
-        return now < self.contentsExpiration and self.hasContentsFile
-
-    def readContentsFile(self, tempFilename = None, freshDownload = False):
-        """ Reads the contents.xml file for this particular host, once
-        it has been downloaded into the indicated temporary file.
-        Returns true on success, false if the contents file is not
-        already on disk or is unreadable.
-
-        If tempFilename is specified, it is the filename read, and it
-        is copied the file into the standard location if it's not
-        there already.  If tempFilename is not specified, the standard
-        filename is read if it is known. """
-
-        if not hasattr(core, 'TiXmlDocument'):
-            return False
-
-        if not tempFilename:
-            if self.hostDir:
-                # If the filename is not specified, we can infer it
-                # if we already know our hostDir
-                hostDir = self.hostDir
-            else:
-                # Otherwise, we have to guess the hostDir.
-                hostDir = self.__determineHostDir(None, self.hostUrl)
-
-            tempFilename = Filename(hostDir, 'contents.xml')
-
-        doc = core.TiXmlDocument(tempFilename.toOsSpecific())
-        if not doc.LoadFile():
-            return False
-
-        xcontents = doc.FirstChildElement('contents')
-        if not xcontents:
-            return False
-
-        maxAge = xcontents.Attribute('max_age')
-        if maxAge:
-            try:
-                maxAge = int(maxAge)
-            except:
-                maxAge = None
-        if maxAge is None:
-            # Default max_age if unspecified (see p3d_plugin.h).
-            from direct.p3d.AppRunner import AppRunner
-            maxAge = AppRunner.P3D_CONTENTS_DEFAULT_MAX_AGE
-
-        # Get the latest possible expiration time, based on the max_age
-        # indication.  Any expiration time later than this is in error.
-        now = int(time.time())
-        self.contentsExpiration = now + maxAge
-
-        if freshDownload:
-            self.contentsSpec.readHash(tempFilename)
-
-            # Update the XML with the new download information.
-            xorig = xcontents.FirstChildElement('orig')
-            while xorig:
-                xcontents.RemoveChild(xorig)
-                xorig = xcontents.FirstChildElement('orig')
-
-            xorig = core.TiXmlElement('orig')
-            self.contentsSpec.storeXml(xorig)
-            xorig.SetAttribute('expiration', str(self.contentsExpiration))
-
-            xcontents.InsertEndChild(xorig)
-
-        else:
-            # Read the download hash and expiration time from the XML.
-            expiration = None
-            xorig = xcontents.FirstChildElement('orig')
-            if xorig:
-                self.contentsSpec.loadXml(xorig)
-                expiration = xorig.Attribute('expiration')
-                if expiration:
-                    try:
-                        expiration = int(expiration)
-                    except:
-                        expiration = None
-            if not self.contentsSpec.hash:
-                self.contentsSpec.readHash(tempFilename)
-
-            if expiration is not None:
-                self.contentsExpiration = min(self.contentsExpiration, expiration)
-
-        # Look for our own entry in the hosts table.
-        if self.hostUrl:
-            self.__findHostXml(xcontents)
-        else:
-            assert self.hostDir
-            self.__findHostXmlForHostDir(xcontents)
-
-        if self.rootDir and not self.hostDir:
-            self.hostDir = self.__determineHostDir(None, self.hostUrl)
-
-        # Get the list of packages available for download and/or import.
-        xpackage = xcontents.FirstChildElement('package')
-        while xpackage:
-            name = xpackage.Attribute('name')
-            platform = xpackage.Attribute('platform')
-            version = xpackage.Attribute('version')
-            try:
-                solo = int(xpackage.Attribute('solo') or '')
-            except ValueError:
-                solo = False
-            try:
-                perPlatform = int(xpackage.Attribute('per_platform') or '')
-            except ValueError:
-                perPlatform = False
-
-            package = self.__makePackage(name, platform, version, solo, perPlatform)
-            package.descFile = FileSpec()
-            package.descFile.loadXml(xpackage)
-            package.setupFilenames()
-
-            package.importDescFile = None
-            ximport = xpackage.FirstChildElement('import')
-            if ximport:
-                package.importDescFile = FileSpec()
-                package.importDescFile.loadXml(ximport)
-
-            xpackage = xpackage.NextSiblingElement('package')
-
-        self.hasContentsFile = True
-
-        # Now save the contents.xml file into the standard location.
-        if self.appRunner and self.appRunner.verifyContents != self.appRunner.P3DVCNever:
-            assert self.hostDir
-            filename = Filename(self.hostDir, 'contents.xml')
-            filename.makeDir()
-            if freshDownload:
-                doc.SaveFile(filename.toOsSpecific())
-            else:
-                if filename != tempFilename:
-                    tempFilename.copyTo(filename)
-
-        return True
-
-    def __findHostXml(self, xcontents):
-        """ Looks for the <host> or <alt_host> entry in the
-        contents.xml that corresponds to the URL that we actually
-        downloaded from. """
-
-        xhost = xcontents.FirstChildElement('host')
-        while xhost:
-            url = xhost.Attribute('url')
-            if url == self.hostUrl:
-                self.readHostXml(xhost)
-                return
-
-            xalthost = xhost.FirstChildElement('alt_host')
-            while xalthost:
-                url = xalthost.Attribute('url')
-                if url == self.hostUrl:
-                    self.readHostXml(xalthost)
-                    return
-                xalthost = xalthost.NextSiblingElement('alt_host')
-
-            xhost = xhost.NextSiblingElement('host')
-
-    def __findHostXmlForHostDir(self, xcontents):
-        """ Looks for the <host> or <alt_host> entry in the
-        contents.xml that corresponds to the host dir that we read the
-        contents.xml from.  This is used when reading a contents.xml
-        file found on disk, as opposed to downloading it from a
-        site. """
-
-        xhost = xcontents.FirstChildElement('host')
-        while xhost:
-            url = xhost.Attribute('url')
-            hostDirBasename = xhost.Attribute('host_dir')
-            hostDir = self.__determineHostDir(hostDirBasename, url)
-            if hostDir == self.hostDir:
-                self.__setHostUrl(url)
-                self.readHostXml(xhost)
-                return
-
-            xalthost = xhost.FirstChildElement('alt_host')
-            while xalthost:
-                url = xalthost.Attribute('url')
-                hostDirBasename = xalthost.Attribute('host_dir')
-                hostDir = self.__determineHostDir(hostDirBasename, url)
-                if hostDir == self.hostDir:
-                    self.__setHostUrl(url)
-                    self.readHostXml(xalthost)
-                    return
-                xalthost = xalthost.NextSiblingElement('alt_host')
-
-            xhost = xhost.NextSiblingElement('host')
-
-    def readHostXml(self, xhost):
-        """ Reads a <host> or <alt_host> entry and applies the data to
-        this object. """
-
-        descriptiveName = xhost.Attribute('descriptive_name')
-        if descriptiveName and not self.descriptiveName:
-            self.descriptiveName = descriptiveName
-
-        hostDirBasename = xhost.Attribute('host_dir')
-        if self.rootDir and not self.hostDir:
-            self.hostDir = self.__determineHostDir(hostDirBasename, self.hostUrl)
-
-        # Get the "download" URL, which is the source from which we
-        # download everything other than the contents.xml file.
-        downloadUrl = xhost.Attribute('download_url')
-        if downloadUrl:
-            self.downloadUrlPrefix = downloadUrl
-            if self.downloadUrlPrefix[-1] != '/':
-                self.downloadUrlPrefix += '/'
-        else:
-            self.downloadUrlPrefix = self.hostUrlPrefix
-
-        xmirror = xhost.FirstChildElement('mirror')
-        while xmirror:
-            url = xmirror.Attribute('url')
-            if url:
-                if url[-1] != '/':
-                    url += '/'
-                if url not in self.mirrors:
-                    self.mirrors.append(url)
-            xmirror = xmirror.NextSiblingElement('mirror')
-
-        xalthost = xhost.FirstChildElement('alt_host')
-        while xalthost:
-            keyword = xalthost.Attribute('keyword')
-            url = xalthost.Attribute('url')
-            if url and keyword:
-                self.altHosts[keyword] = url
-            xalthost = xalthost.NextSiblingElement('alt_host')
-
-    def __makePackage(self, name, platform, version, solo, perPlatform):
-        """ Creates a new PackageInfo entry for the given name,
-        version, and platform.  If there is already a matching
-        PackageInfo, returns it. """
-
-        if not platform:
-            platform = None
-
-        platforms = self.packages.setdefault((name, version or ""), {})
-        package = platforms.get("", None)
-        if not package:
-            package = PackageInfo(self, name, version, platform = platform,
-                                  solo = solo, asMirror = self.asMirror,
-                                  perPlatform = perPlatform)
-            platforms[platform or ""] = package
-
-        return package
-
-    def getPackage(self, name, version, platform = None):
-        """ Returns a PackageInfo that matches the indicated name and
-        version and the indicated platform or the current runtime
-        platform, if one is provided by this host, or None if not. """
-
-        assert self.hasContentsFile
-        platforms = self.packages.get((name, version or ""), {})
-
-        if platform:
-            # In this case, we are looking for a specific platform
-            # only.
-            return platforms.get(platform, None)
-
-        # We are looking for one matching the current runtime
-        # platform.  First, look for a package matching the current
-        # platform exactly.
-        package = platforms.get(PandaSystem.getPlatform(), None)
-
-        # If not found, look for one matching no particular platform.
-        if not package:
-            package = platforms.get("", None)
-
-        return package
-
-    def getPackages(self, name = None, platform = None):
-        """ Returns a list of PackageInfo objects that match the
-        indicated name and/or platform, with no particular regards to
-        version.  If name is None, all packages are returned. """
-
-        assert self.hasContentsFile
-
-        packages = []
-        for (pn, version), platforms in self.packages.items():
-            if name and pn != name:
-                continue
-
-            if not platform:
-                for p2 in platforms:
-                    package = self.getPackage(pn, version, platform = p2)
-                    if package:
-                        packages.append(package)
-            else:
-                package = self.getPackage(pn, version, platform = platform)
-                if package:
-                    packages.append(package)
-
-        return packages
-
-    def getAllPackages(self, includeAllPlatforms = False):
-        """ Returns a list of all available packages provided by this
-        host. """
-
-        result = []
-
-        items = sorted(self.packages.items())
-        for key, platforms in items:
-            if self.perPlatform or includeAllPlatforms:
-                # If we maintain a different answer per platform,
-                # return all of them.
-                pitems = sorted(platforms.items())
-                for pkey, package in pitems:
-                    result.append(package)
-            else:
-                # If we maintain a host for the current platform
-                # only (e.g. a client copy), then return only the
-                # current platform, or no particular platform.
-                package = platforms.get(PandaSystem.getPlatform(), None)
-                if not package:
-                    package = platforms.get("", None)
-
-                if package:
-                    result.append(package)
-
-        return result
-
-    def deletePackages(self, packages):
-        """ Removes all of the indicated packages from the disk,
-        uninstalling them and deleting all of their files.  The
-        packages parameter must be a list of one or more PackageInfo
-        objects, for instance as returned by getPackage().  Returns
-        the list of packages that were NOT found. """
-
-        packages = packages[:]
-
-        for key, platforms in list(self.packages.items()):
-            for platform, package in list(platforms.items()):
-                if package in packages:
-                    self.__deletePackageFiles(package)
-                    del platforms[platform]
-                    packages.remove(package)
-
-            if not platforms:
-                # If we've removed all the platforms for a given
-                # package, remove the key from the toplevel map.
-                del self.packages[key]
-
-        return packages
-
-    def __deletePackageFiles(self, package):
-        """ Called by deletePackage(), this actually removes the files
-        for the indicated package. """
-
-        if self.appRunner:
-            self.notify.info("Deleting package %s: %s" % (package.packageName, package.getPackageDir()))
-            self.appRunner.rmtree(package.getPackageDir())
-
-            self.appRunner.sendRequest('forget_package', self.hostUrl, package.packageName, package.packageVersion or '')
-
-    def __determineHostDir(self, hostDirBasename, hostUrl):
-        """ Hashes the host URL into a (mostly) unique directory
-        string, which will be the root of the host's install tree.
-        Returns the resulting path, as a Filename.
-
-        This code is duplicated in C++, in
-        P3DHost::determine_host_dir(). """
-
-        if hostDirBasename:
-            # If the contents.xml specified a host_dir parameter, use
-            # it.
-            hostDir = str(self.rootDir) + '/hosts'
-            for component in hostDirBasename.split('/'):
-                if component:
-                    if component[0] == '.':
-                        # Forbid ".foo" or "..".
-                        component = 'x' + component
-                    hostDir += '/'
-                    hostDir += component
-            return Filename(hostDir)
-
-        hostDir = 'hosts/'
-
-        # Look for a server name in the URL.  Including this string in the
-        # directory name makes it friendlier for people browsing the
-        # directory.
-
-        # We could use URLSpec, but we do it by hand instead, to make
-        # it more likely that our hash code will exactly match the
-        # similar logic in P3DHost.
-        p = hostUrl.find('://')
-        hostname = ''
-        if p != -1:
-            start = p + 3
-            end = hostUrl.find('/', start)
-            # Now start .. end is something like "username@host:port".
-
-            at = hostUrl.find('@', start)
-            if at != -1 and at < end:
-                start = at + 1
-
-            colon = hostUrl.find(':', start)
-            if colon != -1 and colon < end:
-                end = colon
-
-            # Now start .. end is just the hostname.
-            hostname = hostUrl[start : end]
-
-        # Now build a hash string of the whole URL.  We'll use MD5 to
-        # get a pretty good hash, with a minimum chance of collision.
-        # Even if there is a hash collision, though, it's not the end
-        # of the world; it just means that both hosts will dump their
-        # packages into the same directory, and they'll fight over the
-        # toplevel contents.xml file.  Assuming they use different
-        # version numbers (which should be safe since they have the
-        # same hostname), there will be minimal redownloading.
-
-        hashSize = 16
-        keepHash = hashSize
-        if hostname:
-            hostDir += hostname + '_'
-
-            # If we successfully got a hostname, we don't really need the
-            # full hash.  We'll keep half of it.
-            keepHash = keepHash // 2
-
-        md = HashVal()
-        md.hashString(hostUrl)
-        hostDir += md.asHex()[:keepHash * 2]
-
-        hostDir = Filename(self.rootDir, hostDir)
-        return hostDir

+ 0 - 24
direct/src/p3d/InstalledHostData.py

@@ -1,24 +0,0 @@
-__all__ = ["InstalledHostData"]
-
-from panda3d.core import URLSpec
-
-class InstalledHostData:
-    """ A list of instances of this class is returned by
-    AppRunner.scanInstalledPackages().  Each of these corresponds to a
-    particular host that has provided packages that have been
-    installed on the local client. """
-
-    def __init__(self, host, dirnode):
-        self.host = host
-        self.pathname = dirnode.pathname
-        self.totalSize = dirnode.getTotalSize()
-        self.packages = []
-
-        if self.host:
-            self.hostUrl = self.host.hostUrl
-            self.descriptiveName = self.host.descriptiveName
-            if not self.descriptiveName:
-                self.descriptiveName = URLSpec(self.hostUrl).getServer()
-        else:
-            self.hostUrl = 'unknown'
-            self.descriptiveName = 'unknown'

+ 0 - 30
direct/src/p3d/InstalledPackageData.py

@@ -1,30 +0,0 @@
-__all__ = ["InstalledPackageData"]
-
-class InstalledPackageData:
-    """ A list of instances of this class is maintained by
-    InstalledHostData (which is in turn returned by
-    AppRunner.scanInstalledPackages()).  Each of these corresponds to
-    a particular package that has been installed on the local
-    client. """
-
-    def __init__(self, package, dirnode):
-        self.package = package
-        self.pathname = dirnode.pathname
-        self.totalSize = dirnode.getTotalSize()
-        self.lastUse = None
-
-        if self.package:
-            self.displayName = self.package.getFormattedName()
-            xusage = self.package.getUsage()
-
-            if xusage:
-                lastUse = xusage.Attribute('last_use')
-                try:
-                    lastUse = int(lastUse or '')
-                except ValueError:
-                    lastUse = None
-                self.lastUse = lastUse
-
-        else:
-            self.displayName = dirnode.pathname.getBasename()
-

+ 0 - 298
direct/src/p3d/JavaScript.py

@@ -1,298 +0,0 @@
-""" This module defines some simple classes and instances which are
-useful when writing code that integrates with JavaScript, especially
-code that runs in a browser via the web plugin. """
-
-__all__ = ["UndefinedObject", "Undefined", "ConcreteStruct", "BrowserObject", "MethodWrapper"]
-
-class UndefinedObject:
-    """ This is a special object that is returned by the browser to
-    represent an "undefined" or "void" value, typically the value for
-    an uninitialized variable or undefined property.  It has no
-    attributes, similar to None, but it is a slightly different
-    concept in JavaScript. """
-
-    def __bool__(self):
-        return False
-
-    __nonzero__ = __bool__ # Python 2
-
-    def __str__(self):
-        return "Undefined"
-
-# In fact, we normally always return this precise instance of the
-# UndefinedObject.
-Undefined = UndefinedObject()
-
-class ConcreteStruct:
-    """ Python objects that inherit from this class are passed to
-    JavaScript as a concrete struct: a mapping from string -> value,
-    with no methods, passed by value.  This can be more optimal than
-    traditional Python objects which are passed by reference,
-    especially for small objects which might be repeatedly referenced
-    on the JavaScript side. """
-
-    def __init__(self):
-        pass
-
-    def getConcreteProperties(self):
-        """ Returns a list of 2-tuples of the (key, value) pairs that
-        are to be passed to the concrete instance.  By default, this
-        returns all properties of the object.  You can override this
-        to restrict the set of properties that are uploaded. """
-
-        return list(self.__dict__.items())
-
-class BrowserObject:
-    """ This class provides the Python wrapper around some object that
-    actually exists in the plugin host's namespace, e.g. a JavaScript
-    or DOM object. """
-
-    def __init__(self, runner, objectId):
-        self.__dict__['_BrowserObject__runner'] = runner
-        self.__dict__['_BrowserObject__objectId'] = objectId
-
-        # This element is filled in by __getattr__; it connects
-        # the object to its parent.
-        self.__dict__['_BrowserObject__childObject'] = (None, None)
-
-        # This is a cache of method names to MethodWrapper objects in
-        # the parent object.
-        self.__dict__['_BrowserObject__methods'] = {}
-
-    def __del__(self):
-        # When the BrowserObject destructs, tell the parent process it
-        # doesn't need to keep around its corresponding P3D_object any
-        # more.
-        self.__runner.dropObject(self.__objectId)
-
-    def __cacheMethod(self, methodName):
-        """ Stores a pointer to the named method on this object, so
-        that the next time __getattr__ is called, it can retrieve the
-        method wrapper without having to query the browser.  This
-        cache assumes that callable methods don't generally come and
-        go on and object.
-
-        The return value is the MethodWrapper object. """
-
-        method = self.__methods.get(methodName, None)
-        if method is None:
-            method = MethodWrapper(self.__runner, self, methodName)
-            self.__methods[methodName] = method
-        return method
-
-    def __str__(self):
-        return self.toString()
-
-    def __bool__(self):
-        return True
-
-    __nonzero__ = __bool__ # Python 2
-
-    def __call__(self, *args, **kw):
-        needsResponse = True
-        if 'needsResponse' in kw:
-            needsResponse = kw['needsResponse']
-            del kw['needsResponse']
-        if kw:
-            raise ArgumentError('Keyword arguments not supported')
-
-        try:
-            parentObj, attribName = self.__childObject
-            if parentObj:
-                # Call it as a method.
-                if parentObj is self.__runner.dom and attribName == 'alert':
-                    # As a special hack, we don't wait for the return
-                    # value from the alert() call, since this is a
-                    # blocking call, and waiting for this could cause
-                    # problems.
-                    needsResponse = False
-
-                if parentObj is self.__runner.dom and attribName == 'eval' and len(args) == 1 and isinstance(args[0], str):
-                    # As another special hack, we make dom.eval() a
-                    # special case, and map it directly into an eval()
-                    # call.  If the string begins with 'void ', we further
-                    # assume we're not waiting for a response.
-                    if args[0].startswith('void '):
-                        needsResponse = False
-                    result = self.__runner.scriptRequest('eval', parentObj, value = args[0], needsResponse = needsResponse)
-                else:
-                    # This is a normal method call.
-                    try:
-                        result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse)
-                    except EnvironmentError:
-                        # Problem on the call.  Maybe no such method?
-                        raise AttributeError
-
-                # Hey, the method call appears to have succeeded.
-                # Cache the method object on the parent so we won't
-                # have to look up the method wrapper again next time.
-                parentObj.__cacheMethod(attribName)
-
-            else:
-                # Call it as a plain function.
-                result = self.__runner.scriptRequest('call', self, value = args, needsResponse = needsResponse)
-        except EnvironmentError:
-            # Some odd problem on the call.
-            raise TypeError
-
-        return result
-
-    def __getattr__(self, name):
-        """ Remaps attempts to query an attribute, as in obj.attr,
-        into the appropriate calls to query the actual browser object
-        under the hood.  """
-
-        # First check to see if there's a cached method wrapper from a
-        # previous call.
-        method = self.__methods.get(name, None)
-        if method:
-            return method
-
-        # No cache.  Go query the browser for the desired value.
-        try:
-            value = self.__runner.scriptRequest('get_property', self,
-                                                propertyName = name)
-        except EnvironmentError:
-            # Failed to retrieve the attribute.  But maybe there's a
-            # method instead?
-            if self.__runner.scriptRequest('has_method', self, propertyName = name):
-                # Yes, so create a method wrapper for it.
-                return self.__cacheMethod(name)
-
-            raise AttributeError(name)
-
-        if isinstance(value, BrowserObject):
-            # Fill in the parent object association, so __call__ can
-            # properly call a method.  (Javascript needs to know the
-            # method container at the time of the call, and doesn't
-            # store it on the function object.)
-            value.__dict__['_BrowserObject__childObject'] = (self, name)
-
-        return value
-
-    def __setattr__(self, name, value):
-        if name in self.__dict__:
-            self.__dict__[name] = value
-            return
-
-        result = self.__runner.scriptRequest('set_property', self,
-                                             propertyName = name,
-                                             value = value)
-        if not result:
-            raise AttributeError(name)
-
-    def __delattr__(self, name):
-        if name in self.__dict__:
-            del self.__dict__[name]
-            return
-
-        result = self.__runner.scriptRequest('del_property', self,
-                                             propertyName = name)
-        if not result:
-            raise AttributeError(name)
-
-    def __getitem__(self, key):
-        """ Remaps attempts to query an attribute, as in obj['attr'],
-        into the appropriate calls to query the actual browser object
-        under the hood.  Following the JavaScript convention, we treat
-        obj['attr'] almost the same as obj.attr. """
-
-        try:
-            value = self.__runner.scriptRequest('get_property', self,
-                                                propertyName = str(key))
-        except EnvironmentError:
-            # Failed to retrieve the property.  We return IndexError
-            # for numeric keys so we can properly support Python's
-            # iterators, but we return KeyError for string keys to
-            # emulate mapping objects.
-            if isinstance(key, str):
-                raise KeyError(key)
-            else:
-                raise IndexError(key)
-
-        return value
-
-    def __setitem__(self, key, value):
-        result = self.__runner.scriptRequest('set_property', self,
-                                             propertyName = str(key),
-                                             value = value)
-        if not result:
-            if isinstance(key, str):
-                raise KeyError(key)
-            else:
-                raise IndexError(key)
-
-    def __delitem__(self, key):
-        result = self.__runner.scriptRequest('del_property', self,
-                                             propertyName = str(key))
-        if not result:
-            if isinstance(key, str):
-                raise KeyError(key)
-            else:
-                raise IndexError(key)
-
-class MethodWrapper:
-    """ This is a Python wrapper around a property of a BrowserObject
-    that doesn't appear to be a first-class object in the Python
-    sense, but is nonetheless a callable method. """
-
-    def __init__(self, runner, parentObj, objectId):
-        self.__dict__['_MethodWrapper__runner'] = runner
-        self.__dict__['_MethodWrapper__childObject'] = (parentObj, objectId)
-
-    def __str__(self):
-        parentObj, attribName = self.__childObject
-        return "%s.%s" % (parentObj, attribName)
-
-    def __bool__(self):
-        return True
-
-    __nonzero__ = __bool__ # Python 2
-
-    def __call__(self, *args, **kw):
-        needsResponse = True
-        if 'needsResponse' in kw:
-            needsResponse = kw['needsResponse']
-            del kw['needsResponse']
-        if kw:
-            raise ArgumentError('Keyword arguments not supported')
-
-        try:
-            parentObj, attribName = self.__childObject
-            # Call it as a method.
-            if parentObj is self.__runner.dom and attribName == 'alert':
-                # As a special hack, we don't wait for the return
-                # value from the alert() call, since this is a
-                # blocking call, and waiting for this could cause
-                # problems.
-                needsResponse = False
-
-            if parentObj is self.__runner.dom and attribName == 'eval' and len(args) == 1 and isinstance(args[0], str):
-                # As another special hack, we make dom.eval() a
-                # special case, and map it directly into an eval()
-                # call.  If the string begins with 'void ', we further
-                # assume we're not waiting for a response.
-                if args[0].startswith('void '):
-                    needsResponse = False
-                result = self.__runner.scriptRequest('eval', parentObj, value = args[0], needsResponse = needsResponse)
-            else:
-                # This is a normal method call.
-                try:
-                    result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse)
-                except EnvironmentError:
-                    # Problem on the call.  Maybe no such method?
-                    raise AttributeError
-
-        except EnvironmentError:
-            # Some odd problem on the call.
-            raise TypeError
-
-        return result
-
-    def __setattr__(self, name, value):
-        """ setattr will generally fail on method objects. """
-        raise AttributeError(name)
-
-    def __delattr__(self, name):
-        """ delattr will generally fail on method objects. """
-        raise AttributeError(name)

+ 0 - 1237
direct/src/p3d/PackageInfo.py

@@ -1,1237 +0,0 @@
-__all__ = ["PackageInfo"]
-
-from panda3d.core import Filename, DocumentSpec, Multifile, Decompressor, EUOk, EUSuccess, VirtualFileSystem, Thread, getModelPath, ExecutionEnvironment, PStatCollector, TiXmlDocument, TiXmlDeclaration, TiXmlElement
-import panda3d.core as core
-from direct.p3d.FileSpec import FileSpec
-from direct.p3d.ScanDirectoryNode import ScanDirectoryNode
-from direct.showbase import VFSImporter
-from direct.directnotify.DirectNotifyGlobal import directNotify
-from direct.task.TaskManagerGlobal import taskMgr
-import os
-import sys
-import random
-import time
-import copy
-
-class PackageInfo:
-
-    """ This class represents a downloadable Panda3D package file that
-    can be (or has been) installed into the current runtime.  It is
-    the Python equivalent of the P3DPackage class in the core API. """
-
-    notify = directNotify.newCategory("PackageInfo")
-
-    # Weight factors for computing download progress.  This
-    # attempts to reflect the relative time-per-byte of each of
-    # these operations.
-    downloadFactor = 1
-    uncompressFactor = 0.01
-    unpackFactor = 0.01
-    patchFactor = 0.01
-
-    # These tokens are yielded (not returned) by __downloadFile() and
-    # other InstallStep functions.
-    stepComplete = 1
-    stepFailed = 2
-    restartDownload = 3
-    stepContinue = 4
-
-    UsageBasename = 'usage.xml'
-
-    class InstallStep:
-        """ This class is one step of the installPlan list; it
-        represents a single atomic piece of the installation step, and
-        the relative effort of that piece.  When the plan is executed,
-        it will call the saved function pointer here. """
-        def __init__(self, func, bytes, factor, stepType):
-            self.__funcPtr = func
-            self.bytesNeeded = bytes
-            self.bytesDone = 0
-            self.bytesFactor = factor
-            self.stepType = stepType
-            self.pStatCol = PStatCollector(':App:PackageInstaller:%s' % (stepType))
-
-        def func(self):
-            """ self.__funcPtr(self) will return a generator of
-            tokens.  This function defines a new generator that yields
-            each of those tokens, but wraps each call into the nested
-            generator within a pair of start/stop collector calls. """
-
-            self.pStatCol.start()
-            for token in self.__funcPtr(self):
-                self.pStatCol.stop()
-                yield token
-                self.pStatCol.start()
-
-            # Shouldn't ever get here.
-            self.pStatCol.stop()
-            raise StopIteration
-
-        def getEffort(self):
-            """ Returns the relative amount of effort of this step. """
-            return self.bytesNeeded * self.bytesFactor
-
-        def getProgress(self):
-            """ Returns the progress of this step, in the range
-            0..1. """
-            if self.bytesNeeded == 0:
-                return 1
-            return min(float(self.bytesDone) / float(self.bytesNeeded), 1)
-
-    def __init__(self, host, packageName, packageVersion, platform = None,
-                 solo = False, asMirror = False, perPlatform = False):
-        self.host = host
-        self.packageName = packageName
-        self.packageVersion = packageVersion
-        self.platform = platform
-        self.solo = solo
-        self.asMirror = asMirror
-        self.perPlatform = perPlatform
-
-        # This will be active while we are in the middle of a download
-        # cycle.
-        self.http = None
-
-        # This will be filled in when the host's contents.xml file is
-        # read.
-        self.packageDir = None
-
-        # These will be filled in by HostInfo when the package is read
-        # from contents.xml.
-        self.descFile = None
-        self.importDescFile = None
-
-        # These are filled in when the desc file is successfully read.
-        self.hasDescFile = False
-        self.patchVersion = None
-        self.displayName = None
-        self.guiApp = False
-        self.uncompressedArchive = None
-        self.compressedArchive = None
-        self.extracts = []
-        self.requires = []
-        self.installPlans = None
-
-        # This is updated during downloadPackage().  It is in the
-        # range 0..1.
-        self.downloadProgress = 0
-
-        # This is set true when the package file has been fully
-        # downloaded and unpacked.
-        self.hasPackage = False
-
-        # This is set true when the package has been "installed",
-        # meaning it's been added to the paths and all.
-        self.installed = False
-
-        # This is set true when the package has been updated in this
-        # session, but not yet written to usage.xml.
-        self.updated = False
-        self.diskSpace = None
-
-    def getPackageDir(self):
-        """ Returns the directory in which this package is installed.
-        This may not be known until the host's contents.xml file has
-        been downloaded, which informs us of the host's own install
-        directory. """
-
-        if not self.packageDir:
-            if not self.host.hasContentsFile:
-                if not self.host.readContentsFile():
-                    self.host.downloadContentsFile(self.http)
-
-            # Derive the packageDir from the hostDir.
-            self.packageDir = Filename(self.host.hostDir, self.packageName)
-            if self.packageVersion:
-                self.packageDir = Filename(self.packageDir, self.packageVersion)
-
-            if self.host.perPlatform:
-                # If we're running on a special host that wants us to
-                # include the platform, we include it.
-                includePlatform = True
-            elif self.perPlatform and self.host.appRunner.respectPerPlatform:
-                # Otherwise, if our package spec wants us to include
-                # the platform (and our plugin knows about this), then
-                # we also include it.
-                includePlatform = True
-            else:
-                # Otherwise, we must be running legacy code
-                # somewhere--either an old package or an old
-                # plugin--and we therefore shouldn't include the
-                # platform in the directory hierarchy.
-                includePlatform = False
-
-            if includePlatform and self.platform:
-                self.packageDir = Filename(self.packageDir, self.platform)
-
-        return self.packageDir
-
-    def getDownloadEffort(self):
-        """ Returns the relative amount of effort it will take to
-        download this package.  The units are meaningless, except
-        relative to other packges."""
-
-        if not self.installPlans:
-            return 0
-
-        # Return the size of plan A, assuming it will work.
-        plan = self.installPlans[0]
-        size = sum([step.getEffort() for step in plan])
-
-        return size
-
-    def getPrevDownloadedEffort(self):
-        """ Returns a rough estimate of this package's total download
-        effort, even if it is already downloaded. """
-
-        effort = 0
-        if self.compressedArchive:
-            effort += self.compressedArchive.size * self.downloadFactor
-        if self.uncompressedArchive:
-            effort += self.uncompressedArchive.size * self.uncompressFactor
-        # Don't bother counting unpacking.
-
-        return effort
-
-    def getFormattedName(self):
-        """ Returns the name of this package, for output to the user.
-        This will be the "public" name of the package, as formatted
-        for user consumption; it will include capital letters and
-        spaces where appropriate. """
-
-        if self.displayName:
-            name = self.displayName
-        else:
-            name = self.packageName
-            if self.packageVersion:
-                name += ' %s' % (self.packageVersion)
-
-        if self.patchVersion:
-            name += ' rev %s' % (self.patchVersion)
-
-        return name
-
-
-    def setupFilenames(self):
-        """ This is called by the HostInfo when the package is read
-        from contents.xml, to set up the internal filenames and such
-        that rely on some of the information from contents.xml. """
-
-        dirname, basename = self.descFile.filename.rsplit('/', 1)
-        self.descFileDirname = dirname
-        self.descFileBasename = basename
-
-    def checkStatus(self):
-        """ Checks the current status of the desc file and the package
-        contents on disk. """
-
-        if self.hasPackage:
-            return True
-
-        if not self.hasDescFile:
-            filename = Filename(self.getPackageDir(), self.descFileBasename)
-            if self.descFile.quickVerify(self.getPackageDir(), pathname = filename, notify = self.notify):
-                if self.__readDescFile():
-                    # Successfully read.  We don't need to call
-                    # checkArchiveStatus again, since readDescFile()
-                    # has just done it.
-                    return self.hasPackage
-
-        if self.hasDescFile:
-            if self.__checkArchiveStatus():
-                # It's all good.
-                self.hasPackage = True
-
-        return self.hasPackage
-
-    def hasCurrentDescFile(self):
-        """ Returns true if a desc file file has been successfully
-        read for this package and is still current, false
-        otherwise. """
-
-        if not self.host.hasCurrentContentsFile():
-            return False
-
-        return self.hasDescFile
-
-    def downloadDescFile(self, http):
-        """ Downloads the desc file for this particular package,
-        synchronously, and then reads it.  Returns true on success,
-        false on failure. """
-
-        for token in self.downloadDescFileGenerator(http):
-            if token != self.stepContinue:
-                break
-            Thread.considerYield()
-
-        return (token == self.stepComplete)
-
-    def downloadDescFileGenerator(self, http):
-        """ A generator function that implements downloadDescFile()
-        one piece at a time.  It yields one of stepComplete,
-        stepFailed, or stepContinue. """
-
-        assert self.descFile
-
-        if self.hasDescFile:
-            # We've already got one.
-            yield self.stepComplete; return
-
-        if not self.host.appRunner or self.host.appRunner.verifyContents != self.host.appRunner.P3DVCNever:
-            # We're allowed to download it.
-            self.http = http
-
-            func = lambda step, self = self: self.__downloadFile(
-                None, self.descFile,
-                urlbase = self.descFile.filename,
-                filename = self.descFileBasename)
-            step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc')
-
-            for token in step.func():
-                if token == self.stepContinue:
-                    yield token
-                else:
-                    break
-
-            while token == self.restartDownload:
-                # Try again.
-                func = lambda step, self = self: self.__downloadFile(
-                    None, self.descFile,
-                    urlbase = self.descFile.filename,
-                    filename = self.descFileBasename)
-                step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc')
-                for token in step.func():
-                    if token == self.stepContinue:
-                        yield token
-                    else:
-                        break
-
-            if token == self.stepFailed:
-                # Couldn't download the desc file.
-                yield self.stepFailed; return
-
-            assert token == self.stepComplete
-
-            filename = Filename(self.getPackageDir(), self.descFileBasename)
-            # Now that we've written the desc file, make it read-only.
-            os.chmod(filename.toOsSpecific(), 0o444)
-
-        if not self.__readDescFile():
-            # Weird, it passed the hash check, but we still can't read
-            # it.
-            filename = Filename(self.getPackageDir(), self.descFileBasename)
-            self.notify.warning("Failure reading %s" % (filename))
-            yield self.stepFailed; return
-
-        yield self.stepComplete; return
-
-    def __readDescFile(self):
-        """ Reads the desc xml file for this particular package,
-        assuming it's been already downloaded and verified.  Returns
-        true on success, false on failure. """
-
-        if self.hasDescFile:
-            # No need to read it again.
-            return True
-
-        if self.solo:
-            # If this is a "solo" package, we don't actually "read"
-            # the desc file; that's the entire contents of the
-            # package.
-            self.hasDescFile = True
-            self.hasPackage = True
-            return True
-
-        filename = Filename(self.getPackageDir(), self.descFileBasename)
-
-        if not hasattr(core, 'TiXmlDocument'):
-            return False
-        doc = core.TiXmlDocument(filename.toOsSpecific())
-        if not doc.LoadFile():
-            return False
-
-        xpackage = doc.FirstChildElement('package')
-        if not xpackage:
-            return False
-
-        try:
-            self.patchVersion = int(xpackage.Attribute('patch_version') or '')
-        except ValueError:
-            self.patchVersion = None
-
-        try:
-            perPlatform = int(xpackage.Attribute('per_platform') or '')
-        except ValueError:
-            perPlatform = False
-        if perPlatform != self.perPlatform:
-            self.notify.warning("per_platform disagreement on package %s" % (self.packageName))
-
-        self.displayName = None
-        xconfig = xpackage.FirstChildElement('config')
-        if xconfig:
-            # The name for display to an English-speaking user.
-            self.displayName = xconfig.Attribute('display_name')
-
-            # True if any apps that use this package must be GUI apps.
-            guiApp = xconfig.Attribute('gui_app')
-            if guiApp:
-                self.guiApp = int(guiApp)
-
-        # The uncompressed archive, which will be mounted directly,
-        # and also used for patching.
-        xuncompressedArchive = xpackage.FirstChildElement('uncompressed_archive')
-        if xuncompressedArchive:
-            self.uncompressedArchive = FileSpec()
-            self.uncompressedArchive.loadXml(xuncompressedArchive)
-
-        # The compressed archive, which is what is downloaded.
-        xcompressedArchive = xpackage.FirstChildElement('compressed_archive')
-        if xcompressedArchive:
-            self.compressedArchive = FileSpec()
-            self.compressedArchive.loadXml(xcompressedArchive)
-
-        # The list of files that should be extracted to disk.
-        self.extracts = []
-        xextract = xpackage.FirstChildElement('extract')
-        while xextract:
-            file = FileSpec()
-            file.loadXml(xextract)
-            self.extracts.append(file)
-            xextract = xextract.NextSiblingElement('extract')
-
-        # The list of additional packages that must be installed for
-        # this package to function properly.
-        self.requires = []
-        xrequires = xpackage.FirstChildElement('requires')
-        while xrequires:
-            packageName = xrequires.Attribute('name')
-            version = xrequires.Attribute('version')
-            hostUrl = xrequires.Attribute('host')
-            if packageName and hostUrl:
-                host = self.host.appRunner.getHostWithAlt(hostUrl)
-                self.requires.append((packageName, version, host))
-            xrequires = xrequires.NextSiblingElement('requires')
-
-        self.hasDescFile = True
-
-        # Now that we've read the desc file, go ahead and use it to
-        # verify the download status.
-        if self.__checkArchiveStatus():
-            # It's all fully downloaded, unpacked, and ready.
-            self.hasPackage = True
-            return True
-
-        # Still have to download it.
-        self.__buildInstallPlans()
-        return True
-
-    def __buildInstallPlans(self):
-        """ Sets up self.installPlans, a list of one or more "plans"
-        to download and install the package. """
-
-        pc = PStatCollector(':App:PackageInstaller:buildInstallPlans')
-        pc.start()
-
-        self.hasPackage = False
-
-        if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
-            # We're not allowed to download anything.
-            self.installPlans = []
-            pc.stop()
-            return
-
-        if self.asMirror:
-            # If we're just downloading a mirror archive, we only need
-            # to get the compressed archive file.
-
-            # Build a one-item install plan to download the compressed
-            # archive.
-            downloadSize = self.compressedArchive.size
-            func = lambda step, fileSpec = self.compressedArchive: self.__downloadFile(step, fileSpec, allowPartial = True)
-
-            step = self.InstallStep(func, downloadSize, self.downloadFactor, 'download')
-            installPlan = [step]
-            self.installPlans = [installPlan]
-            pc.stop()
-            return
-
-        # The normal download process.  Determine what we will need to
-        # download, and build a plan (or two) to download it all.
-        self.installPlans = None
-
-        # We know we will at least need to unpack the archive contents
-        # at the end.
-        unpackSize = 0
-        for file in self.extracts:
-            unpackSize += file.size
-        step = self.InstallStep(self.__unpackArchive, unpackSize, self.unpackFactor, 'unpack')
-        planA = [step]
-
-        # If the uncompressed archive file is good, that's all we'll
-        # need to do.
-        self.uncompressedArchive.actualFile = None
-        if self.uncompressedArchive.quickVerify(self.getPackageDir(), notify = self.notify):
-            self.installPlans = [planA]
-            pc.stop()
-            return
-
-        # Maybe the compressed archive file is good.
-        if self.compressedArchive.quickVerify(self.getPackageDir(), notify = self.notify):
-            uncompressSize = self.uncompressedArchive.size
-            step = self.InstallStep(self.__uncompressArchive, uncompressSize, self.uncompressFactor, 'uncompress')
-            planA = [step] + planA
-            self.installPlans = [planA]
-            pc.stop()
-            return
-
-        # Maybe we can download one or more patches.  We'll come back
-        # to that in a minute as plan A.  For now, construct plan B,
-        # which will be to download the whole archive.
-        planB = planA[:]
-
-        uncompressSize = self.uncompressedArchive.size
-        step = self.InstallStep(self.__uncompressArchive, uncompressSize, self.uncompressFactor, 'uncompress')
-        planB = [step] + planB
-
-        downloadSize = self.compressedArchive.size
-        func = lambda step, fileSpec = self.compressedArchive: self.__downloadFile(step, fileSpec, allowPartial = True)
-
-        step = self.InstallStep(func, downloadSize, self.downloadFactor, 'download')
-        planB = [step] + planB
-
-        # Now look for patches.  Start with the md5 hash from the
-        # uncompressedArchive file we have on disk, and see if we can
-        # find a patch chain from this file to our target.
-        pathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
-        fileSpec = self.uncompressedArchive.actualFile
-        if fileSpec is None and pathname.exists():
-            fileSpec = FileSpec()
-            fileSpec.fromFile(self.getPackageDir(), self.uncompressedArchive.filename)
-        plan = None
-        if fileSpec:
-            plan = self.__findPatchChain(fileSpec)
-        if plan:
-            # We can download patches.  Great!  That means this is
-            # plan A, and the full download is plan B (in case
-            # something goes wrong with the patching).
-            planA = plan + planA
-            self.installPlans = [planA, planB]
-        else:
-            # There are no patches to download, oh well.  Stick with
-            # plan B as the only plan.
-            self.installPlans = [planB]
-
-        # In case of unexpected failures on the internet, we will retry
-        # the full download instead of just giving up.
-        retries = core.ConfigVariableInt('package-full-dl-retries', 1).getValue()
-        for retry in range(retries):
-            self.installPlans.append(planB[:])
-
-        pc.stop()
-
-    def __scanDirectoryRecursively(self, dirname):
-        """ Generates a list of Filename objects: all of the files
-        (not directories) within and below the indicated dirname. """
-
-        contents = []
-        for dirpath, dirnames, filenames in os.walk(dirname.toOsSpecific()):
-            dirpath = Filename.fromOsSpecific(dirpath)
-            if dirpath == dirname:
-                dirpath = Filename('')
-            else:
-                dirpath.makeRelativeTo(dirname)
-            for filename in filenames:
-                contents.append(Filename(dirpath, filename))
-        return contents
-
-    def __removeFileFromList(self, contents, filename):
-        """ Removes the indicated filename from the given list, if it is
-        present.  """
-        try:
-            contents.remove(Filename(filename))
-        except ValueError:
-            pass
-
-    def __checkArchiveStatus(self):
-        """ Returns true if the archive and all extractable files are
-        already correct on disk, false otherwise. """
-
-        if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
-            # Assume that everything is just fine.
-            return True
-
-        # Get a list of all of the files in the directory, so we can
-        # remove files that don't belong.
-        contents = self.__scanDirectoryRecursively(self.getPackageDir())
-        self.__removeFileFromList(contents, self.descFileBasename)
-        self.__removeFileFromList(contents, self.compressedArchive.filename)
-        self.__removeFileFromList(contents, self.UsageBasename)
-        if not self.asMirror:
-            self.__removeFileFromList(contents, self.uncompressedArchive.filename)
-            for file in self.extracts:
-                self.__removeFileFromList(contents, file.filename)
-
-        # Now, any files that are still in the contents list don't
-        # belong.  It's important to remove these files before we
-        # start verifying the files that we expect to find here, in
-        # case there is a problem with ambiguous filenames or
-        # something (e.g. case insensitivity).
-        for filename in contents:
-            self.notify.info("Removing %s" % (filename))
-            pathname = Filename(self.getPackageDir(), filename)
-            pathname.unlink()
-            self.updated = True
-
-        if self.asMirror:
-            return self.compressedArchive.quickVerify(self.getPackageDir(), notify = self.notify)
-
-        allExtractsOk = True
-        if not self.uncompressedArchive.quickVerify(self.getPackageDir(), notify = self.notify):
-            self.notify.debug("File is incorrect: %s" % (self.uncompressedArchive.filename))
-            allExtractsOk = False
-
-        if allExtractsOk:
-            # OK, the uncompressed archive is good; that means there
-            # shouldn't be a compressed archive file here.
-            pathname = Filename(self.getPackageDir(), self.compressedArchive.filename)
-            pathname.unlink()
-
-            for file in self.extracts:
-                if not file.quickVerify(self.getPackageDir(), notify = self.notify):
-                    self.notify.debug("File is incorrect: %s" % (file.filename))
-                    allExtractsOk = False
-                    break
-
-        if allExtractsOk:
-            self.notify.debug("All %s extracts of %s seem good." % (
-                len(self.extracts), self.packageName))
-
-        return allExtractsOk
-
-    def __updateStepProgress(self, step):
-        """ This callback is made from within the several step
-        functions as the download step proceeds.  It updates
-        self.downloadProgress with the current progress, so the caller
-        can asynchronously query this value. """
-
-        size = self.totalPlanCompleted + self.currentStepEffort * step.getProgress()
-        self.downloadProgress = min(float(size) / float(self.totalPlanSize), 1)
-
-    def downloadPackage(self, http):
-        """ Downloads the package file, synchronously, then
-        uncompresses and unpacks it.  Returns true on success, false
-        on failure.
-
-        This assumes that self.installPlans has already been filled
-        in, which will have been done by self.__readDescFile().
-        """
-
-        for token in self.downloadPackageGenerator(http):
-            if token != self.stepContinue:
-                break
-            Thread.considerYield()
-
-        return (token == self.stepComplete)
-
-    def downloadPackageGenerator(self, http):
-        """ A generator function that implements downloadPackage() one
-        piece at a time.  It yields one of stepComplete, stepFailed,
-        or stepContinue. """
-
-        assert self.hasDescFile
-
-        if self.hasPackage:
-            # We've already got one.
-            yield self.stepComplete; return
-
-        if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
-            # We're not allowed to download anything. Assume it's already downloaded.
-            yield self.stepComplete; return
-
-        # We should have an install plan by the time we get here.
-        assert self.installPlans
-
-        self.http = http
-        for token in self.__followInstallPlans():
-            if token == self.stepContinue:
-                yield token
-            else:
-                break
-
-        while token == self.restartDownload:
-            # Try again.
-            for token in self.downloadDescFileGenerator(http):
-                if token == self.stepContinue:
-                    yield token
-                else:
-                    break
-            if token == self.stepComplete:
-                for token in self.__followInstallPlans():
-                    if token == self.stepContinue:
-                        yield token
-                    else:
-                        break
-
-        if token == self.stepFailed:
-            yield self.stepFailed; return
-
-        assert token == self.stepComplete
-        yield self.stepComplete; return
-
-
-    def __followInstallPlans(self):
-        """ Performs all of the steps in self.installPlans.  Yields
-        one of stepComplete, stepFailed, restartDownload, or
-        stepContinue. """
-
-        if not self.installPlans:
-            self.__buildInstallPlans()
-
-        installPlans = self.installPlans
-        self.installPlans = None
-        for plan in installPlans:
-            self.totalPlanSize = sum([step.getEffort() for step in plan])
-            self.totalPlanCompleted = 0
-            self.downloadProgress = 0
-
-            planFailed = False
-            for step in plan:
-                self.currentStepEffort = step.getEffort()
-
-                for token in step.func():
-                    if token == self.stepContinue:
-                        yield token
-                    else:
-                        break
-
-                if token == self.restartDownload:
-                    yield token
-                if token == self.stepFailed:
-                    planFailed = True
-                    break
-                assert token == self.stepComplete
-
-                self.totalPlanCompleted += self.currentStepEffort
-
-            if not planFailed:
-                # Successfully downloaded!
-                yield self.stepComplete; return
-
-            if taskMgr.destroyed:
-                yield self.stepFailed; return
-
-        # All plans failed.
-        yield self.stepFailed; return
-
-    def __findPatchChain(self, fileSpec):
-        """ Finds the chain of patches that leads from the indicated
-        patch version to the current patch version.  If found,
-        constructs an installPlan that represents the steps of the
-        patch installation; otherwise, returns None. """
-
-        from direct.p3d.PatchMaker import PatchMaker
-
-        patchMaker = PatchMaker(self.getPackageDir())
-        patchChain = patchMaker.getPatchChainToCurrent(self.descFileBasename, fileSpec)
-        if patchChain is None:
-            # No path.
-            patchMaker.cleanup()
-            return None
-
-        plan = []
-        for patchfile in patchChain:
-            downloadSize = patchfile.file.size
-            func = lambda step, fileSpec = patchfile.file: self.__downloadFile(step, fileSpec, allowPartial = True)
-            step = self.InstallStep(func, downloadSize, self.downloadFactor, 'download')
-            plan.append(step)
-
-            patchSize = patchfile.targetFile.size
-            func = lambda step, patchfile = patchfile: self.__applyPatch(step, patchfile)
-            step = self.InstallStep(func, patchSize, self.patchFactor, 'patch')
-            plan.append(step)
-
-        patchMaker.cleanup()
-        return plan
-
-    def __downloadFile(self, step, fileSpec, urlbase = None, filename = None,
-                       allowPartial = False):
-        """ Downloads the indicated file from the host into
-        packageDir.  Yields one of stepComplete, stepFailed,
-        restartDownload, or stepContinue. """
-
-        if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
-            # We're not allowed to download anything.
-            yield self.stepFailed; return
-
-        self.updated = True
-
-        if not urlbase:
-            urlbase = self.descFileDirname + '/' + fileSpec.filename
-
-        # Build up a list of URL's to try downloading from.  Unlike
-        # the C++ implementation in P3DPackage.cxx, here we build the
-        # URL's in forward order.
-        tryUrls = []
-
-        if self.host.appRunner and self.host.appRunner.superMirrorUrl:
-            # We start with the "super mirror", if it's defined.
-            url = self.host.appRunner.superMirrorUrl + urlbase
-            tryUrls.append((url, False))
-
-        if self.host.mirrors:
-            # Choose two mirrors at random.
-            mirrors = self.host.mirrors[:]
-            for i in range(2):
-                mirror = random.choice(mirrors)
-                mirrors.remove(mirror)
-                url = mirror + urlbase
-                tryUrls.append((url, False))
-                if not mirrors:
-                    break
-
-        # After trying two mirrors and failing (or if there are no
-        # mirrors), go get it from the original host.
-        url = self.host.downloadUrlPrefix + urlbase
-        tryUrls.append((url, False))
-
-        # And finally, if the original host also fails, try again with
-        # a cache-buster.
-        tryUrls.append((url, True))
-
-        for url, cacheBust in tryUrls:
-            request = DocumentSpec(url)
-
-            if cacheBust:
-                # On the last attempt to download a particular file,
-                # we bust through the cache: append a query string to
-                # do this.
-                url += '?' + str(int(time.time()))
-                request = DocumentSpec(url)
-                request.setCacheControl(DocumentSpec.CCNoCache)
-
-            self.notify.info("%s downloading %s" % (self.packageName, url))
-
-            if not filename:
-                filename = fileSpec.filename
-            targetPathname = Filename(self.getPackageDir(), filename)
-            targetPathname.setBinary()
-
-            channel = self.http.makeChannel(False)
-
-            # If there's a previous partial download, attempt to resume it.
-            bytesStarted = 0
-            if allowPartial and not cacheBust and targetPathname.exists():
-                bytesStarted = targetPathname.getFileSize()
-
-            if bytesStarted < 1024*1024:
-                # Not enough bytes downloaded to be worth the risk of
-                # a partial download.
-                bytesStarted = 0
-            elif bytesStarted >= fileSpec.size:
-                # Couldn't possibly be our file.
-                bytesStarted = 0
-
-            if bytesStarted:
-                self.notify.info("Resuming %s after %s bytes already downloaded" % (url, bytesStarted))
-                # Make sure the file is writable.
-                os.chmod(targetPathname.toOsSpecific(), 0o644)
-                channel.beginGetSubdocument(request, bytesStarted, 0)
-            else:
-                # No partial download possible; get the whole file.
-                targetPathname.makeDir()
-                targetPathname.unlink()
-                channel.beginGetDocument(request)
-
-            channel.downloadToFile(targetPathname)
-            while channel.run():
-                if step:
-                    step.bytesDone = channel.getBytesDownloaded() + channel.getFirstByteDelivered()
-                    if step.bytesDone > step.bytesNeeded:
-                        # Oops, too much data.  Might as well abort;
-                        # it's the wrong file.
-                        self.notify.warning("Got more data than expected for download %s" % (url))
-                        break
-
-                    self.__updateStepProgress(step)
-
-                if taskMgr.destroyed:
-                    # If the task manager has been destroyed, we must
-                    # be shutting down.  Get out of here.
-                    self.notify.warning("Task Manager destroyed, aborting %s" % (url))
-                    yield self.stepFailed; return
-
-                yield self.stepContinue
-
-            if step:
-                step.bytesDone = channel.getBytesDownloaded() + channel.getFirstByteDelivered()
-                self.__updateStepProgress(step)
-
-            if not channel.isValid():
-                self.notify.warning("Failed to download %s" % (url))
-
-            elif not fileSpec.fullVerify(self.getPackageDir(), pathname = targetPathname, notify = self.notify):
-                self.notify.warning("After downloading, %s incorrect" % (Filename(fileSpec.filename).getBasename()))
-
-                # This attempt failed.  Maybe the original contents.xml
-                # file is stale.  Try re-downloading it now, just to be
-                # sure.
-                if self.host.redownloadContentsFile(self.http):
-                    # Yes!  Go back and start over from the beginning.
-                    yield self.restartDownload; return
-
-            else:
-                # Success!
-                yield self.stepComplete; return
-
-            # Maybe the mirror is bad.  Go back and try the next
-            # mirror.
-
-        # All attempts failed.  Maybe the original contents.xml file
-        # is stale.  Try re-downloading it now, just to be sure.
-        if self.host.redownloadContentsFile(self.http):
-            # Yes!  Go back and start over from the beginning.
-            yield self.restartDownload; return
-
-        # All mirrors failed; the server (or the internet connection)
-        # must be just fubar.
-        yield self.stepFailed; return
-
-    def __applyPatch(self, step, patchfile):
-        """ Applies the indicated patching in-place to the current
-        uncompressed archive.  The patchfile is removed after the
-        operation.  Yields one of stepComplete, stepFailed,
-        restartDownload, or stepContinue. """
-
-        self.updated = True
-
-        origPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
-        patchPathname = Filename(self.getPackageDir(), patchfile.file.filename)
-        result = Filename.temporary('', 'patch_')
-        self.notify.info("Patching %s with %s" % (origPathname, patchPathname))
-
-        p = core.Patchfile()  # The C++ class
-
-        ret = p.initiate(patchPathname, origPathname, result)
-        if ret == EUSuccess:
-            ret = p.run()
-        while ret == EUOk:
-            step.bytesDone = step.bytesNeeded * p.getProgress()
-            self.__updateStepProgress(step)
-            if taskMgr.destroyed:
-                # If the task manager has been destroyed, we must
-                # be shutting down.  Get out of here.
-                self.notify.warning("Task Manager destroyed, aborting patch %s" % (origPathname))
-                yield self.stepFailed; return
-
-            yield self.stepContinue
-            ret = p.run()
-        del p
-        patchPathname.unlink()
-
-        if ret < 0:
-            self.notify.warning("Patching of %s failed." % (origPathname))
-            result.unlink()
-            yield self.stepFailed; return
-
-        if not result.renameTo(origPathname):
-            self.notify.warning("Couldn't rename %s to %s" % (result, origPathname))
-            yield self.stepFailed; return
-
-        yield self.stepComplete; return
-
-    def __uncompressArchive(self, step):
-        """ Turns the compressed archive into the uncompressed
-        archive.  Yields one of stepComplete, stepFailed,
-        restartDownload, or stepContinue. """
-
-        if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
-            # We're not allowed to!
-            yield self.stepFailed; return
-
-        self.updated = True
-
-        sourcePathname = Filename(self.getPackageDir(), self.compressedArchive.filename)
-        targetPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
-        targetPathname.unlink()
-        self.notify.info("Uncompressing %s to %s" % (sourcePathname, targetPathname))
-        decompressor = Decompressor()
-        decompressor.initiate(sourcePathname, targetPathname)
-        totalBytes = self.uncompressedArchive.size
-        result = decompressor.run()
-        while result == EUOk:
-            step.bytesDone = int(totalBytes * decompressor.getProgress())
-            self.__updateStepProgress(step)
-            result = decompressor.run()
-            if taskMgr.destroyed:
-                # If the task manager has been destroyed, we must
-                # be shutting down.  Get out of here.
-                self.notify.warning("Task Manager destroyed, aborting decompresss %s" % (sourcePathname))
-                yield self.stepFailed; return
-
-            yield self.stepContinue
-
-        if result != EUSuccess:
-            yield self.stepFailed; return
-
-        step.bytesDone = totalBytes
-        self.__updateStepProgress(step)
-
-        if not self.uncompressedArchive.quickVerify(self.getPackageDir(), notify= self.notify):
-            self.notify.warning("after uncompressing, %s still incorrect" % (
-                self.uncompressedArchive.filename))
-            yield self.stepFailed; return
-
-        # Now that we've verified the archive, make it read-only.
-        os.chmod(targetPathname.toOsSpecific(), 0o444)
-
-        # Now we can safely remove the compressed archive.
-        sourcePathname.unlink()
-        yield self.stepComplete; return
-
-    def __unpackArchive(self, step):
-        """ Unpacks any files in the archive that want to be unpacked
-        to disk.  Yields one of stepComplete, stepFailed,
-        restartDownload, or stepContinue. """
-
-        if not self.extracts:
-            # Nothing to extract.
-            self.hasPackage = True
-            yield self.stepComplete; return
-
-        if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
-            # We're not allowed to!
-            yield self.stepFailed; return
-
-        self.updated = True
-
-        mfPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
-        self.notify.info("Unpacking %s" % (mfPathname))
-        mf = Multifile()
-        if not mf.openRead(mfPathname):
-            self.notify.warning("Couldn't open %s" % (mfPathname))
-            yield self.stepFailed; return
-
-        allExtractsOk = True
-        step.bytesDone = 0
-        for file in self.extracts:
-            i = mf.findSubfile(file.filename)
-            if i == -1:
-                self.notify.warning("Not in Multifile: %s" % (file.filename))
-                allExtractsOk = False
-                continue
-
-            targetPathname = Filename(self.getPackageDir(), file.filename)
-            targetPathname.setBinary()
-            targetPathname.unlink()
-            if not mf.extractSubfile(i, targetPathname):
-                self.notify.warning("Couldn't extract: %s" % (file.filename))
-                allExtractsOk = False
-                continue
-
-            if not file.quickVerify(self.getPackageDir(), notify = self.notify):
-                self.notify.warning("After extracting, still incorrect: %s" % (file.filename))
-                allExtractsOk = False
-                continue
-
-            # Make sure it's executable, and not writable.
-            os.chmod(targetPathname.toOsSpecific(), 0o555)
-
-            step.bytesDone += file.size
-            self.__updateStepProgress(step)
-            if taskMgr.destroyed:
-                # If the task manager has been destroyed, we must
-                # be shutting down.  Get out of here.
-                self.notify.warning("Task Manager destroyed, aborting unpacking %s" % (mfPathname))
-                yield self.stepFailed; return
-
-            yield self.stepContinue
-
-        if not allExtractsOk:
-            yield self.stepFailed; return
-
-        self.hasPackage = True
-        yield self.stepComplete; return
-
-    def installPackage(self, appRunner):
-        """ Mounts the package and sets up system paths so it becomes
-        available for use.  Returns true on success, false on failure. """
-
-        assert self.hasPackage
-        if self.installed:
-            # Already installed.
-            return True
-        assert self not in appRunner.installedPackages
-
-        mfPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
-        mf = Multifile()
-        if not mf.openRead(mfPathname):
-            self.notify.warning("Couldn't open %s" % (mfPathname))
-            return False
-
-        # We mount it under its actual location on disk.
-        root = self.getPackageDir()
-
-        vfs = VirtualFileSystem.getGlobalPtr()
-        vfs.mount(mf, root, vfs.MFReadOnly)
-
-        # Add this to the Python search path, if it's not already
-        # there.  We have to take a bit of care to check if it's
-        # already there, since there can be some ambiguity in
-        # os-specific path strings.
-        osRoot = self.getPackageDir().toOsSpecific()
-        foundOnPath = False
-        for p in sys.path:
-            if osRoot == p:
-                # Already here, exactly.
-                foundOnPath = True
-                break
-            elif osRoot == Filename.fromOsSpecific(p).toOsSpecific():
-                # Already here, with some futzing.
-                foundOnPath = True
-                break
-
-        if not foundOnPath:
-            # Not already here; add it.
-            sys.path.append(osRoot)
-
-        # Put it on the model-path, too.  We do this indiscriminantly,
-        # because the Panda3D runtime won't be adding things to the
-        # model-path, so it shouldn't be already there.
-        getModelPath().appendDirectory(self.getPackageDir())
-
-        # Set the environment variable to reference the package root.
-        envvar = '%s_ROOT' % (self.packageName.upper())
-        ExecutionEnvironment.setEnvironmentVariable(envvar, osRoot)
-
-        # Add the package root to the system paths.
-        if sys.platform.startswith('win'):
-            path = os.environ.get('PATH', '')
-            os.environ['PATH'] = "%s;%s" % (osRoot, path)
-        else:
-            path = os.environ.get('PATH', '')
-            os.environ['PATH'] = "%s:%s" % (osRoot, path)
-            path = os.environ.get('LD_LIBRARY_PATH', '')
-            os.environ['LD_LIBRARY_PATH'] = "%s:%s" % (osRoot, path)
-
-        if sys.platform == "darwin":
-            path = os.environ.get('DYLD_LIBRARY_PATH', '')
-            os.environ['DYLD_LIBRARY_PATH'] = "%s:%s" % (osRoot, path)
-
-        # Now that the environment variable is set, read all of the
-        # prc files in the package.
-        appRunner.loadMultifilePrcFiles(mf, self.getPackageDir())
-
-        # Also, find any toplevel Python packages, and add these as
-        # shared packages.  This will allow different packages
-        # installed in different directories to share Python files as
-        # if they were all in the same directory.
-        for filename in mf.getSubfileNames():
-            if filename.endswith('/__init__.pyc') or \
-               filename.endswith('/__init__.pyo') or \
-               filename.endswith('/__init__.py'):
-                components = filename.split('/')[:-1]
-                moduleName = '.'.join(components)
-                VFSImporter.sharedPackages[moduleName] = True
-
-        # Fix up any shared directories so we can load packages from
-        # disparate locations.
-        VFSImporter.reloadSharedPackages()
-
-        self.installed = True
-        appRunner.installedPackages.append(self)
-
-        self.markUsed()
-
-        return True
-
-    def __measureDiskSpace(self):
-        """ Returns the amount of space used by this package, in
-        bytes, as determined by examining the actual contents of the
-        package directory and its subdirectories. """
-
-        thisDir = ScanDirectoryNode(self.getPackageDir(), ignoreUsageXml = True)
-        diskSpace = thisDir.getTotalSize()
-        self.notify.info("Package %s uses %s MB" % (
-            self.packageName, (diskSpace + 524288) // 1048576))
-        return diskSpace
-
-    def markUsed(self):
-        """ Marks the package as having been used.  This is normally
-        called automatically by installPackage(). """
-
-        if not hasattr(core, 'TiXmlDocument'):
-            return
-
-        if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
-            # Not allowed to write any files to the package directory.
-            return
-
-        if self.updated:
-            # If we've just installed a new version of the package,
-            # re-measure the actual disk space used.
-            self.diskSpace = self.__measureDiskSpace()
-
-        filename = Filename(self.getPackageDir(), self.UsageBasename)
-        doc = TiXmlDocument(filename.toOsSpecific())
-        if not doc.LoadFile():
-            decl = TiXmlDeclaration("1.0", "utf-8", "")
-            doc.InsertEndChild(decl)
-
-        xusage = doc.FirstChildElement('usage')
-        if not xusage:
-            doc.InsertEndChild(TiXmlElement('usage'))
-            xusage = doc.FirstChildElement('usage')
-
-        now = int(time.time())
-
-        count = xusage.Attribute('count_app')
-        try:
-            count = int(count or '')
-        except ValueError:
-            count = 0
-            xusage.SetAttribute('first_use', str(now))
-        count += 1
-        xusage.SetAttribute('count_app', str(count))
-
-        xusage.SetAttribute('last_use', str(now))
-
-        if self.updated:
-            xusage.SetAttribute('last_update', str(now))
-            self.updated = False
-        else:
-            # Since we haven't changed the disk space, we can just
-            # read it from the previous xml file.
-            diskSpace = xusage.Attribute('disk_space')
-            try:
-                diskSpace = int(diskSpace or '')
-            except ValueError:
-                # Unless it wasn't set already.
-                self.diskSpace = self.__measureDiskSpace()
-
-        xusage.SetAttribute('disk_space', str(self.diskSpace))
-
-        # Write the file to a temporary filename, then atomically move
-        # it to its actual filename, to avoid race conditions when
-        # updating this file.
-        tfile = Filename.temporary(str(self.getPackageDir()), '.xml')
-        if doc.SaveFile(tfile.toOsSpecific()):
-            tfile.renameTo(filename)
-
-    def getUsage(self):
-        """ Returns the xusage element that is read from the usage.xml
-        file, or None if there is no usage.xml file. """
-
-        if not hasattr(core, 'TiXmlDocument'):
-            return None
-
-        filename = Filename(self.getPackageDir(), self.UsageBasename)
-        doc = TiXmlDocument(filename.toOsSpecific())
-        if not doc.LoadFile():
-            return None
-
-        xusage = doc.FirstChildElement('usage')
-        if not xusage:
-            return None
-
-        return copy.copy(xusage)
-

+ 0 - 640
direct/src/p3d/PackageInstaller.py

@@ -1,640 +0,0 @@
-__all__ = ["PackageInstaller"]
-
-from direct.showbase.DirectObject import DirectObject
-from direct.stdpy.threading import Lock, RLock
-from direct.showbase.MessengerGlobal import messenger
-from direct.task.TaskManagerGlobal import taskMgr
-from direct.p3d.PackageInfo import PackageInfo
-from panda3d.core import TPLow, PStatCollector
-from direct.directnotify.DirectNotifyGlobal import directNotify
-
-class PackageInstaller(DirectObject):
-
-    """ This class is used in a p3d runtime environment to manage the
-    asynchronous download and installation of packages.  If you just
-    want to install a package synchronously, see
-    appRunner.installPackage() for a simpler interface.
-
-    To use this class, you should subclass from it and override any of
-    the six callback methods: downloadStarted(), packageStarted(),
-    packageProgress(), downloadProgress(), packageFinished(),
-    downloadFinished().
-
-    Also see DWBPackageInstaller, which does exactly this, to add a
-    DirectWaitBar GUI.
-
-    """
-
-    notify = directNotify.newCategory("PackageInstaller")
-
-    globalLock = Lock()
-    nextUniqueId = 1
-
-    # This is a chain of state values progressing forward in time.
-    S_initial = 0    # addPackage() calls are being made
-    S_ready = 1      # donePackages() has been called
-    S_started = 2    # download has started
-    S_done = 3       # download is over
-
-    class PendingPackage:
-        """ This class describes a package added to the installer for
-        download. """
-
-        notify = directNotify.newCategory("PendingPackage")
-
-        def __init__(self, packageName, version, host):
-            self.packageName = packageName
-            self.version = version
-            self.host = host
-
-            # This will be filled in properly by checkDescFile() or
-            # getDescFile(); in the meantime, set a placeholder.
-            self.package = PackageInfo(host, packageName, version)
-
-            # Set true when the package has finished downloading,
-            # either successfully or unsuccessfully.
-            self.done = False
-
-            # Set true or false when self.done has been set.
-            self.success = False
-
-            # Set true when the packageFinished() callback has been
-            # delivered.
-            self.notified = False
-
-            # These are used to ensure the callbacks only get
-            # delivered once for a particular package.
-            self.calledPackageStarted = False
-            self.calledPackageFinished = False
-
-            # This is the amount of stuff we have to process to
-            # install this package, and the amount of stuff we have
-            # processed so far.  "Stuff" includes bytes downloaded,
-            # bytes uncompressed, and bytes extracted; and each of
-            # which is weighted differently into one grand total.  So,
-            # the total doesn't really represent bytes; it's a
-            # unitless number, which means something only as a ratio
-            # to other packages.  Filled in by checkDescFile() or
-            # getDescFile().
-            self.downloadEffort = 0
-
-            # Similar, but this is the theoretical effort if the
-            # package were already downloaded.
-            self.prevDownloadedEffort = 0
-
-        def __cmp__(self, pp):
-            """ Python comparision function.  This makes all
-            PendingPackages withe same (packageName, version, host)
-            combination be deemed equivalent. """
-            return cmp((self.packageName, self.version, self.host),
-                       (pp.packageName, pp.version, pp.host))
-
-        def getProgress(self):
-            """ Returns the download progress of this package in the
-            range 0..1. """
-
-            return self.package.downloadProgress
-
-        def checkDescFile(self):
-            """ Returns true if the desc file is already downloaded
-            and good, or false if it needs to be downloaded. """
-
-            if not self.host.hasCurrentContentsFile():
-                # If the contents file isn't ready yet, we can't check
-                # the desc file yet.
-                return False
-
-            # All right, get the package info now.
-            package = self.host.getPackage(self.packageName, self.version)
-            if not package:
-                self.notify.warning("Package %s %s not known on %s" % (
-                    self.packageName, self.version, self.host.hostUrl))
-                return False
-
-            self.package = package
-            self.package.checkStatus()
-
-            if not self.package.hasDescFile:
-                return False
-
-            self.downloadEffort = self.package.getDownloadEffort()
-            self.prevDownloadEffort = 0
-            if self.downloadEffort == 0:
-                self.prevDownloadedEffort = self.package.getPrevDownloadedEffort()
-
-            return True
-
-
-        def getDescFile(self, http):
-            """ Synchronously downloads the desc files required for
-            the package. """
-
-            if not self.host.downloadContentsFile(http):
-                return False
-
-            # All right, get the package info now.
-            package = self.host.getPackage(self.packageName, self.version)
-            if not package:
-                self.notify.warning("Package %s %s not known on %s" % (
-                    self.packageName, self.version, self.host.hostUrl))
-                return False
-
-            self.package = package
-            if not self.package.downloadDescFile(http):
-                return False
-
-            self.package.checkStatus()
-            self.downloadEffort = self.package.getDownloadEffort()
-            self.prevDownloadEffort = 0
-            if self.downloadEffort == 0:
-                self.prevDownloadedEffort = self.package.getPrevDownloadedEffort()
-
-            return True
-
-    def __init__(self, appRunner, taskChain = 'default'):
-        self.globalLock.acquire()
-        try:
-            self.uniqueId = PackageInstaller.nextUniqueId
-            PackageInstaller.nextUniqueId += 1
-        finally:
-            self.globalLock.release()
-
-        self.appRunner = appRunner
-        self.taskChain = taskChain
-
-        # If we're to be running on an asynchronous task chain, and
-        # the task chain hasn't yet been set up already, create the
-        # default parameters now.
-        if taskChain != 'default' and not taskMgr.hasTaskChain(self.taskChain):
-            taskMgr.setupTaskChain(self.taskChain, numThreads = 1,
-                                   threadPriority = TPLow)
-
-        self.callbackLock = Lock()
-        self.calledDownloadStarted = False
-        self.calledDownloadFinished = False
-
-        # A list of all packages that have been added to the
-        # installer.
-        self.packageLock = RLock()
-        self.packages = []
-        self.state = self.S_initial
-
-        # A list of packages that are waiting for their desc files.
-        self.needsDescFile = []
-        self.descFileTask = None
-
-        # A list of packages that are waiting to be downloaded and
-        # installed.
-        self.needsDownload = []
-        self.downloadTask = None
-
-        # A list of packages that were already done at the time they
-        # were passed to addPackage().
-        self.earlyDone = []
-
-        # A list of packages that have been successfully installed, or
-        # packages that have failed.
-        self.done = []
-        self.failed = []
-
-        # This task is spawned on the default task chain, to update
-        # the status during the download.
-        self.progressTask = None
-
-        self.accept('PackageInstaller-%s-allHaveDesc' % self.uniqueId,
-                    self.__allHaveDesc)
-        self.accept('PackageInstaller-%s-packageStarted' % self.uniqueId,
-                    self.__packageStarted)
-        self.accept('PackageInstaller-%s-packageDone' % self.uniqueId,
-                    self.__packageDone)
-
-    def destroy(self):
-        """ Interrupts all pending downloads.  No further callbacks
-        will be made. """
-        self.cleanup()
-
-    def cleanup(self):
-        """ Interrupts all pending downloads.  No further callbacks
-        will be made. """
-
-        self.packageLock.acquire()
-        try:
-            if self.descFileTask:
-                taskMgr.remove(self.descFileTask)
-                self.descFileTask = None
-            if self.downloadTask:
-                taskMgr.remove(self.downloadTask)
-                self.downloadTask = None
-        finally:
-            self.packageLock.release()
-
-        if self.progressTask:
-            taskMgr.remove(self.progressTask)
-            self.progressTask = None
-
-        self.ignoreAll()
-
-    def addPackage(self, packageName, version = None, hostUrl = None):
-        """ Adds the named package to the list of packages to be
-        downloaded.  Call donePackages() to finish the list. """
-
-        if self.state != self.S_initial:
-            raise ValueError('addPackage called after donePackages')
-
-        host = self.appRunner.getHostWithAlt(hostUrl)
-        pp = self.PendingPackage(packageName, version, host)
-
-        self.packageLock.acquire()
-        try:
-            self.__internalAddPackage(pp)
-        finally:
-            self.packageLock.release()
-
-    def __internalAddPackage(self, pp):
-        """ Adds the indicated "pending package" to the appropriate
-        list(s) for downloading and installing.  Assumes packageLock
-        is already held."""
-
-        if pp in self.packages:
-            # Already added.
-            return
-
-        self.packages.append(pp)
-
-        # We always add the package to needsDescFile, even if we
-        # already have its desc file; this guarantees that packages
-        # are downloaded in the order they are added.
-        self.needsDescFile.append(pp)
-        if not self.descFileTask:
-            self.descFileTask = taskMgr.add(
-                self.__getDescFileTask, 'getDescFile',
-                taskChain = self.taskChain)
-
-    def donePackages(self):
-        """ After calling addPackage() for each package to be
-        installed, call donePackages() to mark the end of the list.
-        This is necessary to determine what the complete set of
-        packages is (and therefore how large the total download size
-        is).  None of the low-level callbacks will be made before this
-        call. """
-
-        if self.state != self.S_initial:
-            # We've already been here.
-            return
-
-        # Throw the messages for packages that were already done
-        # before we started.
-        for pp in self.earlyDone:
-            self.__donePackage(pp, True)
-        self.earlyDone = []
-
-        self.packageLock.acquire()
-        try:
-            if self.state != self.S_initial:
-                return
-            self.state = self.S_ready
-            if not self.needsDescFile:
-                # All package desc files are already available; so begin.
-                self.__prepareToStart()
-        finally:
-            self.packageLock.release()
-
-        if not self.packages:
-            # Trivial no-op.
-            self.__callDownloadFinished(True)
-
-    def downloadStarted(self):
-        """ This callback is made at some point after donePackages()
-        is called; at the time of this callback, the total download
-        size is known, and we can sensibly report progress through the
-        whole. """
-
-        self.notify.info("downloadStarted")
-
-    def packageStarted(self, package):
-        """ This callback is made for each package between
-        downloadStarted() and downloadFinished() to indicate the start
-        of a new package. """
-
-        self.notify.debug("packageStarted: %s" % (package.packageName))
-
-    def packageProgress(self, package, progress):
-        """ This callback is made repeatedly between packageStarted()
-        and packageFinished() to update the current progress on the
-        indicated package only.  The progress value ranges from 0
-        (beginning) to 1 (complete). """
-
-        self.notify.debug("packageProgress: %s %s" % (package.packageName, progress))
-
-    def downloadProgress(self, overallProgress):
-        """ This callback is made repeatedly between downloadStarted()
-        and downloadFinished() to update the current progress through
-        all packages.  The progress value ranges from 0 (beginning) to
-        1 (complete). """
-
-        self.notify.debug("downloadProgress: %s" % (overallProgress))
-
-    def packageFinished(self, package, success):
-        """ This callback is made for each package between
-        downloadStarted() and downloadFinished() to indicate that a
-        package has finished downloading.  If success is true, there
-        were no problems and the package is now installed.
-
-        If this package did not require downloading (because it was
-        already downloaded), this callback will be made immediately,
-        *without* a corresponding call to packageStarted(), and may
-        even be made before downloadStarted(). """
-
-        self.notify.info("packageFinished: %s %s" % (package.packageName, success))
-
-    def downloadFinished(self, success):
-        """ This callback is made when all of the packages have been
-        downloaded and installed (or there has been some failure).  If
-        all packages where successfully installed, success is True.
-
-        If there were no packages that required downloading, this
-        callback will be made immediately, *without* a corresponding
-        call to downloadStarted(). """
-
-        self.notify.info("downloadFinished: %s" % (success))
-
-    def __prepareToStart(self):
-        """ This is called internally when transitioning from S_ready
-        to S_started.  It sets up whatever initial values are
-        needed.  Assumes self.packageLock is held.  Returns False if
-        there were no packages to download, and the state was
-        therefore transitioned immediately to S_done. """
-
-        if not self.needsDownload:
-            self.state = self.S_done
-            return False
-
-        self.state = self.S_started
-
-        assert not self.downloadTask
-        self.downloadTask = taskMgr.add(
-            self.__downloadPackageTask, 'downloadPackage',
-            taskChain = self.taskChain)
-
-        assert not self.progressTask
-        self.progressTask = taskMgr.add(
-            self.__progressTask, 'packageProgress')
-
-        return True
-
-    def __allHaveDesc(self):
-        """ This method is called internally when all of the pending
-        packages have their desc info. """
-        working = True
-
-        self.packageLock.acquire()
-        try:
-            if self.state == self.S_ready:
-                # We've already called donePackages(), so move on now.
-                working = self.__prepareToStart()
-        finally:
-            self.packageLock.release()
-
-        if not working:
-            self.__callDownloadFinished(True)
-
-    def __packageStarted(self, pp):
-        """ This method is called when a single package is beginning
-        to download. """
-
-        self.__callDownloadStarted()
-        self.__callPackageStarted(pp)
-
-    def __packageDone(self, pp):
-        """ This method is called when a single package has been
-        downloaded and installed, or has failed. """
-
-        self.__callPackageFinished(pp, pp.success)
-        pp.notified = True
-
-        # See if there are more packages to go.
-        success = True
-        allDone = True
-        self.packageLock.acquire()
-        try:
-            for pp in self.packages:
-                if pp.notified:
-                    success = success and pp.success
-                else:
-                    allDone = False
-        finally:
-            self.packageLock.release()
-
-        if allDone:
-            self.__callDownloadFinished(success)
-
-    def __callPackageStarted(self, pp):
-        """ Calls the packageStarted() callback for a particular
-        package if it has not already been called, being careful to
-        avoid race conditions. """
-
-        self.callbackLock.acquire()
-        try:
-            if not pp.calledPackageStarted:
-                self.packageStarted(pp.package)
-                self.packageProgress(pp.package, 0)
-                pp.calledPackageStarted = True
-        finally:
-            self.callbackLock.release()
-
-    def __callPackageFinished(self, pp, success):
-        """ Calls the packageFinished() callback for a paricular
-        package if it has not already been called, being careful to
-        avoid race conditions. """
-
-        self.callbackLock.acquire()
-        try:
-            if not pp.calledPackageFinished:
-                if success:
-                    self.packageProgress(pp.package, 1)
-                self.packageFinished(pp.package, success)
-                pp.calledPackageFinished = True
-        finally:
-            self.callbackLock.release()
-
-    def __callDownloadStarted(self):
-        """ Calls the downloadStarted() callback if it has not already
-        been called, being careful to avoid race conditions. """
-
-        self.callbackLock.acquire()
-        try:
-            if not self.calledDownloadStarted:
-                self.downloadStarted()
-                self.downloadProgress(0)
-                self.calledDownloadStarted = True
-        finally:
-            self.callbackLock.release()
-
-    def __callDownloadFinished(self, success):
-        """ Calls the downloadFinished() callback if it has not
-        already been called, being careful to avoid race
-        conditions. """
-
-        self.callbackLock.acquire()
-        try:
-            if not self.calledDownloadFinished:
-                if success:
-                    self.downloadProgress(1)
-                self.downloadFinished(success)
-                self.calledDownloadFinished = True
-        finally:
-            self.callbackLock.release()
-
-    def __getDescFileTask(self, task):
-
-        """ This task runs on the aysynchronous task chain; each pass,
-        it extracts one package from self.needsDescFile and downloads
-        its desc file.  On success, it adds the package to
-        self.needsDownload. """
-
-        self.packageLock.acquire()
-        try:
-            # If we've finished all of the packages that need desc
-            # files, stop the task.
-            if not self.needsDescFile:
-                self.descFileTask = None
-
-                eventName = 'PackageInstaller-%s-allHaveDesc' % self.uniqueId
-                messenger.send(eventName, taskChain = 'default')
-
-                return task.done
-            pp = self.needsDescFile[0]
-            del self.needsDescFile[0]
-        finally:
-            self.packageLock.release()
-
-        # Now serve this one package.
-        if not pp.checkDescFile():
-            if not pp.getDescFile(self.appRunner.http):
-                self.__donePackage(pp, False)
-                return task.cont
-
-        # This package is now ready to be downloaded.  We always add
-        # it to needsDownload, even if it's already downloaded, to
-        # guarantee ordering of packages.
-
-        self.packageLock.acquire()
-        try:
-            # Also add any packages required by this one.
-            for packageName, version, host in pp.package.requires:
-                pp2 = self.PendingPackage(packageName, version, host)
-                self.__internalAddPackage(pp2)
-            self.needsDownload.append(pp)
-        finally:
-            self.packageLock.release()
-
-        return task.cont
-
-    def __downloadPackageTask(self, task):
-
-        """ This task runs on the aysynchronous task chain; each pass,
-        it extracts one package from self.needsDownload and downloads
-        it. """
-
-        while True:
-            self.packageLock.acquire()
-            try:
-                # If we're done downloading, stop the task.
-                if self.state == self.S_done or not self.needsDownload:
-                    self.downloadTask = None
-                    self.packageLock.release()
-                    yield task.done; return
-
-                assert self.state == self.S_started
-                pp = self.needsDownload[0]
-                del self.needsDownload[0]
-            except:
-                self.packageLock.release()
-                raise
-            self.packageLock.release()
-
-            # Now serve this one package.
-            eventName = 'PackageInstaller-%s-packageStarted' % self.uniqueId
-            messenger.send(eventName, [pp], taskChain = 'default')
-
-            if not pp.package.hasPackage:
-                for token in pp.package.downloadPackageGenerator(self.appRunner.http):
-                    if token == pp.package.stepContinue:
-                        yield task.cont
-                    else:
-                        break
-
-                if token != pp.package.stepComplete:
-                    pc = PStatCollector(':App:PackageInstaller:donePackage:%s' % (pp.package.packageName))
-                    pc.start()
-                    self.__donePackage(pp, False)
-                    pc.stop()
-                    yield task.cont
-                    continue
-
-            # Successfully downloaded and installed.
-            pc = PStatCollector(':App:PackageInstaller:donePackage:%s' % (pp.package.packageName))
-            pc.start()
-            self.__donePackage(pp, True)
-            pc.stop()
-
-            # Continue the loop without yielding, so we pick up the
-            # next package within this same frame.
-
-    def __donePackage(self, pp, success):
-        """ Marks the indicated package as done, either successfully
-        or otherwise. """
-        assert not pp.done
-
-        if success:
-            pc = PStatCollector(':App:PackageInstaller:install:%s' % (pp.package.packageName))
-            pc.start()
-            pp.package.installPackage(self.appRunner)
-            pc.stop()
-
-        self.packageLock.acquire()
-        try:
-            pp.done = True
-            pp.success = success
-            if success:
-                self.done.append(pp)
-            else:
-                self.failed.append(pp)
-        finally:
-            self.packageLock.release()
-
-        eventName = 'PackageInstaller-%s-packageDone' % self.uniqueId
-        messenger.send(eventName, [pp], taskChain = 'default')
-
-    def __progressTask(self, task):
-        self.callbackLock.acquire()
-        try:
-            if not self.calledDownloadStarted:
-                # We haven't yet officially started the download.
-                return task.cont
-
-            if self.calledDownloadFinished:
-                # We've officially ended the download.
-                self.progressTask = None
-                return task.done
-
-            downloadEffort = 0
-            currentDownloadSize = 0
-            for pp in self.packages:
-                downloadEffort += pp.downloadEffort + pp.prevDownloadedEffort
-                packageProgress = pp.getProgress()
-                currentDownloadSize += pp.downloadEffort * packageProgress + pp.prevDownloadedEffort
-                if pp.calledPackageStarted and not pp.calledPackageFinished:
-                    self.packageProgress(pp.package, packageProgress)
-
-            if not downloadEffort:
-                progress = 1
-            else:
-                progress = float(currentDownloadSize) / float(downloadEffort)
-            self.downloadProgress(progress)
-
-        finally:
-            self.callbackLock.release()
-
-        return task.cont
-

+ 0 - 306
direct/src/p3d/PackageMerger.py

@@ -1,306 +0,0 @@
-__all__ = ["PackageMerger", "PackageMergerError"]
-
-from direct.p3d.FileSpec import FileSpec
-from direct.p3d.SeqValue import SeqValue
-from direct.directnotify.DirectNotifyGlobal import *
-from panda3d.core import *
-import shutil
-import os
-
-class PackageMergerError(Exception):
-    pass
-
-class PackageMerger:
-    """ This class will combine two or more separately-built stage
-    directories, the output of Packager.py or the ppackage tool, into
-    a single output directory.  It assumes that the clocks on all
-    hosts are in sync, so that the file across all builds with the
-    most recent timestamp (indicated in the contents.xml file) is
-    always the most current version of the file. """
-
-    notify = directNotify.newCategory("PackageMerger")
-
-    class PackageEntry:
-        """ This corresponds to a <package> entry in the contents.xml
-        file. """
-
-        def __init__(self, xpackage, sourceDir):
-            self.sourceDir = sourceDir
-            self.loadXml(xpackage)
-
-        def getKey(self):
-            """ Returns a tuple used for sorting the PackageEntry
-            objects uniquely per package. """
-            return (self.packageName, self.platform, self.version)
-
-        def isNewer(self, other):
-            return self.descFile.timestamp > other.descFile.timestamp
-
-        def loadXml(self, xpackage):
-            self.packageName = xpackage.Attribute('name')
-            self.platform = xpackage.Attribute('platform')
-            self.version = xpackage.Attribute('version')
-            solo = xpackage.Attribute('solo')
-            self.solo = int(solo or '0')
-            perPlatform = xpackage.Attribute('per_platform')
-            self.perPlatform = int(perPlatform or '0')
-
-            self.descFile = FileSpec()
-            self.descFile.loadXml(xpackage)
-
-            self.validatePackageContents()
-
-            self.descFile.quickVerify(packageDir = self.sourceDir, notify = PackageMerger.notify, correctSelf = True)
-
-            self.packageSeq = SeqValue()
-            self.packageSeq.loadXml(xpackage, 'seq')
-            self.packageSetVer = SeqValue()
-            self.packageSetVer.loadXml(xpackage, 'set_ver')
-
-            self.importDescFile = None
-            ximport = xpackage.FirstChildElement('import')
-            if ximport:
-                self.importDescFile = FileSpec()
-                self.importDescFile.loadXml(ximport)
-                self.importDescFile.quickVerify(packageDir = self.sourceDir, notify = PackageMerger.notify, correctSelf = True)
-
-        def makeXml(self):
-            """ Returns a new TiXmlElement. """
-            xpackage = TiXmlElement('package')
-            xpackage.SetAttribute('name', self.packageName)
-            if self.platform:
-                xpackage.SetAttribute('platform', self.platform)
-            if self.version:
-                xpackage.SetAttribute('version', self.version)
-            if self.solo:
-                xpackage.SetAttribute('solo', '1')
-            if self.perPlatform:
-                xpackage.SetAttribute('per_platform', '1')
-
-            self.descFile.storeXml(xpackage)
-            self.packageSeq.storeXml(xpackage, 'seq')
-            self.packageSetVer.storeXml(xpackage, 'set_ver')
-
-            if self.importDescFile:
-                ximport = TiXmlElement('import')
-                self.importDescFile.storeXml(ximport)
-                xpackage.InsertEndChild(ximport)
-
-            return xpackage
-
-        def validatePackageContents(self):
-            """ Validates the contents of the package directory itself
-            against the expected hashes and timestamps.  Updates
-            hashes and timestamps where needed. """
-
-            if self.solo:
-                return
-
-            needsChange = False
-            packageDescFullpath = Filename(self.sourceDir, self.descFile.filename)
-            packageDir = Filename(packageDescFullpath.getDirname())
-            doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
-            if not doc.LoadFile():
-                message = "Could not read XML file: %s" % (self.descFile.filename)
-                raise OSError(message)
-
-            xpackage = doc.FirstChildElement('package')
-            if not xpackage:
-                message = "No package definition: %s" % (self.descFile.filename)
-                raise OSError(message)
-
-            xcompressed = xpackage.FirstChildElement('compressed_archive')
-            if xcompressed:
-                spec = FileSpec()
-                spec.loadXml(xcompressed)
-                if not spec.quickVerify(packageDir = packageDir, notify = PackageMerger.notify, correctSelf = True):
-                    spec.storeXml(xcompressed)
-                    needsChange = True
-
-            xpatch = xpackage.FirstChildElement('patch')
-            while xpatch:
-                spec = FileSpec()
-                spec.loadXml(xpatch)
-                if not spec.quickVerify(packageDir = packageDir, notify = PackageMerger.notify, correctSelf = True):
-                    spec.storeXml(xpatch)
-                    needsChange = True
-
-                xpatch = xpatch.NextSiblingElement('patch')
-
-            if needsChange:
-                PackageMerger.notify.info("Rewriting %s" % (self.descFile.filename))
-                doc.SaveFile()
-                self.descFile.quickVerify(packageDir = self.sourceDir, notify = PackageMerger.notify, correctSelf = True)
-
-    # PackageMerger constructor
-    def __init__(self, installDir):
-        self.installDir = installDir
-        self.xhost = None
-        self.contents = {}
-        self.maxAge = None
-        self.contentsSeq = SeqValue()
-
-        # We allow the first one to fail quietly.
-        self.__readContentsFile(self.installDir, None)
-
-    def __readContentsFile(self, sourceDir, packageNames):
-        """ Reads the contents.xml file from the indicated sourceDir,
-        and updates the internal set of packages appropriately. """
-
-        assert sourceDir != None, "No source directory was specified!"
-        contentsFilename = Filename(sourceDir, 'contents.xml')
-        doc = TiXmlDocument(contentsFilename.toOsSpecific())
-        if not doc.LoadFile():
-            # Couldn't read file.
-            return False
-
-        xcontents = doc.FirstChildElement('contents')
-        if xcontents:
-            maxAge = xcontents.Attribute('max_age')
-            if maxAge:
-                maxAge = int(maxAge)
-                if self.maxAge is None:
-                    self.maxAge = maxAge
-                else:
-                    self.maxAge = min(self.maxAge, maxAge)
-
-            contentsSeq = SeqValue()
-            if contentsSeq.loadXml(xcontents):
-                self.contentsSeq = max(self.contentsSeq, contentsSeq)
-
-            xhost = xcontents.FirstChildElement('host')
-            if xhost:
-                self.xhost = xhost.Clone()
-
-            xpackage = xcontents.FirstChildElement('package')
-            while xpackage:
-                pe = self.PackageEntry(xpackage, sourceDir)
-
-                # Filter out any packages not listed in
-                # packageNames (unless packageNames is None,
-                # in which case don't filter anything).
-                if packageNames is None or pe.packageName in packageNames:
-                    other = self.contents.get(pe.getKey(), None)
-                    if not other or pe.isNewer(other):
-                        # Store this package in the resulting output.
-                        self.contents[pe.getKey()] = pe
-
-                xpackage = xpackage.NextSiblingElement('package')
-
-        self.contentsDoc = doc
-
-        return True
-
-    def __writeContentsFile(self):
-        """ Writes the contents.xml file at the end of processing. """
-
-        filename = Filename(self.installDir, 'contents.xml')
-        doc = TiXmlDocument(filename.toOsSpecific())
-        decl = TiXmlDeclaration("1.0", "utf-8", "")
-        doc.InsertEndChild(decl)
-
-        xcontents = TiXmlElement('contents')
-        if self.xhost:
-            xcontents.InsertEndChild(self.xhost)
-
-        if self.maxAge is not None:
-            xcontents.SetAttribute('max_age', str(self.maxAge))
-        self.contentsSeq.storeXml(xcontents)
-
-        contents = list(self.contents.items())
-        contents.sort()
-        for key, pe in contents:
-            xpackage = pe.makeXml()
-            xcontents.InsertEndChild(xpackage)
-
-        doc.InsertEndChild(xcontents)
-        doc.SaveFile()
-
-    def __copySubdirectory(self, pe):
-        """ Copies the subdirectory referenced in the indicated
-        PackageEntry object into the installDir, replacing the
-        contents of any similarly-named subdirectory already
-        there. """
-
-        dirname = Filename(pe.descFile.filename).getDirname()
-        self.notify.info("copying %s" % (dirname))
-        sourceDirname = Filename(pe.sourceDir, dirname)
-        targetDirname = Filename(self.installDir, dirname)
-
-        self.__rCopyTree(sourceDirname, targetDirname)
-
-    def __rCopyTree(self, sourceFilename, targetFilename):
-        """ Recursively copies the contents of sourceDirname onto
-        targetDirname.  This behaves like shutil.copytree, but it does
-        not remove pre-existing subdirectories. """
-
-        if targetFilename.exists():
-            if not targetFilename.isDirectory():
-                # Delete any regular files in the way.
-                targetFilename.unlink()
-
-            elif not sourceFilename.isDirectory():
-                # If the source file is a regular file, but the target
-                # file is a directory, completely remove the target
-                # file.
-                shutil.rmtree(targetFilename.toOsSpecific())
-
-            else:
-                # Both the source file and target file are
-                # directories.
-
-                # We have to clean out the target directory first.
-                # Instead of using shutil.rmtree(), remove the files in
-                # this directory one at a time, so we don't inadvertently
-                # clean out subdirectories too.
-                files = os.listdir(targetFilename.toOsSpecific())
-                for file in files:
-                    f = Filename(targetFilename, file)
-                    if f.isRegularFile():
-                        f.unlink()
-
-        if sourceFilename.isDirectory():
-            # Recursively copying a directory.
-            Filename(targetFilename, '').makeDir()
-            files = os.listdir(sourceFilename.toOsSpecific())
-            for file in files:
-                self.__rCopyTree(Filename(sourceFilename, file),
-                                 Filename(targetFilename, file))
-        else:
-            # Copying a regular file.
-            sourceFilename.copyTo(targetFilename)
-
-            # Also try to copy the timestamp, but don't fuss too much
-            # if it doesn't work.
-            try:
-                st = os.stat(sourceFilename.toOsSpecific())
-                os.utime(targetFilename.toOsSpecific(), (st.st_atime, st.st_mtime))
-            except OSError:
-                pass
-
-    def merge(self, sourceDir, packageNames = None):
-        """ Adds the contents of the indicated source directory into
-        the current pool.  If packageNames is not None, it is a list
-        of package names that we wish to include from the source;
-        packages not named in this list will be unchanged. """
-
-        if not self.__readContentsFile(sourceDir, packageNames):
-            message = "Couldn't read %s" % (sourceDir)
-            raise PackageMergerError(message)
-
-    def close(self):
-        """ Finalizes the results of all of the previous calls to
-        merge(), writes the new contents.xml file, and copies in all
-        of the new contents. """
-
-        dirname = Filename(self.installDir, '')
-        dirname.makeDir()
-
-        for pe in self.contents.values():
-            if pe.sourceDir != self.installDir:
-                # Here's a new subdirectory we have to copy in.
-                self.__copySubdirectory(pe)
-
-        self.contentsSeq += 1
-        self.__writeContentsFile()
-

+ 0 - 3962
direct/src/p3d/Packager.py

@@ -1,3962 +0,0 @@
-""" This module is used to build a "Package", a collection of files
-within a Panda3D Multifile, which can be easily be downloaded and/or
-patched onto a client machine, for the purpose of running a large
-application. """
-
-__all__ = ["Packager", "PackagerError", "OutsideOfPackageError", "ArgumentError"]
-
-# Important to import panda3d first, to avoid naming conflicts with
-# Python's "string" and "Loader" names that are imported later.
-from panda3d.core import *
-import sys
-import os
-import glob
-import struct
-import subprocess
-import copy
-from direct.p3d.FileSpec import FileSpec
-from direct.p3d.SeqValue import SeqValue
-from direct.p3d.HostInfo import HostInfo
-from direct.showbase import Loader
-from direct.showbase import AppRunnerGlobal
-from direct.dist import FreezeTool
-from direct.directnotify.DirectNotifyGlobal import *
-
-vfs = VirtualFileSystem.getGlobalPtr()
-
-class PackagerError(Exception):
-    pass
-
-class OutsideOfPackageError(PackagerError):
-    pass
-
-class ArgumentError(PackagerError):
-    pass
-
-class Packager:
-    notify = directNotify.newCategory("Packager")
-
-    class PackFile:
-        def __init__(self, package, filename,
-                     newName = None, deleteTemp = False,
-                     explicit = False, compress = None, extract = None,
-                     text = None, unprocessed = None,
-                     executable = None, dependencyDir = None,
-                     platformSpecific = None, required = False):
-            assert isinstance(filename, Filename)
-            self.filename = Filename(filename)
-            self.newName = newName
-            self.deleteTemp = deleteTemp
-            self.explicit = explicit
-            self.compress = compress
-            self.extract = extract
-            self.text = text
-            self.unprocessed = unprocessed
-            self.executable = executable
-            self.dependencyDir = dependencyDir
-            self.platformSpecific = platformSpecific
-            self.required = required
-
-            if not self.newName:
-                self.newName = str(self.filename)
-
-            ext = Filename(self.newName).getExtension()
-            if ext == 'pz' or ext == 'gz':
-                # Strip off a .pz extension; we can compress files
-                # within the Multifile without it.
-                filename = Filename(self.newName)
-                filename.setExtension('')
-                self.newName = str(filename)
-                ext = Filename(self.newName).getExtension()
-                if self.compress is None:
-                    self.compress = True
-
-            packager = package.packager
-            if self.compress is None:
-                self.compress = (ext not in packager.uncompressibleExtensions and ext not in packager.imageExtensions)
-
-            if self.executable is None:
-                self.executable = (ext in packager.executableExtensions)
-
-            if self.executable and self.dependencyDir is None:
-                # By default, install executable dependencies in the
-                # root directory, which is the one that's added to PATH.
-                self.dependencyDir = ''
-
-            if self.extract is None:
-                self.extract = self.executable or (ext in packager.extractExtensions)
-            if self.platformSpecific is None:
-                self.platformSpecific = self.executable or (ext in packager.platformSpecificExtensions)
-
-            if self.unprocessed is None:
-                self.unprocessed = self.executable or (ext in packager.unprocessedExtensions)
-
-            if self.executable:
-                # Look up the filename along the system PATH, if necessary.
-                if not packager.resolveLibrary(self.filename):
-                    # If it wasn't found, try looking it up under its
-                    # basename only.  Sometimes a Mac user will copy
-                    # the library file out of a framework and put that
-                    # along the PATH, instead of the framework itself.
-                    basename = Filename(self.filename.getBasename())
-                    if packager.resolveLibrary(basename):
-                        self.filename = basename
-
-            if ext in packager.textExtensions and not self.executable:
-                self.filename.setText()
-            else:
-                self.filename.setBinary()
-
-            # Convert the filename to an unambiguous filename for
-            # searching.
-            self.filename.makeTrueCase()
-            if self.filename.exists() or not self.filename.isLocal():
-                self.filename.makeCanonical()
-
-        def isExcluded(self, package):
-            """ Returns true if this file should be excluded or
-            skipped, false otherwise. """
-
-            if self.newName.lower() in package.skipFilenames:
-                return True
-
-            if not self.explicit:
-                # Make sure it's not one of our auto-excluded system
-                # files.  (But only make this check if this file was
-                # not explicitly added.)
-
-                basename = Filename(self.newName).getBasename()
-                if not package.packager.caseSensitive:
-                    basename = basename.lower()
-                if basename in package.packager.excludeSystemFiles:
-                    return True
-                for exclude in package.packager.excludeSystemGlobs:
-                    if exclude.matches(basename):
-                        return True
-
-                # Also check if it was explicitly excluded.  As above,
-                # omit this check for an explicitly-added file: if you
-                # both include and exclude a file, the file is
-                # included.
-                for exclude in package.excludedFilenames:
-                    if exclude.matches(self.filename):
-                        return True
-
-                # A platform-specific file is implicitly excluded from
-                # not-platform-specific packages.
-                if self.platformSpecific and package.platformSpecificConfig is False:
-                    return True
-
-            return False
-
-    class ExcludeFilename:
-        def __init__(self, packager, filename, caseSensitive):
-            self.packager = packager
-            self.localOnly = (not filename.getDirname())
-            if not self.localOnly:
-                filename = Filename(filename)
-                filename.makeCanonical()
-            self.glob = GlobPattern(str(filename))
-
-            if self.packager.platform.startswith('win'):
-                self.glob.setCaseSensitive(False)
-            elif self.packager.platform.startswith('osx'):
-                self.glob.setCaseSensitive(False)
-
-        def matches(self, filename):
-            if self.localOnly:
-                return self.glob.matches(filename.getBasename())
-            else:
-                return self.glob.matches(str(filename))
-
-    class PackageEntry:
-        """ This corresponds to a <package> entry in the contents.xml
-        file. """
-
-        def __init__(self):
-            # The "seq" value increments automatically with each publish.
-            self.packageSeq = SeqValue()
-
-            # The "set_ver" value is optionally specified in the pdef
-            # file and does not change unless the user says it does.
-            self.packageSetVer = SeqValue()
-
-        def getKey(self):
-            """ Returns a tuple used for sorting the PackageEntry
-            objects uniquely per package. """
-            return (self.packageName, self.platform or "", self.version or "")
-
-        def fromFile(self, packageName, platform, version, solo, perPlatform,
-                     installDir, descFilename, importDescFilename):
-            self.packageName = packageName
-            self.platform = platform
-            self.version = version
-            self.solo = solo
-            self.perPlatform = perPlatform
-
-            self.descFile = FileSpec()
-            self.descFile.fromFile(installDir, descFilename)
-
-            self.importDescFile = None
-            if importDescFilename:
-                self.importDescFile = FileSpec()
-                self.importDescFile.fromFile(installDir, importDescFilename)
-
-        def loadXml(self, xpackage):
-            self.packageName = xpackage.Attribute('name')
-            self.platform = xpackage.Attribute('platform')
-            self.version = xpackage.Attribute('version')
-            solo = xpackage.Attribute('solo')
-            self.solo = int(solo or '0')
-            perPlatform = xpackage.Attribute('per_platform')
-            self.perPlatform = int(perPlatform or '0')
-
-            self.packageSeq = SeqValue()
-            self.packageSeq.loadXml(xpackage, 'seq')
-
-            self.packageSetVer = SeqValue()
-            self.packageSetVer.loadXml(xpackage, 'set_ver')
-
-            self.descFile = FileSpec()
-            self.descFile.loadXml(xpackage)
-
-            self.importDescFile = None
-            ximport = xpackage.FirstChildElement('import')
-            if ximport:
-                self.importDescFile = FileSpec()
-                self.importDescFile.loadXml(ximport)
-
-
-        def makeXml(self):
-            """ Returns a new TiXmlElement. """
-            xpackage = TiXmlElement('package')
-            xpackage.SetAttribute('name', self.packageName)
-            if self.platform:
-                xpackage.SetAttribute('platform', self.platform)
-            if self.version:
-                xpackage.SetAttribute('version', self.version)
-            if self.solo:
-                xpackage.SetAttribute('solo', '1')
-            if self.perPlatform:
-                xpackage.SetAttribute('per_platform', '1')
-
-            self.packageSeq.storeXml(xpackage, 'seq')
-            self.packageSetVer.storeXml(xpackage, 'set_ver')
-            self.descFile.storeXml(xpackage)
-
-            if self.importDescFile:
-                ximport = TiXmlElement('import')
-                self.importDescFile.storeXml(ximport)
-                xpackage.InsertEndChild(ximport)
-
-            return xpackage
-
-    class HostEntry:
-        def __init__(self, url = None, downloadUrl = None,
-                     descriptiveName = None, hostDir = None,
-                     mirrors = None):
-            self.url = url
-            self.downloadUrl = downloadUrl
-            self.descriptiveName = descriptiveName
-            self.hostDir = hostDir
-            self.mirrors = mirrors or []
-            self.altHosts = {}
-
-        def loadXml(self, xhost, packager):
-            self.url = xhost.Attribute('url')
-            self.downloadUrl = xhost.Attribute('download_url')
-            self.descriptiveName = xhost.Attribute('descriptive_name')
-            self.hostDir = xhost.Attribute('host_dir')
-            self.mirrors = []
-            xmirror = xhost.FirstChildElement('mirror')
-            while xmirror:
-                url = xmirror.Attribute('url')
-                self.mirrors.append(url)
-                xmirror = xmirror.NextSiblingElement('mirror')
-
-            xalthost = xhost.FirstChildElement('alt_host')
-            while xalthost:
-                url = xalthost.Attribute('url')
-                he = packager.addHost(url)
-                he.loadXml(xalthost, packager)
-                xalthost = xalthost.NextSiblingElement('alt_host')
-
-        def makeXml(self, packager = None):
-            """ Returns a new TiXmlElement. """
-            xhost = TiXmlElement('host')
-            xhost.SetAttribute('url', self.url)
-            if self.downloadUrl and self.downloadUrl != self.url:
-                xhost.SetAttribute('download_url', self.downloadUrl)
-            if self.descriptiveName:
-                xhost.SetAttribute('descriptive_name', self.descriptiveName)
-            if self.hostDir:
-                xhost.SetAttribute('host_dir', self.hostDir)
-
-            for mirror in self.mirrors:
-                xmirror = TiXmlElement('mirror')
-                xmirror.SetAttribute('url', mirror)
-                xhost.InsertEndChild(xmirror)
-
-            if packager:
-                altHosts = sorted(self.altHosts.items())
-                for keyword, alt in altHosts:
-                    he = packager.hosts.get(alt, None)
-                    if he:
-                        xalthost = he.makeXml()
-                        xalthost.SetValue('alt_host')
-                        xalthost.SetAttribute('keyword', keyword)
-                        xhost.InsertEndChild(xalthost)
-
-            return xhost
-
-
-    class Package:
-        """ This is the full information on a particular package we
-        are constructing.  Don't confuse it with PackageEntry, above,
-        which contains only the information found in the toplevel
-        contents.xml file."""
-
-        def __init__(self, packageName, packager):
-            self.packageName = packageName
-            self.packager = packager
-            self.notify = packager.notify
-
-            # The platform is initially None until we know the file is
-            # platform-specific.
-            self.platform = None
-
-            # This is always true on modern packages.
-            self.perPlatform = True
-
-            # The arch string, though, is pre-loaded from the system
-            # arch string, so we can sensibly call otool.
-            self.arch = self.packager.arch
-
-            self.version = None
-            self.host = None
-            self.p3dApplication = False
-            self.solo = False
-            self.compressionLevel = 0
-            self.importedMapsDir = 'imported_maps'
-            self.mainModule = None
-            self.signParams = []
-            self.requires = []
-
-            # This may be set explicitly in the pdef file to a
-            # particular sequence value.
-            self.packageSetVer = SeqValue()
-
-            # This is the set of config variables assigned to the
-            # package.
-            self.configs = {}
-
-            # This is the set of files and modules, already included
-            # by required packages, that we can skip.
-            self.skipFilenames = {}
-            self.skipModules = {}
-
-            # This is a list of ExcludeFilename objects, representing
-            # the files that have been explicitly excluded.
-            self.excludedFilenames = []
-
-            # This is the list of files we will be adding, and a pair
-            # of cross references.
-            self.files = []
-            self.sourceFilenames = {}
-            self.targetFilenames = {}
-
-            # This is the set of files and modules that are
-            # required and may not be excluded from the package.
-            self.requiredFilenames = []
-            self.requiredModules = []
-
-            # A list of required packages that were missing.
-            self.missingPackages = []
-
-            # This records the current list of modules we have added so
-            # far.
-            self.freezer = FreezeTool.Freezer(platform = self.packager.platform)
-            self.freezer.storePythonSource = self.packager.storePythonSource
-
-            # Map of extensions to files to number (ignored by dir)
-            self.ignoredDirFiles = {}
-
-        def close(self):
-            """ Writes out the contents of the current package.  Returns True
-            if the package was constructed successfully, False if one or more
-            required files or modules are missing. """
-
-            if not self.p3dApplication and not self.packager.allowPackages:
-                message = 'Cannot generate packages without an installDir; use -i'
-                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:
-                self.host = self.packager.host
-
-            # Check the version config variable.
-            version = self.configs.get('version', None)
-            if version is not None:
-                self.version = version
-                del self.configs['version']
-
-            # Check the platform_specific config variable.  This has
-            # only three settings: None (unset), True, or False.
-            self.platformSpecificConfig = self.configs.get('platform_specific', None)
-            if self.platformSpecificConfig is not None:
-                # First, convert it to an int, in case it's "0" or "1".
-                try:
-                    self.platformSpecificConfig = int(self.platformSpecificConfig)
-                except ValueError:
-                    pass
-                # Then, make it a bool.
-                self.platformSpecificConfig = bool(self.platformSpecificConfig)
-                del self.configs['platform_specific']
-
-            # A special case when building the "panda3d" package.  We
-            # enforce that the version number matches what we've been
-            # compiled with.
-            if self.packageName == 'panda3d':
-                if self.version is None:
-                    self.version = PandaSystem.getPackageVersionString()
-
-                if self.version != PandaSystem.getPackageVersionString():
-                    message = 'mismatched Panda3D version: requested %s, but Panda3D is built as %s' % (self.version, PandaSystem.getPackageVersionString())
-                    raise PackagerError(message)
-
-                if self.host != PandaSystem.getPackageHostUrl():
-                    message = 'mismatched Panda3D host: requested %s, but Panda3D is built as %s' % (self.host, PandaSystem.getPackageHostUrl())
-                    raise PackagerError(message)
-
-            if self.p3dApplication:
-                # Default compression level for an app.
-                self.compressionLevel = 6
-
-                # Every p3dapp requires panda3d.
-                if 'panda3d' not in [p.packageName for p in self.requires]:
-                    assert not self.packager.currentPackage
-                    self.packager.currentPackage = self
-                    self.packager.do_require('panda3d')
-                    self.packager.currentPackage = None
-
-                # If this flag is set, enable allow_python_dev.
-                if self.packager.allowPythonDev:
-                    self.configs['allow_python_dev'] = True
-
-            if not self.p3dApplication and not self.version:
-                # If we don't have an implicit version, inherit the
-                # version from the 'panda3d' package on our require
-                # list.
-                for p2 in self.requires:
-                    if p2.packageName == 'panda3d' and p2.version:
-                        self.version = p2.version
-                        break
-
-            if self.solo:
-                result = self.installSolo()
-            else:
-                result = self.installMultifile()
-
-            if self.p3dApplication:
-                allowPythonDev = self.configs.get('allow_python_dev', 0)
-                if int(allowPythonDev):
-                    print("\n*** Generating %s.p3d with allow_python_dev enabled ***\n" % (self.packageName))
-
-            return result
-
-
-        def considerPlatform(self):
-            # Check to see if any of the files are platform-specific,
-            # making the overall package platform-specific.
-
-            platformSpecific = self.platformSpecificConfig
-            for file in self.files:
-                if file.isExcluded(self):
-                    # Skip this file.
-                    continue
-                if file.platformSpecific:
-                    platformSpecific = True
-
-            if platformSpecific and self.platformSpecificConfig is not False:
-                if not self.platform:
-                    self.platform = self.packager.platform
-
-            if self.platform and self.platform.startswith('osx_'):
-                # Get the OSX "arch" specification.
-                self.arch = self.platform[4:]
-
-
-        def installMultifile(self):
-            """ Installs the package, either as a p3d application, or
-            as a true package.  Either is implemented with a
-            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()
-
-            # Write the multifile to a temporary filename until we
-            # know enough to determine the output filename.
-            multifileFilename = Filename.temporary('', self.packageName + '.', '.mf')
-            self.multifile.openReadWrite(multifileFilename)
-
-            if self.p3dApplication:
-                # p3d files should be tagged to make them executable.
-                self.multifile.setHeaderPrefix('#! /usr/bin/env panda3d\n')
-            else:
-                # Package multifiles might be patched, and therefore
-                # don't want to record an internal timestamp, which
-                # would make patching less efficient.
-                self.multifile.setRecordTimestamp(False)
-
-            # Make sure that all required files are present.
-            missing = []
-            for file in self.requiredFilenames:
-                if file not in self.files or file.isExcluded(self):
-                    missing.append(file.filename.getBasename())
-            if len(missing) > 0:
-                self.notify.warning("Cannot build package %s, missing required files: %r" % (self.packageName, missing))
-                self.cleanup()
-                return False
-
-            self.extracts = []
-            self.components = []
-
-            # Add the explicit py files that were requested by the
-            # pdef file.  These get turned into Python modules.
-            for file in self.files:
-                if file.isExcluded(self):
-                    # Skip this file.
-                    continue
-                if file.unprocessed:
-                    # Unprocessed files get dealt with below.
-                    continue
-
-                ext = Filename(file.newName).getExtension()
-                if ext == 'dc':
-                    # Add the modules named implicitly in the dc file.
-                    self.addDcImports(file)
-
-                elif ext == 'py':
-                    self.addPyFile(file)
-
-            # Add the main module, if any.
-            if not self.mainModule and self.p3dApplication:
-                message = 'No main_module specified for application %s' % (self.packageName)
-                raise PackagerError(message)
-            if self.mainModule:
-                moduleName, newName = self.mainModule
-                if newName not in self.freezer.modules:
-                    self.freezer.addModule(moduleName, newName = newName)
-
-            # Now all module files have been added.  Exclude modules
-            # already imported in a required package, and not
-            # explicitly included by this package.
-            for moduleName, mdef in self.skipModules.items():
-                if moduleName not in self.freezer.modules:
-                    self.freezer.excludeModule(
-                        moduleName, allowChildren = mdef.allowChildren,
-                        forbid = mdef.forbid, fromSource = 'skip')
-
-            # Pick up any unfrozen Python files.
-            self.freezer.done()
-
-            # But first, make sure that all required modules are present.
-            missing = []
-            moduleDict = dict(self.freezer.getModuleDefs())
-            for module in self.requiredModules:
-                if module not in moduleDict:
-                    missing.append(module)
-            if len(missing) > 0:
-                self.notify.warning("Cannot build package %s, missing required modules: %r" % (self.packageName, missing))
-                self.cleanup()
-                return False
-
-            # OK, we can add it.
-            self.freezer.addToMultifile(self.multifile, self.compressionLevel)
-            self.addExtensionModules()
-
-            # Add known module names.
-            self.moduleNames = {}
-            modules = sorted(self.freezer.modules.items())
-            for newName, mdef in modules:
-                if mdef.guess:
-                    # Not really a module.
-                    continue
-
-                if mdef.fromSource == 'skip':
-                    # This record already appeared in a required
-                    # module; don't repeat it now.
-                    continue
-
-                if mdef.exclude and mdef.implicit:
-                    # Don't bother mentioning implicitly-excluded
-                    # (i.e. missing) modules.
-                    continue
-
-                #if newName == '__main__':
-                #    # Ignore this special case.
-                #    continue
-
-                self.moduleNames[newName] = mdef
-
-                xmodule = TiXmlElement('module')
-                xmodule.SetAttribute('name', newName)
-                if mdef.exclude:
-                    xmodule.SetAttribute('exclude', '1')
-                if mdef.forbid:
-                    xmodule.SetAttribute('forbid', '1')
-                if mdef.exclude and mdef.allowChildren:
-                    xmodule.SetAttribute('allowChildren', '1')
-                self.components.append(('m', newName.lower(), xmodule))
-
-            # Now look for implicit shared-library dependencies.
-            if self.packager.platform.startswith('win'):
-                self.__addImplicitDependenciesWindows()
-            elif self.packager.platform.startswith('osx'):
-                self.__addImplicitDependenciesOSX()
-            else:
-                self.__addImplicitDependenciesPosix()
-
-            # Now add all the real, non-Python files (except model
-            # files).  This will include the extension modules we just
-            # discovered above.
-            for file in self.files:
-                if file.isExcluded(self):
-                    # Skip this file.
-                    continue
-                ext = Filename(file.newName).getExtension()
-                if file.unprocessed:
-                    # Add an unprocessed file verbatim.
-                    self.addComponent(file)
-                elif ext == 'py':
-                    # Already handled, above.
-                    pass
-                elif file.isExcluded(self):
-                    # Skip this file.
-                    pass
-                elif ext == 'egg' or ext == 'bam':
-                    # Skip model files this pass.
-                    pass
-                elif ext == 'dc':
-                    # dc files get a special treatment.
-                    self.addDcFile(file)
-                elif ext == 'prc':
-                    # So do prc files.
-                    self.addPrcFile(file)
-                else:
-                    # Any other file.
-                    self.addComponent(file)
-
-            # Now add the model files.  It's important to add these
-            # after we have added all of the texture files, so we can
-            # determine which textures need to be implicitly pulled
-            # in.
-
-            # We walk through a copy of the files list, since we might
-            # be adding more files (textures) to this list as we
-            # discover them in model files referenced in this list.
-            for file in self.files[:]:
-                if file.isExcluded(self):
-                    # Skip this file.
-                    continue
-                ext = Filename(file.newName).getExtension()
-                if file.unprocessed:
-                    # Already handled, above.
-                    pass
-                elif ext == 'py':
-                    # Already handled, above.
-                    pass
-                elif file.isExcluded(self):
-                    # Skip this file.
-                    pass
-                elif ext == 'egg':
-                    self.addEggFile(file)
-                elif ext == 'bam':
-                    self.addBamFile(file)
-                else:
-                    # Handled above.
-                    pass
-
-            # Check to see if we should be platform-specific.
-            self.considerPlatform()
-
-            # Now that we've processed all of the component files,
-            # (and set our platform if necessary), we can generate the
-            # output filename and write the output files.
-
-            self.packageBasename = self.packageName
-            packageDir = self.packageName
-            if self.version:
-                self.packageBasename += '.' + self.version
-                packageDir += '/' + self.version
-            if self.platform:
-                self.packageBasename += '.' + self.platform
-                packageDir += '/' + self.platform
-
-            self.packageDesc = self.packageBasename + '.xml'
-            self.packageImportDesc = self.packageBasename + '.import.xml'
-            if self.p3dApplication:
-                self.packageBasename += self.packager.p3dSuffix
-                self.packageBasename += '.p3d'
-                packageDir = ''
-            else:
-                self.packageBasename += '.mf'
-                packageDir += '/'
-
-            self.packageDir = packageDir
-            self.packageFilename = packageDir + self.packageBasename
-            self.packageDesc = packageDir + self.packageDesc
-            self.packageImportDesc = packageDir + self.packageImportDesc
-
-            print("Generating %s" % (self.packageFilename))
-
-            if self.p3dApplication:
-                self.packageFullpath = Filename(self.packager.p3dInstallDir, self.packageFilename)
-                self.packageFullpath.makeDir()
-                self.makeP3dInfo()
-            else:
-                self.packageFullpath = Filename(self.packager.installDir, self.packageFilename)
-                self.packageFullpath.makeDir()
-
-            self.multifile.repack()
-
-            # Also sign the multifile before we close it.
-            for certificate, chain, pkey, password in self.signParams:
-                self.multifile.addSignature(certificate, chain or '', pkey or '', password or '')
-
-            self.multifile.close()
-
-            if not multifileFilename.renameTo(self.packageFullpath):
-                self.notify.error("Cannot move %s to %s" % (multifileFilename, self.packageFullpath))
-
-            if self.p3dApplication:
-                # No patches for an application; just move it into place.
-                # Make the application file executable.
-                os.chmod(self.packageFullpath.toOsSpecific(), 0o755)
-            else:
-                self.readDescFile()
-                self.packageSeq += 1
-                self.perPlatform = True  # always true on modern packages.
-                self.compressMultifile()
-                self.writeDescFile()
-                self.writeImportDescFile()
-
-                # Now that we've written out the desc file, we don't
-                # need to keep around the uncompressed archive
-                # anymore.
-                self.packageFullpath.unlink()
-
-                # Replace or add the entry in the contents.
-                pe = Packager.PackageEntry()
-                pe.fromFile(self.packageName, self.platform, self.version,
-                            False, self.perPlatform, self.packager.installDir,
-                            self.packageDesc, self.packageImportDesc)
-                pe.packageSeq = self.packageSeq
-                pe.packageSetVer = self.packageSetVer
-
-                self.packager.contents[pe.getKey()] = pe
-                self.packager.contentsChanged = True
-
-            self.cleanup()
-            return True
-
-        def installSolo(self):
-            """ Installs the package as a "solo", which means we
-            simply copy the one file into the install directory.  This
-            is primarily intended for the "coreapi" plugin, which is
-            just a single dll and a jpg file; but it can support other
-            kinds of similar "solo" packages as well. """
-
-            self.considerPlatform()
-            self.perPlatform = False  # Not true on "solo" packages.
-
-            packageDir = self.packageName
-            if self.platform:
-                packageDir += '/' + self.platform
-            if self.version:
-                packageDir += '/' + self.version
-
-            if not self.packager.allowPackages:
-                message = 'Cannot generate packages without an installDir; use -i'
-                raise PackagerError(message)
-
-            installPath = Filename(self.packager.installDir, packageDir)
-            # Remove any files already in the installPath.
-            origFiles = vfs.scanDirectory(installPath)
-            if origFiles:
-                for origFile in origFiles:
-                    origFile.getFilename().unlink()
-
-            files = []
-            for file in self.files:
-                if file.isExcluded(self):
-                    # Skip this file.
-                    continue
-                files.append(file)
-
-            if not files:
-                # No files, never mind.
-                return
-
-            if len(files) != 1:
-                raise PackagerError('Multiple files in "solo" package %s' % (self.packageName))
-
-            Filename(installPath, '').makeDir()
-
-            file = files[0]
-            targetPath = Filename(installPath, file.newName)
-            targetPath.setBinary()
-            file.filename.setBinary()
-            if not file.filename.copyTo(targetPath):
-                self.notify.warning("Could not copy %s to %s" % (
-                    file.filename, targetPath))
-
-            # Replace or add the entry in the contents.
-            pe = Packager.PackageEntry()
-            pe.fromFile(self.packageName, self.platform, self.version,
-                        True, self.perPlatform, self.packager.installDir,
-                        Filename(packageDir, file.newName), None)
-            peOrig = self.packager.contents.get(pe.getKey(), None)
-            if peOrig:
-                pe.packageSeq = peOrig.packageSeq + 1
-                pe.packageSetVer = peOrig.packageSetVer
-            if self.packageSetVer:
-                pe.packageSetVer = self.packageSetVer
-
-            self.packager.contents[pe.getKey()] = pe
-            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()
-            return True
-
-        def cleanup(self):
-            # Now that all the files have been packed, we can delete
-            # the temporary files.
-            for file in self.files:
-                if file.deleteTemp:
-                    file.filename.unlink()
-
-        def addFile(self, *args, **kw):
-            """ Adds the named file to the package.  Returns the file
-            object, or None if it was not added by this call. """
-
-            file = Packager.PackFile(self, *args, **kw)
-            if file.filename in self.sourceFilenames:
-                # Don't bother, it's already here.
-                return None
-
-            lowerName = file.newName.lower()
-            if lowerName in self.targetFilenames:
-                # Another file is already in the same place.
-                file2 = self.targetFilenames[lowerName]
-                self.packager.notify.warning(
-                    "%s is shadowing %s" % (file2.filename, file.filename))
-                return None
-
-            self.sourceFilenames[file.filename] = file
-            if file.required:
-                self.requiredFilenames.append(file)
-
-            if file.text is None and not file.filename.exists():
-                if not file.isExcluded(self):
-                    self.packager.notify.warning("No such file: %s" % (file.filename))
-                return None
-
-            self.files.append(file)
-            self.targetFilenames[lowerName] = file
-
-            return file
-
-        def excludeFile(self, filename):
-            """ Excludes the named file (or glob pattern) from the
-            package. """
-            xfile = Packager.ExcludeFilename(self.packager, filename, self.packager.caseSensitive)
-            self.excludedFilenames.append(xfile)
-
-        def __addImplicitDependenciesWindows(self):
-            """ Walks through the list of files, looking for dll's and
-            exe's that might include implicit dependencies on other
-            dll's and assembly manifests.  Tries to determine those
-            dependencies, and adds them back into the filelist. """
-
-            # We walk through the list as we modify it.  That's OK,
-            # because we want to follow the transitive closure of
-            # dependencies anyway.
-            for file in self.files:
-                if not file.executable:
-                    continue
-
-                if file.isExcluded(self):
-                    # Skip this file.
-                    continue
-
-                if file.filename.getExtension().lower() == "manifest":
-                    filenames = self.__parseManifest(file.filename)
-                    if filenames is None:
-                        self.notify.warning("Unable to determine dependent assemblies from %s" % (file.filename))
-                        continue
-
-                else:
-                    tempFile = Filename.temporary('', 'p3d_', '.txt')
-                    command = 'dumpbin /dependents "%s" >"%s"' % (
-                        file.filename.toOsSpecific(),
-                        tempFile.toOsSpecific())
-                    try:
-                        os.system(command)
-                    except:
-                        pass
-                    filenames = None
-
-                    if tempFile.exists():
-                        filenames = self.__parseDependenciesWindows(tempFile)
-                        tempFile.unlink()
-                    if filenames is None:
-                        self.notify.warning("Unable to determine dependencies from %s" % (file.filename))
-                        filenames = []
-
-                    # Extract the manifest file so we can figure out
-                    # the dependent assemblies.
-                    tempFile = Filename.temporary('', 'p3d_', '.manifest')
-                    resindex = 2
-                    if file.filename.getExtension().lower() == "exe":
-                        resindex = 1
-                    command = 'mt -inputresource:"%s";#%d -out:"%s" > nul' % (
-                        file.filename.toOsSpecific(),
-                        resindex, tempFile.toOsSpecific())
-                    try:
-                        out = os.system(command)
-                    except:
-                        pass
-                    afilenames = None
-
-                    if tempFile.exists():
-                        afilenames = self.__parseManifest(tempFile)
-                        tempFile.unlink()
-
-                    # Also check for an explicit private-assembly
-                    # manifest file on disk.
-                    mfile = file.filename + '.manifest'
-                    if mfile.exists():
-                        if afilenames is None:
-                            afilenames = []
-                        afilenames += self.__parseManifest(mfile)
-                        # Since it's an explicit manifest file, it
-                        # means we should include the manifest
-                        # file itself in the package.
-                        newName = Filename(file.dependencyDir, mfile.getBasename())
-                        self.addFile(mfile, newName = str(newName),
-                                     explicit = False, executable = True)
-
-                    if afilenames is None and out != 31:
-                        self.notify.warning("Unable to determine dependent assemblies from %s" % (file.filename))
-
-                    if afilenames is not None:
-                        filenames += afilenames
-
-                # Attempt to resolve the dependent filename relative
-                # to the original filename, before we resolve it along
-                # the PATH.
-                path = DSearchPath(Filename(file.filename.getDirname()))
-
-                for filename in filenames:
-                    filename = Filename.fromOsSpecific(filename)
-                    filename.resolveFilename(path)
-                    filename.makeTrueCase()
-
-                    newName = Filename(file.dependencyDir, filename.getBasename())
-                    self.addFile(filename, newName = str(newName),
-                                 explicit = False, executable = True)
-
-        def __parseDependenciesWindows(self, tempFile):
-            """ Reads the indicated temporary file, the output from
-            dumpbin /dependents, to determine the list of dll's this
-            executable file depends on. """
-
-            lines = open(tempFile.toOsSpecific(), 'rU').readlines()
-            li = 0
-            while li < len(lines):
-                line = lines[li]
-                li += 1
-                if line.find(' has the following dependencies') != -1:
-                    break
-
-            if li < len(lines):
-                line = lines[li]
-                if line.strip() == '':
-                    # Skip a blank line.
-                    li += 1
-
-            # Now we're finding filenames, until the next blank line.
-            filenames = []
-            while li < len(lines):
-                line = lines[li]
-                li += 1
-                line = line.strip()
-                if line == '':
-                    # We're done.
-                    return filenames
-                filenames.append(line)
-
-            # Hmm, we ran out of data.  Oh well.
-            if not filenames:
-                # Some parse error.
-                return None
-
-            # At least we got some data.
-            return filenames
-
-        def __parseManifest(self, tempFile):
-            """ Reads the indicated application manifest file, to
-            determine the list of dependent assemblies this
-            executable file depends on. """
-
-            doc = TiXmlDocument(tempFile.toOsSpecific())
-            if not doc.LoadFile():
-                return None
-
-            assembly = doc.FirstChildElement("assembly")
-            if not assembly:
-                return None
-
-            # Pick up assemblies that it depends on
-            filenames = []
-            dependency = assembly.FirstChildElement("dependency")
-            while dependency:
-                depassembly = dependency.FirstChildElement("dependentAssembly")
-                if depassembly:
-                    ident = depassembly.FirstChildElement("assemblyIdentity")
-                    if ident:
-                        name = ident.Attribute("name")
-                        if name:
-                            filenames.append(name + ".manifest")
-
-                dependency = dependency.NextSiblingElement("dependency")
-
-            # Pick up direct dll dependencies that it lists
-            dfile = assembly.FirstChildElement("file")
-            while dfile:
-                name = dfile.Attribute("name")
-                if name:
-                    filenames.append(name)
-                dfile = dfile.NextSiblingElement("file")
-
-            return filenames
-
-        def __locateFrameworkLibrary(self, library):
-            """ Locates the given library inside its framework on the
-            default framework paths, and returns its location as Filename. """
-
-            # If it's already a full existing path, we
-            # don't search for it anymore, of course.
-            if Filename.fromOsSpecific(library).exists():
-                return Filename.fromOsSpecific(library)
-
-            # DSearchPath appears not to work for directories.
-            fpath = []
-            fpath.append(Filename("/Library/Frameworks"))
-            fpath.append(Filename("/System/Library/Frameworks"))
-            fpath.append(Filename("/Developer/Library/Frameworks"))
-            fpath.append(Filename(os.path.expanduser("~"), "Library/Frameworks"))
-            if "HOME" in os.environ:
-                fpath.append(Filename(os.environ["HOME"], "Library/Frameworks"))
-            ffilename = Filename(library.split('.framework/', 1)[0].split('/')[-1] + '.framework')
-            ffilename = Filename(ffilename, library.split('.framework/', 1)[-1])
-
-            # Look under the system root first, if supplied.
-            if self.packager.systemRoot:
-                for i in fpath:
-                    fw = Filename(self.packager.systemRoot, i)
-                    if Filename(fw, ffilename).exists():
-                        return Filename(fw, ffilename)
-
-            for i in fpath:
-                if Filename(i, ffilename).exists():
-                    return Filename(i, ffilename)
-
-            # Not found? Well, let's just return the framework + file
-            # path, the user will be presented with a warning later.
-            return ffilename
-
-        def __alterFrameworkDependencies(self, file, framework_deps):
-            """ Copies the given library file to a temporary directory,
-            and alters the dependencies so that it doesn't contain absolute
-            framework dependencies. """
-
-            if not file.deleteTemp:
-                # Copy the file to a temporary location because we
-                # don't want to modify the original (there's a big
-                # chance that we break it).
-
-                # Copy it every time, because the source file might
-                # have changed since last time we ran.
-                assert file.filename.exists(), "File doesn't exist: %s" % file.filename
-                tmpfile = Filename.temporary('', "p3d_" + file.filename.getBasename())
-                tmpfile.setBinary()
-                file.filename.copyTo(tmpfile)
-                file.filename = tmpfile
-                file.deleteTemp = True
-
-            # Alter the dependencies to have a relative path rather than absolute
-            for filename in framework_deps:
-                loc = self.__locateFrameworkLibrary(filename)
-
-                if loc == file.filename:
-                    os.system('install_name_tool -id "%s" "%s"' % (os.path.basename(filename), file.filename.toOsSpecific()))
-                elif "/System/" in loc.toOsSpecific():
-                    # Let's keep references to system frameworks absolute
-                    os.system('install_name_tool -change "%s" "%s" "%s"' % (filename, loc.toOsSpecific(), file.filename.toOsSpecific()))
-                else:
-                    os.system('install_name_tool -change "%s" "%s" "%s"' % (filename, os.path.basename(filename), file.filename.toOsSpecific()))
-
-        def __addImplicitDependenciesOSX(self):
-            """ Walks through the list of files, looking for dylib's
-            and executables that might include implicit dependencies
-            on other dylib's.  Tries to determine those dependencies,
-            and adds them back into the filelist. """
-
-            # We walk through the list as we modify it.  That's OK,
-            # because we want to follow the transitive closure of
-            # dependencies anyway.
-            for file in self.files:
-                if not file.executable:
-                    continue
-
-                if file.isExcluded(self):
-                    # Skip this file.
-                    continue
-
-                origFilename = Filename(file.filename)
-
-                tempFile = Filename.temporary('', 'p3d_', '.txt')
-                command = '/usr/bin/otool -arch all -L "%s" >"%s"' % (
-                    origFilename.toOsSpecific(),
-                    tempFile.toOsSpecific())
-                if self.arch:
-                    arch = self.arch
-                    if arch == "amd64":
-                        arch = "x86_64"
-                    command = '/usr/bin/otool -arch %s -L "%s" >"%s"' % (
-                        arch,
-                        origFilename.toOsSpecific(),
-                        tempFile.toOsSpecific())
-                exitStatus = os.system(command)
-                if exitStatus != 0:
-                    self.notify.warning('Command failed: %s' % (command))
-                filenames = None
-
-                if tempFile.exists():
-                    filenames = self.__parseDependenciesOSX(tempFile)
-                    tempFile.unlink()
-                if filenames is None:
-                    self.notify.warning("Unable to determine dependencies from %s" % (origFilename))
-                    continue
-
-                # Attempt to resolve the dependent filename relative
-                # to the original filename, before we resolve it along
-                # the PATH.
-                path = DSearchPath(Filename(origFilename.getDirname()))
-
-                # Find the dependencies that are referencing a framework
-                framework_deps = []
-                for filename in filenames:
-                    if '.framework/' in filename:
-                        framework_deps.append(filename)
-
-                if len(framework_deps) > 0:
-                    # Fixes dependencies like @executable_path/../Library/Frameworks/Cg.framework/Cg
-                    self.__alterFrameworkDependencies(file, framework_deps)
-
-                for filename in filenames:
-                    if '@loader_path' in filename:
-                        filename = filename.replace('@loader_path', origFilename.getDirname())
-
-                    if False and '.framework/' in filename:
-                        # It references a framework, and besides the fact
-                        # that those often contain absolute paths, they
-                        # aren't commonly on the library path either.
-                        filename = self.__locateFrameworkLibrary(filename)
-                        filename.setBinary()
-                    else:
-                        # It's just a normal library - find it on the path.
-                        filename = Filename.fromOsSpecific(filename)
-                        filename.setBinary()
-
-                        if filename.isLocal():
-                            filename.resolveFilename(path)
-                        else:
-                            # It's a fully-specified filename; look
-                            # for it under the system root first.
-                            if self.packager.systemRoot:
-                                f2 = Filename(self.packager.systemRoot, filename)
-                                if f2.exists():
-                                    filename = f2
-
-                    # Skip libraries and frameworks in system directory
-                    if "/System/" in filename.toOsSpecific():
-                        continue
-
-                    newName = Filename(file.dependencyDir, filename.getBasename())
-                    self.addFile(filename, newName = str(newName),
-                                 explicit = False, executable = True)
-
-        def __parseDependenciesOSX(self, tempFile):
-            """ Reads the indicated temporary file, the output from
-            otool -L, to determine the list of dylibs this
-            executable file depends on. """
-
-            lines = open(tempFile.toOsSpecific(), 'rU').readlines()
-
-            filenames = []
-            for line in lines:
-                if not line[0].isspace():
-                    continue
-                line = line.strip()
-                s = line.find(' (compatibility')
-                if s != -1:
-                    line = line[:s]
-                else:
-                    s = line.find('.dylib')
-                    if s != -1:
-                        line = line[:s + 6]
-                    else:
-                        continue
-                filenames.append(line)
-
-            return filenames
-
-        def __readAndStripELF(self, file):
-            """ Reads the indicated ELF binary, and returns a list with
-            dependencies.  If it contains data that should be stripped,
-            it writes the stripped library to a temporary file.  Returns
-            None if the file failed to read (e.g. not an ELF file). """
-
-            # Read the first 16 bytes, which identify the ELF file.
-            elf = open(file.filename.toOsSpecific(), 'rb')
-            try:
-                ident = elf.read(16)
-            except IOError:
-                elf.close()
-                return None
-
-            if not ident.startswith(b"\177ELF"):
-                # No elf magic!  Beware of orcs.
-                return None
-
-            # Make sure we read in the correct endianness and integer size
-            byteOrder = "<>"[ord(ident[5:6]) - 1]
-            elfClass = ord(ident[4:5]) - 1 # 0 = 32-bits, 1 = 64-bits
-            headerStruct = byteOrder + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[elfClass]
-            sectionStruct = byteOrder + ("4xI8xIII8xI", "4xI16xQQI12xQ")[elfClass]
-            dynamicStruct = byteOrder + ("iI", "qQ")[elfClass]
-
-            type, machine, version, entry, phoff, shoff, flags, ehsize, phentsize, phnum, shentsize, shnum, shstrndx \
-              = struct.unpack(headerStruct, elf.read(struct.calcsize(headerStruct)))
-            dynamicSections = []
-            stringTables = {}
-
-            # Seek to the section header table and find the .dynamic section.
-            elf.seek(shoff)
-            for i in range(shnum):
-                type, offset, size, link, entsize = struct.unpack_from(sectionStruct, elf.read(shentsize))
-                if type == 6 and link != 0: # DYNAMIC type, links to string table
-                    dynamicSections.append((offset, size, link, entsize))
-                    stringTables[link] = None
-
-            # Read the relevant string tables.
-            for idx in stringTables.keys():
-                elf.seek(shoff + idx * shentsize)
-                type, offset, size, link, entsize = struct.unpack_from(sectionStruct, elf.read(shentsize))
-                if type != 3: continue
-                elf.seek(offset)
-                stringTables[idx] = elf.read(size)
-
-            # Loop through the dynamic sections and rewrite it if it has an rpath/runpath.
-            rewriteSections = []
-            filenames = []
-            rpath = []
-            for offset, size, link, entsize in dynamicSections:
-                elf.seek(offset)
-                data = elf.read(entsize)
-                tag, val = struct.unpack_from(dynamicStruct, data)
-                newSectionData = b""
-                startReplace = None
-                pad = 0
-
-                # Read tags until we find a NULL tag.
-                while tag != 0:
-                    if tag == 1: # A NEEDED entry.  Read it from the string table.
-                        filenames.append(stringTables[link][val : stringTables[link].find(b'\0', val)])
-
-                    elif tag == 15 or tag == 29:
-                        rpath += stringTables[link][val : stringTables[link].find(b'\0', val)].split(b':')
-                        # An RPATH or RUNPATH entry.
-                        if not startReplace:
-                            startReplace = elf.tell() - entsize
-                        if startReplace:
-                            pad += entsize
-
-                    elif startReplace is not None:
-                        newSectionData += data
-
-                    data = elf.read(entsize)
-                    tag, val = struct.unpack_from(dynamicStruct, data)
-
-                if startReplace is not None:
-                    newSectionData += data + (b"\0" * pad)
-                    rewriteSections.append((startReplace, newSectionData))
-            elf.close()
-
-            # No rpaths/runpaths found, so nothing to do any more.
-            if len(rewriteSections) == 0:
-                return filenames
-
-            # Attempt to resolve any of the directly
-            # dependent filenames along the RPATH.
-            for f in range(len(filenames)):
-                filename = filenames[f]
-                for rdir in rpath:
-                    if os.path.isfile(os.path.join(rdir, filename)):
-                        filenames[f] = os.path.join(rdir, filename)
-                        break
-
-            if not file.deleteTemp:
-                # Copy the file to a temporary location because we
-                # don't want to modify the original (there's a big
-                # chance that we break it).
-
-                tmpfile = Filename.temporary('', "p3d_" + file.filename.getBasename())
-                tmpfile.setBinary()
-                file.filename.copyTo(tmpfile)
-                file.filename = tmpfile
-                file.deleteTemp = True
-
-            # Open the temporary file and rewrite the dynamic sections.
-            elf = open(file.filename.toOsSpecific(), 'r+b')
-            for offset, data in rewriteSections:
-                elf.seek(offset)
-                elf.write(data)
-            elf.write(b"\0" * pad)
-            elf.close()
-            return filenames
-
-        def __addImplicitDependenciesPosix(self):
-            """ Walks through the list of files, looking for so's
-            and executables that might include implicit dependencies
-            on other so's.  Tries to determine those dependencies,
-            and adds them back into the filelist. """
-
-            # We walk through the list as we modify it.  That's OK,
-            # because we want to follow the transitive closure of
-            # dependencies anyway.
-            for file in self.files:
-                if not file.executable:
-                    continue
-
-                if file.isExcluded(self):
-                    # Skip this file.
-                    continue
-
-                # Check if this is an ELF binary.
-                filenames = self.__readAndStripELF(file)
-
-                # If that failed, perhaps ldd will help us.
-                if filenames is None:
-                    self.notify.warning("Reading ELF library %s failed, using ldd instead" % (file.filename))
-                    tempFile = Filename.temporary('', 'p3d_', '.txt')
-                    command = 'ldd "%s" >"%s"' % (
-                        file.filename.toOsSpecific(),
-                        tempFile.toOsSpecific())
-                    try:
-                        os.system(command)
-                    except:
-                        pass
-
-                    if tempFile.exists():
-                        filenames = self.__parseDependenciesPosix(tempFile)
-                        tempFile.unlink()
-
-                if filenames is None:
-                    self.notify.warning("Unable to determine dependencies from %s" % (file.filename))
-                    continue
-
-                # Attempt to resolve the dependent filename relative
-                # to the original filename, before we resolve it along
-                # the PATH.
-                path = DSearchPath(Filename(file.filename.getDirname()))
-
-                for filename in filenames:
-                    # These vDSO's provided by Linux aren't
-                    # supposed to be anywhere on the system.
-                    if filename in ["linux-gate.so.1", "linux-vdso.so.1"]:
-                        continue
-
-                    filename = Filename.fromOsSpecific(filename)
-                    filename.resolveFilename(path)
-                    filename.setBinary()
-
-                    newName = Filename(file.dependencyDir, filename.getBasename())
-                    self.addFile(filename, newName = str(newName),
-                                 explicit = False, executable = True)
-
-        def __parseDependenciesPosix(self, tempFile):
-            """ Reads the indicated temporary file, the output from
-            ldd, to determine the list of so's this executable file
-            depends on. """
-
-            lines = open(tempFile.toOsSpecific(), 'rU').readlines()
-
-            filenames = []
-            for line in lines:
-                line = line.strip()
-                s = line.find(' => ')
-                if s == -1:
-                    continue
-
-                line = line[:s].strip()
-                filenames.append(line)
-
-            return filenames
-
-        def addExtensionModules(self):
-            """ Adds the extension modules detected by the freezer to
-            the current list of files. """
-
-            freezer = self.freezer
-            for moduleName, filename in freezer.extras:
-                filename = Filename.fromOsSpecific(filename)
-                newName = filename.getBasename()
-                if '.' in moduleName:
-                    newName = '/'.join(moduleName.split('.')[:-1])
-                    newName += '/' + filename.getBasename()
-                # Sometimes the PYTHONPATH has the wrong case in it.
-                filename.makeTrueCase()
-                self.addFile(filename, newName = newName,
-                             explicit = False, extract = True,
-                             executable = True,
-                             platformSpecific = True)
-            freezer.extras = []
-
-
-        def makeP3dInfo(self):
-            """ Makes the p3d_info.xml file that defines the
-            application startup parameters and such. """
-
-            doc = TiXmlDocument()
-            decl = TiXmlDeclaration("1.0", "utf-8", "")
-            doc.InsertEndChild(decl)
-
-            xpackage = TiXmlElement('package')
-            xpackage.SetAttribute('name', self.packageName)
-            if self.platform:
-                xpackage.SetAttribute('platform', self.platform)
-            if self.version:
-                xpackage.SetAttribute('version', self.version)
-
-            xpackage.SetAttribute('main_module', self.mainModule[1])
-
-            self.__addConfigs(xpackage)
-
-            requireHosts = {}
-            for package in self.requires:
-                xrequires = TiXmlElement('requires')
-                xrequires.SetAttribute('name', package.packageName)
-                if package.version:
-                    xrequires.SetAttribute('version', package.version)
-                xrequires.SetAttribute('host', package.host)
-                package.packageSeq.storeXml(xrequires, 'seq')
-                package.packageSetVer.storeXml(xrequires, 'set_ver')
-                requireHosts[package.host] = True
-                xpackage.InsertEndChild(xrequires)
-
-            for host in requireHosts.keys():
-                he = self.packager.hosts.get(host, None)
-                if he:
-                    xhost = he.makeXml(packager = self.packager)
-                    xpackage.InsertEndChild(xhost)
-
-            self.extracts.sort()
-            for name, xextract in self.extracts:
-                xpackage.InsertEndChild(xextract)
-
-            doc.InsertEndChild(xpackage)
-
-            # Write the xml file to a temporary file on disk, so we
-            # can add it to the multifile.
-            filename = Filename.temporary('', 'p3d_', '.xml')
-
-            # This should really be setText() for an xml file, but it
-            # doesn't really matter that much since tinyxml can read
-            # it either way; and if we use setBinary() it will remain
-            # compatible with older versions of the core API that
-            # didn't understand the SF_text flag.
-            filename.setBinary()
-
-            doc.SaveFile(filename.toOsSpecific())
-
-            # It's important not to compress this file: the core API
-            # runtime can't decode compressed subfiles.
-            self.multifile.addSubfile('p3d_info.xml', filename, 0)
-
-            self.multifile.flush()
-            filename.unlink()
-
-
-        def compressMultifile(self):
-            """ Compresses the .mf file into an .mf.pz file. """
-
-            if self.oldCompressedBasename:
-                # Remove the previous compressed file first.
-                compressedPath = Filename(self.packager.installDir, Filename(self.packageDir, self.oldCompressedBasename))
-                compressedPath.unlink()
-
-            newCompressedFilename = '%s.pz' % (self.packageFilename)
-
-            # Now build the new version.
-            compressedPath = Filename(self.packager.installDir, newCompressedFilename)
-            if not compressFile(self.packageFullpath, compressedPath, 6):
-                message = 'Unable to write %s' % (compressedPath)
-                raise PackagerError(message)
-
-        def readDescFile(self):
-            """ Reads the existing package.xml file before rewriting
-            it.  We need this to preserve the list of patches, and
-            similar historic data, between sessions. """
-
-            self.packageSeq = SeqValue()
-            self.packageSetVer = SeqValue()
-            self.patchVersion = None
-            self.patches = []
-
-            self.oldCompressedBasename = None
-
-            packageDescFullpath = Filename(self.packager.installDir, self.packageDesc)
-            doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
-            if not doc.LoadFile():
-                return
-
-            xpackage = doc.FirstChildElement('package')
-            if not xpackage:
-                return
-
-            perPlatform = xpackage.Attribute('per_platform')
-            self.perPlatform = int(perPlatform or '0')
-
-            self.packageSeq.loadXml(xpackage, 'seq')
-            self.packageSetVer.loadXml(xpackage, 'set_ver')
-
-            xcompressed = xpackage.FirstChildElement('compressed_archive')
-            if xcompressed:
-                compressedFilename = xcompressed.Attribute('filename')
-                if compressedFilename:
-                    self.oldCompressedBasename = compressedFilename
-
-            patchVersion = xpackage.Attribute('patch_version')
-            if not patchVersion:
-                patchVersion = xpackage.Attribute('last_patch_version')
-            if patchVersion:
-                self.patchVersion = patchVersion
-
-            # Extract the base_version, top_version, and patch
-            # entries, if any, and preserve these entries verbatim for
-            # the next version.
-            xbase = xpackage.FirstChildElement('base_version')
-            if xbase:
-                self.patches.append(xbase.Clone())
-            xtop = xpackage.FirstChildElement('top_version')
-            if xtop:
-                self.patches.append(xtop.Clone())
-
-            xpatch = xpackage.FirstChildElement('patch')
-            while xpatch:
-                self.patches.append(xpatch.Clone())
-                xpatch = xpatch.NextSiblingElement('patch')
-
-        def writeDescFile(self):
-            """ Makes the package.xml file that describes the package
-            and its contents, for download. """
-
-            packageDescFullpath = Filename(self.packager.installDir, self.packageDesc)
-            doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
-            decl = TiXmlDeclaration("1.0", "utf-8", "")
-            doc.InsertEndChild(decl)
-
-            xpackage = TiXmlElement('package')
-            xpackage.SetAttribute('name', self.packageName)
-            if self.platform:
-                xpackage.SetAttribute('platform', self.platform)
-            if self.version:
-                xpackage.SetAttribute('version', self.version)
-            if self.perPlatform:
-                xpackage.SetAttribute('per_platform', '1')
-
-            if self.patchVersion:
-                xpackage.SetAttribute('last_patch_version', self.patchVersion)
-
-            self.packageSeq.storeXml(xpackage, 'seq')
-            self.packageSetVer.storeXml(xpackage, 'set_ver')
-
-            self.__addConfigs(xpackage)
-
-            for package in self.requires:
-                xrequires = TiXmlElement('requires')
-                xrequires.SetAttribute('name', package.packageName)
-                if self.platform and package.platform:
-                    xrequires.SetAttribute('platform', package.platform)
-                if package.version:
-                    xrequires.SetAttribute('version', package.version)
-                package.packageSeq.storeXml(xrequires, 'seq')
-                package.packageSetVer.storeXml(xrequires, 'set_ver')
-                xrequires.SetAttribute('host', package.host)
-                xpackage.InsertEndChild(xrequires)
-
-            xuncompressedArchive = self.getFileSpec(
-                'uncompressed_archive', self.packageFullpath,
-                self.packageBasename)
-            xpackage.InsertEndChild(xuncompressedArchive)
-
-            xcompressedArchive = self.getFileSpec(
-                'compressed_archive', self.packageFullpath + '.pz',
-                self.packageBasename + '.pz')
-            xpackage.InsertEndChild(xcompressedArchive)
-
-            # Copy in the patch entries read from the previous version
-            # of the desc file.
-            for xpatch in self.patches:
-                xpackage.InsertEndChild(xpatch)
-
-            self.extracts.sort()
-            for name, xextract in self.extracts:
-                xpackage.InsertEndChild(xextract)
-
-            doc.InsertEndChild(xpackage)
-            doc.SaveFile()
-
-        def __addConfigs(self, xpackage):
-            """ Adds the XML config values defined in self.configs to
-            the indicated XML element. """
-
-            if self.configs:
-                xconfig = TiXmlElement('config')
-
-                for variable, value in self.configs.items():
-                    if sys.version_info < (3, 0) and isinstance(value, unicode):
-                        xconfig.SetAttribute(variable, value.encode('utf-8'))
-                    elif isinstance(value, bool):
-                        # True or False must be encoded as 1 or 0.
-                        xconfig.SetAttribute(variable, str(int(value)))
-                    else:
-                        xconfig.SetAttribute(variable, str(value))
-
-                xpackage.InsertEndChild(xconfig)
-
-        def writeImportDescFile(self):
-            """ Makes the package.import.xml file that describes the
-            package and its contents, for other packages and
-            applications that may wish to "require" this one. """
-
-            packageImportDescFullpath = Filename(self.packager.installDir, self.packageImportDesc)
-            doc = TiXmlDocument(packageImportDescFullpath.toOsSpecific())
-            decl = TiXmlDeclaration("1.0", "utf-8", "")
-            doc.InsertEndChild(decl)
-
-            xpackage = TiXmlElement('package')
-            xpackage.SetAttribute('name', self.packageName)
-            if self.platform:
-                xpackage.SetAttribute('platform', self.platform)
-            if self.version:
-                xpackage.SetAttribute('version', self.version)
-            xpackage.SetAttribute('host', self.host)
-
-            self.packageSeq.storeXml(xpackage, 'seq')
-            self.packageSetVer.storeXml(xpackage, 'set_ver')
-
-            requireHosts = {}
-            requireHosts[self.host] = True
-
-            for package in self.requires:
-                xrequires = TiXmlElement('requires')
-                xrequires.SetAttribute('name', package.packageName)
-                if self.platform and package.platform:
-                    xrequires.SetAttribute('platform', package.platform)
-                if package.version:
-                    xrequires.SetAttribute('version', package.version)
-                xrequires.SetAttribute('host', package.host)
-                package.packageSeq.storeXml(xrequires, 'seq')
-                package.packageSetVer.storeXml(xrequires, 'set_ver')
-                requireHosts[package.host] = True
-                xpackage.InsertEndChild(xrequires)
-
-            # Make sure we also write the full host descriptions for
-            # any hosts we reference, so we can find these guys later.
-            for host in requireHosts.keys():
-                he = self.packager.hosts.get(host, None)
-                if he:
-                    xhost = he.makeXml(packager = self.packager)
-                    xpackage.InsertEndChild(xhost)
-
-            self.components.sort()
-            for type, name, xcomponent in self.components:
-                xpackage.InsertEndChild(xcomponent)
-
-            doc.InsertEndChild(xpackage)
-            doc.SaveFile()
-
-        def readImportDescFile(self, filename):
-            """ Reads the import desc file.  Returns True on success,
-            False on failure. """
-
-            self.packageSeq = SeqValue()
-            self.packageSetVer = SeqValue()
-
-            doc = TiXmlDocument(filename.toOsSpecific())
-            if not doc.LoadFile():
-                return False
-            xpackage = doc.FirstChildElement('package')
-            if not xpackage:
-                return False
-
-            self.packageName = xpackage.Attribute('name')
-            self.platform = xpackage.Attribute('platform')
-            self.version = xpackage.Attribute('version')
-            self.host = xpackage.Attribute('host')
-
-            # Get any new host descriptors.
-            xhost = xpackage.FirstChildElement('host')
-            while xhost:
-                he = self.packager.HostEntry()
-                he.loadXml(xhost, self)
-                if he.url not in self.packager.hosts:
-                    self.packager.hosts[he.url] = he
-                xhost = xhost.NextSiblingElement('host')
-
-            self.packageSeq.loadXml(xpackage, 'seq')
-            self.packageSetVer.loadXml(xpackage, 'set_ver')
-
-            self.requires = []
-            xrequires = xpackage.FirstChildElement('requires')
-            while xrequires:
-                packageName = xrequires.Attribute('name')
-                platform = xrequires.Attribute('platform')
-                version = xrequires.Attribute('version')
-                host = xrequires.Attribute('host')
-                if packageName:
-                    package = self.packager.findPackage(
-                        packageName, platform = platform, version = version,
-                        host = host, requires = self.requires)
-                    if package:
-                        self.requires.append(package)
-                xrequires = xrequires.NextSiblingElement('requires')
-
-            self.targetFilenames = {}
-            xcomponent = xpackage.FirstChildElement('component')
-            while xcomponent:
-                name = xcomponent.Attribute('filename')
-                if name:
-                    self.targetFilenames[name.lower()] = True
-                xcomponent = xcomponent.NextSiblingElement('component')
-
-            self.moduleNames = {}
-            xmodule = xpackage.FirstChildElement('module')
-            while xmodule:
-                moduleName = xmodule.Attribute('name')
-                exclude = int(xmodule.Attribute('exclude') or 0)
-                forbid = int(xmodule.Attribute('forbid') or 0)
-                allowChildren = int(xmodule.Attribute('allowChildren') or 0)
-
-                if moduleName:
-                    mdef = FreezeTool.Freezer.ModuleDef(
-                        moduleName, exclude = exclude, forbid = forbid,
-                        allowChildren = allowChildren)
-                    self.moduleNames[moduleName] = mdef
-                xmodule = xmodule.NextSiblingElement('module')
-
-            return True
-
-        def getFileSpec(self, element, pathname, newName):
-            """ Returns an xcomponent or similar element with the file
-            information for the indicated file. """
-
-            xspec = TiXmlElement(element)
-
-            size = pathname.getFileSize()
-            timestamp = pathname.getTimestamp()
-
-            hv = HashVal()
-            hv.hashFile(pathname)
-            hash = hv.asHex()
-
-            xspec.SetAttribute('filename', newName)
-            xspec.SetAttribute('size', str(size))
-            xspec.SetAttribute('timestamp', str(timestamp))
-            xspec.SetAttribute('hash', hash)
-
-            return xspec
-
-        def addPyFile(self, file):
-            """ Adds the indicated python file, identified by filename
-            instead of by module name, to the package. """
-
-            # Convert the raw filename back to a module name, so we
-            # can see if we've already loaded this file.  We assume
-            # that all Python files within the package will be rooted
-            # at the top of the package.
-
-            filename = file.newName.rsplit('.', 1)[0]
-            moduleName = filename.replace("/", ".")
-            if moduleName.endswith('.__init__'):
-                moduleName = moduleName.rsplit('.', 1)[0]
-
-            if moduleName in self.freezer.modules:
-                # This Python file is already known.  We don't have to
-                # deal with it again.
-                return
-
-            # Make sure that it is actually in a package.
-            parentName = moduleName
-            while '.' in parentName:
-                parentName = parentName.rsplit('.', 1)[0]
-                if parentName not in self.freezer.modules:
-                    message = 'Cannot add Python file %s; not in package' % (file.newName)
-                    if file.required or file.explicit:
-                        raise Exception(message)
-                    else:
-                        self.notify.warning(message)
-                    return
-
-            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):
-            # Precompile egg files to bam's.
-            np = self.packager.loader.loadModel(file.filename, self.packager.loaderOptions)
-            if not np:
-                raise Exception('Could not read egg file %s' % (file.filename))
-
-            bamName = Filename(file.newName)
-            bamName.setExtension('bam')
-            self.addNode(np.node(), file.filename, str(bamName))
-
-        def addBamFile(self, file):
-            # Load the bam file so we can massage its textures.
-            bamFile = BamFile()
-            if not bamFile.openRead(file.filename):
-                raise Exception('Could not read bam file %s' % (file.filename))
-
-            bamFile.getReader().setLoaderOptions(self.packager.loaderOptions)
-
-            if not bamFile.resolve():
-                raise Exception('Could not resolve bam file %s' % (file.filename))
-
-            node = bamFile.readNode()
-            if not node:
-                raise Exception('Not a model file: %s' % (file.filename))
-
-            self.addNode(node, file.filename, file.newName)
-
-        def addNode(self, node, filename, newName):
-            """ Converts the indicated node to a bam stream, and adds the
-            bam file to the multifile under the indicated newName. """
-
-            # If the Multifile already has a file by this name, don't
-            # bother adding it again.
-            if self.multifile.findSubfile(newName) >= 0:
-                return
-
-            # Be sure to import all of the referenced textures, and tell
-            # them their new location within the multifile.
-
-            for tex in NodePath(node).findAllTextures():
-                if not tex.hasFullpath() and tex.hasRamImage():
-                    # We need to store this texture as a raw-data image.
-                    # Clear the newName so this will happen
-                    # automatically.
-                    tex.clearFilename()
-                    tex.clearAlphaFilename()
-
-                else:
-                    # We can store this texture as a file reference to its
-                    # image.  Copy the file into our multifile, and rename
-                    # its reference in the texture.
-                    if tex.hasFilename():
-                        tex.setFilename(self.addFoundTexture(tex.getFullpath()))
-                    if tex.hasAlphaFilename():
-                        tex.setAlphaFilename(self.addFoundTexture(tex.getAlphaFullpath()))
-
-            # Now generate an in-memory bam file.  Tell the bam writer to
-            # keep the textures referenced by their in-multifile path.
-            bamFile = BamFile()
-            stream = StringStream()
-            bamFile.openWrite(stream)
-            bamFile.getWriter().setFileTextureMode(bamFile.BTMUnchanged)
-            bamFile.writeObject(node)
-            bamFile.close()
-
-            # Clean the node out of memory.
-            node.removeAllChildren()
-
-            # Now we have an in-memory bam file.
-            stream.seekg(0)
-            self.multifile.addSubfile(newName, stream, self.compressionLevel)
-
-            # Flush it so the data gets written to disk immediately, so we
-            # don't have to keep it around in ram.
-            self.multifile.flush()
-
-            xcomponent = TiXmlElement('component')
-            xcomponent.SetAttribute('filename', newName)
-            self.components.append(('c', newName.lower(), xcomponent))
-
-        def addFoundTexture(self, filename):
-            """ Adds the newly-discovered texture to the output, if it has
-            not already been included.  Returns the new name within the
-            package tree. """
-
-            filename = Filename(filename)
-            filename.makeCanonical()
-
-            file = self.sourceFilenames.get(filename, None)
-            if file:
-                # Never mind, it's already on the list.
-                return file.newName
-
-            # We have to copy the image into the plugin tree somewhere.
-            newName = self.importedMapsDir + '/' + filename.getBasename()
-            uniqueId = 0
-            while newName.lower() in self.targetFilenames:
-                uniqueId += 1
-                newName = '%s/%s_%s.%s' % (
-                    self.importedMapsDir, filename.getBasenameWoExtension(),
-                    uniqueId, filename.getExtension())
-
-            file = self.addFile(
-                filename, newName = newName, explicit = False,
-                compress = False)
-
-            if file:
-                # If we added the file in this pass, then also
-                # immediately add it to the multifile (because we
-                # won't be visiting the files list again).
-                self.addComponent(file)
-
-            return newName
-
-        def addDcFile(self, file):
-            """ Adds a dc file to the archive.  A dc file gets its
-            internal comments and parameter names stripped out of the
-            final result automatically.  This is as close as we can
-            come to "compiling" a dc file, since all of the remaining
-            symbols are meaningful at runtime. """
-
-            # First, read in the dc file
-            from panda3d.direct import DCFile
-            dcFile = DCFile()
-            if not dcFile.read(file.filename):
-                self.notify.error("Unable to parse %s." % (file.filename))
-
-            # And then write it out without the comments and such.
-            stream = StringStream()
-            if not dcFile.write(stream, True):
-                self.notify.error("Unable to write %s." % (file.filename))
-
-            file.text = stream.getData()
-            self.addComponent(file)
-
-        def addDcImports(self, file):
-            """ Adds the Python modules named by the indicated dc
-            file. """
-
-            from panda3d.direct import DCFile
-            dcFile = DCFile()
-            if not dcFile.read(file.filename):
-                self.notify.error("Unable to parse %s." % (file.filename))
-
-            for n in range(dcFile.getNumImportModules()):
-                moduleName = dcFile.getImportModule(n)
-                moduleSuffixes = []
-                if '/' in moduleName:
-                    moduleName, suffixes = moduleName.split('/', 1)
-                    moduleSuffixes = suffixes.split('/')
-                self.freezer.addModule(moduleName)
-
-                for suffix in self.packager.dcClientSuffixes:
-                    if suffix in moduleSuffixes:
-                        self.freezer.addModule(moduleName + suffix)
-
-                for i in range(dcFile.getNumImportSymbols(n)):
-                    symbolName = dcFile.getImportSymbol(n, i)
-                    symbolSuffixes = []
-                    if '/' in symbolName:
-                        symbolName, suffixes = symbolName.split('/', 1)
-                        symbolSuffixes = suffixes.split('/')
-
-                    # "from moduleName import symbolName".
-
-                    # Maybe this symbol is itself a module; if that's
-                    # the case, we need to add it to the list also.
-                    self.freezer.addModule('%s.%s' % (moduleName, symbolName),
-                                           implicit = True)
-                    for suffix in self.packager.dcClientSuffixes:
-                        if suffix in symbolSuffixes:
-                            self.freezer.addModule('%s.%s%s' % (moduleName, symbolName, suffix),
-                                                   implicit = True)
-
-
-        def addPrcFile(self, file):
-            """ Adds a prc file to the archive.  Like the dc file,
-            this strips comments and such before adding.  It's also
-            possible to set prcEncryptionKey and/or prcSignCommand to
-            further manipulate prc files during processing. """
-
-            # First, read it in.
-            if file.text:
-                textLines = file.text.split('\n')
-            else:
-                textLines = open(file.filename.toOsSpecific(), 'rU').readlines()
-
-            # Then write it out again, without the comments.
-            tempFilename = Filename.temporary('', 'p3d_', '.prc')
-            tempFilename.setBinary()  # Binary is more reliable for signing.
-            temp = open(tempFilename.toOsSpecific(), 'w')
-            for line in textLines:
-                line = line.strip()
-                if line and line[0] != '#':
-                    # Write the line out only if it's not a comment.
-                    temp.write(line + '\n')
-            temp.close()
-
-            if self.packager.prcSignCommand:
-                # Now sign the file.
-                command = '%s -n "%s"' % (
-                    self.packager.prcSignCommand, tempFilename.toOsSpecific())
-                self.notify.info(command)
-                exitStatus = os.system(command)
-                if exitStatus != 0:
-                    self.notify.error('Command failed: %s' % (command))
-
-            if self.packager.prcEncryptionKey:
-                # And now encrypt it.
-                if file.newName.endswith('.prc'):
-                    # Change .prc -> .pre
-                    file.newName = file.newName[:-1] + 'e'
-
-                preFilename = Filename.temporary('', 'p3d_', '.pre')
-                preFilename.setBinary()
-                tempFilename.setText()
-                encryptFile(tempFilename, preFilename, self.packager.prcEncryptionKey)
-                tempFilename.unlink()
-                tempFilename = preFilename
-
-            if file.deleteTemp:
-                file.filename.unlink()
-
-            file.filename = tempFilename
-            file.text = None
-            file.deleteTemp = True
-
-            self.addComponent(file)
-
-        def addComponent(self, file):
-            compressionLevel = 0
-            if file.compress:
-                compressionLevel = self.compressionLevel
-
-            if file.text:
-                stream = StringStream(file.text)
-                self.multifile.addSubfile(file.newName, stream, compressionLevel)
-                self.multifile.flush()
-
-            elif file.executable and self.arch:
-                if not self.__addOsxExecutable(file):
-                    return
-
-            else:
-                # Copy an ordinary file into the multifile.
-                self.multifile.addSubfile(file.newName, file.filename, compressionLevel)
-            if file.extract:
-                if file.text:
-                    # Better write it to a temporary file, so we can
-                    # get its hash.
-                    tfile = Filename.temporary('', 'p3d_')
-                    open(tfile.toOsSpecific(), 'wb').write(file.text)
-                    xextract = self.getFileSpec('extract', tfile, file.newName)
-                    tfile.unlink()
-
-                else:
-                    # The file data exists on disk already.
-                    xextract = self.getFileSpec('extract', file.filename, file.newName)
-                self.extracts.append((file.newName.lower(), xextract))
-
-            xcomponent = TiXmlElement('component')
-            xcomponent.SetAttribute('filename', file.newName)
-            self.components.append(('c', file.newName.lower(), xcomponent))
-
-        def __addOsxExecutable(self, file):
-            """ Adds an executable or shared library to the multifile,
-            with respect to OSX's fat-binary features.  Returns true
-            on success, false on failure. """
-
-            compressionLevel = 0
-            if file.compress:
-                compressionLevel = self.compressionLevel
-
-            # If we're on OSX and adding only files for a
-            # particular architecture, use lipo to strip out the
-            # part of the file for that architecture.
-
-            arch = self.arch
-            if arch == "amd64":
-                arch = "x86_64"
-
-            # First, we need to verify that it is in fact a
-            # universal binary.
-            tfile = Filename.temporary('', 'p3d_')
-            tfile.setBinary()
-            command = '/usr/bin/lipo -info "%s" >"%s"' % (
-                file.filename.toOsSpecific(),
-                tfile.toOsSpecific())
-            exitStatus = os.system(command)
-            if exitStatus != 0:
-                self.notify.warning("Not an executable file: %s" % (file.filename))
-                # Just add it anyway.
-                file.filename.setBinary()
-                self.multifile.addSubfile(file.newName, file.filename, compressionLevel)
-                return True
-
-            # The lipo command succeeded, so it really is an
-            # executable file.  Parse the lipo output to figure out
-            # which architectures the file supports.
-            arches = []
-            lipoData = open(tfile.toOsSpecific(), 'r').read()
-            tfile.unlink()
-            if ':' in lipoData:
-                arches = lipoData.rsplit(':', 1)[1]
-                arches = arches.split()
-
-            if arches == [arch]:
-                # The file only contains the one architecture that
-                # we want anyway.
-                file.filename.setBinary()
-                self.multifile.addSubfile(file.newName, file.filename, compressionLevel)
-                return True
-
-            if arch not in arches:
-                # The file doesn't support the architecture that we
-                # want at all.  Omit the file.
-                self.notify.warning("%s doesn't support architecture %s" % (
-                    file.filename, self.arch))
-                return False
-
-            # The file contains multiple architectures.  Get
-            # out just the one we want.
-            command = '/usr/bin/lipo -thin %s -output "%s" "%s"' % (
-                arch, tfile.toOsSpecific(),
-                file.filename.toOsSpecific())
-            exitStatus = os.system(command)
-            if exitStatus != 0:
-                self.notify.error('Command failed: %s' % (command))
-            self.multifile.addSubfile(file.newName, tfile, compressionLevel)
-            if file.deleteTemp:
-                file.filename.unlink()
-            file.filename = tfile
-            file.deleteTemp = True
-            return True
-
-
-        def requirePackage(self, package):
-            """ Indicates a dependency on the given package.  This
-            also implicitly requires all of the package's requirements
-            as well (though this transitive requirement happens at
-            runtime, not here at build time). """
-
-            if package not in self.requires:
-                self.requires.append(package)
-                for lowerName in package.targetFilenames:
-                    ext = Filename(lowerName).getExtension()
-                    if ext not in self.packager.nonuniqueExtensions:
-                        self.skipFilenames[lowerName] = True
-
-                for moduleName, mdef in package.moduleNames.items():
-                    if not mdef.exclude:
-                        self.skipModules[moduleName] = mdef
-
-    # Packager constructor
-    def __init__(self, platform = None):
-
-        # The following are config settings that the caller may adjust
-        # before calling any of the command methods.
-
-        # The platform string.
-        self.setPlatform(platform)
-
-        # This should be set to a Filename.
-        self.installDir = None
-
-        # If specified, this is a directory to search first for any
-        # library references, before searching the system.
-        # Particularly useful on OSX to reference the universal SDK.
-        self.systemRoot = None
-
-        # Set this true to treat setHost() the same as addHost(), thus
-        # ignoring any request to specify a particular download host,
-        # e.g. for testing and development.
-        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,
-        # before the .p3d extension.
-        self.p3dSuffix = ''
-
-        # The download URL at which these packages will eventually be
-        # hosted.
-        self.hosts = {}
-        self.host = PandaSystem.getPackageHostUrl()
-        self.addHost(self.host)
-
-        # This will be used when we're not compiling in the packaged
-        # environment.
-        self.__hostInfos = {}
-        self.http = HTTPClient.getGlobalPtr()
-
-        # The maximum amount of time a client should cache the
-        # contents.xml before re-querying the server, in seconds.
-        self.maxAge = 0
-
-        # The contents seq: a tuple of integers, representing the
-        # current seq value.  The contents seq generally increments
-        # with each modification to the contents.xml file.  There is
-        # also a package seq for each package, which generally
-        # increments with each modification to the package.
-
-        # The contents seq and package seq are used primarily for
-        # documentation purposes, to note when a new version is
-        # released.  The package seq value can also be used to verify
-        # that the contents.xml, desc.xml, and desc.import.xml files
-        # were all built at the same time.
-
-        # Although the package seqs are used at runtime to verify that
-        # the latest contents.xml file has been downloaded, they are
-        # not otherwise used at runtime, and they are not binding on
-        # the download version.  The md5 hash, not the package seq, is
-        # actually used to differentiate different download versions.
-        self.contentsSeq = SeqValue()
-
-        # A search list for previously-built local packages.
-
-        # We use a bit of caution to read the Filenames out of the
-        # config variable.  Since cvar.getDirectories() returns a list
-        # of references to Filename objects stored within the config
-        # variable itself, we have to make a copy of each Filename
-        # returned, so they will persist beyond the lifespan of the
-        # config variable.
-        cvar = ConfigVariableSearchPath('pdef-path')
-        self.installSearch = list(map(Filename, cvar.getDirectories()))
-
-        # This is where we cache the location of libraries.
-        self.libraryCache = {}
-
-        # The system PATH, for searching dll's and exe's.
-        self.executablePath = DSearchPath()
-
-        # By convention, we include sys.path at the front of
-        # self.executablePath, mainly to aid makepanda when building
-        # an rtdist build.
-        for dirname in sys.path:
-            self.executablePath.appendDirectory(Filename.fromOsSpecific(dirname))
-
-        # Now add the actual system search path.
-        if self.platform.startswith('win'):
-            self.addWindowsSearchPath(self.executablePath, "PATH")
-
-        else:
-            if self.platform.startswith('osx'):
-                self.addPosixSearchPath(self.executablePath, "DYLD_LIBRARY_PATH")
-
-            self.addPosixSearchPath(self.executablePath, "LD_LIBRARY_PATH")
-            self.addPosixSearchPath(self.executablePath, "PATH")
-
-            if self.platform.startswith('linux'):
-                # It used to be okay to just add some common paths on Linux.
-                # But nowadays, each distribution has their own convention for
-                # where they put their libraries.  Instead, we query the ldconfig
-                # cache, which contains the location of all libraries.
-
-                if not self.loadLdconfigCache():
-                    # Ugh, failure.  All that remains is to guess.  This should
-                    # work for the most common Debian configurations.
-                    multiarchDir = "/lib/%s-linux-gnu" % (os.uname()[4])
-                    if os.path.isdir(multiarchDir):
-                        self.executablePath.appendDirectory(multiarchDir)
-                    if os.path.isdir("/usr/" + multiarchDir):
-                        self.executablePath.appendDirectory("/usr/" + multiarchDir)
-
-            else:
-                # FreeBSD, or some other system that still makes sense.
-                self.executablePath.appendDirectory('/lib')
-                self.executablePath.appendDirectory('/usr/lib')
-                self.executablePath.appendDirectory('/usr/local/lib')
-
-        if self.platform.startswith('freebsd') and os.uname()[1] == "pcbsd":
-            self.executablePath.appendDirectory('/usr/PCBSD/local/lib')
-
-        # Set this flag true to automatically add allow_python_dev to
-        # any applications.
-        self.allowPythonDev = False
-
-        # Set this flag to store the original Python source files,
-        # without compiling them to .pyc or .pyo.
-        self.storePythonSource = False
-
-        # Fill this with a list of (certificate, chain, pkey,
-        # password) tuples to automatically sign each p3d file
-        # generated.
-        self.signParams = []
-
-        # Optional signing and encrypting features.
-        self.encryptionKey = None
-        self.prcEncryptionKey = None
-        self.prcSignCommand = None
-
-        # This is a list of filename extensions and/or basenames that
-        # indicate files that should be encrypted within the
-        # multifile.  This provides obfuscation only, not real
-        # security, since the decryption key must be part of the
-        # client and is therefore readily available to any hacker.
-        # Not only is this feature useless, but using it also
-        # increases the size of your patchfiles, since encrypted files
-        # can't really be patched.  But it's here if you really want
-        # it. ** Note: Actually, this isn't implemented yet.
-        #self.encryptExtensions = []
-        #self.encryptFiles = []
-
-        # This is the list of DC import suffixes that should be
-        # available to the client.  Other suffixes, like AI and UD,
-        # are server-side only and should be ignored by the Scrubber.
-        self.dcClientSuffixes = ['OV']
-
-        # Is this file system case-sensitive?
-        self.caseSensitive = True
-        if self.platform.startswith('win'):
-            self.caseSensitive = False
-        elif self.platform.startswith('osx'):
-            self.caseSensitive = False
-
-        # Get the list of filename extensions that are recognized as
-        # image files.
-        self.imageExtensions = []
-        for type in PNMFileTypeRegistry.getGlobalPtr().getTypes():
-            self.imageExtensions += type.getExtensions()
-
-        # Other useful extensions.  The .pz extension is implicitly
-        # stripped.
-
-        # Model files.
-        self.modelExtensions = [ 'egg', 'bam' ]
-
-        # Text files that are copied (and compressed) to the package
-        # with end-of-line conversion.
-        self.textExtensions = [ 'prc', 'ptf', 'txt', 'cg', 'sha', 'dc', 'xml' ]
-
-        # Binary files that are copied (and compressed) without
-        # processing.
-        self.binaryExtensions = [ 'ttf', 'TTF', 'mid', 'ico', 'cur' ]
-
-        # Files that can have an existence in multiple different
-        # packages simultaneously without conflict.
-        self.nonuniqueExtensions = [ 'prc' ]
-
-        # Files that represent an executable or shared library.
-        if self.platform.startswith('win'):
-            self.executableExtensions = [ 'dll', 'pyd', 'exe' ]
-        elif self.platform.startswith('osx'):
-            self.executableExtensions = [ 'so', 'dylib' ]
-        else:
-            self.executableExtensions = [ 'so' ]
-
-        # Files that represent a Windows "manifest" file.  These files
-        # must be explicitly extracted to disk so the OS can find
-        # them.
-        if self.platform.startswith('win'):
-            self.manifestExtensions = [ 'manifest' ]
-        else:
-            self.manifestExtensions = [ ]
-
-        # Extensions that are automatically remapped by convention.
-        self.remapExtensions = {}
-        if self.platform.startswith('win'):
-            pass
-        elif self.platform.startswith('osx'):
-            self.remapExtensions = {
-                'dll' : 'dylib',
-                'pyd' : 'so',
-                'exe' : ''
-                }
-        else:
-            self.remapExtensions = {
-                'dll' : 'so',
-                'pyd' : 'so',
-                'exe' : ''
-                }
-
-        # Files that should be extracted to disk.
-        self.extractExtensions = self.executableExtensions[:] + self.manifestExtensions[:] + [ 'ico', 'cur' ]
-
-        # Files that indicate a platform dependency.
-        self.platformSpecificExtensions = self.executableExtensions[:]
-
-        # Binary files that are considered uncompressible, and are
-        # copied without compression.
-        self.uncompressibleExtensions = [ 'mp3', 'ogg', 'ogv', 'wav', 'rml', 'rcss', 'otf' ]
-        # wav files are compressible, but p3openal_audio won't load
-        # them compressed.
-        # rml, rcss and otf files must be added here because
-        # libRocket wants to be able to seek in these files.
-
-        # Files which are not to be processed further, but which
-        # should be added exactly byte-for-byte as they are.
-        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
-        # case-insensitive filesystems (like Windows and OSX), put the
-        # lowercase filename here.  Case-sensitive filesystems should
-        # use the correct case.
-        self.excludeSystemFiles = [
-            'kernel32.dll', 'user32.dll', 'wsock32.dll', 'ws2_32.dll',
-            'advapi32.dll', 'opengl32.dll', 'glu32.dll', 'gdi32.dll',
-            'shell32.dll', 'ntdll.dll', 'ws2help.dll', 'rpcrt4.dll',
-            'imm32.dll', 'ddraw.dll', 'shlwapi.dll', 'secur32.dll',
-            'dciman32.dll', 'comdlg32.dll', 'comctl32.dll', 'ole32.dll',
-            'oleaut32.dll', 'gdiplus.dll', 'winmm.dll', 'iphlpapi.dll',
-            'msvcrt.dll', 'kernelbase.dll', 'msimg32.dll', 'msacm32.dll',
-
-            'libsystem.b.dylib', 'libmathcommon.a.dylib', 'libmx.a.dylib',
-            'libstdc++.6.dylib', 'libobjc.a.dylib', 'libauto.dylib',
-            ]
-
-        # As above, but with filename globbing to catch a range of
-        # filenames.
-        self.excludeSystemGlobs = [
-            GlobPattern('d3dx9_*.dll'),
-            GlobPattern('api-ms-win-*.dll'),
-
-            GlobPattern('libGL.so*'),
-            GlobPattern('libGLU.so*'),
-            GlobPattern('libGLcore.so*'),
-            GlobPattern('libGLES*.so*'),
-            GlobPattern('libEGL.so*'),
-            GlobPattern('libX11.so*'),
-            GlobPattern('libXau.so*'),
-            GlobPattern('libXdmcp.so*'),
-            GlobPattern('libxcb*.so*'),
-            GlobPattern('libc.so*'),
-            GlobPattern('libgcc_s.so*'),
-            GlobPattern('libdl.so*'),
-            GlobPattern('libm.so*'),
-            GlobPattern('libnvidia*.so*'),
-            GlobPattern('libpthread.so*'),
-            GlobPattern('libthr.so*'),
-            GlobPattern('ld-linux.so*'),
-            GlobPattern('ld-linux-*.so*'),
-            GlobPattern('librt.so*'),
-            ]
-
-        # A Loader for loading models.
-        self.loader = Loader.Loader(self)
-        self.sfxManagerList = None
-        self.musicManager = None
-
-        # These options will be used when loading models and textures.  By
-        # default we don't load textures beyond the header and don't store
-        # models in the RAM cache in order to conserve on memory usage.
-        opts = LoaderOptions()
-        opts.setFlags(opts.getFlags() | LoaderOptions.LFNoRamCache)
-        opts.setTextureFlags(opts.getTextureFlags() & ~LoaderOptions.TFPreload)
-        self.loaderOptions = opts
-
-        # This is filled in during readPackageDef().
-        self.packageList = []
-
-        # A table of all known packages by name.
-        self.packages = {}
-
-        # A list of PackageEntry objects read from the contents.xml
-        # file.
-        self.contents = {}
-
-    def loadLdconfigCache(self):
-        """ On GNU/Linux, runs ldconfig -p to find out where all the
-        libraries on the system are located.  Assumes that the platform
-        has already been set. """
-
-        if not os.path.isfile('/sbin/ldconfig'):
-            return False
-
-        handle = subprocess.Popen(['/sbin/ldconfig', '-p'], stdout=subprocess.PIPE, universal_newlines=True)
-        out, err = handle.communicate()
-
-        if handle.returncode != 0:
-            self.notify.warning("/sbin/ldconfig -p returned code %d" %(handle.returncode))
-            return False
-
-        for line in out.splitlines():
-            if '=>' not in line:
-                continue
-
-            prefix, location = line.rsplit('=>', 1)
-            prefix = prefix.strip()
-            location = location.strip()
-
-            if not location or not prefix or ' ' not in prefix:
-                self.notify.warning("Ignoring malformed ldconfig -p line: " + line)
-                continue
-
-            lib, opts = prefix.split(' ', 1)
-            if ('x86-64' in opts) != self.platform.endswith('_amd64'):
-                # This entry isn't meant for our architecture.  I think
-                # x86-64 is the only platform where ldconfig supplies
-                # this extra arch string.
-                continue
-
-            self.libraryCache[lib] = Filename.fromOsSpecific(location)
-
-        return True
-
-    def resolveLibrary(self, filename):
-        """ Resolves the given shared library filename along the executable path,
-        or by cross-referencing it with the library cache. """
-
-        path = str(filename)
-
-        if path in self.libraryCache:
-            filename.setFullpath(self.libraryCache[path].getFullpath())
-            return True
-
-        if filename.resolveFilename(self.executablePath):
-            self.libraryCache[path] = Filename(filename)
-            return True
-
-        return False
-
-    def setPlatform(self, platform = None):
-        """ Sets the platform that this Packager will compute for.  On
-        OSX, this can be used to specify the particular architecture
-        we are building; on other platforms, it is probably a mistake
-        to set this.
-
-        You should call this before doing anything else with the
-        Packager.  It's even better to pass the platform string to the
-        constructor.  """
-
-        self.platform = platform or PandaSystem.getPlatform()
-
-        # OSX uses this "arch" string for the otool and lipo commands.
-        self.arch = None
-        if self.platform.startswith('osx_'):
-            self.arch = self.platform[4:]
-
-
-    def setHost(self, host, downloadUrl = None,
-                descriptiveName = None, hostDir = None,
-                mirrors = None):
-        """ Specifies the URL that will ultimately host these
-        contents. """
-
-        if not self.ignoreSetHost:
-            self.host = host
-
-        self.addHost(host, downloadUrl = downloadUrl,
-                     descriptiveName = descriptiveName, hostDir = hostDir,
-                     mirrors = mirrors)
-
-    def addHost(self, host, downloadUrl = None, descriptiveName = None,
-                hostDir = None, mirrors = None):
-        """ Adds a host to the list of known download hosts.  This
-        information will be written into any p3d files that reference
-        this host; this can be used to pre-define the possible mirrors
-        for a given host, for instance.  Returns the newly-created
-        HostEntry object."""
-
-        scheme = URLSpec(host).getScheme()
-        if scheme == 'https' and downloadUrl is None:
-            # If we specified an SSL-protected host URL, but no
-            # explicit download URL, then assume the download URL is
-            # the same, over cleartext.
-            url = URLSpec(host)
-            url.setScheme('http')
-            downloadUrl = url.getUrl()
-
-        he = self.hosts.get(host, None)
-        if he is None:
-            # Define a new host entry
-            he = self.HostEntry(host, downloadUrl = downloadUrl,
-                                descriptiveName = descriptiveName,
-                                hostDir = hostDir, mirrors = mirrors)
-            self.hosts[host] = he
-        else:
-            # Update an existing host entry
-            if downloadUrl is not None:
-                he.downloadUrl = downloadUrl
-            if descriptiveName is not None:
-                he.descriptiveName = descriptiveName
-            if hostDir is not None:
-                he.hostDir = hostDir
-            if mirrors is not None:
-                he.mirrors = mirrors
-
-        return he
-
-    def addAltHost(self, keyword, altHost, origHost = None,
-                   downloadUrl = None, descriptiveName = None,
-                   hostDir = None, mirrors = None):
-        """ Adds an alternate host to any already-known host.  This
-        defines an alternate server that may be contacted, if
-        specified on the HTML page, which hosts a different version of
-        the server's contents.  (This is different from a mirror,
-        which hosts an identical version of the server's contents.)
-        """
-
-        if not origHost:
-            origHost = self.host
-
-        self.addHost(altHost, downloadUrl = downloadUrl,
-                     descriptiveName = descriptiveName, hostDir = hostDir,
-                     mirrors = mirrors)
-        he = self.addHost(origHost)
-        he.altHosts[keyword] = altHost
-
-    def addWindowsSearchPath(self, searchPath, varname):
-        """ Expands $varname, interpreting as a Windows-style search
-        path, and adds its contents to the indicated DSearchPath. """
-
-        path = ExecutionEnvironment.getEnvironmentVariable(varname)
-        if len(path) == 0:
-            if varname not in os.environ:
-                return
-            path = os.environ[varname]
-        for dirname in path.split(';'):
-            dirname = Filename.fromOsSpecific(dirname)
-            if dirname.makeTrueCase():
-                searchPath.appendDirectory(dirname)
-
-    def addPosixSearchPath(self, searchPath, varname):
-        """ Expands $varname, interpreting as a Posix-style search
-        path, and adds its contents to the indicated DSearchPath. """
-
-        path = ExecutionEnvironment.getEnvironmentVariable(varname)
-        if len(path) == 0:
-            if varname not in os.environ:
-                return
-            path = os.environ[varname]
-        for dirname in path.split(':'):
-            dirname = Filename.fromOsSpecific(dirname)
-            if dirname.makeTrueCase():
-                searchPath.appendDirectory(dirname)
-
-    def _ensureExtensions(self):
-        self.knownExtensions = \
-            self.imageExtensions + \
-            self.modelExtensions + \
-            self.textExtensions + \
-            self.binaryExtensions + \
-            self.uncompressibleExtensions + \
-            self.unprocessedExtensions
-
-    def setup(self):
-        """ Call this method to initialize the class after filling in
-        some of the values in the constructor. """
-
-        self._ensureExtensions()
-
-        self.currentPackage = None
-
-        if self.installDir:
-            # If we were given an install directory, we can build
-            # packages as well as plain p3d files, and it all goes
-            # into the specified directory.
-            self.p3dInstallDir = self.installDir
-            self.allowPackages = True
-        else:
-            # If we don't have an actual install directory, we can
-            # only build p3d files, and we drop them into the current
-            # directory.
-            self.p3dInstallDir = '.'
-            self.allowPackages = False
-
-        if not PandaSystem.getPackageVersionString() or not PandaSystem.getPackageHostUrl():
-            raise PackagerError('This script must be run using a version of Panda3D that has been built\nfor distribution.  Try using ppackage.p3d or packp3d.p3d instead.\nIf you are running this script for development purposes, you may also\nset the Config variable panda-package-host-url to the URL you expect\nto download these contents from (for instance, a file:// URL).')
-
-        self.readContentsFile()
-
-    def close(self):
-        """ Called after reading all of the package def files, this
-        performs any final cleanup appropriate. """
-
-        self.writeContentsFile()
-
-    def buildPatches(self, packages):
-        """ Call this after calling close(), to build patches for the
-        indicated packages. """
-
-        # We quietly ignore any p3d applications or solo packages
-        # passed in the packages list; we only build patches for
-        # actual Multifile-based packages.
-        packageNames = []
-        for package in packages:
-            if not package.p3dApplication and not package.solo:
-                packageNames.append(package.packageName)
-
-        if packageNames:
-            from .PatchMaker import PatchMaker
-            pm = PatchMaker(self.installDir)
-            pm.buildPatches(packageNames = packageNames)
-
-    def readPackageDef(self, packageDef, packageNames = None):
-        """ Reads the named .pdef file and constructs the named
-        packages, or all packages if packageNames is None.  Raises an
-        exception if the pdef file is invalid.  Returns the list of
-        packages constructed. """
-
-        self.notify.info('Reading %s' % (packageDef))
-
-        # We use exec to "read" the .pdef file.  This has the nice
-        # side-effect that the user can put arbitrary Python code in
-        # there to control conditional execution, and such.
-
-        # Set up the namespace dictionary for exec.
-        globals = {}
-        globals['__name__'] = packageDef.getBasenameWoExtension()
-        globals['__dir__'] = Filename(packageDef.getDirname()).toOsSpecific()
-        globals['__file__'] = packageDef.toOsSpecific()
-        globals['packageDef'] = packageDef
-
-        globals['platform'] = self.platform
-        globals['packager'] = self
-
-        # We'll stuff all of the predefined functions, and the
-        # predefined classes, in the global dictionary, so the pdef
-        # file can reference them.
-
-        # By convention, the existence of a method of this class named
-        # do_foo(self) is sufficient to define a pdef method call
-        # foo().
-        for methodName in list(self.__class__.__dict__.keys()):
-            if methodName.startswith('do_'):
-                name = methodName[3:]
-                c = func_closure(name)
-                globals[name] = c.generic_func
-
-        globals['p3d'] = class_p3d
-        globals['package'] = class_package
-        globals['solo'] = class_solo
-
-        # Now exec the pdef file.  Assuming there are no syntax
-        # errors, and that the pdef file doesn't contain any really
-        # crazy Python code, all this will do is fill in the
-        # '__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
-        # dictionary causes problems with resolving symbols within a
-        # class scope.  So, we just use one dictionary, the globals.
-        exec(code, globals)
-
-        packages = []
-
-        # Now iterate through the statements and operate on them.
-        statements = globals.get('__statements', [])
-        if not statements:
-            self.notify.info("No packages defined.")
-
-        try:
-            for (lineno, stype, name, args, kw) in statements:
-                if stype == 'class':
-                    if packageNames is None or name in packageNames:
-                        classDef = globals[name]
-                        p3dApplication = (class_p3d in classDef.__bases__)
-                        solo = (class_solo in classDef.__bases__)
-                        self.beginPackage(name, p3dApplication = p3dApplication,
-                                          solo = solo)
-                        statements = classDef.__dict__.get('__statements', [])
-                        if not statements:
-                            self.notify.info("No files added to %s" % (name))
-                        for (lineno, stype, sname, args, kw) in statements:
-                            if stype == 'class':
-                                raise PackagerError('Nested classes not allowed')
-                            self.__evalFunc(sname, args, kw)
-                        package = self.endPackage()
-                        if package is not None:
-                            packages.append(package)
-                        elif packageNames is not None:
-                            # If the name is explicitly specified, this means
-                            # we should abort if the package faild to construct.
-                            raise PackagerError('Failed to construct %s' % name)
-                else:
-                    self.__evalFunc(name, args, kw)
-        except PackagerError:
-            # Append the line number and file name to the exception
-            # error message.
-            inst = sys.exc_info()[1]
-            if not inst.args:
-                inst.args = ('Error',)
-
-            inst.args = (inst.args[0] + ' on line %s of %s' % (lineno, packageDef),)
-            raise
-
-        return packages
-
-    def __evalFunc(self, name, args, kw):
-        """ This is called from readPackageDef(), above, to call the
-        function do_name(*args, **kw), as extracted from the pdef
-        file. """
-
-        funcname = 'do_%s' % (name)
-        func = getattr(self, funcname)
-        try:
-            func(*args, **kw)
-        except OutsideOfPackageError:
-            message = '%s encountered outside of package definition' % (name)
-            raise OutsideOfPackageError(message)
-
-    def __expandTabs(self, line, tabWidth = 8):
-        """ Expands tab characters in the line to 8 spaces. """
-        p = 0
-        while p < len(line):
-            if line[p] == '\t':
-                # Expand a tab.
-                nextStop = ((p + tabWidth) / tabWidth) * tabWidth
-                numSpaces = nextStop - p
-                line = line[:p] + ' ' * numSpaces + line[p + 1:]
-                p = nextStop
-            else:
-                p += 1
-
-        return line
-
-    def __countLeadingWhitespace(self, line):
-        """ Returns the number of leading whitespace characters in the
-        line. """
-
-        line = self.__expandTabs(line)
-        return len(line) - len(line.lstrip())
-
-    def __stripLeadingWhitespace(self, line, whitespaceCount):
-        """ Removes the indicated number of whitespace characters, but
-        no more. """
-
-        line = self.__expandTabs(line)
-        line = line[:whitespaceCount].lstrip() + line[whitespaceCount:]
-        return line
-
-    def __parseArgs(self, words, argList):
-        args = {}
-
-        while len(words) > 1:
-            arg = words[-1]
-            if '=' not in arg:
-                return args
-
-            parameter, value = arg.split('=', 1)
-            parameter = parameter.strip()
-            value = value.strip()
-            if parameter not in argList:
-                message = 'Unknown parameter %s' % (parameter)
-                raise PackagerError(message)
-            if parameter in args:
-                message = 'Duplicate parameter %s' % (parameter)
-                raise PackagerError(message)
-
-            args[parameter] = value
-
-            del words[-1]
-
-
-    def beginPackage(self, packageName, p3dApplication = False,
-                     solo = False):
-        """ Begins a new package specification.  packageName is the
-        basename of the package.  Follow this with a number of calls
-        to file() etc., and close the package with endPackage(). """
-
-        if self.currentPackage:
-            raise PackagerError('unclosed endPackage %s' % (self.currentPackage.packageName))
-
-        package = self.Package(packageName, self)
-        self.currentPackage = package
-
-        package.p3dApplication = p3dApplication
-        package.solo = solo
-
-        if not package.p3dApplication and not self.allowPackages:
-            message = 'Cannot generate packages without an installDir; use -i'
-            raise PackagerError(message)
-
-
-    def endPackage(self):
-        """ Closes the current package specification.  This actually
-        generates the package file.  Returns the finished package,
-        or None if the package failed to close (e.g. missing files). """
-
-        if not self.currentPackage:
-            raise PackagerError('unmatched endPackage')
-
-        package = self.currentPackage
-        package.signParams += self.signParams[:]
-
-        self.currentPackage = None
-        if not package.close():
-            return None
-
-        self.packageList.append(package)
-        self.packages[(package.packageName, package.platform, package.version)] = package
-        self.currentPackage = None
-
-        return package
-
-    def findPackage(self, packageName, platform = None, version = None,
-                    host = None, requires = None):
-        """ Searches for the named package from a previous publish
-        operation along the install search path.
-
-        If requires is not None, it is a list of Package objects that
-        are already required.  The new Package object must be
-        compatible with the existing Packages, or an error is
-        returned.  This is also useful for determining the appropriate
-        package version to choose when a version is not specified.
-
-        Returns the Package object, or None if the package cannot be
-        located. """
-
-        # Is it a package we already have resident?
-        package = self.packages.get((packageName, platform or self.platform, version, host), None)
-        if package:
-            return package
-
-        # Look on the searchlist.
-        for dirname in self.installSearch:
-            package = self.__scanPackageDir(dirname, packageName, platform or self.platform, version, host, requires = requires)
-            if not package:
-                package = self.__scanPackageDir(dirname, packageName, platform, version, host, requires = requires)
-
-            if package and host and package.host != host:
-                # Wrong host.
-                package = None
-
-            if package:
-                break
-
-        if not package:
-            # Query the indicated host.
-            package = self.__findPackageOnHost(packageName, platform or self.platform, version or None, host, requires = requires)
-            if not package:
-                package = self.__findPackageOnHost(packageName, platform, version, host, requires = requires)
-
-        if package:
-            package = self.packages.setdefault((package.packageName, package.platform, package.version, package.host), package)
-            self.packages[(packageName, platform or self.platform, version, host)] = package
-            return package
-
-        return None
-
-    def __scanPackageDir(self, rootDir, packageName, platform, version,
-                         host, requires = None):
-        """ Scans a directory on disk, looking for *.import.xml files
-        that match the indicated packageName and optional version.  If a
-        suitable xml file is found, reads it and returns the assocated
-        Package definition.
-
-        If a version is not specified, and multiple versions are
-        available, the highest-numbered version that matches will be
-        selected.
-        """
-
-        packages = []
-
-        if version:
-            # A specific version package.
-            versionList = [version]
-        else:
-            # An unversioned package, or any old version.
-            versionList = [None, '*']
-
-        for version in versionList:
-            packageDir = Filename(rootDir, packageName)
-            basename = packageName
-
-            if version:
-                # A specific or nonspecific version package.
-                packageDir = Filename(packageDir, version)
-                basename += '.%s' % (version)
-
-            if platform:
-                packageDir = Filename(packageDir, platform)
-                basename += '.%s' % (platform)
-
-            # Actually, the host means little for this search, since we're
-            # only looking in a local directory at this point.
-
-            basename += '.import.xml'
-            filename = Filename(packageDir, basename)
-            filelist = glob.glob(filename.toOsSpecific())
-            if not filelist:
-                # It doesn't exist in the nested directory; try the root
-                # directory.
-                filename = Filename(rootDir, basename)
-                filelist = glob.glob(filename.toOsSpecific())
-
-            for file in filelist:
-                package = self.__readPackageImportDescFile(Filename.fromOsSpecific(file))
-                packages.append(package)
-
-        self.__sortImportPackages(packages)
-        for package in packages:
-            if package and self.__packageIsValid(package, requires, platform):
-                return package
-
-        return None
-
-    def __findPackageOnHost(self, packageName, platform, version, hostUrl, requires = None):
-        appRunner = AppRunnerGlobal.appRunner
-
-        # Make sure we have a fresh version of the contents file.
-        host = self.__getHostInfo(hostUrl)
-        if not host.downloadContentsFile(self.http):
-            return None
-
-        packageInfos = []
-        packageInfo = host.getPackage(packageName, version, platform = platform)
-        if not packageInfo and not version:
-            # No explicit version is specified, first fallback: look
-            # for the compiled-in version.
-            packageInfo = host.getPackage(packageName, PandaSystem.getPackageVersionString(), platform = platform)
-
-        if not packageInfo and not version:
-            # No explicit version is specified, second fallback: get
-            # the highest-numbered version available.
-            packageInfos = host.getPackages(packageName, platform = platform)
-            self.__sortPackageInfos(packageInfos)
-
-        if packageInfo and not packageInfos:
-            packageInfos = [packageInfo]
-
-        for packageInfo in packageInfos:
-            if not packageInfo or not packageInfo.importDescFile:
-                continue
-
-            # Now we've retrieved a PackageInfo.  Get the import desc file
-            # from it.
-            if host.hostDir:
-                filename = Filename(host.hostDir, 'imports/' + packageInfo.importDescFile.basename)
-            else:
-                # We're not running in the packaged environment, so download
-                # to a temporary file instead of the host directory.
-                filename = Filename.temporary('', 'import_' + packageInfo.importDescFile.basename, '.xml')
-
-            if not host.freshenFile(self.http, packageInfo.importDescFile, filename):
-                self.notify.error("Couldn't download import file.")
-                continue
-
-            # Now that we have the import desc file, use it to load one of
-            # our Package objects.
-            package = self.Package('', self)
-            success = package.readImportDescFile(filename)
-
-            if not host.hostDir:
-                # Don't forget to delete the temporary file we created.
-                filename.unlink()
-
-            if success and self.__packageIsValid(package, requires, platform):
-                return package
-
-        # Couldn't find a suitable package.
-        return None
-
-    def __getHostInfo(self, hostUrl = None):
-        """ This shadows appRunner.getHost(), for the purpose of running
-        outside the packaged environment. """
-
-        if not hostUrl:
-            hostUrl = PandaSystem.getPackageHostUrl()
-
-        if AppRunnerGlobal.appRunner:
-            return AppRunnerGlobal.appRunner.getHost(hostUrl)
-
-        host = self.__hostInfos.get(hostUrl, None)
-        if not host:
-            host = HostInfo(hostUrl)
-            self.__hostInfos[hostUrl] = host
-        return host
-
-    def __sortImportPackages(self, packages):
-        """ Given a list of Packages read from *.import.xml filenames,
-        sorts them in reverse order by version, so that the
-        highest-numbered versions appear first in the list. """
-
-        tuples = []
-        for package in packages:
-            version = self.__makeVersionTuple(package.version)
-            tuples.append((version, package))
-        tuples.sort(reverse = True)
-
-        return [t[1] for t in tuples]
-
-    def __sortPackageInfos(self, packages):
-        """ Given a list of PackageInfos retrieved from a Host, sorts
-        them in reverse order by version, so that the highest-numbered
-        versions appear first in the list. """
-
-        tuples = []
-        for package in packages:
-            version = self.__makeVersionTuple(package.packageVersion)
-            tuples.append((version, package))
-        tuples.sort(reverse = True)
-
-        return [t[1] for t in tuples]
-
-    def __makeVersionTuple(self, version):
-        """ Converts a version string into a tuple for sorting, by
-        separating out numbers into separate numeric fields, so that
-        version numbers sort numerically where appropriate. """
-
-        if not version:
-            return ('',)
-
-        words = []
-        p = 0
-        while p < len(version):
-            # Scan to the first digit.
-            w = ''
-            while p < len(version) and not version[p].isdigit():
-                w += version[p]
-                p += 1
-            words.append(w)
-
-            # Scan to the end of the string of digits.
-            w = ''
-            while p < len(version) and version[p].isdigit():
-                w += version[p]
-                p += 1
-            if w:
-                words.append(int(w))
-
-        return tuple(words)
-
-    def __packageIsValid(self, package, requires, platform):
-        """ Returns true if the package is valid, meaning it can be
-        imported without conflicts with existing packages already
-        required (such as different versions of panda3d). """
-
-        if package.platform and package.platform != platform:
-            # Incorrect platform.
-            return False
-
-        if not requires:
-            # No other restrictions.
-            return True
-
-        # Really, we only check the panda3d package.  The other
-        # packages will list this as a dependency, and this is all
-        # that matters.
-
-        panda1 = self.__findPackageInRequires('panda3d', [package] + package.requires)
-        panda2 = self.__findPackageInRequires('panda3d', requires)
-
-        if not panda1 or not panda2:
-            return True
-
-        if panda1.version == panda2.version:
-            return True
-
-        print('Rejecting package %s, version "%s": depends on %s, version "%s" instead of version "%s"' % (
-            package.packageName, package.version,
-            panda1.packageName, panda1.version, panda2.version))
-        return False
-
-    def __findPackageInRequires(self, packageName, list):
-        """ Returns the first package with the indicated name in the
-        list of packages, or in the list of packages required by the
-        packages in the list. """
-
-        for package in list:
-            if package.packageName == packageName:
-                return package
-            p2 = self.__findPackageInRequires(packageName, package.requires)
-            if p2:
-                return p2
-
-        return None
-
-    def __readPackageImportDescFile(self, filename):
-        """ Reads the named xml file as a Package, and returns it if
-        valid, or None otherwise. """
-
-        package = self.Package('', self)
-        if package.readImportDescFile(filename):
-            return package
-
-        return None
-
-    def do_setVer(self, value):
-        """ Sets an explicit set_ver number for the package, as a tuple
-        of integers, or as a string of dot-separated integers. """
-
-        self.currentPackage.packageSetVer = SeqValue(value)
-
-    def do_config(self, **kw):
-        """ Called with any number of keyword parameters.  For each
-        keyword parameter, sets the corresponding p3d config variable
-        to the given value.  This will be written into the
-        p3d_info.xml file at the top of the application, or to the
-        package desc file for a package file. """
-
-        if not self.currentPackage:
-            raise OutsideOfPackageError
-
-        for keyword, value in list(kw.items()):
-            self.currentPackage.configs[keyword] = value
-
-    def do_require(self, *args, **kw):
-        """ Indicates a dependency on the named package(s), supplied
-        as a name.
-
-        Attempts to install this package will implicitly install the
-        named package also.  Files already included in the named
-        package will be omitted from this one when building it. """
-
-        self.requirePackagesNamed(args, **kw)
-
-    def requirePackagesNamed(self, names, version = None, host = None):
-        """ Indicates a dependency on the named package(s), supplied
-        as a name.
-
-        Attempts to install this package will implicitly install the
-        named package also.  Files already included in the named
-        package will be omitted from this one when building it. """
-
-        if not self.currentPackage:
-            raise OutsideOfPackageError
-
-        for packageName in names:
-            # A special case when requiring the "panda3d" package.  We
-            # supply the version number which we've been compiled with
-            # as a default.
-            pversion = version
-            phost = host
-            if packageName == 'panda3d':
-                if not pversion:
-                    pversion = PandaSystem.getPackageVersionString()
-                if not phost:
-                    phost = PandaSystem.getPackageHostUrl()
-
-            package = self.findPackage(packageName, version = pversion, host = phost,
-                                       requires = self.currentPackage.requires)
-            if not package:
-                message = 'Unknown package %s, version "%s"' % (packageName, version)
-                self.notify.warning(message)
-                self.currentPackage.missingPackages.append((packageName, pversion))
-                continue
-
-            self.requirePackage(package)
-
-    def requirePackage(self, package):
-        """ Indicates a dependency on the indicated package, supplied
-        as a Package object.
-
-        Attempts to install this package will implicitly install the
-        named package also.  Files already included in the named
-        package will be omitted from this one. """
-
-        if not self.currentPackage:
-            raise OutsideOfPackageError
-
-        # A special case when requiring the "panda3d" package.  We
-        # complain if the version number doesn't match what we've been
-        # compiled with.
-        if package.packageName == 'panda3d':
-            if package.version != PandaSystem.getPackageVersionString():
-                self.notify.warning("Requiring panda3d version %s, which does not match the current build of Panda, which is version %s." % (package.version, PandaSystem.getPackageVersionString()))
-            elif package.host != PandaSystem.getPackageHostUrl():
-                self.notify.warning("Requiring panda3d host %s, which does not match the current build of Panda, which is host %s." % (package.host, PandaSystem.getPackageHostUrl()))
-
-        self.currentPackage.requirePackage(package)
-
-    def do_module(self, *args, **kw):
-        """ Adds the indicated Python module(s) to the current package. """
-        self.addModule(args, **kw)
-
-    def addModule(self, moduleNames, newName = None, filename = None, required = False):
-        if not self.currentPackage:
-            raise OutsideOfPackageError
-
-        if (newName or filename) and len(moduleNames) != 1:
-            raise PackagerError('Cannot specify newName with multiple modules')
-
-        if required:
-            self.currentPackage.requiredModules += moduleNames
-
-        for moduleName in moduleNames:
-            self.currentPackage.freezer.addModule(moduleName, newName = newName, filename = filename)
-
-    def do_excludeModule(self, *args):
-        """ Marks the indicated Python module as not to be included. """
-
-        if not self.currentPackage:
-            raise OutsideOfPackageError
-
-        for moduleName in args:
-            self.currentPackage.freezer.excludeModule(moduleName)
-
-    def do_main(self, filename):
-        """ Includes the indicated file as __main__ module of the application.
-        Also updates mainModule to point to this module. """
-
-        self.addModule(['__main__'], '__main__', filename, required = True)
-        self.currentPackage.mainModule = ('__main__', '__main__')
-
-    def do_mainModule(self, moduleName, newName = None, filename = None):
-        """ Names the indicated module as the "main" module of the
-        application or exe.  In most cases, you will want to use main()
-        instead. """
-
-        if not self.currentPackage:
-            raise OutsideOfPackageError
-
-        if self.currentPackage.mainModule and self.currentPackage.mainModule[0] != moduleName:
-            self.notify.warning("Replacing mainModule %s with %s" % (
-                self.currentPackage.mainModule[0], moduleName))
-
-        if not newName:
-            newName = moduleName
-
-        if filename:
-            filename = Filename(filename)
-            newFilename = Filename('/'.join(moduleName.split('.')))
-            newFilename.setExtension(filename.getExtension())
-            self.currentPackage.addFile(
-                filename, newName = str(newFilename),
-                explicit = True, extract = True, required = True)
-
-        self.currentPackage.mainModule = (moduleName, newName)
-
-    def do_sign(self, certificate, chain = None, pkey = None, password = None):
-        """ Signs the resulting p3d file (or package multifile) with
-        the indicated certificate.  If needed, the chain file should
-        contain the list of additional certificate authorities needed
-        to validate the signing certificate.  The pkey file should
-        contain the private key.
-
-        It is also legal for the certificate file to contain the chain
-        and private key embedded within it.
-
-        If the private key is encrypted, the password should be
-        supplied. """
-
-        self.currentPackage.signParams.append((certificate, chain, pkey, password))
-
-    def do_setupPanda3D(self, p3dpythonName=None, p3dpythonwName=None):
-        """ A special convenience command that adds the minimum
-        startup modules for a panda3d package, intended for developers
-        producing their own custom panda3d for download.  Should be
-        called before any other Python modules are named. """
-
-        # 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()
-
-        self.do_file('panda3d/core.pyd', newDir='panda3d')
-
-        # This is the key Python module that is imported at runtime to
-        # start an application running.
-        self.do_module('direct.p3d.AppRunner')
-
-        # This is the main program that drives the runtime Python.  It
-        # 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'):
-            # On Mac, we package up a P3DPython.app bundle.  This
-            # includes specifications in the plist file to avoid
-            # creating a dock icon and stuff.
-
-            resources = []
-
-            # Find p3dpython.plist in the direct source tree.
-            import direct
-            plist = Filename(direct.__path__[0], 'plugin/p3dpython.plist')
-
-##             # Find panda3d.icns in the models tree.
-##             filename = Filename('plugin_images/panda3d.icns')
-##             found = filename.resolveFilename(getModelPath().getValue())
-##             if not found:
-##                 found = filename.resolveFilename("models")
-##             if found:
-##                 resources.append(filename)
-
-            self.do_makeBundle('P3DPython.app', plist, executable = 'p3dpython',
-                               resources = resources, dependencyDir = '')
-
-        else:
-            # Anywhere else, we just ship the executable file p3dpython.exe.
-            if p3dpythonName is None:
-                p3dpythonName = 'p3dpython'
-            else:
-                self.do_config(p3dpython_name=p3dpythonName)
-
-            if self.platform.startswith('win'):
-                self.do_file('p3dpython.exe', newName=p3dpythonName+'.exe')
-            else:
-                self.do_file('p3dpython.exe', newName=p3dpythonName)
-
-            # The "Windows" executable appends a 'w' to whatever name is used
-            # above, unless an override name is explicitly specified.
-            if self.platform.startswith('win'):
-                if p3dpythonwName is None:
-                    p3dpythonwName = p3dpythonName+'w'
-                else:
-                    self.do_config(p3dpythonw_name=p3dpythonwName)
-
-                if self.platform.startswith('win'):
-                    self.do_file('p3dpythonw.exe', newName=p3dpythonwName+'.exe')
-                else:
-                    self.do_file('p3dpythonw.exe', newName=p3dpythonwName)
-
-        self.do_file('libp3dpython.dll')
-
-    def do_freeze(self, filename, compileToExe = False):
-        """ Freezes all of the current Python code into either an
-        executable (if compileToExe is true) or a dynamic library (if
-        it is false).  The resulting compiled binary is added to the
-        current package under the indicated filename.  The filename
-        should not include an extension; that will be added. """
-
-        if not self.currentPackage:
-            raise OutsideOfPackageError
-
-        package = self.currentPackage
-        freezer = package.freezer
-
-        if package.mainModule and not compileToExe:
-            self.notify.warning("Ignoring main_module for dll %s" % (filename))
-            package.mainModule = None
-        if not package.mainModule and compileToExe:
-            message = "No main_module specified for exe %s" % (filename)
-            raise PackagerError(message)
-
-        if package.mainModule:
-            moduleName, newName = package.mainModule
-            if compileToExe:
-                # If we're producing an exe, the main module must
-                # be called "__main__".
-                newName = '__main__'
-                package.mainModule = (moduleName, newName)
-
-            if newName not in freezer.modules:
-                freezer.addModule(moduleName, newName = newName)
-            else:
-                freezer.modules[newName] = freezer.modules[moduleName]
-        freezer.done(addStartupModules = compileToExe)
-
-        dirname = ''
-        basename = filename
-        if '/' in basename:
-            dirname, basename = filename.rsplit('/', 1)
-            dirname += '/'
-
-        basename = freezer.generateCode(basename, compileToExe = compileToExe)
-
-        package.addFile(Filename(basename), newName = dirname + basename,
-                        deleteTemp = True, explicit = True, extract = True)
-        package.addExtensionModules()
-        if not package.platform:
-            package.platform = self.platform
-
-        # Reset the freezer for more Python files.
-        freezer.reset()
-        package.mainModule = None
-
-    def do_makeBundle(self, bundleName, plist, executable = None,
-                      resources = None, dependencyDir = None):
-        """ Constructs a minimal OSX "bundle" consisting of an
-        executable and a plist file, with optional resource files
-        (such as icons), and adds it to the package under the given
-        name. """
-
-        contents = bundleName + '/Contents'
-
-        self.addFiles([plist], newName = contents + '/Info.plist',
-                      extract = True)
-        if executable:
-            basename = Filename(executable).getBasename()
-            self.addFiles([executable], newName = contents + '/MacOS/' + basename,
-                          extract = True, executable = True, dependencyDir = dependencyDir)
-        if resources:
-            self.addFiles(resources, newDir = contents + '/Resources',
-                          extract = True, dependencyDir = dependencyDir)
-
-
-
-    def do_file(self, *args, **kw):
-        """ Adds the indicated file or files to the current package.
-        See addFiles(). """
-
-        self.addFiles(args, **kw)
-
-    def addFiles(self, filenames, text = None, newName = None,
-                 newDir = None, extract = None, executable = None,
-                 deleteTemp = False, literal = False,
-                 dependencyDir = None, required = False):
-
-        """ Adds the indicated arbitrary files to the current package.
-
-        filenames is a list of Filename or string objects, and each
-        may include shell globbing characters.
-
-        Each file is placed in the named directory, or the toplevel
-        directory if no directory is specified.
-
-        Certain special behavior is invoked based on the filename
-        extension.  For instance, .py files may be automatically
-        compiled and stored as Python modules.
-
-        If newDir is not None, it specifies the directory in which the
-        file should be placed.  In this case, all files matched by the
-        filename expression are placed in the named directory.
-
-        If newName is not None, it specifies a new filename.  In this
-        case, newDir is ignored, and the filename expression must
-        match only one file.
-
-        If newName and newDir are both None, the file is placed in the
-        toplevel directory, regardless of its source directory.
-
-        If text is nonempty, it contains the text of the file.  In
-        this case, the filename is not read, but the supplied text is
-        used instead.
-
-        If extract is true, the file is explicitly extracted at
-        runtime.
-
-        If executable is true, the file is marked as an executable
-        filename, for special treatment.
-
-        If deleteTemp is true, the file is a temporary file and will
-        be deleted after its contents are copied to the package.
-
-        If literal is true, then the file extension will be respected
-        exactly as it appears, and glob characters will not be
-        expanded.  If this is false, then .dll or .exe files will be
-        renamed to .dylib and no extension on OSX (or .so on Linux);
-        and glob characters will be expanded.
-
-        If required is true, then the file is marked a vital part of
-        the package.  The package will not be built if this file
-        somehow cannot be added to the package.
-
-        """
-
-        if not self.currentPackage:
-            raise OutsideOfPackageError
-
-        files = []
-        explicit = True
-
-        for filename in filenames:
-            filename = Filename(filename)
-
-            if literal:
-                thisFiles = [filename.toOsSpecific()]
-
-            else:
-                ext = filename.getExtension()
-
-                # A special case, since OSX and Linux don't have a
-                # standard extension for program files.
-                if executable is None and ext == 'exe':
-                    executable = True
-
-                newExt = self.remapExtensions.get(ext, None)
-                if newExt is not None:
-                    filename.setExtension(newExt)
-
-                thisFiles = glob.glob(filename.toOsSpecific())
-                if not thisFiles:
-                    thisFiles = [filename.toOsSpecific()]
-
-                if newExt == 'dll' or (ext == 'dll' and newExt is None):
-                    # Go through the dsoFilename interface on Windows,
-                    # to insert a _d if we are running on a debug
-                    # build.
-                    dllFilename = Filename(filename)
-                    dllFilename.setExtension('so')
-                    dllFilename = Filename.dsoFilename(str(dllFilename))
-                    if dllFilename != filename:
-                        thisFiles = glob.glob(filename.toOsSpecific())
-                        if not thisFiles:
-                            # We have to resolve this filename to
-                            # determine if it's a _d or not.
-                            if self.resolveLibrary(dllFilename):
-                                thisFiles = [dllFilename.toOsSpecific()]
-                            else:
-                                thisFiles = [filename.toOsSpecific()]
-
-            if len(thisFiles) > 1:
-                explicit = False
-            files += thisFiles
-
-        prefix = ''
-        if newDir is not None:
-            prefix = str(Filename(newDir))
-            if prefix and prefix[-1] != '/':
-                prefix += '/'
-
-        if newName:
-            if len(files) != 1:
-                message = 'Cannot install multiple files on target filename %s' % (newName)
-                raise PackagerError(message)
-
-        if text:
-            if len(files) != 1:
-                message = 'Cannot install text to multiple files'
-                raise PackagerError(message)
-            if not newName:
-                newName = str(filenames[0])
-
-        for filename in files:
-            filename = Filename.fromOsSpecific(filename)
-            basename = filename.getBasename()
-            name = newName
-            if not name:
-                name = prefix + basename
-
-            self.currentPackage.addFile(
-                filename, newName = name, extract = extract,
-                explicit = explicit, executable = executable,
-                text = text, deleteTemp = deleteTemp,
-                dependencyDir = dependencyDir, required = required)
-
-    def do_exclude(self, filename):
-        """ Marks the indicated filename as not to be included.  The
-        filename may include shell globbing characters, and may or may
-        not include a dirname.  (If it does not include a dirname, it
-        refers to any file with the given basename from any
-        directory.)"""
-
-        if not self.currentPackage:
-            raise OutsideOfPackageError
-
-        filename = Filename(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):
-
-        """ Adds the indicated directory hierarchy to the current
-        package.  The directory hierarchy is walked recursively, and
-        all files that match a known extension are added to the package.
-
-        newDir specifies the directory name within the package which
-        the contents of the named directory should be installed to.
-        If it is omitted, the contents of the named directory are
-        installed to the root of the package.
-
-        If unprocessed is false (the default), bam files are loaded and
-        scanned for textures, and these texture paths within the bam
-        files are manipulated to point to the new paths within the
-        package.  If unprocessed is true, this operation is bypassed,
-        and bam files are packed exactly as they are.
-        """
-
-        if not self.currentPackage:
-            raise OutsideOfPackageError
-
-        dirname = Filename(dirname)
-        if not newDir:
-            newDir = ''
-
-        # Adding the directory to sys.path is a cheesy way to help the
-        # modulefinder find it.
-        sys.path.append(dirname.toOsSpecific())
-        self.__recurseDir(dirname, newDir, unprocessed = unprocessed)
-
-    def __recurseDir(self, filename, newName, unprocessed = None, packageTree = None):
-        if filename.isDirectory():
-            # It's a directory name.  Recurse.
-            prefix = newName
-            if prefix and prefix[-1] != '/':
-                prefix += '/'
-
-            # First check if this is a Python package tree.  If so, add it
-            # implicitly as a module.
-            dirList = vfs.scanDirectory(filename)
-            for subfile in dirList:
-                filename = subfile.getFilename()
-                if filename.getBasename() == '__init__.py':
-                    moduleName = newName.replace("/", ".")
-                    self.addModule([moduleName], filename=filename)
-
-            for subfile in dirList:
-                filename = subfile.getFilename()
-                self.__recurseDir(filename, prefix + filename.getBasename(),
-                                  unprocessed = unprocessed)
-            return
-        elif not filename.exists():
-            # It doesn't exist.  Perhaps it's a virtual file.  Ignore it.
-            return
-
-        # It's a file name.  Add it.
-        ext = filename.getExtension()
-        if ext == 'py':
-            self.currentPackage.addFile(filename, newName = newName,
-                                        explicit = False, unprocessed = unprocessed)
-        else:
-            if ext == 'pz' or ext == 'gz':
-                # Strip off an implicit .pz extension.
-                newFilename = Filename(filename)
-                newFilename.setExtension('')
-                newFilename = Filename(str(newFilename))
-                ext = newFilename.getExtension()
-
-            if ext in self.knownExtensions:
-                if ext in self.textExtensions:
-                    filename.setText()
-                else:
-                    filename.setBinary()
-                self.currentPackage.addFile(filename, newName = newName,
-                                            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):
-        """ Reads the contents.xml file at the beginning of
-        processing. """
-
-        self.hosts = {}
-        # Since we've blown away the self.hosts map, we have to make
-        # sure that our own host at least is added to the map.
-        self.addHost(self.host)
-
-        self.maxAge = 0
-        self.contentsSeq = SeqValue()
-        self.contents = {}
-        self.contentsChanged = False
-
-        if not self.allowPackages:
-            # Don't bother.
-            return
-
-        contentsFilename = Filename(self.installDir, 'contents.xml')
-        doc = TiXmlDocument(contentsFilename.toOsSpecific())
-        if not doc.LoadFile():
-            # Couldn't read file.
-            return
-
-        xcontents = doc.FirstChildElement('contents')
-        if xcontents:
-            maxAge = xcontents.Attribute('max_age')
-            if maxAge:
-                self.maxAge = int(maxAge)
-
-            self.contentsSeq.loadXml(xcontents)
-
-            xhost = xcontents.FirstChildElement('host')
-            if xhost:
-                he = self.HostEntry()
-                he.loadXml(xhost, self)
-                self.hosts[he.url] = he
-                self.host = he.url
-
-            xpackage = xcontents.FirstChildElement('package')
-            while xpackage:
-                pe = self.PackageEntry()
-                pe.loadXml(xpackage)
-                self.contents[pe.getKey()] = pe
-                xpackage = xpackage.NextSiblingElement('package')
-
-    def writeContentsFile(self):
-        """ Rewrites the contents.xml file at the end of
-        processing. """
-
-        if not self.contentsChanged:
-            # No need to rewrite.
-            return
-
-        contentsFilename = Filename(self.installDir, 'contents.xml')
-        doc = TiXmlDocument(contentsFilename.toOsSpecific())
-        decl = TiXmlDeclaration("1.0", "utf-8", "")
-        doc.InsertEndChild(decl)
-
-        xcontents = TiXmlElement('contents')
-        if self.maxAge:
-            xcontents.SetAttribute('max_age', str(self.maxAge))
-
-        self.contentsSeq += 1
-        self.contentsSeq.storeXml(xcontents)
-
-        if self.host:
-            he = self.hosts.get(self.host, None)
-            if he:
-                xhost = he.makeXml(packager = self)
-                xcontents.InsertEndChild(xhost)
-
-        contents = sorted(self.contents.items())
-        for key, pe in contents:
-            xpackage = pe.makeXml()
-            xcontents.InsertEndChild(xpackage)
-
-        doc.InsertEndChild(xcontents)
-        doc.SaveFile()
-
-
-# The following class and function definitions represent a few sneaky
-# Python tricks to allow the pdef syntax to contain the pseudo-Python
-# code they do.  These tricks bind the function and class definitions
-# into a bit table as they are parsed from the pdef file, so we can
-# walk through that table later and perform the operations requested
-# in order.
-
-class metaclass_def(type):
-    """ A metaclass is invoked by Python when the class definition is
-    read, for instance to define a child class.  By defining a
-    metaclass for class_p3d and class_package, we can get a callback
-    when we encounter "class foo(p3d)" in the pdef file.  The callback
-    actually happens after all of the code within the class scope has
-    been parsed first. """
-
-    def __new__(self, name, bases, dict):
-
-        # At the point of the callback, now, "name" is the name of the
-        # class we are instantiating, "bases" is the list of parent
-        # classes, and "dict" is the class dictionary we have just
-        # parsed.
-
-        # If "dict" contains __metaclass__, then we must be parsing
-        # class_p3d or class_ppackage, below--skip it.  But if it
-        # doesn't contain __metaclass__, then we must be parsing
-        # "class foo(p3d)" (or whatever) from the pdef file.
-
-        if '__metaclass__' not in dict:
-            # Get the context in which this class was created
-            # (presumably, the module scope) out of the stack frame.
-            frame = sys._getframe(1)
-            mdict = frame.f_locals
-            lineno = frame.f_lineno
-
-            # Store the class name on a statements list in that
-            # context, so we can later resolve the class names in
-            # the order they appeared in the file.
-            mdict.setdefault('__statements', []).append((lineno, 'class', name, None, None))
-
-        return type.__new__(self, name, bases, dict)
-
-# Define these dynamically to stay compatible with Python 2 and 3.
-class_p3d = metaclass_def(str('class_p3d'), (), {})
-class_package = metaclass_def(str('class_package'), (), {})
-class_solo = metaclass_def(str('class_solo'), (), {})
-
-class func_closure:
-
-    """ This class is used to create a closure on the function name,
-    and also allows the *args, **kw syntax.  In Python, the lambda
-    syntax, used with default parameters, is used more often to create
-    a closure (that is, a binding of one or more variables into a
-    callable object), but that syntax doesn't work with **kw.
-    Fortunately, a class method is also a form of a closure, because
-    it binds self; and this doesn't have any syntax problems with
-    **kw. """
-
-    def __init__(self, name):
-        self.name = name
-
-    def generic_func(self, *args, **kw):
-        """ This method is bound to all the functions that might be
-        called from the pdef file.  It's a special function; when it is
-        called, it does nothing but store its name and arguments in the
-        caller's local scope, where they can be pulled out later. """
-
-        # Get the context in which this function was called (presumably,
-        # the class dictionary) out of the stack frame.
-        frame = sys._getframe(1)
-        cldict = frame.f_locals
-        lineno = frame.f_lineno
-
-        # Store the function on a statements list in that context, so we
-        # can later walk through the function calls for each class.
-        cldict.setdefault('__statements', []).append((lineno, 'func', self.name, args, kw))

+ 0 - 814
direct/src/p3d/PatchMaker.py

@@ -1,814 +0,0 @@
-__all__ = ["PatchMaker"]
-
-from direct.p3d.FileSpec import FileSpec
-from direct.p3d.SeqValue import SeqValue
-from panda3d.core import *
-import copy
-
-class PatchMaker:
-    """ This class will operate on an existing package install
-    directory, as generated by the Packager, and create patchfiles
-    between versions as needed.  It is also used at runtime, to apply
-    the downloaded patches. """
-
-    class PackageVersion:
-        """ A specific patch version of a package.  This is not just
-        the package's "version" string; it also corresponds to the
-        particular patch version, which increments independently of
-        the "version". """
-
-        def __init__(self, packageName, platform, version, hostUrl, file):
-            self.packageName = packageName
-            self.platform = platform
-            self.version = version
-            self.hostUrl = hostUrl
-            self.file = file
-            self.printName = None
-
-            # The Package object that produces this version, if this
-            # is the current, base, or top form, respectively.
-            self.packageCurrent = None
-            self.packageBase = None
-            self.packageTop = None
-
-            # A list of patchfiles that can produce this version.
-            self.fromPatches = []
-
-            # A list of patchfiles that can start from this version.
-            self.toPatches = []
-
-            # A temporary file for re-creating the archive file for
-            # this version.
-            self.tempFile = None
-
-        def cleanup(self):
-            if self.tempFile:
-                self.tempFile.unlink()
-
-        def getPatchChain(self, startPv, alreadyVisited = []):
-            """ Returns a list of patches that, when applied in
-            sequence to the indicated PackageVersion object, will
-            produce this PackageVersion object.  Returns None if no
-            chain can be found. """
-
-            if self is startPv:
-                # We're already here.  A zero-length patch chain is
-                # therefore the answer.
-                return []
-            if self in alreadyVisited:
-                # We've already been here; this is a loop.  Avoid
-                # infinite recursion.
-                return None
-
-            alreadyVisited = alreadyVisited[:]
-            alreadyVisited.append(self)
-
-            bestPatchChain = None
-            for patchfile in self.fromPatches:
-                fromPv = patchfile.fromPv
-                patchChain = fromPv.getPatchChain(startPv, alreadyVisited)
-                if patchChain is not None:
-                    # There's a path through this patchfile.
-                    patchChain = patchChain + [patchfile]
-                    if bestPatchChain is None or len(patchChain) < len(bestPatchChain):
-                        bestPatchChain = patchChain
-
-            # Return the shortest path found, or None if there were no
-            # paths found.
-            return bestPatchChain
-
-        def getRecreateFilePlan(self, alreadyVisited = []):
-            """ Returns the tuple (startFile, startPv, plan),
-            describing how to recreate the archive file for this
-            version.  startFile and startPv is the Filename and
-            packageVersion of the file to start with, and plan is a
-            list of tuples (patchfile, pv), listing the patches to
-            apply in sequence, and the packageVersion object
-            associated with each patch.  Returns (None, None, None) if
-            there is no way to recreate this archive file.  """
-
-            if self.tempFile:
-                return (self.tempFile, self, [])
-
-            if self in alreadyVisited:
-                # We've already been here; this is a loop.  Avoid
-                # infinite recursion.
-                return (None, None, None)
-
-            alreadyVisited = alreadyVisited[:]
-            alreadyVisited.append(self)
-
-            if self.packageCurrent:
-                # This PackageVersion instance represents the current
-                # version of some package.
-                package = self.packageCurrent
-                return (Filename(package.packageDir, package.compressedFilename), self, [])
-
-            if self.packageBase:
-                # This PackageVersion instance represents the base
-                # (oldest) version of some package.
-                package = self.packageBase
-                return (Filename(package.packageDir, package.baseFile.filename  + '.pz'), self, [])
-
-            # We'll need to re-create the file.
-            bestPlan = None
-            bestStartFile = None
-            bestStartPv = None
-            for patchfile in self.fromPatches:
-                fromPv = patchfile.fromPv
-                startFile, startPv, plan = fromPv.getRecreateFilePlan(alreadyVisited)
-                if plan is not None:
-                    # There's a path through this patchfile.
-                    plan = plan + [(patchfile, self)]
-                    if bestPlan is None or len(plan) < len(bestPlan):
-                        bestPlan = plan
-                        bestStartFile = startFile
-                        bestStartPv = startPv
-
-            # Return the shortest path found, or None if there were no
-            # paths found.
-            return (bestStartFile, bestStartPv, bestPlan)
-
-        def getFile(self):
-            """ Returns the Filename of the archive file associated
-            with this version.  If the file doesn't actually exist on
-            disk, a temporary file will be created.  Returns None if
-            the file can't be recreated. """
-
-            startFile, startPv, plan = self.getRecreateFilePlan()
-
-            if startFile.getExtension() in ('pz', 'gz'):
-                # If the starting file is compressed, we have to
-                # decompress it first.
-                assert startPv.tempFile is None
-                startPv.tempFile = Filename.temporary('', 'patch_')
-                if not decompressFile(startFile, startPv.tempFile):
-                    # Failure trying to decompress the file.
-                    return None
-                startFile = startPv.tempFile
-
-            if not plan:
-                # If plan is a zero-length list, we're already
-                # here--return startFile.  If plan is None, there's no
-                # solution, and startFile is None.  In either case, we
-                # can return startFile.
-                return startFile
-
-            # If plan is a non-empty list, we have to walk the list to
-            # apply the patch plan.
-            prevFile = startFile
-            for patchfile, pv in plan:
-                fromPv = patchfile.fromPv
-                patchFilename = Filename(patchfile.package.packageDir, patchfile.file.filename)
-                result = self.applyPatch(prevFile, patchFilename)
-                if not result:
-                    # Failure trying to re-create the file.
-                    return None
-
-                pv.tempFile = result
-                prevFile = result
-
-            # Successfully patched.
-            assert pv is self and prevFile is self.tempFile
-            return prevFile
-
-        def applyPatch(self, origFile, patchFilename):
-            """ Applies the named patch to the indicated original
-            file, storing the results in a temporary file, and returns
-            that temporary Filename.  Returns None on failure. """
-
-            result = Filename.temporary('', 'patch_')
-            p = Patchfile()
-            if not p.apply(patchFilename, origFile, result):
-                print("Internal patching failed: %s" % (patchFilename))
-                return None
-
-            return result
-
-        def getNext(self, package):
-            """ Gets the next patch in the chain towards this
-            package. """
-            for patch in self.toPatches:
-                if patch.packageName == package.packageName and \
-                   patch.platform == package.platform and \
-                   patch.version == package.version and \
-                   patch.hostUrl == package.hostUrl:
-                    return patch.toPv
-
-            return None
-
-    class Patchfile:
-        """ A single patchfile for a package. """
-
-        def __init__(self, package):
-            self.package = package
-            self.packageName = package.packageName
-            self.platform = package.platform
-            self.version = package.version
-            self.hostUrl = None
-
-            # FileSpec for the patchfile itself
-            self.file = None
-
-            # FileSpec for the package file that the patch is applied to
-            self.sourceFile = None
-
-            # FileSpec for the package file that the patch generates
-            self.targetFile = None
-
-            # The PackageVersion corresponding to our sourceFile
-            self.fromPv = None
-
-            # The PackageVersion corresponding to our targetFile
-            self.toPv = None
-
-        def getSourceKey(self):
-            """ Returns the key for locating the package that this
-            patchfile can be applied to. """
-            return (self.packageName, self.platform, self.version, self.hostUrl, self.sourceFile)
-
-        def getTargetKey(self):
-            """ Returns the key for locating the package that this
-            patchfile will generate. """
-            return (self.packageName, self.platform, self.version, self.hostUrl, self.targetFile)
-
-        def fromFile(self, packageDir, patchFilename, sourceFile, targetFile):
-            """ Creates the data structures from an existing patchfile
-            on disk. """
-
-            self.file = FileSpec()
-            self.file.fromFile(packageDir, patchFilename)
-            self.sourceFile = sourceFile
-            self.targetFile = targetFile
-
-        def loadXml(self, xpatch):
-            """ Reads the data structures from an xml file. """
-
-            self.packageName = xpatch.Attribute('name') or self.packageName
-            self.platform = xpatch.Attribute('platform') or self.platform
-            self.version = xpatch.Attribute('version') or self.version
-            self.hostUrl = xpatch.Attribute('host') or self.hostUrl
-
-            self.file = FileSpec()
-            self.file.loadXml(xpatch)
-
-            xsource = xpatch.FirstChildElement('source')
-            if xsource:
-                self.sourceFile = FileSpec()
-                self.sourceFile.loadXml(xsource)
-
-            xtarget = xpatch.FirstChildElement('target')
-            if xtarget:
-                self.targetFile = FileSpec()
-                self.targetFile.loadXml(xtarget)
-
-        def makeXml(self, package):
-            xpatch = TiXmlElement('patch')
-
-            if self.packageName != package.packageName:
-                xpatch.SetAttribute('name', self.packageName)
-            if self.platform != package.platform:
-                xpatch.SetAttribute('platform', self.platform)
-            if self.version != package.version:
-                xpatch.SetAttribute('version', self.version)
-            if self.hostUrl != package.hostUrl:
-                xpatch.SetAttribute('host', self.hostUrl)
-
-            self.file.storeXml(xpatch)
-
-            xsource = TiXmlElement('source')
-            self.sourceFile.storeMiniXml(xsource)
-            xpatch.InsertEndChild(xsource)
-
-            xtarget = TiXmlElement('target')
-            self.targetFile.storeMiniXml(xtarget)
-            xpatch.InsertEndChild(xtarget)
-
-            return xpatch
-
-    class Package:
-        """ This is a particular package.  This contains all of the
-        information needed to reconstruct the package's desc file. """
-
-        def __init__(self, packageDesc, patchMaker, xpackage = None):
-            self.packageDir = Filename(patchMaker.installDir, packageDesc.getDirname())
-            self.packageDesc = packageDesc
-            self.patchMaker = patchMaker
-            self.contentsDocPackage = xpackage
-            self.patchVersion = 1
-            self.currentPv = None
-            self.basePv = None
-            self.topPv = None
-
-            self.packageName = None
-            self.platform = None
-            self.version = None
-            self.hostUrl = None
-            self.currentFile = None
-            self.baseFile = None
-
-            self.doc = None
-            self.anyChanges = False
-            self.patches = []
-
-        def getCurrentKey(self):
-            """ Returns the key to locate the current version of this
-            package. """
-
-            return (self.packageName, self.platform, self.version, self.hostUrl, self.currentFile)
-
-        def getBaseKey(self):
-            """ Returns the key to locate the "base" or oldest version
-            of this package. """
-
-            return (self.packageName, self.platform, self.version, self.hostUrl, self.baseFile)
-
-        def getTopKey(self):
-            """ Returns the key to locate the "top" or newest version
-            of this package. """
-
-            return (self.packageName, self.platform, self.version, self.hostUrl, self.topFile)
-
-        def getGenericKey(self, fileSpec):
-            """ Returns the key that has the indicated hash. """
-            return (self.packageName, self.platform, self.version, self.hostUrl, fileSpec)
-
-        def readDescFile(self, doProcessing = False):
-            """ Reads the existing package.xml file and stores it in
-            this class for later rewriting.  if doProcessing is true,
-            it may massage the file and the directory contents in
-            preparation for building patches.  Returns true on
-            success, false on failure. """
-
-            self.anyChanges = False
-
-            packageDescFullpath = Filename(self.patchMaker.installDir, self.packageDesc)
-            self.doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
-            if not self.doc.LoadFile():
-                print("Couldn't read %s" % (packageDescFullpath))
-                return False
-
-            xpackage = self.doc.FirstChildElement('package')
-            if not xpackage:
-                return False
-            self.packageName = xpackage.Attribute('name')
-            self.platform = xpackage.Attribute('platform')
-            self.version = xpackage.Attribute('version')
-
-            # All packages we defined in-line are assigned to the
-            # "none" host.  TODO: support patching from packages on
-            # other hosts, which means we'll need to fill in a value
-            # here for those hosts.
-            self.hostUrl = None
-
-            self.currentFile = None
-            self.baseFile = None
-            self.topFile = None
-            self.compressedFilename = None
-            compressedFile = None
-
-            # Assume there are changes for this version, until we
-            # discover that there aren't.
-            isNewVersion = True
-
-            # Get the actual current version.
-            xarchive = xpackage.FirstChildElement('uncompressed_archive')
-            if xarchive:
-                self.currentFile = FileSpec()
-                self.currentFile.loadXml(xarchive)
-
-            # Get the top_version--the top (newest) of the patch
-            # chain.
-            xarchive = xpackage.FirstChildElement('top_version')
-            if xarchive:
-                self.topFile = FileSpec()
-                self.topFile.loadXml(xarchive)
-
-                if self.topFile.hash == self.currentFile.hash:
-                    # No new version this pass.
-                    isNewVersion = False
-                else:
-                    # There's a new version this pass.  Update it.
-                    self.anyChanges = True
-
-            else:
-                # If there isn't a top_version yet, we have to make
-                # one, by duplicating the currentFile.
-                self.topFile = copy.copy(self.currentFile)
-                self.anyChanges = True
-
-            # Get the current patch version.  If we have a
-            # patch_version attribute, it refers to this particular
-            # instance of the file, and that is the current patch
-            # version number.  If we only have a last_patch_version
-            # attribute, it means a patch has not yet been built for
-            # this particular instance, and that number is the
-            # previous version's patch version number.
-            patchVersion = xpackage.Attribute('patch_version')
-            if patchVersion:
-                self.patchVersion = int(patchVersion)
-            else:
-                patchVersion = xpackage.Attribute('last_patch_version')
-                if patchVersion:
-                    self.patchVersion = int(patchVersion)
-                    if isNewVersion:
-                        self.patchVersion += 1
-                self.anyChanges = True
-
-            # Put the patchVersion in the compressed filename, for
-            # cache-busting.  This means when the version changes, its
-            # URL will also change, guaranteeing that users will
-            # download the latest version, and not some stale cache
-            # file.
-            xcompressed = xpackage.FirstChildElement('compressed_archive')
-            if xcompressed:
-                compressedFile = FileSpec()
-                compressedFile.loadXml(xcompressed)
-
-                oldCompressedFilename = compressedFile.filename
-                self.compressedFilename = oldCompressedFilename
-
-                if doProcessing:
-                    newCompressedFilename = '%s.%s.pz' % (self.currentFile.filename, self.patchVersion)
-                    if newCompressedFilename != oldCompressedFilename:
-                        oldCompressedPathname = Filename(self.packageDir, oldCompressedFilename)
-                        newCompressedPathname = Filename(self.packageDir, newCompressedFilename)
-                        if oldCompressedPathname.renameTo(newCompressedPathname):
-                            compressedFile.fromFile(self.packageDir, newCompressedFilename)
-                            compressedFile.storeXml(xcompressed)
-
-                        self.compressedFilename = newCompressedFilename
-                        self.anyChanges = True
-
-            # Get the base_version--the bottom (oldest) of the patch
-            # chain.
-            xarchive = xpackage.FirstChildElement('base_version')
-            if xarchive:
-                self.baseFile = FileSpec()
-                self.baseFile.loadXml(xarchive)
-            else:
-                # If there isn't a base_version yet, we have to make
-                # one, by duplicating the currentFile.
-                self.baseFile = copy.copy(self.currentFile)
-
-                # Note that the we only store the compressed version
-                # of base_filename on disk, but we store the md5 of
-                # the uncompressed version in the xml file.  To
-                # emphasize this, we name it without the .pz extension
-                # in the xml file, even though the compressed file on
-                # disk actually has a .pz extension.
-                self.baseFile.filename += '.base'
-
-                # Also duplicate the (compressed) file itself.
-                if doProcessing and self.compressedFilename:
-                    fromPathname = Filename(self.packageDir, self.compressedFilename)
-                    toPathname = Filename(self.packageDir, self.baseFile.filename + '.pz')
-                    fromPathname.copyTo(toPathname)
-                self.anyChanges = True
-
-            self.patches = []
-            xpatch = xpackage.FirstChildElement('patch')
-            while xpatch:
-                patchfile = PatchMaker.Patchfile(self)
-                patchfile.loadXml(xpatch)
-                self.patches.append(patchfile)
-                xpatch = xpatch.NextSiblingElement('patch')
-
-            return True
-
-        def writeDescFile(self):
-            """ Rewrites the desc file with the new patch
-            information. """
-
-            if not self.anyChanges:
-                # No need to rewrite.
-                return
-
-            xpackage = self.doc.FirstChildElement('package')
-            if not xpackage:
-                return
-
-            packageSeq = SeqValue()
-            packageSeq.loadXml(xpackage, 'seq')
-            packageSeq += 1
-            packageSeq.storeXml(xpackage, 'seq')
-
-            # Remove all of the old patch entries from the desc file
-            # we read earlier.
-            xremove = []
-            for value in ['base_version', 'top_version', 'patch']:
-                xpatch = xpackage.FirstChildElement(value)
-                while xpatch:
-                    xremove.append(xpatch)
-                    xpatch = xpatch.NextSiblingElement(value)
-
-            for xelement in xremove:
-                xpackage.RemoveChild(xelement)
-
-            xpackage.RemoveAttribute('last_patch_version')
-
-            # Now replace them with the current patch information.
-            xpackage.SetAttribute('patch_version', str(self.patchVersion))
-
-            xarchive = TiXmlElement('base_version')
-            self.baseFile.storeXml(xarchive)
-            xpackage.InsertEndChild(xarchive)
-
-            # The current version is now the top version.
-            xarchive = TiXmlElement('top_version')
-            self.currentFile.storeXml(xarchive)
-            xpackage.InsertEndChild(xarchive)
-
-            for patchfile in self.patches:
-                xpatch = patchfile.makeXml(self)
-                xpackage.InsertEndChild(xpatch)
-
-            self.doc.SaveFile()
-
-            # Also copy the seq to the import desc file, for
-            # documentation purposes.
-
-            importDescFilename = str(self.packageDesc)[:-3] + 'import.xml'
-            importDescFullpath = Filename(self.patchMaker.installDir, importDescFilename)
-            doc = TiXmlDocument(importDescFullpath.toOsSpecific())
-            if doc.LoadFile():
-                xpackage = doc.FirstChildElement('package')
-                if xpackage:
-                    packageSeq.storeXml(xpackage, 'seq')
-                    doc.SaveFile()
-            else:
-                print("Couldn't read %s" % (importDescFullpath))
-
-            if self.contentsDocPackage:
-                # Now that we've rewritten the xml file, we have to
-                # change the contents.xml file that references it to
-                # indicate the new file hash.
-                fileSpec = FileSpec()
-                fileSpec.fromFile(self.patchMaker.installDir, self.packageDesc)
-                fileSpec.storeXml(self.contentsDocPackage)
-
-                # Also important to update the import.xml hash.
-                ximport = self.contentsDocPackage.FirstChildElement('import')
-                if ximport:
-                    fileSpec = FileSpec()
-                    fileSpec.fromFile(self.patchMaker.installDir, importDescFilename)
-                    fileSpec.storeXml(ximport)
-
-                # Also copy the package seq value into the
-                # contents.xml file, mainly for documentation purposes
-                # (the authoritative seq value is within the desc
-                # file).
-                packageSeq.storeXml(self.contentsDocPackage, 'seq')
-
-
-    # PatchMaker constructor.
-    def __init__(self, installDir):
-        self.installDir = installDir
-        self.packageVersions = {}
-        self.packages = []
-
-    def buildPatches(self, packageNames = None):
-        """ Makes the patches required in a particular directory
-        structure on disk.  If packageNames is None, this makes
-        patches for all packages; otherwise, it should be a list of
-        package name strings, limiting the set of packages that are
-        processed. """
-
-        if not self.readContentsFile():
-            return False
-        self.buildPatchChains()
-        if packageNames is None:
-            self.processAllPackages()
-        else:
-            self.processSomePackages(packageNames)
-
-        self.writeContentsFile()
-        self.cleanup()
-        return True
-
-    def cleanup(self):
-        """ Should be called on exit to remove temporary files and
-        such created during processing. """
-
-        for pv in self.packageVersions.values():
-            pv.cleanup()
-
-    def getPatchChainToCurrent(self, descFilename, fileSpec):
-        """ Reads the package defined in the indicated desc file, and
-        constructs a patch chain from the version represented by
-        fileSpec to the current version of this package, if possible.
-        Returns the patch chain if successful, or None otherwise. """
-
-        package = self.readPackageDescFile(descFilename)
-        if not package:
-            return None
-
-        self.buildPatchChains()
-        fromPv = self.getPackageVersion(package.getGenericKey(fileSpec))
-        toPv = package.currentPv
-
-        patchChain = None
-        if toPv and fromPv:
-            patchChain = toPv.getPatchChain(fromPv)
-
-        return patchChain
-
-    def readPackageDescFile(self, descFilename):
-        """ Reads a desc file associated with a particular package,
-        and adds the package to self.packages.  Returns the Package
-        object, or None on failure. """
-
-        package = self.Package(Filename(descFilename), self)
-        if not package.readDescFile(doProcessing = False):
-            return None
-
-        self.packages.append(package)
-        return package
-
-    def readContentsFile(self):
-        """ Reads the contents.xml file at the beginning of
-        processing. """
-
-        contentsFilename = Filename(self.installDir, 'contents.xml')
-        doc = TiXmlDocument(contentsFilename.toOsSpecific())
-        if not doc.LoadFile():
-            # Couldn't read file.
-            print("couldn't read %s" % (contentsFilename))
-            return False
-
-        xcontents = doc.FirstChildElement('contents')
-        if xcontents:
-            contentsSeq = SeqValue()
-            contentsSeq.loadXml(xcontents)
-            contentsSeq += 1
-            contentsSeq.storeXml(xcontents)
-
-            xpackage = xcontents.FirstChildElement('package')
-            while xpackage:
-                solo = xpackage.Attribute('solo')
-                solo = int(solo or '0')
-                filename = xpackage.Attribute('filename')
-                if filename and not solo:
-                    filename = Filename(filename)
-                    package = self.Package(filename, self, xpackage)
-                    package.readDescFile(doProcessing = True)
-                    self.packages.append(package)
-
-                xpackage = xpackage.NextSiblingElement('package')
-
-        self.contentsDoc = doc
-
-        return True
-
-    def writeContentsFile(self):
-        """ Writes the contents.xml file at the end of processing. """
-
-        # We also have to write the desc file for all packages that
-        # might need it, because we might have changed some of them on
-        # read.
-        for package in self.packages:
-            package.writeDescFile()
-
-        # The above writeDescFile() call should also update each
-        # package's element within the contents.xml document, so all
-        # we have to do now is write out the document.
-        self.contentsDoc.SaveFile()
-
-    def getPackageVersion(self, key):
-        """ Returns a shared PackageVersion object for the indicated
-        key. """
-
-        packageName, platform, version, hostUrl, file = key
-
-        # We actually key on the hash, not the FileSpec itself.
-        k = (packageName, platform, version, hostUrl, file.hash)
-        pv = self.packageVersions.get(k, None)
-        if not pv:
-            pv = self.PackageVersion(*key)
-            self.packageVersions[k] = pv
-        return pv
-
-    def buildPatchChains(self):
-        """ Builds up the chains of PackageVersions and the patchfiles
-        that connect them. """
-
-        self.patchFilenames = {}
-
-        for package in self.packages:
-            if not package.baseFile:
-                # This package doesn't have any versions yet.
-                continue
-
-            currentPv = self.getPackageVersion(package.getCurrentKey())
-            package.currentPv = currentPv
-            currentPv.packageCurrent = package
-            currentPv.printName = package.currentFile.filename
-
-            basePv = self.getPackageVersion(package.getBaseKey())
-            package.basePv = basePv
-            basePv.packageBase = package
-            basePv.printName = package.baseFile.filename
-
-            topPv = self.getPackageVersion(package.getTopKey())
-            package.topPv = topPv
-            topPv.packageTop = package
-
-            for patchfile in package.patches:
-                self.recordPatchfile(patchfile)
-
-    def recordPatchfile(self, patchfile):
-        """ Adds the indicated patchfile to the patch chains. """
-        self.patchFilenames[patchfile.file.filename] = patchfile
-
-        fromPv = self.getPackageVersion(patchfile.getSourceKey())
-        patchfile.fromPv = fromPv
-        fromPv.toPatches.append(patchfile)
-
-        toPv = self.getPackageVersion(patchfile.getTargetKey())
-        patchfile.toPv = toPv
-        toPv.fromPatches.append(patchfile)
-        toPv.printName = patchfile.file.filename
-
-    def processSomePackages(self, packageNames):
-        """ Builds missing patches only for the named packages. """
-
-        remainingNames = packageNames[:]
-        for package in self.packages:
-            if package.packageName in packageNames:
-                self.processPackage(package)
-            if package.packageName in remainingNames:
-                remainingNames.remove(package.packageName)
-
-        if remainingNames:
-            print("Unknown packages: %s" % (remainingNames,))
-
-    def processAllPackages(self):
-        """ Walks through the list of packages, and builds missing
-        patches for each one. """
-
-        for package in self.packages:
-            self.processPackage(package)
-
-    def processPackage(self, package):
-        """ Builds missing patches for the indicated package. """
-
-        if not package.baseFile:
-            # No versions.
-            return
-
-        # What's the current version on the top of the tree?
-        topPv = package.topPv
-        currentPv = package.currentPv
-
-        if topPv != currentPv:
-            # They're different, so build a new patch.
-            filename = Filename(package.currentFile.filename + '.%s.patch' % (package.patchVersion))
-            assert filename not in self.patchFilenames
-            if not self.buildPatch(topPv, currentPv, package, filename):
-                raise Exception("Couldn't build patch.")
-
-    def buildPatch(self, v1, v2, package, patchFilename):
-        """ Builds a patch from PackageVersion v1 to PackageVersion
-        v2, and stores it in patchFilename.pz.  Returns true on
-        success, false on failure."""
-
-        pathname = Filename(package.packageDir, patchFilename)
-        if not self.buildPatchFile(v1.getFile(), v2.getFile(), pathname,
-                                   v1.printName, v2.printName):
-            return False
-
-        compressedPathname = Filename(pathname + '.pz')
-        compressedPathname.unlink()
-        if not compressFile(pathname, compressedPathname, 9):
-            raise Exception("Couldn't compress patch.")
-        pathname.unlink()
-
-        patchfile = self.Patchfile(package)
-        patchfile.fromFile(package.packageDir, patchFilename + '.pz',
-                           v1.file, v2.file)
-        package.patches.append(patchfile)
-        package.anyChanges = True
-
-        self.recordPatchfile(patchfile)
-
-        return True
-
-    def buildPatchFile(self, origFilename, newFilename, patchFilename,
-                       printOrigName, printNewName):
-        """ Creates a patch file from origFilename to newFilename,
-        storing the result in patchFilename.  Returns true on success,
-        false on failure. """
-
-        if not origFilename.exists():
-            # No original version to patch from.
-            return False
-
-        print("Building patch from %s to %s" % (printOrigName, printNewName))
-        patchFilename.unlink()
-        p = Patchfile()  # The C++ class
-        if p.build(origFilename, newFilename, patchFilename):
-            return True
-
-        # Unable to build a patch for some reason.
-        patchFilename.unlink()
-        return False

+ 0 - 95
direct/src/p3d/ScanDirectoryNode.py

@@ -1,95 +0,0 @@
-__all__ = ["ScanDirectoryNode"]
-
-from panda3d.core import VirtualFileSystem, VirtualFileMountSystem, Filename, TiXmlDocument
-vfs = VirtualFileSystem.getGlobalPtr()
-
-class ScanDirectoryNode:
-    """ This class is used to scan a list of files on disk. """
-
-    def __init__(self, pathname, ignoreUsageXml = False):
-        self.pathname = pathname
-        self.filenames = []
-        self.fileSize = 0
-        self.nested = []
-        self.nestedSize = 0
-
-        xusage = None
-        if not ignoreUsageXml:
-            # Look for a usage.xml file in this directory.  If we find
-            # one, we read it for the file size and then stop here, as
-            # an optimization.
-            usageFilename = Filename(pathname, 'usage.xml')
-            doc = TiXmlDocument(usageFilename.toOsSpecific())
-            if doc.LoadFile():
-                xusage = doc.FirstChildElement('usage')
-                if xusage:
-                    diskSpace = xusage.Attribute('disk_space')
-                    try:
-                        diskSpace = int(diskSpace or '')
-                    except ValueError:
-                        diskSpace = None
-                    if diskSpace is not None:
-                        self.fileSize = diskSpace
-                        return
-
-        files = vfs.scanDirectory(self.pathname)
-        if files is None:
-            files = []
-        for vfile in files:
-            if hasattr(vfile, 'getMount'):
-                if not isinstance(vfile.getMount(), VirtualFileMountSystem):
-                    # Not a real file; ignore it.
-                    continue
-
-            if vfile.isDirectory():
-                # A nested directory.
-                subdir = ScanDirectoryNode(vfile.getFilename(), ignoreUsageXml = ignoreUsageXml)
-                self.nested.append(subdir)
-                self.nestedSize += subdir.getTotalSize()
-
-            elif vfile.isRegularFile():
-                # A nested file.
-                self.filenames.append(vfile.getFilename())
-                self.fileSize += vfile.getFileSize()
-
-            else:
-                # Some other wacky file thing.
-                self.filenames.append(vfile.getFilename())
-
-        if xusage:
-            # Now update the usage.xml file with the newly-determined
-            # disk space.
-            xusage.SetAttribute('disk_space', str(self.getTotalSize()))
-            tfile = Filename.temporary(str(pathname), '.xml')
-            if doc.SaveFile(tfile.toOsSpecific()):
-                tfile.renameTo(usageFilename)
-
-    def getTotalSize(self):
-        return self.nestedSize + self.fileSize
-
-    def extractSubdir(self, pathname):
-        """ Finds the ScanDirectoryNode within this node that
-        corresponds to the indicated full pathname.  If it is found,
-        removes it from its parent, and returns it.  If it is not
-        found, returns None. """
-
-        # We could be a little smarter here, but why bother.  Just
-        # recursively search all children.
-        for subdir in self.nested:
-            if subdir.pathname == pathname:
-                self.nested.remove(subdir)
-                self.nestedSize -= subdir.getTotalSize()
-                return subdir
-
-            result = subdir.extractSubdir(pathname)
-            if result:
-                self.nestedSize -= result.getTotalSize()
-                if subdir.getTotalSize() == 0:
-                    # No other files in the subdirectory that contains
-                    # this package; remove it too.
-                    self.nested.remove(subdir)
-                return result
-
-        return None
-
-

+ 0 - 90
direct/src/p3d/SeqValue.py

@@ -1,90 +0,0 @@
-__all__ = ["SeqValue"]
-
-class SeqValue:
-
-    """ This represents a sequence value read from a contents.xml
-    file, either from the <contents> or the <package> section.  It's
-    represented as series of dotted integers in the xml file, and
-    stored internally as a tuple of integers.
-
-    It may be incremented, which increments only the last integer in
-    the series; or it may be compared with another SeqValue, which
-    compares all of the integers componentwise. """
-
-    def __init__(self, value = None):
-        self.value = ()
-        if value is not None:
-            self.set(value)
-
-    def set(self, value):
-        """ Sets the seq from the indicated value of unspecified
-        type. """
-        if isinstance(value, tuple):
-            self.setFromTuple(value)
-        elif isinstance(value, str):
-            self.setFromString(value)
-        else:
-            raise TypeError('Invalid sequence type: %s' % (value,))
-
-    def setFromTuple(self, value):
-        """ Sets the seq from the indicated tuple of integers. """
-        assert isinstance(value, tuple)
-        self.value = value
-
-    def setFromString(self, value):
-        """ Sets the seq from the indicated string of dot-separated
-        integers.  Raises ValueError on error. """
-        assert isinstance(value, str)
-
-        self.value = ()
-        if value:
-            value = value.split('.')
-            value = map(int, value)
-            self.value = tuple(value)
-
-    def loadXml(self, xelement, attribute = 'seq'):
-        """ Reads the seq from the indicated XML element.  Returns
-        true if loaded, false if not given or if there was an
-        error. """
-
-        self.value = ()
-        value = xelement.Attribute(attribute)
-        if value:
-            try:
-                self.setFromString(value)
-            except ValueError:
-                return False
-            return True
-
-        return False
-
-
-    def storeXml(self, xelement, attribute = 'seq'):
-        """ Adds the seq to the indicated XML element. """
-        if self.value:
-            value = '.'.join(map(str, self.value))
-            xelement.SetAttribute(attribute, value)
-
-    def __add__(self, inc):
-        """ Increments the seq value, returning the new value. """
-        if not self.value:
-            value = (1,)
-        else:
-            value = self.value[:-1] + (self.value[-1] + inc,)
-        return SeqValue(value)
-
-    def __cmp__(self, other):
-        """ Compares to another seq value. """
-        return cmp(self.value, other.value)
-
-    def __lt__(self, other):
-        return self.value < other.value
-
-    def __gt__(self, other):
-        return self.value > other.value
-
-    def __bool__(self):
-        return bool(self.value)
-
-    def __str__(self):
-        return 'SeqValue%s' % (repr(self.value))

+ 0 - 4
direct/src/p3d/__init__.py

@@ -1,4 +0,0 @@
-"""
-This package provides the Python interface to functionality relating to
-the Panda3D Runtime environment.
-"""

+ 0 - 124
direct/src/p3d/coreapi.pdef

@@ -1,124 +0,0 @@
-from panda3d.core import getModelPath, Filename, ConfigVariableFilename, DSearchPath, ExecutionEnvironment, PandaSystem
-
-# This file defines a number of standard "packages" that correspond to
-# a Panda3D distribution.  These packages are built by passing this
-# file to the ppackage utility, either as a packaged application, or
-# as the module direct.p3d.ppackage.
-
-# The packages in this file define the "Core API".  This is the second
-# installed piece of the three-part plugin system (the plugin, the
-# core API, Panda3D).
-
-# These packages are downloaded directly by the plugin, from the host
-# specified by the value of PANDA_PACKAGE_HOST_URL compiled into the
-# plugin.  Thus, these packages are inextricably tied to the
-# particular plugin they have been built with.  They do not have to be
-# present on a server that hosts a version of Panda3D for download,
-# just on the server that hosts the plugin itself.  These packages do
-# not need to be updated with each new version of Panda3D.
-
-# Also see panda3d.pdef.
-
-class coreapi(solo):
-    # The special "coreapi" package.  As a "solo", this is just a
-    # single .dll (or dylib, or whatever).
-    setVer(PandaSystem.getP3dCoreapiVersionString())
-    file('p3d_plugin.dll')
-
-class images(package):
-    # The default startup images are stored as their own package.
-    names = ['download', 'failed', 'play_click', 'play_ready', 'play_rollover',
-             'auth_click', 'auth_ready', 'auth_rollover']
-    configDict = {}
-
-    # Construct a search path to look for the images.
-    search = DSearchPath()
-
-    # First on the path: an explicit $PLUGIN_IMAGES env var.
-    if ExecutionEnvironment.hasEnvironmentVariable('PLUGIN_IMAGES'):
-        search.appendDirectory(Filename.expandFrom('$PLUGIN_IMAGES'))
-
-    # Next on the path: the models/plugin_images directory within the
-    # current directory.
-    search.appendDirectory('models/plugin_images')
-
-    # Finally on the path: models/plugin_images within the model
-    # search path.
-    for dir in getModelPath().getDirectories():
-        search.appendDirectory(Filename(dir, 'plugin_images'))
-
-    for name in names:
-        # Look for a png image first.
-        basename = '%s.png' % (name)
-        filename = Filename(basename)
-        found = filename.resolveFilename(search)
-        if not found:
-            # Then try a jpeg image.
-            basename = '%s.jpg' % (name)
-            filename = Filename(basename)
-            found = filename.resolveFilename(search)
-
-        if found:
-            # Add the image file to the package
-            file(filename, newName = basename, extract = True)
-
-            # And set the config variable to reference it.
-            token = '%s_img' % (name)
-            configDict[token] = basename
-        else:
-            print("Could not locate %s" % (filename))
-
-    # Also make a few special cases.  We use the same default image
-    # for many of the states.
-    download = configDict.get('download_img', None)
-    if download:
-        configDict['ready_img'] = download
-        configDict['unauth_img'] = download
-        configDict['launch_img'] = download
-        configDict['active_img'] = download
-
-    config(**configDict)
-
-class certlist(package):
-    # This package holds any certificates that should be considered
-    # pre-approved by the plugin vendor.  If you build and host your
-    # own version of the Panda3D plugin, you can add your own
-    # certificates to this list.  The certificates in this package
-    # will be assumed to have been approved by the user when he/she
-    # installed the plugin.
-
-    # They should be PEM-encoded and their filenames must end in the
-    # ".pem" or ".crt" extension, and they should be added with the
-    # extract = True flag so they will be extracted to disk.
-    pass
-
-
-class p3dcert(package):
-    # This special application, used to pop up a dialog to prompt the
-    # user to accept or deny unknown applications, is its own package.
-    config(display_name = "Authorization Dialog")
-
-    if platform.startswith('osx'):
-        # On Mac, we package up a P3DCert.app bundle.  This includes
-        # specifications in the plist file to avoid creating a dock
-        # icon and stuff.
-
-        # Find p3dcert.plist in the direct source tree.
-        import direct
-        plist = Filename(direct.__path__[0], 'plugin/p3dcert.plist')
-        makeBundle('P3DCert.app', plist, executable = 'p3dcert')
-
-    else:
-        # Anywhere else, we just ship the executable file p3dcert.exe.
-        file('p3dcert.exe', required = True)
-
-
-class p3dembed(package):
-    # This contains the p3dembed executable, that is used by
-    # pdeploy to generate a self-extracting .p3d executable.
-
-    config(platform_specific = True)
-    file('p3dembed.exe', required = True)
-
-    if platform.startswith('win'):
-        file('p3dembedw.exe', required = True)

+ 0 - 127
direct/src/p3d/p3dWrapper.c

@@ -1,127 +0,0 @@
-/* Filename: p3dWrapper.c
- * Created by:  rdb (16Jan10)
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * 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."
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-/* p3dWrapper is a small wrapper executable that locates a .p3d file
-   in the same directory as this executable file, with the same name
-   (except .p3d instead of .exe of course). It is only meant to be
-   used on Windows system, it is not needed on Unix-like systems. */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <windows.h>
-#include <process.h>
-#include <assert.h>
-#include <malloc.h>
-
-#define BUFFER_SIZE 1024
-
-/* It makes sense to use "App Paths\panda3d.exe".  However, Microsoft
-   decided in their infinite wisdom to disable Redirection for that
-   key from Windows 7 onward, so we can't rely on it producing a
-   result appropriate to the right architecture when both the 32-bit
-   and 64-bit versions of the runtime are installed.  Beh. */
-
-#define UNINST_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Panda3D Game Engine"
-
-int main (int argc, char* argv[]) {
-  int i;
-  char buffer[BUFFER_SIZE];
-  char* p3dfile;
-  char* runtime = NULL;
-  DWORD size;
-  STARTUPINFO si;
-  PROCESS_INFORMATION pi;
-  char *cmd;
-  char *newcmd;
-  HKEY hKey = 0;
-  char buf[1024] = {0};
-  DWORD dwType = REG_SZ;
-  DWORD dwBufSize = sizeof(buf);
-  size = GetModuleFileName(NULL, buffer, BUFFER_SIZE);
-  assert (size > 0);
-
-  /* Chop off the .exe and replace it by .p3d. */
-  p3dfile = (char*) _alloca(size + 1);
-  memcpy(p3dfile, buffer, size);
-  p3dfile[size] = 0;
-  memcpy(p3dfile + size - 3, "p3d", 3);
-
-  /* Find the location of panda3d.exe using the registry path. */
-#ifdef _WIN64
-  /* If we're on 64-bit Windows, try the 64-bit registry first. */
-  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, UNINST_KEY, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKey) == ERROR_SUCCESS) {
-    if (RegQueryValueEx(hKey, "DisplayIcon", 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) {
-      char *slash = strrchr(buf, '\\');
-      if (slash != NULL) {
-        strcpy(slash, "\\panda3d.exe");
-        runtime = buf;
-      }
-    }
-    RegCloseKey(hKey);
-  }
-#endif
-
-  /* On 32-bit Windows, or no 64-bit Runtime installed.  Try 32-bit registry. */
-  if (runtime == NULL) {
-    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, UNINST_KEY, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS) {
-      if (RegQueryValueEx(hKey, "DisplayIcon", 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) {
-        char *slash = strrchr(buf, '\\');
-        if (slash != NULL) {
-          strcpy(slash, "\\panda3d.exe");
-          runtime = buf;
-        }
-      }
-      RegCloseKey(hKey);
-    }
-  }
-
-  /* Backward compatibility: Runtime 1.0.4 and below looked for the below key, even though the
-     above keys should work fine, but let's just be certain.
-     Find the Panda3D applet\DefaultIcon key and extract the path to the runtime from there.  */
-  if (runtime == NULL) {
-    if (RegOpenKey(HKEY_CLASSES_ROOT, "Panda3D applet\\DefaultIcon", &hKey) == ERROR_SUCCESS) {
-      if (RegQueryValueEx(hKey, 0, 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) {
-        char *slash = strrchr(buf, '\\');
-        if (slash != NULL) {
-          strcpy(slash, "\\panda3d.exe");
-          runtime = buf;
-        }
-      } else {
-        fprintf(stderr, "Failed to read registry key. Try reinstalling the Panda3D Runtime.\n");
-        return 1;
-      }
-      RegCloseKey(hKey);
-    } else {
-      fprintf(stderr, "The Panda3D Runtime does not appear to be installed!\n");
-      return 1;
-    }
-  }
-
-  if (runtime == NULL) {
-    fprintf(stderr, "Failed to find panda3d.exe in registry. Try reinstalling the Panda3D Runtime.\n");
-    return 1;
-  }
-
-  /* Build the command-line and run panda3d.exe. */
-  cmd = GetCommandLine();
-  newcmd = (char*) _alloca(strlen(runtime) + strlen(p3dfile) + strlen(cmd) - strlen (argv[0]) + 7);
-  sprintf(newcmd, "\"%s\" \"%s\" %s", runtime, p3dfile, cmd + strlen(argv[0]));
-  memset(&si, 0, sizeof(si));
-  si.cb = sizeof(STARTUPINFO);
-  if (CreateProcess(runtime, newcmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
-    WaitForSingleObject(pi.hProcess, INFINITE);
-  }
-  return 0;
-}

+ 0 - 241
direct/src/p3d/packp3d.py

@@ -1,241 +0,0 @@
-#! /usr/bin/env python
-
-usageText = """
-
-This command will pack a Panda application, consisting of a directory
-tree of .py files and models, into a p3d file for convenient
-distribution.  The resulting p3d file can be run by the Panda3D
-runtime executable, or by the Panda3D web browser plugin.
-
-This command will build p3d files that reference Panda3D %s,
-from host %s .
-
-Also see ppackage, a more powerful (but more complex) tool that can
-also be used to build p3d applications, using a pdef description file.
-
-Usage:
-
-  %s [opts] -o app.p3d
-
-Options:
-
-  -o app.p3d
-     Specify the name of the p3d file to generate.  This is required.
-
-  -d application_root
-     Specify the root directory of the application source; this is a
-     directory tree that contains all of your .py files and models.
-     If this is omitted, the default is the current directory.
-
-  -m main.py
-     Names the Python file that begins the application.  This should
-     be a file within the root directory. If this is omitted, the
-     default is a file named "main.py", or if there is only one Python
-     file present, it is used.  If this file contains a function
-     called main(), that function will be called after importing it
-     (this is preferable to having the module start itself immediately
-     upon importing).
-
-  -S file.crt[,chain.crt[,file.key[,\"password\"]]]
-     Signs the resulting p3d with the indicated certificate.  You may
-     specify the signing certificate, the optional authorization
-     chain, and the private key in three different files, or they may
-     all be combined in the first file.  If the private key is
-     encrypted, the password will be required to decrypt it.
-
-  -e ext
-     Adds a new extension to be processed as a generic, compressible
-     file type.  Do not include the leading dot.  Files matching this
-     extension found within the root directory will be automatically
-     added to the p3d file, in compressed form.  This option may be
-     repeated as necessary.
-
-  -n ext
-     Adds a new extension to be processed as a noncompressible file
-     type.  Files matching this extension will be added to the p3d
-     file, in their original, uncompressed form.  You should use this
-     instead of -e for files that are uncompressible by their nature
-     (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
-     Adds a directory to search for additional Python modules.  You
-     can use this to add your system's Python path, to allow packp3d
-     to find any system modules not included in the standard Panda3D
-     release, but your version of Python must match this one (%s).
-     This option may be repeated to add multiple directories.
-
-  -c config=value
-     Sets the indicated config flag in the application.  This option
-     may be repeated as necessary.
-
-  -r package[,version[,hostURL]]
-     Names an additional package that this application requires at
-     startup time.  The default package is 'panda3d'; you may repeat
-     this option to indicate dependencies on additional packages.
-
-  -s search_dir
-     Additional directories to search for previously-built packages.
-     This option may be repeated as necessary.  These directories may
-     also be specified with the pdef-path Config.prc variable.
-
-  -D
-     Sets the allow_python_dev flag in the application.  This enables
-     additional runtime debug operations, particularly the -i option
-     to the panda3d command, which enables a live Python prompt within
-     the application's environment.  Setting this flag may be useful
-     to develop an application initially, but should not be set on an
-     application intended for deployment.
-
-"""
-
-import sys
-import os
-import getopt
-import glob
-from direct.p3d import Packager
-from panda3d.core import *
-
-# Temp hack for debugging.
-#from direct.p3d.AppRunner import dummyAppRunner; dummyAppRunner()
-
-class ArgumentError(Exception):
-    pass
-
-def makePackedApp(args):
-    opts, args = getopt.getopt(args, 'o:d:m:S:e:n:x:p:c:r:s:Dh')
-
-    packager = Packager.Packager()
-
-    appFilename = None
-    root = Filename('.')
-    main = None
-    configFlags = []
-    requires = []
-    allowPythonDev = False
-
-    for option, value in opts:
-        if option == '-o':
-            appFilename = Filename.fromOsSpecific(value)
-        elif option == '-d':
-            root = Filename.fromOsSpecific(value)
-        elif option == '-m':
-            main = value
-        elif option == '-S':
-            tokens = value.split(',')
-            while len(tokens) < 4:
-                tokens.append('')
-            certificate, chain, pkey, password = tokens[:4]
-            packager.signParams.append((Filename.fromOsSpecific(certificate),
-                                        Filename.fromOsSpecific(chain),
-                                        Filename.fromOsSpecific(pkey),
-                                        Filename.fromOsSpecific(password)))
-        elif option == '-e':
-            packager.binaryExtensions.append(value)
-        elif option == '-n':
-            packager.uncompressibleExtensions.append(value)
-        elif option == '-x':
-            packager.extractExtensions.append(value)
-        elif option == '-p':
-            sys.path.append(value)
-        elif option == '-c':
-            configFlags.append(value.split('=', 1))
-        elif option == '-r':
-            tokens = value.split(',')
-            while len(tokens) < 3:
-                tokens.append('')
-            name, version, host = tokens[:3]
-            requires.append((name, version, host))
-        elif option == '-s':
-            packager.installSearch.append(Filename.fromOsSpecific(value))
-        elif option == '-D':
-            allowPythonDev = True
-        elif option == '-h':
-            print(usageText % (
-                PandaSystem.getPackageVersionString(),
-                PandaSystem.getPackageHostUrl(),
-                os.path.split(sys.argv[0])[1],
-                '%s.%s' % (sys.version_info[0], sys.version_info[1])))
-            sys.exit(0)
-
-    if not appFilename:
-        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:
-        raise ArgumentError("Extra arguments on command line.")
-
-    if appFilename.getExtension() != 'p3d':
-        raise ArgumentError('Application filename must end in ".p3d".')
-
-    appDir = Filename(appFilename.getDirname())
-    if not appDir:
-      appDir = Filename('.')
-    appBase = appFilename.getBasenameWoExtension()
-
-    if main:
-        main = Filename.fromOsSpecific(main)
-        main.makeAbsolute(root)
-    else:
-        main = Filename(root, 'main.py')
-        if not main.exists():
-            main = glob.glob(os.path.join(root.toOsSpecific(), '*.py'))
-            if len(main) == 0:
-                raise ArgumentError('No Python files in root directory.')
-            elif len(main) > 1:
-                raise ArgumentError('Multiple Python files in root directory; specify the main application with -m "main".')
-
-            main = Filename.fromOsSpecific(os.path.split(main[0])[1])
-            main.makeAbsolute(root)
-
-    packager.installDir = appDir
-    packager.allowPythonDev = allowPythonDev
-
-    # Put the root directory on the front of the model-path, so that
-    # any texture references in egg or bam files that reference
-    # textures from the top of the root directory will be properly
-    # resolved.
-    getModelPath().prependDirectory(root)
-
-    try:
-        packager.setup()
-        packager.beginPackage(appBase, p3dApplication = True)
-
-        # Pre-require panda3d, to give a less-confusing error message
-        # if one of our requirements pulls in a wrong version of
-        # panda3d.
-        if 'panda3d' not in [t[0] for t in requires]:
-            packager.do_require('panda3d')
-
-        for name, version, host in requires:
-            packager.do_require(name, version = version, host = host)
-
-        if configFlags:
-            packager.do_config(**dict(configFlags))
-
-        packager.do_dir(root)
-        packager.do_main(main)
-        packager.endPackage()
-        packager.close()
-
-    except Packager.PackagerError:
-        # Just print the error message and exit gracefully.
-        inst = sys.exc_info()[1]
-        print(inst.args[0])
-        sys.exit(1)
-
-try:
-    makePackedApp(sys.argv[1:])
-except ArgumentError as e:
-    print(e.args[0])
-    sys.exit(1)
-
-# An explicit call to exit() is required to exit the program, when
-# this module is packaged in a p3d file.
-sys.exit(0)

+ 0 - 518
direct/src/p3d/panda3d.pdef

@@ -1,518 +0,0 @@
-import sys
-from panda3d.core import Filename, PandaSystem, getModelPath
-
-# This file defines a number of standard "packages" that correspond to
-# a Panda3D distribution.  These packages are built by passing this
-# file to the ppackage utility, either as a packaged application, or
-# as the module direct.p3d.ppackage.
-
-# The packages in this file define Panda3D itself.  This is the third
-# installed piece of the three-part plugin system (the plugin, the
-# core API, Panda3D).
-
-# When needed, these packages are downloaded by the core API, from the
-# host URL specified in a given p3d file, and not from any hardcoded
-# URL.  Thus, any custom version of Panda3D may be hosted on any
-# server in the world, and any version of the plugin can download it.
-
-# Also see coreapi.pdef.
-
-class panda3d(package):
-    # The main Panda3D package.  Contains Python and most of the graphics
-    # code in Panda3D.
-
-    config(display_name = "Panda3D")
-
-    # First, add the minimum startup files for a Panda3D package.
-    # These are files that the Panda3D runtime will explicitly look
-    # for by name in order to get itself bootstrapped.
-    setupPanda3D()
-
-    # These are Python modules that are needed by most Panda3D
-    # applications.  It doesn't matter too much if we miss one or two
-    # here, since any module imported by any of this code will
-    # automatically be included as well, and we end up with a pretty
-    # complete list that way.
-    module('direct.directbase.DirectStart',
-           # Don't want to include all files in direct.p3d, because
-           # that picks up the runtime scripts too, which are their
-           # own p3d files below.
-           'direct.p3d.AppRunner',
-           'direct.p3d.DWBPackageInstaller',
-           'direct.actor.*',
-           'direct.controls.*',
-           'direct.directdevices.*',
-           'direct.directnotify.*',
-           'direct.directtools.*',
-           'direct.directutil.*',
-           'direct.distributed.*',
-           'direct.filter.*',
-           'direct.fsm.*',
-           'direct.gui.*',
-           'direct.interval.*',
-           'direct.particles.*',
-           'direct.showbase.*',
-           'direct.showutil.*',
-           'direct.stdpy.*',
-           'direct.task.*')
-
-    # Keep these modules for backward compatibility.
-    module('pandac.PandaModules', 'pandac.extension_native_helpers')
-
-    module('panda3d.core',
-           'panda3d.direct',
-           'panda3d.fx',
-           'panda3d.physics')
-
-    # Include various standard Python encodings.  The rest is in morepy.
-    module('encodings', 'encodings.aliases', 'encodings.undefined',
-           'encodings.utf_8', 'encodings.ascii', 'encodings.mbcs',
-           'encodings.latin_1', 'io')
-    if sys.version_info < (3, 0):
-        module('encodings.string_escape')
-
-    # pandac.PandaModules pulls in other Panda3D libs automatically.
-    # Exclude these Panda3D libs; they are big and many applications don't
-    # use them.  We define them as separate, optional packages, below,
-    # except for skel - this is useless in a shipped game anyways.
-    excludeModule('panda3d.egg')
-    excludeModule('panda3d.ode')
-    excludeModule('panda3d.bullet')
-    excludeModule('panda3d.vision')
-    excludeModule('panda3d.skel')
-    excludeModule('panda3d.physx')
-    excludeModule('panda3d.ai')
-    excludeModule('panda3d.vrpn')
-
-    excludeModule('libpandaegg')
-    excludeModule('libpandaode')
-    excludeModule('libpandabullet')
-    excludeModule('libp3vision')
-    excludeModule('libpandaskel')
-    excludeModule('libpandaphysx')
-    excludeModule('libpandaai')
-    excludeModule('libp3vrpn')
-
-    # Exclude these GUI toolkits; they're big, and many applications don't
-    # use them.  We define them as separate, optional packages, below.
-    excludeModule('wx',
-                  'direct.showbase.WxGlobal')
-
-    excludeModule('Tkinter', 'tkinter', 'Pmw', 'tkinter.simpledialog',
-                  'direct.showbase.TkGlobal',
-                  'direct.tkpanels', 'direct.tkwidgets',
-                  'tkCommonDialog', 'tkMessageBox', 'tkSimpleDialog')
-
-    excludeModule('MySQLdb', 'MySQLdb.connections', 'MySQLdb.constants',
-                  'MySQLdb.converters', '_mysql')
-
-    # Some code in distributed conditionally imports these.
-    excludeModule('otp.ai', 'otp.ai.AIZoneData')
-
-    # Most of the core Panda3D DLL's will be included implicitly due to
-    # being referenced by the above Python code.  Here we name a few more
-    # that are also needed, but aren't referenced by any code.  Again,
-    # note that the .dll extension is automatically replaced with the
-    # platform-specific extension for an executable.
-    file('libpandagl.dll', 'libp3tinydisplay.dll')
-    if platform.startswith('win'):
-        file('libpandadx9.dll')
-
-    # A basic config file is needed to lay some some fundamental runtime
-    # variables.
-    if platform.startswith('win'):
-        auxDisplays = """
-aux-display pandagl
-aux-display pandadx9
-aux-display p3tinydisplay
-"""
-    else:
-        auxDisplays = """
-aux-display pandagl
-aux-display p3tinydisplay
-"""
-
-    file('Config.prc', extract = True, text = """
-plugin-path $PANDA3D_ROOT
-default-model-extension .bam
-cull-bin gui-popup 60 unsorted
-""" + auxDisplays)
-
-class morepy(package):
-    # Additional Python modules provided by the standard Python
-    # library.  Include this package to get the full suite of standard
-    # Python functionality.
-
-    config(display_name = "Python standard library")
-    require('panda3d')
-
-    module('string', 're', 'struct', 'difflib',
-           'textwrap', 'codecs', 'unicodedata', 'stringprep', 'datetime',
-           'calendar', 'collections', 'heapq', 'bisect', 'array',
-           'sched', 'queue', 'weakref', 'types', 'copy', 'pprint',
-           'numbers', 'math', 'cmath', 'decimal', 'fractions',
-           'random', 'itertools', 'functools', 'operator', 'os.path',
-           'fileinput', 'filecmp', 'tempfile', 'glob', 'fnmatch', 'linecache',
-           'shutil', 'macpath', 'pickle', 'shelve', 'marshal', 'zlib',
-           'gzip', 'bz2', 'zipfile', 'tarfile', 'csv', 'netrc',
-           'xdrlib', 'plistlib', 'hashlib', 'hmac', 'os',
-           'time', 'optparse', 'getopt', 'logging', 'logging.*')
-    module('getpass', 'curses', 'curses.textpad', 'curses.wrapper',
-           'curses.ascii', 'curses.panel', 'platform', 'errno',
-           'ctypes', 'select', 'threading', 'dummy_threading', 'dummy_thread',
-           'multiprocessing', 'mmap', 'readline', 'rlcompleter', 'subprocess',
-           'socket', 'ssl', 'signal', 'asyncore', 'asynchat', 'email', 'json',
-           'mailcap', 'mailbox', 'mimetypes', 'base64', 'binhex', 'binascii',
-           'quopri', 'uu', 'xml.parsers.expat', 'xml.dom', 'xml.dom.minidom',
-           'xml.dom.pulldom', 'xml.sax', 'xml.sax.handler',
-           'xml.sax.saxutils', 'xml.sax.xmlreader',
-           'xml.etree.ElementTree', 'webbrowser', 'cgi', 'cgitb')
-    module('wsgiref', 'ftplib', 'poplib', 'imaplib', 'nntplib', 'smtplib',
-           'smtpd', 'telnetlib', 'uuid', 'audioop', 'aifc', 'sunau',
-           'wave', 'chunk', 'colorsys', 'imghdr', 'sndhdr',
-           'ossaudiodev', 'gettext', 'locale', 'cmd', 'shlex',
-           'pydoc', 'doctest', 'unittest', 'test',
-           'bdb', 'pdb', 'timeit', 'trace', 'sys')
-    module('warnings', 'contextlib', 'abc',
-           'atexit', 'traceback', '__future__', 'gc', 'inspect',
-           'site', 'fpectl', 'code', 'codeop', 'zipimport', 'pkgutil',
-           'modulefinder', 'runpy', 'parser', 'symtable', 'symbol',
-           'token', 'keyword', 'tokenize', 'tabnanny', 'pyclbr',
-           'py_compile', 'compileall', 'dis', 'pickletools',
-           'distutils', 'msilib', 'msvcrt', 'winsound', 'posix', 'pwd', 'spwd',
-           'grp', 'crypt', 'termios', 'tty', 'pty', 'fcntl', 'pipes',
-           'resource', 'nis', 'syslog', 'ast')
-
-    # Handle module name differences between Python 2 and Python 3 (see PEP3108)
-    if sys.version_info[0] < 3:
-        # Deprecated modules that were removed in Python 3
-        module('posixfile', 'rfc822', 'mimetools','MimeWriter', 'mimify',
-               'multifile', 'sets', 'md5', 'sha', 'imp',  'formatter')
-        # Mac-specific modules that were removed
-        module('autoGIL', 'ColorPicker', 'EasyDialogs', 'findertools',
-               'FrameWork', 'ic',  'MacOS', 'macostools')
-        # Removed because they were hardly used
-        module('imputil', 'mutex', 'user', 'new')
-        # Removed because they were obsolete
-        module('Bastion', 'rexec', 'commands', 'dircache', 'dl', 'fpformat',
-               'htmllib', 'imageop', 'mhlib', 'popen2', 'sgmllib', 'stat',
-               'statvfs', 'thread', 'UserDict', 'UserList', 'UserString',
-               'future_builtins', 'hotshot', 'bsddb')
-        # Renamed to fix PEP8 violations
-        module('_winreg', 'ConfigParser', 'copy_reg', 'SocketServer')
-        # C and Python implementations of the same interface were merged
-        module('cPickle', 'cStringIO', 'StringIO')
-        # Renamed because of poorly chosen names
-        module('repr', 'test.test_support', '__builtins__')
-        # Modules that got grouped under packages
-        module('anydbm', 'whichdb', 'dbm', 'dumbdbm', 'gdbm', 'dbhash')
-        module('HTMLParser', 'htmlentitydefs')
-        module('BaseHTTPServer', 'SimpleHTTPServer', 'cookielib', 'Cookie',
-               'CGIHTTPServer', 'httplib')
-        module('urllib', 'urllib2', 'urlparse', 'robotparser')
-        module('xmlrpclib', 'SimpleXMLRPCServer', 'DocXMLRPCServer')
-    else:
-        # Renamed to fix PEP8 violations
-        module('winreg', 'configparser', 'copyreg', 'socketserver')
-        # Renamed because of poorly chosen names
-        module('reprlib', 'test.support', 'builtins')
-        # Modules that got grouped under packages
-        module('dbm')
-        module('html')
-        module('http')
-        module('urllib')
-        module('xmlrpc')
-
-    # To add the multitude of standard Python string encodings.
-    module('encodings', 'encodings.*')
-
-class models(package):
-    # The standard models package.  This is the contents of the
-    # "models" directory that ships with Panda; it includes a few
-    # default fonts, and some silly little sample models like smiley
-    # and teapot.
-    config(display_name = "Standard models")
-
-    # We assign it the same version as the panda3d package.  This
-    # would be assigned by default if we had a requirement on panda3d,
-    # but there's no real reason to declare that requirement.
-    config(version = PandaSystem.getPackageVersionString())
-
-    # Look for cmss12.egg on the model-path.  Wherever this is found,
-    # we assume this is the models directory.
-    pathname = getModelPath().findFile('cmss12.egg')
-    if pathname:
-        dir(pathname.getDirname(), newDir = 'models')
-
-    # Some people are used to loading the models without models/ prefix.
-    file('models.prc', extract = True, text = "model-path $MODELS_ROOT/models")
-
-
-class fmod(package):
-    # This package includes the FMod audio library.  This is
-    # full-featured and robust, but it is closed-source and the
-    # licensing is cost-free only for non-commercial products.
-
-    config(display_name = "FMod audio library")
-    require('panda3d')
-
-    file('libp3fmod_audio.dll', required = True)
-
-    file('fmod.prc', extract = True, text = """
-plugin-path $FMOD_ROOT
-audio-library-name p3fmod_audio
-""")
-
-class openal(package):
-    # This package includes the OpenAL audio libraries.  This is free
-    # in both senses, but there are some technical issues, especially
-    # on certain platforms.
-
-    config(display_name = "OpenAL audio library")
-    require('panda3d')
-
-    file('libp3openal_audio.dll', required = True)
-    file('OpenAL32.dll')
-    file('soft_oal.dll')
-    file('wrap_oal.dll')
-
-    file('openal.prc', extract = True, text = """
-plugin-path $OPENAL_ROOT
-audio-library-name p3openal_audio
-""")
-
-class audio(package):
-    # This package includes the best audio library for the given
-    # platform, assuming a non-commercial application.
-    require('panda3d')
-
-    # OpenAL seems to work pretty well universally now.
-    require('openal')
-
-class ffmpeg(package):
-    # This package includes the ffmpeg libraries and integration.
-    # It's a bit big, and is restricted by patents and the restrictive
-    # GPLv3 license, which is why it is included as a separate package.
-
-    config(display_name = "FFMpeg multimedia decoder")
-    require('panda3d')
-
-    file('libp3ffmpeg.dll', required = True)
-
-    file('ffmpeg.prc', extract = True, text = """
-plugin-path $FFMPEG_ROOT
-load-audio-type * p3ffmpeg
-load-video-type * p3ffmpeg
-""")
-
-class egg(package):
-    # This package contains the code for reading and operating on egg
-    # files and other model files.  Since the Packager automatically
-    # converts egg files to bam files, this is not needed for most
-    # Panda3D applications.
-
-    config(display_name = "Panda3D egg loader")
-    require('panda3d')
-
-    module('panda3d.egg', required = True)
-    file('libpandaegg.dll', required = True)
-    file('libp3ptloader.dll', required = True)
-
-    file('egg.prc', extract = True, text = """
-plugin-path $EGG_ROOT
-load-file-type egg pandaegg
-load-file-type p3ptloader
-
-# These are excerpted from the default Confauto.prc file.
-egg-object-type-portal          <Scalar> portal { 1 }
-egg-object-type-polylight       <Scalar> polylight { 1 }
-egg-object-type-seq24           <Switch> { 1 } <Scalar> fps { 24 }
-egg-object-type-seq12           <Switch> { 1 } <Scalar> fps { 12 }
-egg-object-type-indexed         <Scalar> indexed { 1 }
-egg-object-type-seq10           <Switch> { 1 } <Scalar> fps { 10 }
-egg-object-type-seq8            <Switch> { 1 } <Scalar> fps { 8 }
-egg-object-type-seq6            <Switch> { 1 } <Scalar>  fps { 6 }
-egg-object-type-seq4            <Switch> { 1 } <Scalar>  fps { 4 }
-egg-object-type-seq2            <Switch> { 1 } <Scalar>  fps { 2 }
-
-egg-object-type-binary          <Scalar> alpha { binary }
-egg-object-type-dual            <Scalar> alpha { dual }
-egg-object-type-glass           <Scalar> alpha { blend_no_occlude }
-
-egg-object-type-model           <Model> { 1 }
-egg-object-type-dcs             <DCS> { 1 }
-egg-object-type-notouch         <DCS> { no_touch }
-
-egg-object-type-barrier         <Collide> { Polyset descend }
-egg-object-type-sphere          <Collide> { Sphere descend }
-egg-object-type-invsphere       <Collide> { InvSphere descend }
-egg-object-type-tube            <Collide> { Tube descend }
-egg-object-type-trigger         <Collide> { Polyset descend intangible }
-egg-object-type-trigger-sphere  <Collide> { Sphere descend intangible }
-egg-object-type-floor           <Collide> { Polyset descend level }
-egg-object-type-dupefloor       <Collide> { Polyset keep descend level }
-egg-object-type-bubble          <Collide> { Sphere keep descend }
-egg-object-type-ghost           <Scalar> collide-mask { 0 }
-egg-object-type-glow            <Scalar> blend { add }
-egg-object-type-direct-widget   <Scalar> collide-mask { 0x80000000 } <Collide> { Polyset descend }
-""")
-
-class ode(package):
-    # This package contains the code for the ODE integration.
-    # As not every application uses the ODE layers, and to cut down
-    # the download size, it is provided as separate package.
-
-    config(display_name = "Panda3D Open Dynamics Engine integration")
-    require('panda3d')
-
-    module('panda3d.ode', required = True)
-    file('libpandaode.dll', required = True)
-
-class bullet(package):
-    # This package contains the code for the Bullet integration.
-    # As not every application uses the Bullet layers, and to cut down
-    # the download size, it is provided as separate package.
-
-    config(display_name = "Panda3D Bullet integration")
-    require('panda3d')
-
-    module('panda3d.bullet', required = True)
-    file('libpandabullet.dll', required = True)
-
-class physx(package):
-    # This package contains the code for the NVIDIA PhysX integration.
-    # As not every application uses the NVIDIA PhysX layers, and to cut down
-    # the download size, it is provided as separate package.
-
-    config(display_name = "Panda3D PhysX integration")
-    require('panda3d')
-
-    module('panda3d.physx', required = True)
-    file('libpandaphysx.dll', required = True)
-    file('physxcudart_20.dll')
-    file('PhysXDevice.dll')
-
-class ai(package):
-    # This package contains the PandAI library for pathfinding
-    # and seek/flee behaviors. As some games may not need this
-    # functionality, it is provided as separate package to save
-    # on package download size.
-
-    config(display_name = "Panda3D AI modules")
-    require('panda3d')
-
-    module('panda3d.ai', required = True)
-    file('libpandaai.dll', required = True)
-
-class vision(package):
-    # This package contains the code for webcam support, augmented
-    # reality and computer vision. As many games will not need this
-    # functionality, it is provided as separate package to save
-    # on package download size.
-
-    config(display_name = "Panda3D vision modules")
-    require('panda3d')
-
-    module('panda3d.vision', required = True)
-    file('libp3vision.dll', required = True)
-
-class rocket(package):
-    # This package contains the code for libRocket integration.
-    # As not every application uses libRocket GUI, and to cut down
-    # the download size, it is provided as separate package.
-
-    config(display_name = "Panda3D libRocket support")
-    require('panda3d')
-
-    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)
-
-class vrpn(package):
-    # This package contains the code for VRPN integration.
-    # Most applications don't use this, which is why it's separate.
-
-    config(display_name = "Panda3D VRPN support")
-    require('panda3d')
-
-    module('panda3d.vrpn', required = True)
-    file('libp3vrpn.dll', required = True)
-
-
-class packp3d(p3d):
-    # This application is a command-line convenience for building a p3d
-    # application out of a directory hierarchy on disk.  We build it here
-    # into its own p3d application, to allow end-users to easily build p3d
-    # applications using the appropriate version of Python and Panda for
-    # the targeted runtime.
-
-    config(display_name = "Panda3D Application Packer",
-           hidden = True, platform_specific = False,
-           keep_user_env = True)
-    require('panda3d', 'egg')
-
-    mainModule('direct.p3d.packp3d')
-
-    file('packp3d.prc', extract = True, text = "preload-textures false")
-
-
-class ppackage(p3d):
-    # As above, a packaging utility.  This is the fully-general ppackage
-    # utility, which reads pdef files (like this one!) and creates one or
-    # more packages or p3d applications.
-
-    config(display_name = "Panda3D General Package Utility",
-           hidden = True, platform_specific = False,
-           keep_user_env = True)
-    require('panda3d', 'egg')
-
-    mainModule('direct.p3d.ppackage')
-
-    file('ppackage.prc', extract = True, text = "preload-textures false")
-
-
-class ppatcher(p3d):
-    # A handy utility to go along with ppackage.  This builds
-    # patchfiles as needed in the directory structure created by
-    # ppackage.
-
-    config(display_name = "Panda3D Patch Maker",
-           hidden = True, platform_specific = False,
-           keep_user_env = True)
-    require('panda3d')
-
-    mainModule('direct.p3d.ppatcher')
-
-
-class pmerge(p3d):
-    # Another handy utility to go along with ppackage.  This
-    # merges multiple directory structures as created by
-    # ppackage into a single one.
-
-    config(display_name = "Panda3D Package Merger",
-           hidden = True, platform_specific = False,
-           keep_user_env = True)
-    require('panda3d')
-
-    mainModule('direct.p3d.pmerge')
-
-
-class pdeploy(p3d):
-    # This utility can distribute a game in the form of
-    # a standalone executable or a graphical installer.
-
-    config(display_name = "Panda3D Deployment Tool",
-           hidden = True, platform_specific = False,
-           keep_user_env = True)
-    require('panda3d', 'morepy')
-
-    mainModule('direct.p3d.pdeploy')
-

+ 0 - 359
direct/src/p3d/pdeploy.py

@@ -1,359 +0,0 @@
-#! /usr/bin/env python
-
-usageText = """
-
-This command will help you to distribute your Panda application,
-consisting of a .p3d package, into a standalone executable, graphical
-installer or an HTML webpage. It will attempt to create packages
-for every platform, if possible.
-Note that pdeploy requires an internet connection to run.
-
-When used with the 'installer' option, it is strongly advisable
-to specify most if not all of the descriptive information that can
-be passed on the command-line.
-
-Usage:
-
-  %(prog)s [opts] app.p3d standalone|installer|html
-
-Modes:
-
-  standalone
-    A standalone executable will be created that embeds the given
-    p3d file. The resulting executable will require an
-    internet connection in order to run properly.
-
-  installer
-    In this mode, installable packages will be created for as many
-    platforms as possible. To create Windows installers on
-    non-Windows platforms, you need to have the "makensis" utility
-    on your system PATH environment variable.
-
-  html
-    An HTML webpage will be generated that can be used to view
-    the provided p3d file in a browser.
-
-Options:
-
-  -n your_app
-     Short, lowercase name of the application or game. May only
-     contain alphanumeric characters, underscore or dash. This
-     name will also define the output file(s) of the process.
-     If omitted, the basename of the p3d file is used.
-
-  -N "Your Application"
-     Full name of the application or game. This is the
-     name that will be displayed to the end-user.
-     The 'display_name' config is used by default.  If it
-     is missing, the short name is used.
-
-  -v version_number
-     This should define the version number of your application
-     or game. In some deploy modes, this argument is required.
-     This should only contain alphanumeric characters, dots and
-     dashes, as otherwise the result of the deployment may be
-     invalid on some platforms.
-
-  -o output_dir
-     Indicates the directory where the output will be stored.
-     Within this directory, subdirectories will be created
-     for every platform, unless -t is provided.
-     If omitted, the current working directory is assumed.
-
-  -t token=value
-     Defines a web token or parameter to pass to the application.
-     Use this to configure how the application will be run.
-     You can pass as many -t options as you need. Some examples of
-     useful token names are width, height, log_basename, auto_start,
-     hidden and console_environment.
-
-  -P platform
-     If this option is provided, it should specify a comma-
-     separated list of platforms that the p3d package will be
-     deployed for. If omitted, it will be built for all platforms.
-     This option may be specified multiple times.
-     Examples of valid platforms are win_i386, linux_amd64 and osx_ppc.
-
-  -c
-     If this option is provided, any -P options are ignored and
-     the p3d package is only deployed for the current platform.
-     Furthermore, no per-platform subdirectories will be created
-     inside the output dirctory.
-
-  -s
-     This option only has effect in 'installer' mode. If it is
-     provided, the resulting installers will be fully self-contained,
-     will not require an internet connection to run, and start up
-     much faster. Note that pdeploy will also take a very long time
-     to finish when -s is provided.
-     If it is omitted, pdeploy will finish much quicker, and the
-     resulting installers will be smaller, but they will require
-     an internet connection for the first run, and the load time
-     will be considerably longer.
-
-  -l "License Name"
-     Specifies the name of the software license that the game
-     or application is licensed under.
-     Only relevant when generating a graphical installer.
-
-  -L licensefile.txt
-     This should point to a file that contains the full text
-     describing the software license that the game or application
-     is licensed under.
-     Only relevant when generating a graphical installer.
-
-  -O
-     Specify this option when generating a graphical installer to omit
-     the default checkboxes for "run this program" and "install a
-     desktop shortcut" on completion.
-
-  -a com.your_company
-     Short identifier of the author of the application. The default
-     is "org.panda3d", but you will most likely want to change
-     it to your own name or that of your organization or company.
-     Only relevant when generating a graphical installer.
-
-  -A "Your Company"
-     Full name of the author of the application.  The default is
-     determined from the GECOS information of the current user if
-     available; if not, your username is used.
-     Only relevant when generating a graphical installer.
-
-  -e "you@your_company.com"
-     E-mail address of the maintainer of the application.  The default
-     is username@hostname.
-     Only relevant when generating a graphical installer.
-
-  -i "path/icon32.png" -i "path/icon48.png" -i "path/icon128.png"
-     This option should be repeated several times with different square
-     image sizes.  These images will then be combined to form an icon
-     file that will be used to represent the installed application.
-     To support all platforms, it is recommended to supply images of
-     the sizes 16x16, 32x32, 48x48, 128x128, 256x256, and 512x512.
-     The larger icon sizes can safely be omitted if you cannot
-     provide images in that resolution.
-     It is recommended to use .png images for correct transparency.
-     If no images are provided, no icon will be generated.
-     Only relevant when generating a graphical installer.
-
-  -h
-     Display this help
-
-"""
-
-DEPLOY_MODES = ["standalone", "installer", "html"]
-
-import sys
-import os
-import getopt
-from direct.p3d.DeploymentTools import Standalone, Installer, Icon
-from panda3d.core import Filename, PandaSystem
-
-def usage(code, msg = ''):
-    if not msg:
-        sys.stderr.write(usageText % {'prog' : os.path.split(sys.argv[0])[1]})
-    sys.stderr.write(msg + '\n')
-    sys.exit(code)
-
-shortname = ""
-fullname = ""
-version = ""
-outputDir = Filename("./")
-tokens = {}
-platforms = []
-currentPlatform = False
-licensename = ""
-licensefile = Filename()
-authorid = ""
-authorname = ""
-authoremail = ""
-iconFiles = []
-includeRequires = False
-omitDefaultCheckboxes = False
-
-try:
-    opts, args = getopt.getopt(sys.argv[1:], 'n:N:v:o:t:P:csOl:L:a:A:e:i:h')
-except getopt.error as msg:
-    usage(1, msg or 'Invalid option')
-
-for opt, arg in opts:
-    if opt == '-n':
-        shortname = arg.strip()
-    elif opt == '-N':
-        fullname = arg.strip()
-    elif opt == '-v':
-        version = arg.strip()
-    elif opt == '-o':
-        outputDir = Filename.fromOsSpecific(arg)
-    elif opt == '-t':
-        token = arg.strip().split("=", 1)
-        tokens[token[0]] = token[1]
-    elif opt == '-P':
-        platforms.append(arg)
-    elif opt == '-c':
-        currentPlatform = True
-    elif opt == '-s':
-        includeRequires = True
-    elif opt == '-O':
-        omitDefaultCheckboxes = True
-    elif opt == '-l':
-        licensename = arg.strip()
-    elif opt == '-L':
-        licensefile = Filename.fromOsSpecific(arg)
-    elif opt == '-a':
-        authorid = arg.strip()
-    elif opt == '-A':
-        authorname = arg.strip()
-    elif opt == '-e':
-        authoremail = arg.strip()
-    elif opt == '-i':
-        iconFiles.append(Filename.fromOsSpecific(arg))
-
-    elif opt == '-h':
-        usage(0)
-    else:
-        msg = 'illegal option: ' + flag
-        print(msg)
-        sys.exit(1, msg)
-
-if not args or len(args) != 2:
-    msg = 'Wrong number of arguments, received %s, expected 2' % (
-        len(args or []))
-    usage(1, msg)
-
-appFilename = Filename.fromOsSpecific(args[0])
-if appFilename.getExtension().lower() != 'p3d':
-    print('Application filename must end in ".p3d".')
-    sys.exit(1)
-deploy_mode = args[1].lower()
-
-if not appFilename.exists():
-    print('Application filename does not exist!')
-    sys.exit(1)
-
-if shortname == '':
-    shortname = appFilename.getBasenameWoExtension()
-
-if shortname.lower() != shortname or ' ' in shortname:
-    print('\nProvided short name should be lowercase, and may not contain spaces!\n')
-
-if version == '' and deploy_mode == 'installer':
-    print('\nA version number is required in "installer" mode.\n')
-    sys.exit(1)
-
-if not outputDir:
-    print('\nYou must name the output directory with the -o parameter.\n')
-    sys.exit(1)
-if not outputDir.exists():
-    print('\nThe specified output directory does not exist!\n')
-    sys.exit(1)
-elif not outputDir.isDirectory():
-    print('\nThe specified output directory is a file!\n')
-    sys.exit(1)
-
-if deploy_mode == 'standalone':
-    s = Standalone(appFilename, tokens)
-    s.basename = shortname
-
-    if currentPlatform:
-        platform = PandaSystem.getPlatform()
-        if platform.startswith("win"):
-            s.build(Filename(outputDir, shortname + ".exe"), platform)
-        else:
-            s.build(Filename(outputDir, shortname), platform)
-    elif len(platforms) == 0:
-        s.buildAll(outputDir)
-    else:
-        for platform in platforms:
-            if platform.startswith("win"):
-                s.build(Filename(outputDir, platform + "/" + shortname + ".exe"), platform)
-            else:
-                s.build(Filename(outputDir, platform + "/" + shortname), platform)
-
-elif deploy_mode == 'installer':
-    if includeRequires:
-        tokens["verify_contents"] = "never"
-    i = Installer(appFilename, shortname, fullname, version, tokens = tokens)
-    i.includeRequires = includeRequires
-    if omitDefaultCheckboxes:
-        i.offerRun = False
-        i.offerDesktopShortcut = False
-    i.licensename = licensename
-    i.licensefile = licensefile
-    if authorid:
-        i.authorid = authorid
-    if authorname:
-        i.authorname = authorname
-    if authoremail:
-        i.authoremail = authoremail
-    if not authorname or not authoremail or not authorid:
-        print("Using author \"%s\" <%s> with ID %s" % \
-            (i.authorname, i.authoremail, i.authorid))
-
-    # Add the supplied icon images
-    if len(iconFiles) > 0:
-        failed = False
-        i.icon = Icon()
-        for iconFile in iconFiles:
-            if not i.icon.addImage(iconFile):
-                print('\nFailed to add icon image "%s"!\n' % iconFile)
-                failed = True
-        if failed:
-            sys.exit(1)
-
-    # Now build for the requested platforms.
-    if currentPlatform:
-        platform = PandaSystem.getPlatform()
-        if platform.startswith("win"):
-            i.build(outputDir, platform)
-        else:
-            i.build(outputDir, platform)
-    elif len(platforms) == 0:
-        i.buildAll(outputDir)
-    else:
-        for platform in platforms:
-            output = Filename(outputDir, platform + "/")
-            output.makeDir()
-            i.build(output, platform)
-
-    del i
-
-elif deploy_mode == 'html':
-    w, h = tokens.get("width", 800), tokens.get("height", 600)
-    if "data" not in tokens:
-        tokens["data"] = appFilename.getBasename()
-
-    print("Creating %s.html..." % shortname)
-    html = open(Filename(outputDir, shortname + ".html").toOsSpecific(), "w")
-    html.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n")
-    html.write("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n")
-    html.write("  <head>\n")
-    html.write("    <title>%s</title>\n" % fullname)
-    html.write("    <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />\n")
-    if authorname:
-        html.write("    <meta name=\"Author\" content=\"%s\" />\n" % authorname.replace('"', '\\"'))
-    html.write("  </head>\n")
-    html.write("  <body>\n")
-    html.write("    <object")
-    for key, value in tokens.items():
-        html.write(" %s=\"%s\"" % (key, value.replace('"', '\\"')))
-    if "width" not in tokens:
-        html.write(" width=\"%s\"" % w)
-    if "height" not in tokens:
-        html.write(" height=\"%s\"" % h)
-    html.write(" type=\"application/x-panda3d\">")
-    html.write("      <object width=\"%s\" height=\"%s\" classid=\"CLSID:924B4927-D3BA-41EA-9F7E-8A89194AB3AC\">\n" % (w, h))
-    for key, value in tokens.items():
-        html.write("        <param name=\"%s\" value=\"%s\" />\n" % (key, value.replace('"', '\\"')))
-    html.write("      </object>\n")
-    html.write("    </object>\n")
-    html.write("  </body>\n")
-    html.write("</html>\n")
-    html.close()
-else:
-    usage(1, 'Invalid deployment mode!')
-
-# An explicit call to exit() is required to exit the program, when
-# this module is packaged in a p3d file.
-sys.exit(0)
-

+ 0 - 106
direct/src/p3d/pmerge.py

@@ -1,106 +0,0 @@
-#! /usr/bin/env python
-
-usageText = """
-
-This script can be used to merge together the contents of two or more
-separately-built stage directories, built independently via ppackage,
-or via Packager.py.  This script also verifies the hash, file size,
-and timestamp values in the stage directory as it runs, so it can be
-run on a single standalone directory just to perform this validation.
-
-This script is actually a wrapper around Panda's PackageMerger.py.
-
-Usage:
-
-  %(prog)s [opts] [inputdir1 .. inputdirN]
-
-Parameters:
-
-  inputdir1 .. inputdirN
-    Specify the full path to the input directories you wish to merge.
-    These are the directories specified by -i on the previous
-    invocations of ppackage.  The order is mostly unimportant.
-
-Options:
-
-  -i install_dir
-     The full path to the final install directory.  This may also
-     contain some pre-existing contents; if so, it is merged with all
-     of the input directories as well.  The contents of this directory
-     are checked for self-consistency with regards to hashes and
-     timestamps.
-
-  -p packageName[,packageName...]
-     Specifies one or more particular packages by name that are to be
-     included from the input directories.  Any packages not in this
-     list are left unchanged in the install directory, even if there
-     is a newer version in one of the input directories.  If no
-     packages are named, all packages are involved.  This option may
-     be repeated.
-
-  -h
-     Display this help
-
-"""
-
-import sys
-import getopt
-import os
-
-from direct.p3d import PackageMerger
-from panda3d.core import Filename
-
-def usage(code, msg = ''):
-    sys.stderr.write(usageText % {'prog' : os.path.split(sys.argv[0])[1]})
-    sys.stderr.write(msg + '\n')
-    sys.exit(code)
-
-try:
-    opts, args = getopt.getopt(sys.argv[1:], 'i:p:h')
-except getopt.error as msg:
-    usage(1, msg)
-
-installDir = None
-packageNames = []
-for opt, arg in opts:
-    if opt == '-i':
-        installDir = Filename.fromOsSpecific(arg)
-    elif opt == '-p':
-        packageNames += arg.split(',')
-
-    elif opt == '-h':
-        usage(0)
-    else:
-        print('illegal option: ' + arg)
-        sys.exit(1)
-
-if not packageNames:
-    # No package names means allow all packages.
-    packageNames = None
-
-inputDirs = []
-for arg in args:
-    inputDirs.append(Filename.fromOsSpecific(arg))
-
-# It's now legal to have no input files if you only want to verify
-# timestamps and hashes.
-## if not inputDirs:
-##     print "no input directories specified."
-##     sys.exit(1)
-
-try:
-    pm = PackageMerger.PackageMerger(installDir)
-    for dir in inputDirs:
-        pm.merge(dir, packageNames = packageNames)
-    pm.close()
-
-except PackageMerger.PackageMergerError:
-    # Just print the error message and exit gracefully.
-    inst = sys.exc_info()[1]
-    print(inst.args[0])
-    sys.exit(1)
-
-
-# An explicit call to exit() is required to exit the program, when
-# this module is packaged in a p3d file.
-sys.exit(0)

+ 0 - 267
direct/src/p3d/ppackage.py

@@ -1,267 +0,0 @@
-#! /usr/bin/env python
-
-usageText = """
-This script can be used to produce a Panda3D downloadable "package",
-which may contain arbitrary files--for instance, Python code, bam
-files, and/or compiled DLL's--and which may be downloaded by
-application code to extend an application at runtime.
-
-In addition to packages, this script can also be used to build
-standalone p3d applications, that is, packaged Python code in a p3d
-file, for execution by the Panda3D plugin or runtime.  (But also see
-packp3d, which is designed to be a simpler interface for building
-applications.)
-
-This command will build p3d files that reference Panda3D %(version)s,
-from host %(host)s .
-
-This script is actually a wrapper around Panda's Packager.py, which
-can be invoked directly by Python code that requires a programmatic
-interface to building packages.
-
-Usage:
-
-  %(prog)s [opts] package.pdef [packageName1 .. packageNameN]
-
-Parameters:
-
-  package.pdef
-    The config file that describes the contents of the package file(s)
-    to be built, in excruciating detail.  See the Panda3D manual for
-    the syntax of this file.
-
-  packageName1 .. packageNameN
-    Specify the names of the package(s) you wish to build out of the
-    package.pdef file.  This allows you to build only a subset of the
-    packages defined in this file.  If you omit these parameters, all
-    packages are built, and packages that cannot be built are silently
-    ignored.
-
-Options:
-
-  -i install_dir
-     The full path to a local directory to copy the
-     ready-to-be-published files into.  This directory structure may
-     contain multiple different packages from multiple different
-     invocations of this script.  It is the user's responsibility to
-     copy this directory structure to a server, which will have the
-     URL specified by the packager.setHost() call appearing within the
-     pdef file.
-
-  -p
-     Automatically build patches against previous versions after
-     generating the results.  Patches are difference files that users
-     can download when updating a package, in lieu of redownloading
-     the whole package; this happens automatically if patches are
-     present.  You should generally build patches when you are
-     committing to a final, public-facing release.  Patches are
-     usually a good idea, but generating a patch for each internal
-     test build may needlessly generate a lot of small, inefficient
-     patch files instead of a few larger ones.  You can also generate
-     patches after the fact, by running ppatcher on the install
-     directory.
-
-  -s search_dir
-     Additional directories to search for previously-built packages.
-     This option may be repeated as necessary.  These directories may
-     also be specified with the pdef-path Config.prc variable.
-
-  -S file.crt[,chain.crt[,file.key[,\"password\"]]]
-     Signs any resulting p3d file(s) with the indicated certificate.
-     You may specify the signing certificate, the optional
-     authorization chain, and the private key in three different
-     files, or they may all be combined in the first file.  If the
-     private key is encrypted, the password will be required to
-     decrypt it.
-
-  -D
-     Sets the allow_python_dev flag in any applications built with
-     this command.  This enables additional runtime debug operations,
-     particularly the -i option to the panda3d command, which enables
-     a live Python prompt within the application's environment.
-     Setting this flag may be useful to develop an application
-     initially, but should not be set on an application intended for
-     deployment.
-
-  -N
-     If this option is set, Packager will not try to compile any Python
-     files to .pyc or .pyo, instead storing the original source files.
-
-  -u
-     On the Mac OSX platform, this means that Panda was built with
-     universal binaries, and the package should be built that way as
-     well (that is, a version of the the package should be created for
-     each supported architecture).  On other platforms, this option
-     does nothing.  This is therefore safe to apply in all cases, if
-     you wish to take advantage of universal binaries.  This is
-     equivalent to "-P osx_i386 -P osx_amd64" on Mac platforms.
-
-  -P platform
-     Specify the platform to masquerade as.  The default is whatever
-     platform Panda has been built for.  You can use this on Mac OSX
-     in order to build packages for an alternate architecture if you
-     have built Panda with universal binaries; you may repeat this
-     option for each architecture you wish to support.  For other
-     platforms, it is probably a mistake to set this.  However, see
-     the option -u.
-
-  -R sysroot
-     Specify the sysroot that these files were compiled against.  This
-     will shadow the system shared libraries, so that alternate
-     versions are used instead of the system versions.  If any program
-     references a library, say /usr/lib/libfoo.so, and
-     /sysroot/usr/lib/libfoo.so exists instead, that file will be used
-     instead of the system library.  This is particularly useful for
-     cross-compilation.  At the moment, this is supported only on OSX.
-
-  -H
-     Treats a packager.setHost() call in the pdef file as if it were
-     merely a call to packager.addHost().  This allows producing a
-     package for an alternate host than its normally configured host,
-     which is sometimes useful in development.
-
-  -a suffix
-     Appends the given suffix to the p3d filename, before the extension.
-     This is useful when the same packages are compiled several times
-     but using different settings, and you want to mark those packages
-     as such.  This only applies to .p3d packages, not to other types
-     of packages!
-
-  -v
-     Emit a warning for any file not recognized by the dir() command
-     (indicating there may be a need for addExtensions(...)).
-
-  -h
-     Display this help
-"""
-
-import sys
-import getopt
-import os
-
-from direct.p3d import Packager
-from panda3d.core import *
-
-def usage(code, msg = ''):
-    sys.stderr.write(usageText % {
-        'version' : PandaSystem.getPackageVersionString(),
-        'host' : PandaSystem.getPackageHostUrl(),
-        'prog' : os.path.split(sys.argv[0])[1]
-        })
-    sys.stderr.write(msg + '\n')
-    sys.exit(code)
-
-installDir = None
-buildPatches = False
-installSearch = []
-signParams = []
-allowPythonDev = False
-storePythonSource = False
-universalBinaries = False
-systemRoot = None
-ignoreSetHost = False
-verbosePrint = False
-p3dSuffix = ''
-platforms = []
-
-try:
-    opts, args = getopt.getopt(sys.argv[1:], 'i:ps:S:DNuP:R:Ha:hv')
-except getopt.error as msg:
-    usage(1, msg)
-
-for opt, arg in opts:
-    if opt == '-i':
-        installDir = Filename.fromOsSpecific(arg)
-    elif opt == '-p':
-        buildPatches = True
-    elif opt == '-s':
-        installSearch.append(Filename.fromOsSpecific(arg))
-    elif opt == '-S':
-        tokens = arg.split(',')
-        while len(tokens) < 4:
-            tokens.append('')
-        certificate, chain, pkey, password = tokens[:4]
-        signParams.append((Filename.fromOsSpecific(certificate),
-                           Filename.fromOsSpecific(chain),
-                           Filename.fromOsSpecific(pkey),
-                           Filename.fromOsSpecific(password)))
-    elif opt == '-D':
-        allowPythonDev = True
-    elif opt == '-N':
-        storePythonSource = True
-    elif opt == '-u':
-        universalBinaries = True
-    elif opt == '-P':
-        platforms.append(arg)
-    elif opt == '-R':
-        systemRoot = arg
-    elif opt == '-H':
-        ignoreSetHost = True
-    elif opt == '-a':
-        p3dSuffix = arg
-
-    elif opt == '-v':
-        verbosePrint = True
-
-    elif opt == '-h':
-        usage(0)
-    else:
-        print('illegal option: ' + arg)
-        sys.exit(1)
-
-if not args:
-    usage(0)
-
-packageDef = Filename.fromOsSpecific(args[0])
-packageNames = None
-if len(args) > 1:
-    packageNames = args[1:]
-
-# Add the directory containing the pdef file itself to sys.path, to
-# help the Packager locate modules where a pathname isn't specified.
-dirname = packageDef.getDirname()
-if dirname:
-    sys.path.append(Filename(dirname).toOsSpecific())
-else:
-    sys.path.append('.')
-
-if universalBinaries:
-    if platforms:
-        print('\nYou may not specify both -u and -P.\n')
-        sys.exit(1)
-    if PandaSystem.getPlatform().startswith('osx_'):
-        platforms = ['osx_i386', 'osx_amd64']
-
-if not platforms:
-    platforms = [PandaSystem.getPlatform()]
-
-for platform in platforms:
-    packager = Packager.Packager(platform = platform)
-    packager.installDir = installDir
-    packager.installSearch = installSearch + packager.installSearch
-    if installDir is not None:
-        packager.installSearch = [installDir] + packager.installSearch
-    packager.signParams = signParams
-    packager.allowPythonDev = allowPythonDev
-    packager.storePythonSource = storePythonSource
-    packager.systemRoot = systemRoot
-    packager.ignoreSetHost = ignoreSetHost
-    packager.verbosePrint = verbosePrint
-    packager.p3dSuffix = p3dSuffix
-
-    try:
-        packager.setup()
-        packages = packager.readPackageDef(packageDef, packageNames = packageNames)
-        packager.close()
-        if buildPatches:
-            packager.buildPatches(packages)
-
-    except Packager.PackagerError:
-        # Just print the error message and exit gracefully.
-        inst = sys.exc_info()[1]
-        print(inst.args[0])
-        sys.exit(1)
-
-# An explicit call to exit() is required to exit the program, when
-# this module is packaged in a p3d file.
-sys.exit(0)

+ 0 - 101
direct/src/p3d/ppatcher.py

@@ -1,101 +0,0 @@
-#! /usr/bin/env python
-
-usageText = """
-
-This script generates the patches required to support incremental
-download of Panda3D packages.  It can be run as a post-process on a
-directory hierarchy created by ppackage; it will examine the directory
-hierarchy, and create any patches that appear to be missing.
-
-You may run ppackage on the same directory hierarchy as many times as
-you like, without creating patches.  You may then download and test
-the resulting files--users connecting to the tree without fresh
-patches will be forced to download the entire file, instead of making
-an incremental download, but the entire process will work otherwise.
-When you are satisfied that all of the files are ready to be released,
-you may run ppackage on the directory hierarchy to generate the
-required patches.
-
-Generating the patches just before final release is a good idea to
-limit the number of trivially small patches that are created.  Each
-time this script is run, a patch is created from the previous version,
-and these patches daisy-chain together to define a complete update
-sequence.  If you run this script on internal releases, you will
-generate a long chain of small patches that your users must download;
-this is pointless if there is no possibility of anyone having
-downloaded one of the intervening versions.
-
-You can also generate patches with the -p option to ppackage, but that
-only generates patches for the specific packages built by that
-invocation of ppackage.  If you use the ppatcher script instead, it
-will generate patches for all packages (or the set of packages that
-you name specifically).
-
-This script is actually a wrapper around Panda's PatchMaker.py.
-
-Usage:
-
-  %(prog)s [opts] [packageName1 .. packageNameN]
-
-Parameters:
-
-  packageName1 .. packageNameN
-    Specify the names of the package(s) you wish to generate patches
-    for.  This allows you to build patches for only a subset of the
-    packages found in the tree.  If you omit these parameters, patches
-    are built for all packages that require them.
-
-Options:
-
-  -i install_dir
-     The full path to the install directory.  This should be the same
-     directory named by the -i parameter to ppackage.
-
-  -h
-     Display this help
-
-"""
-
-import sys
-import getopt
-import os
-
-from direct.p3d.PatchMaker import PatchMaker
-from panda3d.core import Filename
-
-def usage(code, msg = ''):
-    sys.stderr.write(usageText % {'prog' : os.path.split(sys.argv[0])[1]})
-    sys.stderr.write(msg + '\n')
-    sys.exit(code)
-
-try:
-    opts, args = getopt.getopt(sys.argv[1:], 'i:h')
-except getopt.error as msg:
-    usage(1, msg)
-
-installDir = None
-for opt, arg in opts:
-    if opt == '-i':
-        installDir = Filename.fromOsSpecific(arg)
-
-    elif opt == '-h':
-        usage(0)
-    else:
-        print('illegal option: ' + arg)
-        sys.exit(1)
-
-packageNames = args
-
-if not installDir:
-    installDir = Filename('install')
-
-if not packageNames:
-    # "None" means all packages.
-    packageNames = None
-
-pm = PatchMaker(installDir)
-pm.buildPatches(packageNames = packageNames)
-
-# An explicit call to exit() is required to exit the program, when
-# this module is packaged in a p3d file.
-sys.exit(0)

+ 0 - 79
direct/src/p3d/runp3d.py

@@ -1,79 +0,0 @@
-#! /usr/bin/env python
-
-"""
-
-This tool will invoke the AppRunner to execute a packaged p3d
-application.  It requires that that the current Panda3D and Python
-versions match the version expected by the application.
-
-Normally, you do not need to use this tool; instead, use the provided
-standalone panda3d executable to invoke any p3d application.  Using
-panda3d will guarantee that the correct versions of Panda3D and Python
-are used to run the application.  However, there may be occasions when
-it is useful to use this tool to run the application with the current
-build instead of with its advertised version requirements.
-
-Usage:
-
-  runp3d.py app.p3d [args]
-
-The command-line arguments following the application name are passed
-into the application unchanged.
-
-See pack3d.p3d for an application that generates these p3d files.
-
-"""
-
-import sys
-import getopt
-from .AppRunner import AppRunner, ArgumentError
-from direct.task.TaskManagerGlobal import taskMgr
-from panda3d.core import Filename
-
-def parseSysArgs():
-    """ Handles sys.argv, if there are any local arguments, and
-    returns a new argv suitable for passing into the
-    application. """
-
-    # We prefix a "+" sign, following the GNU convention, to tell
-    # getopt not to parse options following the first non-option
-    # parameter.
-    opts, args = getopt.getopt(sys.argv[1:], '+h')
-
-    for option, value in opts:
-        if option == '-h':
-            print(__doc__)
-            sys.exit(1)
-
-    if not args or not args[0]:
-        raise ArgumentError("No Panda app specified.  Use:\nrunp3d.py app.p3d")
-
-    arg0 = args[0]
-    p3dFilename = Filename.fromOsSpecific(arg0)
-    if p3dFilename.exists():
-        p3dFilename.makeAbsolute()
-        arg0 = p3dFilename.toOsSpecific()
-
-    return [arg0] + args[1:]
-
-def runPackedApp(pathname):
-    runner = AppRunner()
-    runner.gotWindow = True
-    try:
-        runner.setP3DFilename(pathname, tokens = [], argv = [],
-                              instanceId = 0, interactiveConsole = False)
-    except ArgumentError as e:
-        print(e.args[0])
-        sys.exit(1)
-
-if __name__ == '__main__':
-    runner = AppRunner()
-    runner.gotWindow = True
-    try:
-        argv = parseSysArgs()
-        runner.setP3DFilename(argv[0], tokens = [], argv = argv,
-                              instanceId = 0, interactiveConsole = False)
-    except ArgumentError as e:
-        print(e.args[0])
-        sys.exit(1)
-    taskMgr.run()

+ 0 - 167
direct/src/p3d/thirdparty.pdef

@@ -1,167 +0,0 @@
-# This file defines a number of standard "packages" that correspond to
-# a Panda3D distribution.  These packages are built by passing this
-# file to the ppackage utility, either as a packaged application, or
-# as the module direct.p3d.ppackage.
-
-# The packages in this file define the thirdparty
-# packages that could be used by a .p3d archive.
-
-# When needed, these packages are downloaded by the core API, from the
-# host URL specified in a given p3d file, and not from any hardcoded
-# URL.  Thus, any custom version of Panda3D may be hosted on any
-# server in the world, and any version of the plugin can download it.
-
-# Also see panda3d.pdef.
-
-from panda3d.core import Filename
-import sys
-
-class wx(package):
-    config(display_name = "wxPython GUI Toolkit")
-    #config(gui_app = True)
-    require('panda3d')
-
-    module('direct.showbase.WxGlobal')
-    module('direct.wxwidgets')
-    module('direct.wxwidgets.*')
-    module('wx', required = True)
-    module('wx.*')
-
-class tk(package):
-    config(display_name = "Tk GUI Toolkit")
-    #config(gui_app = True)
-    require('panda3d')
-
-    if sys.version_info >= (3, 0):
-        module('tkinter', '_tkinter', required = True)
-        module('tkinter.colorchooser', 'tkinter.commondialog',
-               'tkinter.constants', 'tkinter.dialog', 'tkinter.dnd',
-               'tkinter.filedialog', 'tkinter.font', 'tkinter.messagebox',
-               'tkinter.scrolledtext', 'tkinter.simpledialog', 'tkinter.tix',
-               'tkinter.ttk')
-    else:
-        module('Tkinter', '_tkinter', required = True)
-        module('Tkconstants', 'Tkdnd', 'Tix', 'ScrolledText', 'turtle',
-               'tkColorChooser', 'tkCommonDialog', 'tkFileDialog',
-               'tkFont', 'tkMessageBox', 'tkSimpleDialog')
-
-    module('direct.showbase.TkGlobal',
-           'direct.tkpanels',
-           'direct.tkwidgets')
-
-    try:
-        if sys.version_info >= (3, 0):
-            from tkinter import Tcl
-        else:
-            from Tkinter import Tcl
-        tcl = Tcl()
-        dir = Filename.fromOsSpecific(tcl.eval("info library"))
-        ver = tcl.eval("info tclversion")
-
-        file(Filename(dir, "*.tcl"), newDir = 'lib/tcl' + ver, extract = True)
-    except ImportError:
-        pass
-
-    try:
-        import Pmw
-    except ImportError:
-        Pmw = None
-
-    if Pmw is not None:
-        Pmw = sys.modules['_Pmw']
-        sys.modules['Pmw'] = Pmw
-        sys.modules['Pmw'].__name__ = 'Pmw'
-        module('Pmw', 'Pmw.Pmw_1_3.lib.*')
-        path = Filename.fromOsSpecific(Pmw.__path__[0])
-        file(Filename(path, 'Pmw_1_3/lib/Pmw.def'), newDir = 'Pmw/Pmw_1_3/lib')
-
-class sqlite(package):
-    config(display_name = "SQLite Interface")
-    require('panda3d')
-
-    module('sqlite', 'sqlite3')
-
-class numpy(package):
-    config(display_name = "NumPy")
-    require('panda3d')
-
-    module('numpy', required = True)
-
-class pygame(package):
-    config(display_name = "PyGame")
-    require('panda3d', 'numpy')
-
-    module('pygame', required = True)
-    module('pygame.*', 'pygame._view')
-
-class twisted(package):
-    config(display_name = "Twisted")
-    require('panda3d')
-
-    module('twisted', 'twisted._version', required = True)
-    module('twisted.application', 'twisted.conch', 'twisted.cred',
-           'twisted.enterprise', 'twisted.internet', 'twisted.lore',
-           'twisted.mail', 'twisted.manhole', 'twisted.names',
-           'twisted.news', 'twisted.pair', 'twisted.persisted',
-           'twisted.plugin', 'twisted.plugins', 'twisted.python',
-           'twisted.runner', 'twisted.scripts', 'twisted.spread',
-           'twisted.tap', 'twisted.trial', 'twisted.vfs',
-           'twisted.web', 'twisted.web2', 'twisted.words')
-
-    module('twisted.*', 'twisted.*.*')
-
-class pil(package):
-    config(display_name = "Python Imaging Library")
-    require('panda3d')
-
-    module('Image', required = True)
-    module('PIL', 'ArgImagePlugin', 'BdfFontFile',
-           'BmpImagePlugin', 'BufrStubImagePlugin', 'ContainerIO',
-           'CurImagePlugin', 'DcxImagePlugin', 'EpsImagePlugin',
-           'ExifTags', 'FitsStubImagePlugin', 'FliImagePlugin',
-           'FontFile', 'FpxImagePlugin', 'GbrImagePlugin', 'GdImageFile',
-           'GifImagePlugin', 'GimpGradientFile', 'GimpPaletteFile',
-           'GribStubImagePlugin', 'Hdf5StubImagePlugin',
-           'IcnsImagePlugin', 'IcoImagePlugin', 'ImImagePlugin',
-           'ImageChops', 'ImageCms', 'ImageColor', 'ImageDraw',
-           'ImageDraw2', 'ImageEnhance', 'ImageFile', 'ImageFileIO',
-           'ImageFilter', 'ImageFont', 'ImageGL', 'ImageGrab',
-           'ImageMath', 'ImageMode', 'ImageOps', 'ImagePalette',
-           'ImagePath', 'ImageQt', 'ImageSequence', 'ImageShow',
-           'ImageStat', 'ImageTk', 'ImageTransform', 'ImageWin',
-           'ImtImagePlugin', 'IptcImagePlugin', 'JpegImagePlugin',
-           'McIdasImagePlugin', 'MicImagePlugin', 'MpegImagePlugin',
-           'MspImagePlugin', 'OleFileIO', 'PSDraw', 'PaletteFile',
-           'PalmImagePlugin', 'PcdImagePlugin', 'PcfFontFile',
-           'PcxImagePlugin', 'PdfImagePlugin', 'PixarImagePlugin',
-           'PngImagePlugin', 'PpmImagePlugin', 'PsdImagePlugin',
-           'SgiImagePlugin', 'SpiderImagePlugin', 'SunImagePlugin',
-           'TarIO', 'TgaImagePlugin', 'TiffImagePlugin', 'TiffTags',
-           'WalImageFile', 'WmfImagePlugin', 'XVThumbImagePlugin',
-           'XbmImagePlugin', 'XpmImagePlugin', '_imaging',
-           '_imagingmath', '_imagingtk')
-
-class pyopengl(package):
-    config(display_name = "PyOpenGL")
-    require('panda3d', 'numpy')
-
-    module('OpenGL', 'OpenGL.GL', required = True)
-    module('OpenGL.GLU', 'OpenGL.GLUT', 'OpenGL.GLE', 'OpenGL.GLX')
-
-class httplib2(package):
-    config(display_name = "httplib2", platform_specific = False)
-    require('panda3d')
-
-    module('httplib2', required = True)
-
-class box2d(package):
-    config(display_name = "Box2D")
-    require('panda3d')
-
-    module('Box2D', required = True)
-
-class pyglet(package):
-    config(display_name = "pyglet", platform_specific = False)
-    require('panda3d', 'morepy')
-
-    module('pyglet', required = True)

+ 0 - 351
direct/src/plugin/binaryXml.cxx

@@ -1,351 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file binaryXml.cxx
- * @author drose
- * @date 2009-07-13
- */
-
-#include "binaryXml.h"
-#include "p3d_lock.h"
-#include <sstream>
-
-using std::istream;
-using std::ostream;
-using std::ostringstream;
-using std::string;
-
-static const bool debug_xml_output = false;
-
-static LOCK xml_lock;
-static bool xml_lock_initialized = false;
-
-#define DO_BINARY_XML 1
-
-enum NodeType {
-  NT_unknown,
-  NT_document,
-  NT_element,
-  NT_text,
-};
-
-// This typedef defines a 32-bit unsigned integer.  It's used for passing
-// values through the binary XML stream.
-typedef unsigned int xml_uint32;
-
-// These are both prime numbers, though I don't know if that really matters.
-// Mainly, they're big random numbers.
-static const xml_uint32 length_nonce1 = 812311453;
-static const xml_uint32 length_nonce2 = 612811373;
-
-/**
- * Should be called before spawning any threads to ensure the lock is
- * initialized.
- */
-void
-init_xml() {
-  if (!xml_lock_initialized) {
-    INIT_LOCK(xml_lock);
-    xml_lock_initialized = true;
-  }
-}
-
-/**
- * Recursively writes a node and all of its children to the given stream.
- */
-static void
-write_xml_node(ostream &out, TiXmlNode *xnode) {
-  const string &value = xnode->ValueStr();
-  xml_uint32 value_length = value.length();
-  xml_uint32 value_proof = (value_length + length_nonce1) * length_nonce2;
-
-  // We write out not only value_length, but the same value again hashed by
-  // length_nonce1 and 2 (and truncated back to xml_uint32), just to prove to
-  // the reader that we're still on the same page.  We do this only on the top
-  // node; we don't bother for the nested nodes.
-  out.write((char *)&value_length, sizeof(value_length));
-  out.write((char *)&value_proof, sizeof(value_proof));
-  out.write(value.data(), value_length);
-
-  // Now write out the node type.
-  NodeType type = NT_element;
-  if (xnode->ToDocument() != nullptr) {
-    type = NT_document;
-  } else if (xnode->ToElement() != nullptr) {
-    type = NT_element;
-  } else if (xnode->ToText() != nullptr) {
-    type = NT_text;
-  } else {
-    type = NT_unknown;
-  }
-
-  out.put((char)type);
-  // We don't bother to write any further data for the unknown types.
-  if (type == NT_unknown) {
-    return;
-  }
-
-  if (type == NT_element) {
-    // Write the element attributes.
-    TiXmlElement *xelement = xnode->ToElement();
-    assert(xelement != nullptr);
-    const TiXmlAttribute *xattrib = xelement->FirstAttribute();
-
-    while (xattrib != nullptr) {
-      // We have an attribute.
-      out.put((char)true);
-
-      string name = xattrib->Name();
-      xml_uint32 name_length = name.length();
-      out.write((char *)&name_length, sizeof(name_length));
-      out.write(name.data(), name_length);
-
-      const string &value = xattrib->ValueStr();
-      xml_uint32 value_length = value.length();
-      out.write((char *)&value_length, sizeof(value_length));
-      out.write(value.data(), value_length);
-
-      xattrib = xattrib->Next();
-    }
-
-    // The end of the attributes list.
-    out.put((char)false);
-  }
-
-  // Now write all of the children.
-  TiXmlNode *xchild = xnode->FirstChild();
-  while (xchild != nullptr) {
-    // We have a child.
-    out.put((char)true);
-    write_xml_node(out, xchild);
-    xchild = xchild->NextSibling();
-  }
-
-  // The end of the children list.
-  out.put((char)false);
-}
-
-/**
- * Recursively reads a node and all of its children to the given stream.
- * Returns the newly-allocated node.  The caller is responsible for eventually
- * deleting the return value.  Returns NULL on error.
- */
-static TiXmlNode *
-read_xml_node(istream &in, char *&buffer, xml_uint32 &buffer_length,
-              ostream &logfile) {
-  xml_uint32 value_length;
-  in.read((char *)&value_length, sizeof(value_length));
-  if (in.gcount() != sizeof(value_length)) {
-    return nullptr;
-  }
-  xml_uint32 value_proof_expect = (value_length + length_nonce1) * length_nonce2;
-  xml_uint32 value_proof;
-  in.read((char *)&value_proof, sizeof(value_proof));
-  if (in.gcount() != sizeof(value_proof)) {
-    return nullptr;
-  }
-  if (value_proof != value_proof_expect) {
-    // Hey, we ran into garbage: the proof value didn't match our expected
-    // proof value.
-    logfile << "Garbage on XML stream!\n";
-
-    // Print out the garbage; maybe it will help the developer figure out
-    // where it came from.
-    logfile << "Begin garbage:\n";
-    ostringstream strm;
-    strm.write((char *)&value_length, sizeof(value_length));
-    strm.write((char *)&value_proof, sizeof(value_proof));
-    logfile << strm.str();
-    for (size_t i = 0; i < 100; ++i) {
-      int ch = in.get();
-      if (ch != EOF) {
-        logfile.put(ch);
-      }
-    }
-    logfile << "\n";
-    logfile << "End garbage.\n";
-    return nullptr;
-  }
-
-  if (value_length > buffer_length) {
-    delete[] buffer;
-    buffer_length = value_length;
-    buffer = new char[buffer_length];
-  }
-
-  in.read(buffer, value_length);
-  string value(buffer, value_length);
-
-  // Read the node type.
-  NodeType type = (NodeType)in.get();
-  if (type == NT_unknown) {
-    return nullptr;
-  }
-
-  TiXmlNode *xnode = nullptr;
-  if (type == NT_element) {
-    xnode = new TiXmlElement(value);
-  } else if (type == NT_document) {
-    xnode = new TiXmlDocument;
-  } else if (type == NT_text) {
-    xnode = new TiXmlText(value);
-  } else {
-    assert(false);
-  }
-
-  if (type == NT_element) {
-    // Read the element attributes.
-    TiXmlElement *xelement = xnode->ToElement();
-    assert(xelement != nullptr);
-    bool got_attrib = (bool)(in.get() != 0);
-
-    while (got_attrib && in && !in.eof()) {
-      // We have an attribute.
-      xml_uint32 name_length;
-      in.read((char *)&name_length, sizeof(name_length));
-      if (in.gcount() != sizeof(name_length)) {
-        delete xnode;
-        return nullptr;
-      }
-
-      if (name_length > buffer_length) {
-        delete[] buffer;
-        buffer_length = name_length;
-        buffer = new char[buffer_length];
-      }
-
-      in.read(buffer, name_length);
-      string name(buffer, name_length);
-
-      xml_uint32 value_length;
-      in.read((char *)&value_length, sizeof(value_length));
-      if (in.gcount() != sizeof(value_length)) {
-        delete xnode;
-        return nullptr;
-      }
-
-      if (value_length > buffer_length) {
-        delete[] buffer;
-        buffer_length = value_length;
-        buffer = new char[buffer_length];
-      }
-
-      in.read(buffer, value_length);
-      string value(buffer, value_length);
-
-      xelement->SetAttribute(name, value);
-
-      got_attrib = (bool)(in.get() != 0);
-    }
-  }
-
-  // Now read all of the children.
-  bool got_child = (bool)(in.get() != 0);
-
-  while (got_child && in && !in.eof()) {
-    // We have a child.
-    TiXmlNode *xchild = read_xml_node(in, buffer, buffer_length, logfile);
-    if (xchild != nullptr) {
-      xnode->LinkEndChild(xchild);
-    }
-
-    got_child = (bool)(in.get() != 0);
-  }
-
-  return xnode;
-}
-
-
-
-/**
- * Writes the indicated TinyXml document to the given stream.
- */
-void
-write_xml(ostream &out, TiXmlDocument *doc, ostream &logfile) {
-  assert(xml_lock_initialized);
-  ACQUIRE_LOCK(xml_lock);
-
-#ifdef DO_BINARY_XML
-  // Binary write.
-  write_xml_node(out, doc);
-
-#else
-  // Formatted ASCII write.
-
-  // We need a declaration to write it safely.
-  TiXmlDeclaration decl("1.0", "utf-8", "");
-  doc->InsertBeforeChild(doc->FirstChild(), decl);
-
-  out << *doc;
-#endif
-
-  out << flush;
-
-  if (debug_xml_output) {
-    // Write via ostringstream, so it all goes in one operation, to help out
-    // the interleaving from multiple threads.
-    ostringstream logout;
-    logout << "sent: " << *doc << "\n";
-    logfile << logout.str() << flush;
-  }
-
-  RELEASE_LOCK(xml_lock);
-}
-
-/**
- * Reads a TinyXml document from the given stream, and returns it.  If the
- * document is not yet available, blocks until it is, or until there is an
- * error condition on the input.
- *
- * The return value is NULL if there is an error, or the newly-allocated
- * document if it is successfully read.  If not NULL, the document has been
- * allocated with new, and should be eventually freed by the caller with
- * delete.
- */
-TiXmlDocument *
-read_xml(istream &in, ostream &logfile) {
-  // We don't acquire xml_lock while reading.  We can't, because our XML
-  // readers are all designed to block until data is available, and they can't
-  // block while holding the lock.
-
-  // Fortunately, there should be only one reader at a time, so a lock isn't
-  // really needed here.
-
-#if DO_BINARY_XML
-  // binary read.
-  xml_uint32 buffer_length = 128;
-  char *buffer = new char[buffer_length];
-  TiXmlNode *xnode = read_xml_node(in, buffer, buffer_length, logfile);
-  delete[] buffer;
-  if (xnode == nullptr) {
-    return nullptr;
-  }
-
-  TiXmlDocument *doc = xnode->ToDocument();
-  assert(doc != nullptr);
-
-#else
-  // standard ASCII read.
-  TiXmlDocument *doc = new TiXmlDocument;
-  in >> *doc;
-  if (in.fail() || in.eof()) {
-    delete doc;
-    return nullptr;
-  }
-#endif
-
-  if (debug_xml_output) {
-    // Write via ostringstream, so it all goes in one operation, to help out
-    // the interleaving from multiple threads.
-    ostringstream logout;
-    logout << "received: " << *doc << "\n";
-    logfile << logout.str() << flush;
-  }
-
-  return doc;
-}

+ 0 - 29
direct/src/plugin/binaryXml.h

@@ -1,29 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file binaryXml.h
- * @author drose
- * @date 2009-07-13
- */
-
-#ifndef BINARYXML_H
-#define BINARYXML_H
-
-#include "get_tinyxml.h"
-#include "handleStream.h"
-#include <iostream>
-
-// A pair of functions to input and output the TinyXml constructs on the
-// indicated streams.  We could, of course, use the TinyXml output operators,
-// but this is a smidge more efficient and gives us more control.
-
-void init_xml();
-void write_xml(std::ostream &out, TiXmlDocument *doc, std::ostream &logfile);
-TiXmlDocument *read_xml(std::istream &in, std::ostream &logfile);
-
-#endif

BIN
direct/src/plugin/failed.pgm


BIN
direct/src/plugin/failed.xcf


+ 0 - 31
direct/src/plugin/fhandle.h

@@ -1,31 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file fhandle.h
- * @author drose
- * @date 2009-08-29
- */
-
-#ifndef FHANDLE_H
-#define FHANDLE_H
-
-// This header file simply defines the FHandle type, which is used to pass
-// around a handle to an open file object.
-
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-typedef HANDLE FHandle;
-static const HANDLE invalid_fhandle = INVALID_HANDLE_VALUE;
-#else
-// On POSIX, we use a file descriptor as a "handle".
-typedef int FHandle;
-static const int invalid_fhandle = -1;
-#endif
-
-#endif

+ 0 - 102
direct/src/plugin/fileSpec.I

@@ -1,102 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file fileSpec.I
- * @author drose
- * @date 2009-06-29
- */
-
-/**
- * Returns the relative path to this file on disk, within the package root
- * directory.
- */
-inline const std::string &FileSpec::
-get_filename() const {
-  return _filename;
-}
-
-/**
- * Changes the relative path to this file on disk, within the package root
- * directory.
- */
-inline void FileSpec::
-set_filename(const std::string &filename) {
-  _filename = filename;
-}
-
-/**
- * Returns the full path to this file on disk.
- */
-inline std::string FileSpec::
-get_pathname(const std::string &package_dir) const {
-  return package_dir + "/" + _filename;
-}
-
-/**
- * Returns the expected size of this file on disk, in bytes.
- */
-inline size_t FileSpec::
-get_size() const {
-  return _size;
-}
-
-/**
- * Returns the expected last-modify timestamp of this file on disk.
- */
-inline time_t FileSpec::
-get_timestamp() const {
-  return _timestamp;
-}
-
-/**
- * Returns true if we have successfully read a hash value, false otherwise.
- */
-inline bool FileSpec::
-has_hash() const {
-  return _got_hash;
-}
-
-/**
- * After a call to quick_verify() or full_verify(), this method *may* return a
- * pointer to a FileSpec that represents the actual data read on disk, or it
- * may return NULL.  If this returns a non-NULL value, you may use it to
- * extract the md5 hash of the existing file, thus saving the effort of
- * performing the hash twice.
- */
-inline const FileSpec *FileSpec::
-get_actual_file() const {
-  return _actual_file;
-}
-
-/**
- * Returns the integer value corresponding to the indicated hex digit.
- * Returns -1 if it is not a hex digit.
- */
-inline int FileSpec::
-decode_hexdigit(char c) {
-  if (isdigit(c)) {
-    return c - '0';
-  }
-  c = tolower(c);
-  if (c >= 'a' && c <= 'f') {
-    return c - 'a' + 10;
-  }
-
-  return -1;
-}
-
-/**
- * Returns the hex digit corresponding to the indicated integer value.
- */
-inline char FileSpec::
-encode_hexdigit(int c) {
-  if (c >= 10) {
-    return c - 10 + 'a';
-  }
-  return c + '0';
-}

+ 0 - 486
direct/src/plugin/fileSpec.cxx

@@ -1,486 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file fileSpec.cxx
- * @author drose
- * @date 2009-06-29
- */
-
-#include "fileSpec.h"
-#include "wstring_encode.h"
-#include <openssl/md5.h>
-
-#include <fstream>
-#include <fcntl.h>
-#include <assert.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <time.h>
-
-#ifdef _WIN32
-#include <sys/utime.h>
-#include <direct.h>
-#define utimbuf _utimbuf
-
-#else
-#include <utime.h>
-
-#endif
-
-using std::istream;
-using std::ostream;
-using std::string;
-using std::wstring;
-
-/**
- *
- */
-FileSpec::
-FileSpec() {
-  _size = 0;
-  _timestamp = 0;
-  memset(_hash, 0, hash_size);
-  _got_hash = false;
-  _actual_file = nullptr;
-}
-
-/**
- *
- */
-FileSpec::
-FileSpec(const FileSpec &copy) :
-  _filename(copy._filename),
-  _size(copy._size),
-  _timestamp(copy._timestamp),
-  _got_hash(copy._got_hash)
-{
-  memcpy(_hash, copy._hash, hash_size);
-  _actual_file = nullptr;
-}
-
-/**
- *
- */
-void FileSpec::
-operator = (const FileSpec &copy) {
-  _filename = copy._filename;
-  _size = copy._size;
-  _timestamp = copy._timestamp;
-  memcpy(_hash, copy._hash, hash_size);
-  _got_hash = copy._got_hash;
-}
-
-/**
- *
- */
-FileSpec::
-~FileSpec() {
-  if (_actual_file != nullptr) {
-    delete _actual_file;
-  }
-}
-
-/**
- * Reads the data from the indicated XML file.
- */
-void FileSpec::
-load_xml(TiXmlElement *xelement) {
-  const char *filename = xelement->Attribute("filename");
-  if (filename != nullptr) {
-    _filename = filename;
-  }
-
-  const char *size = xelement->Attribute("size");
-  if (size != nullptr) {
-    char *endptr;
-    _size = strtoul(size, &endptr, 10);
-  }
-
-  const char *timestamp = xelement->Attribute("timestamp");
-  if (timestamp != nullptr) {
-    char *endptr;
-    _timestamp = strtoul(timestamp, &endptr, 10);
-  }
-
-  _got_hash = false;
-  const char *hash = xelement->Attribute("hash");
-  if (hash != nullptr && strlen(hash) == (hash_size * 2)) {
-    // Decode the hex hash string.
-    _got_hash = decode_hex(_hash, hash, hash_size);
-  }
-}
-
-/**
- * Stores the data to the indicated XML file.
- */
-void FileSpec::
-store_xml(TiXmlElement *xelement) {
-  if (!_filename.empty()) {
-    xelement->SetAttribute("filename", _filename);
-  }
-  if (_size != 0) {
-    xelement->SetAttribute("size", _size);
-  }
-  if (_timestamp != 0) {
-    xelement->SetAttribute("timestamp", (int)_timestamp);
-  }
-  if (_got_hash) {
-    char hash[hash_size * 2 + 1];
-    encode_hex(hash, _hash, hash_size);
-    hash[hash_size * 2] = '\0';
-    xelement->SetAttribute("hash", hash);
-  }
-}
-
-/**
- * Performs a quick test to ensure the file has not been modified.  This test
- * is vulnerable to people maliciously attempting to fool the program (by
- * setting datestamps etc.).
- *
- * Returns true if it is intact, false if it needs to be redownloaded.
- */
-bool FileSpec::
-quick_verify(const string &package_dir) {
-  return quick_verify_pathname(get_pathname(package_dir));
-}
-
-/**
- * Works like quick_verify(), above, with an explicit pathname.  Useful for
- * verifying the copy of a file in a temporary location.
- */
-bool FileSpec::
-quick_verify_pathname(const string &pathname) {
-  if (_actual_file != nullptr) {
-    delete _actual_file;
-    _actual_file = nullptr;
-  }
-
-  int result = 1;
-#ifdef _WIN32
-  struct _stat st;
-  wstring pathname_w;
-  if (string_to_wstring(pathname_w, pathname)) {
-    result = _wstat(pathname_w.c_str(), &st);
-  }
-#else // _WIN32
-  struct stat st;
-  result = stat(pathname.c_str(), &st);
-#endif  // _WIN32
-
-  if (result != 0) {
-    // cerr << "file not found: " << _filename << "\n";
-    return false;
-  }
-
-  if (st.st_size != _size) {
-    // If the size is wrong, the file fails.  cerr << "size wrong: " <<
-    // _filename << "\n";
-    return false;
-  }
-
-  if (st.st_mtime == _timestamp) {
-    // If the size is right and the timestamp is right, the file passes.  cerr
-    // << "file ok: " << _filename << "\n";
-    return true;
-  }
-
-  // cerr << "modification time wrong: " << _filename << "\n";
-
-  // If the size is right but the timestamp is wrong, the file soft-fails.  We
-  // follow this up with a hash check.
-  if (!priv_check_hash(pathname, &st)) {
-    // Hard fail, the hash is wrong.  cerr << "hash check wrong: " <<
-    // _filename << "\n";
-    return false;
-  }
-
-  // cerr << "hash check ok: " << _filename << "\n";
-
-  // The hash is OK after all.  Change the file's timestamp back to what we
-  // expect it to be, so we can quick-verify it successfully next time.
-  utimbuf utb;
-  utb.actime = st.st_atime;
-  utb.modtime = _timestamp;
-
-#ifdef _WIN32
-  _wutime(pathname_w.c_str(), &utb);
-#else // _WIN32
-  utime(pathname.c_str(), &utb);
-#endif  // _WIN32
-
-  return true;
-}
-
-/**
- * Performs a more thorough test to ensure the file has not been modified.
- * This test is less vulnerable to malicious attacks, since it reads and
- * verifies the entire file.
- *
- * Returns true if it is intact, false if it needs to be redownloaded.
- */
-bool FileSpec::
-full_verify(const string &package_dir) {
-  if (_actual_file != nullptr) {
-    delete _actual_file;
-    _actual_file = nullptr;
-  }
-
-  string pathname = get_pathname(package_dir);
-  int result = 1;
-#ifdef _WIN32
-  struct _stat st;
-  wstring pathname_w;
-  if (string_to_wstring(pathname_w, pathname)) {
-    result = _wstat(pathname_w.c_str(), &st);
-  }
-#else // _WIN32
-  struct stat st;
-  result = stat(pathname.c_str(), &st);
-#endif  // _WIN32
-
-  if (result != 0) {
-    // cerr << "file not found: " << _filename << "\n";
-    return false;
-  }
-
-  if (st.st_size != _size) {
-    // If the size is wrong, the file fails.  cerr << "size wrong: " <<
-    // _filename << "\n";
-    return false;
-  }
-
-  if (!priv_check_hash(pathname, &st)) {
-    // Hard fail, the hash is wrong.  cerr << "hash check wrong: " <<
-    // _filename << "\n";
-    return false;
-  }
-
-  // cerr << "hash check ok: " << _filename << "\n";
-
-  // The hash is OK.  If the timestamp is wrong, change it back to what we
-  // expect it to be, so we can quick-verify it successfully next time.
-
-  if (st.st_mtime != _timestamp) {
-    utimbuf utb;
-    utb.actime = st.st_atime;
-    utb.modtime = _timestamp;
-#ifdef _WIN32
-    _wutime(pathname_w.c_str(), &utb);
-#else // _WIN32
-    utime(pathname.c_str(), &utb);
-#endif  // _WIN32
-  }
-
-  return true;
-}
-
-/**
- * Returns a FileSpec that represents the actual data read on disk.  This will
- * read the disk to determine the data if necessary.
- */
-const FileSpec *FileSpec::
-force_get_actual_file(const string &pathname) {
-  if (_actual_file == nullptr) {
-#ifdef _WIN32
-    struct _stat st;
-    wstring pathname_w;
-    if (string_to_wstring(pathname_w, pathname)) {
-      _wstat(pathname_w.c_str(), &st);
-    }
-#else // _WIN32
-    struct stat st;
-    stat(pathname.c_str(), &st);
-#endif  // _WIN32
-
-    priv_check_hash(pathname, &st);
-  }
-
-  return _actual_file;
-}
-
-/**
- * Returns true if the file has the expected md5 hash, false otherwise.
- */
-bool FileSpec::
-check_hash(const string &pathname) const {
-  FileSpec other;
-  if (!other.read_hash(pathname)) {
-    return false;
-  }
-
-  return (memcmp(_hash, other._hash, hash_size) == 0);
-}
-
-/**
- * Computes the hash from the indicated pathname and stores it within the
- * FileSpec.
- */
-bool FileSpec::
-read_hash(const string &pathname) {
-  memset(_hash, 0, hash_size);
-  _got_hash = false;
-
-  ifstream stream;
-#ifdef _WIN32
-  wstring pathname_w;
-  if (string_to_wstring(pathname_w, pathname)) {
-    stream.open(pathname_w.c_str(), std::ios::in | std::ios::binary);
-  }
-#else // _WIN32
-  stream.open(pathname.c_str(), std::ios::in | std::ios::binary);
-#endif  // _WIN32
-
-  if (!stream) {
-    // cerr << "unable to read " << pathname << "\n";
-    return false;
-  }
-
-  MD5_CTX ctx;
-  MD5_Init(&ctx);
-
-  static const int buffer_size = 4096;
-  char buffer[buffer_size];
-
-  stream.read(buffer, buffer_size);
-  size_t count = stream.gcount();
-  while (count != 0) {
-    MD5_Update(&ctx, buffer, count);
-    stream.read(buffer, buffer_size);
-    count = stream.gcount();
-  }
-
-  MD5_Final(_hash, &ctx);
-  _got_hash = true;
-
-  return true;
-}
-
-/**
- * Reads the hash from the next 16 bytes on the indicated istream, in the same
- * unusual order observed by Panda's HashVal::read_stream() method.
- */
-bool FileSpec::
-read_hash_stream(istream &in) {
-  for (int i = 0; i < hash_size; i += 4) {
-    unsigned int a = in.get();
-    unsigned int b = in.get();
-    unsigned int c = in.get();
-    unsigned int d = in.get();
-    _hash[i + 0] = d;
-    _hash[i + 1] = c;
-    _hash[i + 2] = b;
-    _hash[i + 3] = a;
-  }
-
-  return !in.fail();
-}
-
-/**
- * Returns < 0 if this hash sorts before the other hash, > 0 if it sorts
- * after, 0 if they are the same.
- */
-int FileSpec::
-compare_hash(const FileSpec &other) const {
-  return memcmp(_hash, other._hash, hash_size);
-}
-
-/**
- * Describes the data in the FileSpec.
- */
-void FileSpec::
-write(ostream &out) const {
-  out << "filename: " << _filename << ", " << _size << " bytes, "
-      << asctime(localtime(&_timestamp));
-  // asctime includes a newline.
-  out << "hash: ";
-  stream_hex(out, _hash, hash_size);
-  out << "\n";
-}
-
-/**
- * Writes just the hash code.
- */
-void FileSpec::
-output_hash(ostream &out) const {
-  stream_hex(out, _hash, hash_size);
-}
-
-/**
- * Returns true if the file has the expected md5 hash, false otherwise.
- * Updates _actual_file with the data read from disk, including the hash, for
- * future reference.
- *
- * The parameter stp is a pointer to a stat structure.  It's declared as a
- * void * to get around issues with the nonstandard declaration of this
- * structure in Windows.
- */
-bool FileSpec::
-priv_check_hash(const string &pathname, void *stp) {
-  const struct stat &st = *(const struct stat *)stp;
-  assert(_actual_file == nullptr);
-  _actual_file = new FileSpec;
-  _actual_file->_filename = pathname;
-  _actual_file->_size = st.st_size;
-  _actual_file->_timestamp = st.st_mtime;
-
-  if (!_actual_file->read_hash(pathname)) {
-    return false;
-  }
-
-  return (memcmp(_hash, _actual_file->_hash, hash_size) == 0);
-}
-
-/**
- * Decodes the hex string in source into the character array in dest.  dest
- * must have has least size bytes; source must have size * 2 bytes.
- *
- * Returns true on success, false if there was a non-hex digit in the string.
- */
-bool FileSpec::
-decode_hex(unsigned char *dest, const char *source, size_t size) {
-  for (size_t i = 0; i < size; ++i) {
-    int high = decode_hexdigit(source[i * 2]);
-    int low = decode_hexdigit(source[i * 2 + 1]);
-    if (high < 0 || low < 0) {
-      return false;
-    }
-    dest[i] = (high << 4) | low;
-  }
-
-  return true;
-}
-
-/**
- * Encodes a character array into a hex string for output.  dest must have at
- * least size * 2 bytes; source must have size bytes.  The result is not null-
- * terminated.
- */
-void FileSpec::
-encode_hex(char *dest, const unsigned char *source, size_t size) {
-  for (size_t i = 0; i < size; ++i) {
-    int high = (source[i] >> 4) & 0xf;
-    int low = source[i] & 0xf;
-    dest[2 * i] = encode_hexdigit(high);
-    dest[2 * i + 1] = encode_hexdigit(low);
-  }
-}
-
-/**
- * Writes the indicated buffer as a string of hex characters to the given
- * ostream.
- */
-void FileSpec::
-stream_hex(ostream &out, const unsigned char *source, size_t size) {
-  for (size_t i = 0; i < size; ++i) {
-    int high = (source[i] >> 4) & 0xf;
-    int low = source[i] & 0xf;
-    out.put(encode_hexdigit(high));
-    out.put(encode_hexdigit(low));
-  }
-}

+ 0 - 78
direct/src/plugin/fileSpec.h

@@ -1,78 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file fileSpec.h
- * @author drose
- * @date 2009-06-29
- */
-
-#ifndef FILESPEC_H
-#define FILESPEC_H
-
-#include "get_tinyxml.h"
-#include <string>
-
-/**
- * This simple class is used both within the core API in this module, as well
- * as within the plugin_npapi plugin implementation, to represent a file on
- * disk that may need to be verified or (re)downloaded.
- */
-class FileSpec {
-public:
-  FileSpec();
-  FileSpec(const FileSpec &copy);
-  void operator = (const FileSpec &copy);
-  ~FileSpec();
-
-  void load_xml(TiXmlElement *xelement);
-  void store_xml(TiXmlElement *xelement);
-
-  inline const std::string &get_filename() const;
-  inline void set_filename(const std::string &filename);
-  inline std::string get_pathname(const std::string &package_dir) const;
-  inline size_t get_size() const;
-  inline time_t get_timestamp() const;
-  inline bool has_hash() const;
-
-  bool quick_verify(const std::string &package_dir);
-  bool quick_verify_pathname(const std::string &pathname);
-  bool full_verify(const std::string &package_dir);
-  inline const FileSpec *get_actual_file() const;
-  const FileSpec *force_get_actual_file(const std::string &pathname);
-
-  bool check_hash(const std::string &pathname) const;
-  bool read_hash(const std::string &pathname);
-  bool read_hash_stream(std::istream &in);
-  int compare_hash(const FileSpec &other) const;
-
-  void write(std::ostream &out) const;
-  void output_hash(std::ostream &out) const;
-
-private:
-  bool priv_check_hash(const std::string &pathname, void *stp);
-  static inline int decode_hexdigit(char c);
-  static inline char encode_hexdigit(int c);
-
-  static bool decode_hex(unsigned char *dest, const char *source, size_t size);
-  static void encode_hex(char *dest, const unsigned char *source, size_t size);
-  static void stream_hex(std::ostream &out, const unsigned char *source, size_t size);
-
-  enum { hash_size = 16 };
-
-  std::string _filename;
-  size_t _size;
-  time_t _timestamp;
-  unsigned char _hash[hash_size];
-  bool _got_hash;
-
-  FileSpec *_actual_file;
-};
-
-#include "fileSpec.I"
-
-#endif

+ 0 - 311
direct/src/plugin/find_root_dir.cxx

@@ -1,311 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file find_root_dir.cxx
- * @author drose
- * @date 2009-06-29
- */
-
-#include "find_root_dir.h"
-#include "mkdir_complete.h"
-#include "get_tinyxml.h"
-#include "wstring_encode.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#include <shlobj.h>
-#else
-#include <unistd.h>
-#include <stdlib.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/stat.h>
-#endif
-
-using std::cerr;
-using std::string;
-using std::wstring;
-
-#ifdef _WIN32
-// From KnownFolders.h (part of Vista SDK):
-#define DEFINE_KNOWN_FOLDER(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
-   const GUID name = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
-DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppData, 0xF1B32785, 0x6FBA, 0x4FCF, 0x9D, 0x55, 0x7B, 0x8E, 0x7F, 0x15, 0x70, 0x91);
-DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppDataLow, 0xA520A1A4, 0x1780, 0x4FF6, 0xBD, 0x18, 0x16, 0x73, 0x43, 0xC5, 0xAF, 0x16);
-DEFINE_KNOWN_FOLDER(FOLDERID_InternetCache, 0x352481E8, 0x33BE, 0x4251, 0xBA, 0x85, 0x60, 0x07, 0xCA, 0xED, 0xCF, 0x9D);
-#endif  // _WIN32
-
-
-#ifdef _WIN32
-/**
- * A wrapper around SHGetSpecialFolderPath(), to return the Panda3D directory
- * under the indicated CSIDL folder.
- */
-static wstring
-get_csidl_dir_w(int csidl) {
-  static const int buffer_size = MAX_PATH;
-  wchar_t buffer[buffer_size];
-  if (SHGetSpecialFolderPathW(nullptr, buffer, csidl, true)) {
-    wstring root = buffer;
-    root += wstring(L"/Panda3D");
-
-    if (mkdir_complete_w(root, cerr)) {
-      return root;
-    }
-  }
-
-  // Something went wrong.
-  return wstring();
-}
-#endif  // _WIN32
-
-#ifdef _WIN32
-/**
- * Wide-character implementation of find_root_dir_default(), only needed for
- * Windows.
- */
-static wstring
-find_root_dir_default_w() {
-  // First, use IEIsProtectedModeProcess() to determine if we are running in
-  // IE's "protected mode" under Vista.
-
-  wstring root;
-  bool is_protected = false;
-  HMODULE ieframe = LoadLibrary("ieframe.dll");
-  if (ieframe != nullptr) {
-    typedef HRESULT STDAPICALLTYPE IEIsProtectedModeProcess(BOOL *pbResult);
-    IEIsProtectedModeProcess *func = (IEIsProtectedModeProcess *)GetProcAddress(ieframe, "IEIsProtectedModeProcess");
-    if (func != nullptr) {
-      BOOL result = false;
-      HRESULT hr = (*func)(&result);
-      if (hr == S_OK) {
-        is_protected = (result != 0);
-      }
-      // Any other return value means some error, especially E_NOTIMPL, which
-      // means we're not running under Vista.  In this case we can assume
-      // we're not running in protected mode.
-    }
-
-    if (is_protected) {
-      // If we *are* running in protected mode, we need to use
-      // FOLDERID_LocalAppDataLow.
-
-      // We should be able to use IEGetWriteableFolderPath() to query this
-      // folder, but for some reason, that function returns E_ACCESSDENIED on
-      // FOLDERID_LocalAppDataLow, even though this is certainly a folder we
-      // have write access to.
-
-      // Well, SHGetKnownFolderPath() does work.  This function only exists on
-      // Vista and above, though, so we still have to pull it out of the DLL
-      // instead of hard-linking it.
-
-      HMODULE shell32 = LoadLibrary("shell32.dll");
-      if (shell32 != nullptr) {
-        typedef HRESULT STDAPICALLTYPE SHGetKnownFolderPath(REFGUID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
-        SHGetKnownFolderPath *func = (SHGetKnownFolderPath *)GetProcAddress(shell32, "SHGetKnownFolderPath");
-        if (func != nullptr) {
-          LPWSTR cache_path = nullptr;
-          HRESULT hr = (*func)(FOLDERID_LocalAppDataLow, 0, nullptr, &cache_path);
-
-          if (SUCCEEDED(hr)) {
-            root = cache_path;
-            CoTaskMemFree(cache_path);
-
-            root += wstring(L"/Panda3D");
-            if (mkdir_complete_w(root, cerr)) {
-              FreeLibrary(shell32);
-              FreeLibrary(ieframe);
-              return root;
-            }
-          }
-        }
-        FreeLibrary(shell32);
-      }
-
-      // Couldn't get FOLDERID_LocalAppDataLow for some reason.  We're in
-      // fallback mode now.  Use IEGetWriteableFolderPath to get the standard
-      // cache folder.
-      typedef HRESULT STDAPICALLTYPE IEGetWriteableFolderPath(REFGUID clsidFolderID, LPWSTR* lppwstrPath);
-      IEGetWriteableFolderPath *func = (IEGetWriteableFolderPath *)GetProcAddress(ieframe, "IEGetWriteableFolderPath");
-      if (func != nullptr) {
-        LPWSTR cache_path = nullptr;
-
-        // Since we're here, we'll start by asking for LocalAppDataLow, even
-        // though I know it doesn't work.
-        HRESULT hr = (*func)(FOLDERID_LocalAppDataLow, &cache_path);
-        if (FAILED(hr)) {
-          // This one should work.
-          hr = (*func)(FOLDERID_InternetCache, &cache_path);
-        }
-
-        if (SUCCEEDED(hr)) {
-          root = cache_path;
-          CoTaskMemFree(cache_path);
-          root += wstring(L"/Panda3D");
-          if (mkdir_complete_w(root, cerr)) {
-            FreeLibrary(ieframe);
-            return root;
-          }
-        }
-      }
-    }
-
-    FreeLibrary(ieframe);
-  }
-
-  // All right, here we are in the normal, unprotected mode.  This is also the
-  // normal XP codepath.
-
-  // e.g., c:Documents and Settings<username>Local SettingsApplication
-  // DataPanda3D
-  root = get_csidl_dir_w(CSIDL_LOCAL_APPDATA);
-  if (!root.empty()) {
-    return root;
-  }
-
-  // For some crazy reason, we can't get CSIDL_LOCAL_APPDATA.  Fall back to
-  // the cache folder.
-
-  // e.g.  c:Documents and Settings<username>Local SettingsTemporary Internet
-  // FilesPanda3D
-  root = get_csidl_dir_w(CSIDL_INTERNET_CACHE);
-  if (!root.empty()) {
-    return root;
-  }
-
-  // If we couldn't get any of those folders, huh.  Punt and try for the old
-  // standby GetTempPath, for lack of anything better.
-  static const int buffer_size = MAX_PATH;
-  wchar_t buffer[buffer_size];
-  if (GetTempPathW(buffer_size, buffer) != 0) {
-    root = buffer;
-    root += wstring(L"Panda3D");
-    if (mkdir_complete_w(root, cerr)) {
-      return root;
-    }
-  }
-
-  return wstring();
-}
-#endif  // _WIN32
-
-
-/**
- * Returns the path to the system-default for the root directory.  This is
- * where we look first.
- */
-static string
-find_root_dir_default() {
-#ifdef _WIN32
-  wstring root_w = find_root_dir_default_w();
-  if (!root_w.empty()) {
-    string root;
-    if (wstring_to_string(root, root_w)) {
-      return root;
-    }
-  }
-
-#elif defined(__APPLE__)
-  // e.g., Users<username>LibraryCachesPanda3D
-  string root = find_osx_root_dir();
-  if (!root.empty()) {
-    return root;
-  }
-
-#else  // The Linux/*BSD case
-  // e.g., home<username>.panda3d
-
-  string root;
-  const passwd *pwdata = getpwuid(getuid());
-  if (pwdata == nullptr) {
-    char *home = getenv("HOME");
-    if (home == nullptr) {
-      // Beh.  Let's hope it never gets to this point.
-      return ".";
-    } else {
-      root = home;
-    }
-  } else {
-    root = pwdata->pw_dir;
-  }
-
-  root += "/.panda3d";
-  if (mkdir(root.c_str(), 0700) == 0 || errno == EEXIST) {
-    return root;
-  }
-
-#endif
-
-  // Couldn't find a directory.  Punt.
-  return ".";
-}
-
-
-/**
- * Returns the path to the installable Panda3D directory on the user's
- * machine.
- */
-static string
-find_root_dir_actual() {
-  string root = find_root_dir_default();
-
-  // Now look for a config.xml file in that directory, which might redirect us
-  // elsewhere.
-  string config_filename = root + "/config.xml";
-  TiXmlDocument doc(config_filename);
-  if (!doc.LoadFile()) {
-    // No config.xml found, or not valid xml.
-    return root;
-  }
-
-  TiXmlElement *xconfig = doc.FirstChildElement("config");
-  if (xconfig == nullptr) {
-    // No <config> element within config.xml.
-    return root;
-  }
-
-  const char *new_root = xconfig->Attribute("root_dir");
-  if (new_root == nullptr || *new_root == '\0') {
-    // No root_dir specified.
-    return root;
-  }
-
-  if (!mkdir_complete(new_root, cerr)) {
-    // The specified root_dir wasn't valid.
-    return root;
-  }
-
-  // We've been redirected to another location.  Respect that.
-  return new_root;
-}
-
-/**
- * This is the public interface to the above functions.
- */
-string
-find_root_dir() {
-  string root = find_root_dir_actual();
-
-#ifdef _WIN32
-  // Now map that (possibly utf-8) filename into its 8.3 equivalent, so we can
-  // safely pass it around to Python and other tools that might not understand
-  // Unicode filenames.  Silly Windows, creating an entirely new and
-  // incompatible kind of filename.
-  wstring root_w;
-  string_to_wstring(root_w, root);
-
-  DWORD length = GetShortPathNameW(root_w.c_str(), nullptr, 0);
-  wchar_t *short_name = new wchar_t[length];
-  GetShortPathNameW(root_w.c_str(), short_name, length);
-
-  wstring_to_string(root, short_name);
-  delete[] short_name;
-#endif  // _WIN32
-
-  return root;
-}

+ 0 - 26
direct/src/plugin/find_root_dir.h

@@ -1,26 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file find_root_dir.h
- * @author drose
- * @date 2009-06-29
- */
-
-#ifndef FIND_ROOT_DIR_H
-#define FIND_ROOT_DIR_H
-
-#include <string>
-#include <iostream>
-
-std::string find_root_dir();
-
-#ifdef __APPLE__
-std::string find_osx_root_dir();
-#endif  // __APPLE__
-
-#endif

+ 0 - 87
direct/src/plugin/find_root_dir_assist.mm

@@ -1,87 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file find_root_dir_assist.mm
- * @author drose
- * @date 2009-04-13
- */
-
-#include "find_root_dir.h"
-
-#ifdef __APPLE__
-
-#include <Foundation/Foundation.h>
-#include <AppKit/AppKit.h>
-
-/**
- * Copy the Objective-C string to a C++ string.
- */
-static string
-NSString_to_cpp_string(NSString *str) {
-  size_t length = [str length];
-  string result;
-  for (size_t i = 0; i < length; ++i) {
-    result += (char)[str characterAtIndex: i];
-  }
-
-  return result;
-}
-
-/**
- *
- */
-static string
-call_NSSearchPathForDirectories(NSSearchPathDirectory dirkey, NSSearchPathDomainMask domain) {
-  // Ensure that Carbon has been initialized, and that we have an auto-release
-  // pool.
-  NSApplicationLoad();
-  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
-  NSArray *paths = NSSearchPathForDirectoriesInDomains(dirkey, domain, YES);
-  string result;
-  if ([paths count] != 0) {
-    result = NSString_to_cpp_string([paths objectAtIndex:0]);
-  }
-  [pool release];
-
-  return result;
-}
-
-/**
- *
- */
-static string
-get_osx_home_directory() {
-  NSApplicationLoad();
-  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
-  NSString *dir = NSHomeDirectory();
-  string result = NSString_to_cpp_string(dir);
-  [pool release];
-
-  return result;
-}
-
-/**
- *
- */
-std::string
-find_osx_root_dir() {
-  string result = call_NSSearchPathForDirectories(NSCachesDirectory, NSUserDomainMask);
-  if (!result.empty()) {
-    return result + "/Panda3D";
-  }
-  result = get_osx_home_directory();
-  if (!result.empty()) {
-    return result + "/Panda3D";
-  }
-
-  return string();
-}
-
-#endif  // __APPLE__

+ 0 - 27
direct/src/plugin/get_tinyxml.h

@@ -1,27 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file get_tinyxml.h
- * @author drose
- * @date 2009-07-01
- */
-
-#ifndef GET_TINYXML_H
-#define GET_TINYXML_H
-
-// This header file exists just to include tinyxml.h safely.  We need this
-// since tinyxml.h requires having the symbol TIXML_USE_STL already defined
-// before you include it.
-
-#ifndef TIXML_USE_STL
-#define TIXML_USE_STL
-#endif
-
-#include <tinyxml.h>
-
-#endif

+ 0 - 963
direct/src/plugin/get_twirl_data.cxx

@@ -1,963 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file get_twirl_data.cxx
- * @author drose
- * @date 2011-08-24
- */
-
-#include "get_twirl_data.h"
-#include <string.h>
-
-struct twirl_flip {
-  int _index;
-  bool _flip_x;
-  bool _flip_y;
-  bool _exchange;
-};
-
-static twirl_flip twirl_flip_table[twirl_num_steps + 1] = {
-  { 0, false, false, false },   // 0
-  { 1, false, false, false },   // 30
-  { 2, false, false, false },   // 60
-  { 0, false, true, true },     // 90
-  { 1, false, true, true },     // 120
-  { 2, false, true, true },     // 150
-  { 0, true, true, false },     // 180
-  { 1, true, true, false },     // 210
-  { 2, true, true, false },     // 240
-  { 0, true, false, true },     // 270
-  { 1, true, false, true },     // 300
-  { 2, true, false, true },     // 330
-  { 3, false, false, false }    // the "failed" icon
-};
-
-// These tables are the raw data for the twirl icon data.  They're extracted
-// with bin2c from the three layers of twirl.xcf, in this directory.
-static const unsigned char twirl_0_data[] = {
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xe4, 0xe4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xff, 0xfe, 0xfd, 0xff, 0xa5, 0x1f, 0x1f, 0xa5, 0xff, 0xfd, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xd7, 0x16, 0x00, 0x00,
-  0x16, 0xd7, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xff, 0xeb, 0xba, 0xe6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0x91, 0x00, 0x05, 0x05, 0x00, 0x91, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xff, 0xfd, 0xfa, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xd7, 0x2f, 0x06, 0x23, 0xa6, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0x66, 0x00, 0x04, 0x04, 0x00, 0x66, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xf8, 0xec, 0xe8, 0xed, 0xfb, 0xff, 0xff,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x80, 0x00,
-  0x0a, 0x04, 0x0c, 0xb0, 0xff, 0xfe, 0xff, 0xfb, 0x5c, 0x00, 0x03,
-  0x03, 0x00, 0x5c, 0xfb, 0xff, 0xfe, 0xff, 0xf9, 0xe9, 0xe8, 0xe8,
-  0xe8, 0xf3, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0x7c, 0x08, 0x11, 0x14, 0x03, 0x30, 0xe6, 0xff, 0xff,
-  0xfb, 0x5c, 0x00, 0x03, 0x03, 0x00, 0x5c, 0xfb, 0xff, 0xff, 0xfd,
-  0xed, 0xe8, 0xe9, 0xe9, 0xe8, 0xf3, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xab, 0x0a, 0x0f, 0x15, 0x13,
-  0x07, 0x79, 0xff, 0xff, 0xfd, 0x62, 0x00, 0x03, 0x03, 0x00, 0x61,
-  0xfd, 0xff, 0xff, 0xf4, 0xe8, 0xe9, 0xea, 0xe9, 0xe9, 0xf7, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf1,
-  0x2c, 0x0a, 0x14, 0x15, 0x0d, 0x1e, 0xcb, 0xff, 0xff, 0x79, 0x00,
-  0x04, 0x04, 0x00, 0x79, 0xff, 0xff, 0xfb, 0xeb, 0xe9, 0xea, 0xe9,
-  0xe9, 0xeb, 0xfd, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0x97, 0x04, 0x14, 0x15, 0x18, 0x00, 0x82,
-  0xff, 0xff, 0x99, 0x00, 0x03, 0x03, 0x00, 0x99, 0xff, 0xff, 0xf4,
-  0xe7, 0xea, 0xea, 0xe9, 0xe8, 0xf5, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf2, 0x42, 0x03,
-  0x17, 0x16, 0x0b, 0x39, 0xe9, 0xff, 0xb5, 0x01, 0x00, 0x00, 0x01,
-  0xb6, 0xff, 0xfd, 0xee, 0xe9, 0xea, 0xea, 0xe8, 0xed, 0xfd, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xdd, 0x8b, 0x84, 0xad, 0xee, 0xff, 0xff,
-  0xff, 0xff, 0xc2, 0x18, 0x0b, 0x15, 0x13, 0x09, 0xbf, 0xff, 0xda,
-  0x25, 0x00, 0x00, 0x25, 0xd9, 0xff, 0xf9, 0xe9, 0xe9, 0xea, 0xe9,
-  0xe9, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf0, 0xe7, 0xe8, 0xf8,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x48, 0x17, 0x1f,
-  0x20, 0x3b, 0x98, 0xee, 0xff, 0xff, 0xff, 0xa2, 0x0f, 0x0e, 0x15,
-  0x05, 0x7a, 0xff, 0xff, 0x54, 0x00, 0x00, 0x53, 0xff, 0xff, 0xf3,
-  0xe8, 0xea, 0xe9, 0xe9, 0xf5, 0xff, 0xfe, 0xff, 0xfc, 0xec, 0xd9,
-  0xd3, 0xd2, 0xd1, 0xd9, 0xfb, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xcc, 0x1f, 0x20, 0x28, 0x26, 0x22, 0x1a, 0x4c, 0xbf, 0xff, 0xff,
-  0xff, 0x86, 0x02, 0x0e, 0x09, 0x41, 0xfa, 0xff, 0x8d, 0x00, 0x00,
-  0x8d, 0xff, 0xfa, 0xee, 0xe8, 0xe9, 0xe8, 0xf3, 0xff, 0xff, 0xff,
-  0xf4, 0xdd, 0xd2, 0xd3, 0xd3, 0xd4, 0xd3, 0xd2, 0xf2, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xf0, 0x3f, 0x1a, 0x2a, 0x2b, 0x2a, 0x2a,
-  0x1c, 0x28, 0xa1, 0xff, 0xff, 0xff, 0x76, 0x02, 0x04, 0x1e, 0xee,
-  0xff, 0xd7, 0x07, 0x07, 0xd7, 0xff, 0xfc, 0xeb, 0xe8, 0xe8, 0xf1,
-  0xff, 0xff, 0xff, 0xee, 0xd5, 0xd1, 0xd4, 0xd4, 0xd5, 0xd4, 0xd1,
-  0xd7, 0xfa, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xba, 0x28,
-  0x1a, 0x28, 0x2b, 0x2a, 0x2d, 0x24, 0x23, 0x87, 0xff, 0xff, 0xff,
-  0x80, 0x01, 0x0b, 0xc5, 0xff, 0xff, 0x24, 0x24, 0xff, 0xff, 0xfa,
-  0xe9, 0xe7, 0xf2, 0xfe, 0xff, 0xff, 0xe9, 0xd4, 0xd3, 0xd5, 0xd5,
-  0xd5, 0xd4, 0xd1, 0xd3, 0xef, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe,
-  0xfe, 0xff, 0xff, 0xc0, 0x4a, 0x1f, 0x22, 0x2e, 0x2c, 0x2b, 0x25,
-  0x18, 0x79, 0xfb, 0xff, 0xff, 0x97, 0x0b, 0x7b, 0xff, 0xff, 0x9b,
-  0x9b, 0xff, 0xff, 0xf4, 0xe8, 0xf4, 0xff, 0xff, 0xff, 0xe6, 0xd1,
-  0xd3, 0xd5, 0xd5, 0xd5, 0xd3, 0xd2, 0xda, 0xf1, 0xff, 0xff, 0xfe,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xef, 0x8e, 0x38,
-  0x17, 0x21, 0x29, 0x2b, 0x26, 0x17, 0x81, 0xff, 0xff, 0xff, 0xa0,
-  0x94, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf5, 0xf5, 0xff, 0xff,
-  0xff, 0xe8, 0xd1, 0xd3, 0xd5, 0xd4, 0xd3, 0xd1, 0xd7, 0xe7, 0xfa,
-  0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff,
-  0xfe, 0xff, 0xff, 0xd6, 0x95, 0x53, 0x22, 0x1c, 0x20, 0x1c, 0x15,
-  0x96, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xff, 0xff, 0xec, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xdb,
-  0xe9, 0xf6, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xee, 0xca,
-  0x8d, 0x58, 0x35, 0x24, 0x1e, 0x9f, 0xff, 0xfb, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xed, 0xd3, 0xd3, 0xd6,
-  0xdc, 0xe7, 0xf3, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf3, 0xd1, 0x8f, 0x9c, 0xfc,
-  0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff,
-  0xec, 0xe7, 0xf5, 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xe1, 0xac,
-  0x8d, 0x85, 0x85, 0x89, 0x9b, 0xb3, 0xc8, 0xe3, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5,
-  0xec, 0xe5, 0xdd, 0xd7, 0xd6, 0xd6, 0xd9, 0xe3, 0xf5, 0xff, 0xff,
-  0xff, 0xbb, 0x50, 0x30, 0x2f, 0x33, 0x33, 0x32, 0x2f, 0x30, 0x41,
-  0x5b, 0x7e, 0xaa, 0xe1, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xf5, 0xe3, 0xd4, 0xc8, 0xbf, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xb9,
-  0xba, 0xc4, 0xe8, 0xff, 0xeb, 0x57, 0x31, 0x44, 0x42, 0x42, 0x42,
-  0x42, 0x43, 0x42, 0x3e, 0x38, 0x30, 0x36, 0x45, 0x5c, 0xb4, 0xff,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xe6, 0xc8, 0xc0, 0xbb, 0xb9, 0xbc, 0xbe, 0xbf, 0xc0,
-  0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xb9, 0xc7, 0xf8, 0xeb, 0x57, 0x31,
-  0x44, 0x42, 0x42, 0x42, 0x42, 0x43, 0x42, 0x3e, 0x38, 0x30, 0x36,
-  0x46, 0x5c, 0xb4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xe6, 0xc8, 0xc0, 0xbb, 0xb9,
-  0xbc, 0xbe, 0xbf, 0xc0, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xb9, 0xc7,
-  0xf8, 0xff, 0xbb, 0x50, 0x30, 0x2f, 0x33, 0x33, 0x32, 0x2f, 0x30,
-  0x41, 0x5b, 0x7e, 0xaa, 0xe1, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xff, 0xf5, 0xe3, 0xd4, 0xc8, 0xbf, 0xb9, 0xb9, 0xba, 0xba, 0xba,
-  0xb9, 0xba, 0xc4, 0xe8, 0xff, 0xff, 0xff, 0xe1, 0xac, 0x8d, 0x85,
-  0x85, 0x89, 0x9b, 0xb3, 0xc8, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xec, 0xe5,
-  0xdd, 0xd7, 0xd6, 0xd6, 0xd9, 0xe3, 0xf5, 0xff, 0xff, 0xfe, 0xfd,
-  0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xfb, 0xf2, 0xd5, 0x9f, 0xb2, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xd7, 0xd2, 0xed, 0xfa, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xef, 0xd0, 0x9f, 0x74, 0x5b, 0x4e, 0x4d, 0xba, 0xff, 0xfc,
-  0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xd9,
-  0xa5, 0xa7, 0xae, 0xbc, 0xd1, 0xea, 0xf8, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff,
-  0xfe, 0xff, 0xff, 0xda, 0xa4, 0x6f, 0x4c, 0x49, 0x4c, 0x48, 0x46,
-  0xb3, 0xff, 0xff, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xd5, 0xa1, 0xa4, 0xa5, 0xa4, 0xa6, 0xb9,
-  0xd4, 0xef, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xed, 0x9e, 0x5c, 0x44, 0x4e, 0x53,
-  0x55, 0x50, 0x47, 0xa3, 0xff, 0xff, 0xff, 0xbc, 0xba, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xcf, 0xd4, 0xff, 0xff, 0xff, 0xcd, 0xa2,
-  0xa8, 0xaa, 0xa9, 0xa6, 0xa1, 0xaf, 0xd2, 0xf8, 0xff, 0xff, 0xfe,
-  0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xc5, 0x69, 0x4b,
-  0x4f, 0x57, 0x56, 0x55, 0x50, 0x47, 0x9c, 0xff, 0xff, 0xff, 0xb5,
-  0x61, 0xb0, 0xff, 0xff, 0xcd, 0xcd, 0xff, 0xff, 0xc3, 0x90, 0xd0,
-  0xff, 0xff, 0xfd, 0xc9, 0xa2, 0xa7, 0xaa, 0xaa, 0xab, 0xa6, 0xa5,
-  0xb6, 0xe5, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xbe, 0x4f, 0x48, 0x53, 0x55, 0x55, 0x56, 0x4e, 0x50, 0xa7, 0xff,
-  0xff, 0xfc, 0xa6, 0x5b, 0x65, 0xde, 0xff, 0xff, 0x92, 0x92, 0xff,
-  0xff, 0xe5, 0x90, 0x8c, 0xc6, 0xff, 0xff, 0xff, 0xcf, 0xa6, 0xa7,
-  0xaa, 0xa9, 0xaa, 0xa9, 0xa3, 0xa9, 0xe3, 0xff, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xff, 0xed, 0x5f, 0x48, 0x54, 0x55, 0x54, 0x54, 0x47,
-  0x57, 0xbb, 0xff, 0xff, 0xff, 0xa1, 0x5c, 0x5f, 0x72, 0xf7, 0xff,
-  0xeb, 0x83, 0x83, 0xeb, 0xff, 0xf7, 0x99, 0x8d, 0x8c, 0xc1, 0xff,
-  0xff, 0xff, 0xd9, 0xa9, 0xa4, 0xa9, 0xa9, 0xaa, 0xa9, 0xa3, 0xb2,
-  0xf9, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xcd, 0x4a, 0x4d, 0x52,
-  0x50, 0x4d, 0x49, 0x76, 0xd3, 0xff, 0xff, 0xff, 0xab, 0x5c, 0x66,
-  0x62, 0x8a, 0xfd, 0xff, 0xc7, 0x79, 0x79, 0xc7, 0xff, 0xfd, 0xa8,
-  0x8f, 0x92, 0x8c, 0xc8, 0xff, 0xff, 0xff, 0xe6, 0xb7, 0xa3, 0xa6,
-  0xa7, 0xa8, 0xa5, 0xa5, 0xeb, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xf0, 0x67, 0x45, 0x4b, 0x4d, 0x65, 0xb3, 0xf5, 0xff, 0xff, 0xff,
-  0xbd, 0x64, 0x66, 0x6a, 0x5f, 0xaf, 0xff, 0xff, 0xa9, 0x75, 0x75,
-  0xa9, 0xff, 0xff, 0xc3, 0x8d, 0x95, 0x91, 0x92, 0xd5, 0xff, 0xff,
-  0xff, 0xf8, 0xd5, 0xb0, 0xa5, 0xa5, 0xa2, 0xb5, 0xfa, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xe2, 0xa3, 0xa0, 0xc2, 0xf5, 0xff,
-  0xff, 0xff, 0xff, 0xd2, 0x68, 0x65, 0x6a, 0x68, 0x64, 0xd9, 0xff,
-  0xed, 0x92, 0x7a, 0x7a, 0x92, 0xed, 0xff, 0xe2, 0x90, 0x94, 0x95,
-  0x90, 0x96, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xde, 0xcd, 0xd0,
-  0xf1, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf3, 0x81, 0x5f, 0x6b, 0x6b,
-  0x63, 0x86, 0xf3, 0xff, 0xda, 0x81, 0x7f, 0x7f, 0x81, 0xda, 0xff,
-  0xf5, 0xa5, 0x90, 0x95, 0x96, 0x8c, 0xa9, 0xf9, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xb7,
-  0x5e, 0x69, 0x69, 0x6c, 0x5b, 0xb4, 0xff, 0xff, 0xcc, 0x75, 0x81,
-  0x81, 0x75, 0xcc, 0xff, 0xff, 0xc6, 0x8a, 0x96, 0x95, 0x94, 0x8d,
-  0xcf, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xf3, 0x75, 0x63, 0x69, 0x6a, 0x64, 0x73, 0xe2, 0xff,
-  0xff, 0xbc, 0x75, 0x81, 0x81, 0x75, 0xbc, 0xff, 0xff, 0xe7, 0x99,
-  0x91, 0x95, 0x94, 0x90, 0x9f, 0xf8, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xc5, 0x62, 0x66, 0x6a, 0x68,
-  0x62, 0xb0, 0xff, 0xff, 0xfe, 0xb0, 0x77, 0x81, 0x81, 0x77, 0xb0,
-  0xfe, 0xff, 0xff, 0xc2, 0x8e, 0x94, 0x95, 0x92, 0x90, 0xd9, 0xff,
-  0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xa9,
-  0x61, 0x68, 0x69, 0x5e, 0x80, 0xf4, 0xff, 0xff, 0xfd, 0xae, 0x77,
-  0x81, 0x81, 0x77, 0xae, 0xfd, 0xff, 0xff, 0xf4, 0xa1, 0x8d, 0x94,
-  0x93, 0x8f, 0xc4, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xad, 0x5c, 0x62, 0x5e, 0x68, 0xd3, 0xff, 0xfe,
-  0xff, 0xfd, 0xae, 0x77, 0x81, 0x81, 0x77, 0xae, 0xfd, 0xff, 0xfe,
-  0xff, 0xdb, 0x91, 0x8d, 0x90, 0x8b, 0xc5, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xe7, 0x7f, 0x62, 0x78,
-  0xce, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb3, 0x75, 0x81, 0x81, 0x75,
-  0xb3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd6, 0x9b, 0x8e, 0xa0, 0xed,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
-  0xff, 0xf6, 0xdc, 0xf4, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xc8,
-  0x75, 0x82, 0x82, 0x75, 0xc8, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xf4, 0xe0, 0xf6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xfe, 0xff, 0xeb, 0x8a, 0x76, 0x76, 0x8a, 0xeb, 0xff, 0xfe,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xd2, 0x8f, 0x8f,
-  0xd2, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xf1, 0xf1, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff
-};
-
-static const unsigned char twirl_30_data[] = {
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xe7, 0xe7, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xff, 0xfe, 0xfd, 0xff, 0xac, 0x31, 0x31, 0xac, 0xff, 0xfd, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xda, 0x28, 0x02, 0x02,
-  0x28, 0xda, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xff, 0xed, 0xc1, 0xe9, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0x9a, 0x02, 0x1a, 0x1a, 0x02, 0x9a, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xff, 0xed, 0xc2, 0xef, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xdb, 0x42, 0x1d, 0x37, 0xae, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0x73, 0x01, 0x18, 0x18, 0x01, 0x73, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xab, 0x18, 0x00, 0x23, 0xd7, 0xff, 0xff,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x8c, 0x18,
-  0x21, 0x1b, 0x23, 0xb7, 0xff, 0xfe, 0xff, 0xfb, 0x6a, 0x05, 0x17,
-  0x17, 0x05, 0x6a, 0xfb, 0xff, 0xfe, 0xff, 0xb4, 0x00, 0x00, 0x00,
-  0x00, 0x73, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0x88, 0x1f, 0x28, 0x2a, 0x1b, 0x44, 0xe8, 0xff, 0xff,
-  0xfb, 0x6a, 0x05, 0x17, 0x17, 0x05, 0x6a, 0xfb, 0xff, 0xff, 0xec,
-  0x25, 0x00, 0x00, 0x00, 0x00, 0x6b, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xb4, 0x21, 0x25, 0x2b, 0x29,
-  0x1e, 0x86, 0xff, 0xff, 0xfd, 0x6e, 0x04, 0x17, 0x17, 0x04, 0x6e,
-  0xfd, 0xff, 0xff, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf3,
-  0x3f, 0x21, 0x2a, 0x2b, 0x24, 0x34, 0xd0, 0xff, 0xff, 0x85, 0x00,
-  0x18, 0x18, 0x00, 0x84, 0xff, 0xff, 0xce, 0x10, 0x00, 0x00, 0x00,
-  0x00, 0x13, 0xea, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xa1, 0x1b, 0x2a, 0x2b, 0x2e, 0x16, 0x8e,
-  0xff, 0xff, 0xa2, 0x01, 0x18, 0x18, 0x01, 0xa2, 0xff, 0xff, 0x7f,
-  0x00, 0x03, 0x00, 0x00, 0x00, 0x83, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf3, 0x54, 0x1b,
-  0x2d, 0x2c, 0x22, 0x4b, 0xeb, 0xff, 0xbc, 0x16, 0x13, 0x13, 0x16,
-  0xbc, 0xff, 0xea, 0x2f, 0x00, 0x02, 0x02, 0x00, 0x28, 0xeb, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xe0, 0x96, 0x91, 0xb5, 0xf0, 0xff, 0xff,
-  0xff, 0xff, 0xc8, 0x2e, 0x22, 0x2b, 0x29, 0x20, 0xc5, 0xff, 0xdd,
-  0x37, 0x0b, 0x0b, 0x36, 0xdd, 0xff, 0xc0, 0x00, 0x00, 0x01, 0x00,
-  0x00, 0xb2, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xf7, 0xf3, 0xf3, 0xfb,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x5a, 0x2e, 0x35,
-  0x36, 0x4e, 0xa2, 0xf0, 0xff, 0xff, 0xff, 0xab, 0x26, 0x24, 0x2b,
-  0x1c, 0x87, 0xff, 0xff, 0x62, 0x02, 0x02, 0x61, 0xff, 0xff, 0x76,
-  0x00, 0x00, 0x00, 0x00, 0x8d, 0xff, 0xff, 0xff, 0xfd, 0xf6, 0xec,
-  0xe8, 0xe8, 0xe8, 0xec, 0xfd, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xd1, 0x35, 0x36, 0x3d, 0x3b, 0x38, 0x31, 0x5d, 0xc5, 0xff, 0xff,
-  0xff, 0x91, 0x1a, 0x25, 0x20, 0x52, 0xfb, 0xff, 0x97, 0x09, 0x09,
-  0x97, 0xff, 0xfc, 0x36, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff,
-  0xf9, 0xee, 0xe8, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xf9, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xf2, 0x52, 0x31, 0x3f, 0x40, 0x3f, 0x3f,
-  0x32, 0x3d, 0xaa, 0xff, 0xff, 0xff, 0x83, 0x19, 0x1b, 0x33, 0xef,
-  0xff, 0xdb, 0x1c, 0x1c, 0xdb, 0xff, 0xf1, 0x0d, 0x00, 0x00, 0x5e,
-  0xff, 0xff, 0xff, 0xf7, 0xea, 0xe8, 0xe9, 0xe9, 0xea, 0xe9, 0xe8,
-  0xeb, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xc0, 0x3d,
-  0x30, 0x3d, 0x40, 0x3f, 0x41, 0x39, 0x38, 0x93, 0xff, 0xff, 0xff,
-  0x8c, 0x19, 0x22, 0xcb, 0xff, 0xff, 0x36, 0x37, 0xff, 0xff, 0xc8,
-  0x00, 0x00, 0x68, 0xfb, 0xff, 0xff, 0xf4, 0xe9, 0xe9, 0xea, 0xea,
-  0xea, 0xe9, 0xe8, 0xe9, 0xf7, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe,
-  0xfe, 0xff, 0xff, 0xc7, 0x5c, 0x35, 0x38, 0x42, 0x41, 0x40, 0x3b,
-  0x2f, 0x86, 0xfb, 0xff, 0xff, 0xa1, 0x22, 0x88, 0xff, 0xff, 0xa3,
-  0xa3, 0xff, 0xff, 0x77, 0x00, 0x81, 0xff, 0xff, 0xfe, 0xf2, 0xe8,
-  0xe9, 0xea, 0xea, 0xea, 0xe9, 0xe8, 0xec, 0xf8, 0xff, 0xff, 0xfe,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xf0, 0x99, 0x4c,
-  0x2e, 0x37, 0x3e, 0x40, 0x3b, 0x2e, 0x8e, 0xff, 0xff, 0xff, 0xa9,
-  0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x89, 0x8b, 0xff, 0xff,
-  0xff, 0xf3, 0xe8, 0xe9, 0xea, 0xe9, 0xe9, 0xe8, 0xeb, 0xf3, 0xfd,
-  0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff,
-  0xfe, 0xff, 0xff, 0xda, 0x9f, 0x63, 0x38, 0x32, 0x36, 0x32, 0x2c,
-  0xa0, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
-  0xfc, 0xff, 0xff, 0xff, 0xf5, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xed,
-  0xf4, 0xfa, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xcf,
-  0x99, 0x69, 0x4a, 0x3a, 0x34, 0xa9, 0xff, 0xfb, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfc, 0xff, 0xf6, 0xe9, 0xe9, 0xea,
-  0xee, 0xf3, 0xf9, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xf4, 0xd5, 0x9a, 0xa6, 0xfc,
-  0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff,
-  0xf5, 0xf3, 0xf9, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xe4, 0xb5,
-  0x99, 0x93, 0x92, 0x96, 0xa6, 0xbb, 0xce, 0xe6, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
-  0xf2, 0xee, 0xe8, 0xe5, 0xe4, 0xe4, 0xe6, 0xed, 0xf8, 0xff, 0xff,
-  0xff, 0xc3, 0x63, 0x47, 0x46, 0x49, 0x49, 0x49, 0x46, 0x46, 0x56,
-  0x6d, 0x8d, 0xb3, 0xe4, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xf8, 0xec, 0xe2, 0xdb, 0xd5, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd1,
-  0xd1, 0xd8, 0xf0, 0xff, 0xed, 0x6a, 0x47, 0x58, 0x57, 0x57, 0x57,
-  0x57, 0x57, 0x57, 0x53, 0x4e, 0x47, 0x4c, 0x5a, 0x6d, 0xbc, 0xff,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xee, 0xdb, 0xd6, 0xd2, 0xd1, 0xd3, 0xd4, 0xd5, 0xd5,
-  0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd1, 0xda, 0xfa, 0xed, 0x69, 0x47,
-  0x58, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x53, 0x4e, 0x47, 0x4c,
-  0x5a, 0x6d, 0xbc, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xee, 0xdb, 0xd6, 0xd2, 0xd1,
-  0xd3, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd1, 0xda,
-  0xfa, 0xff, 0xc3, 0x63, 0x47, 0x46, 0x49, 0x49, 0x49, 0x46, 0x46,
-  0x56, 0x6d, 0x8d, 0xb3, 0xe4, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xff, 0xf8, 0xec, 0xe2, 0xdb, 0xd5, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2,
-  0xd1, 0xd1, 0xd8, 0xf0, 0xff, 0xff, 0xff, 0xe4, 0xb5, 0x99, 0x93,
-  0x92, 0x96, 0xa6, 0xbb, 0xce, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf2, 0xee,
-  0xe8, 0xe5, 0xe4, 0xe4, 0xe6, 0xed, 0xf8, 0xff, 0xff, 0xfe, 0xfd,
-  0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xfc, 0xf4, 0xda, 0xab, 0xbb, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xe1, 0xdd, 0xf1, 0xfb, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xf1, 0xd6, 0xaa, 0x86, 0x6f, 0x63, 0x63, 0xc2, 0xff, 0xfc,
-  0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xe2,
-  0xbb, 0xbd, 0xc2, 0xcd, 0xdd, 0xef, 0xf9, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff,
-  0xfe, 0xff, 0xff, 0xde, 0xb0, 0x80, 0x62, 0x60, 0x62, 0x5f, 0x5d,
-  0xbd, 0xff, 0xff, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xdf, 0xb8, 0xba, 0xbb, 0xba, 0xbc, 0xca,
-  0xdf, 0xf3, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xef, 0xaa, 0x70, 0x5b, 0x64, 0x69,
-  0x6a, 0x66, 0x5e, 0xae, 0xff, 0xff, 0xff, 0xc5, 0xc4, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xd8, 0xdc, 0xff, 0xff, 0xff, 0xd9, 0xb9,
-  0xbd, 0xbf, 0xbe, 0xbc, 0xb8, 0xc3, 0xdd, 0xfa, 0xff, 0xff, 0xfe,
-  0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xcd, 0x7b, 0x61,
-  0x65, 0x6c, 0x6b, 0x6a, 0x65, 0x5e, 0xa8, 0xff, 0xff, 0xff, 0xc0,
-  0x78, 0xbc, 0xff, 0xff, 0xd5, 0xd5, 0xff, 0xff, 0xcf, 0xa6, 0xd9,
-  0xff, 0xff, 0xfd, 0xd6, 0xb9, 0xbd, 0xbf, 0xbf, 0xc0, 0xbc, 0xbb,
-  0xc8, 0xec, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xc6, 0x64, 0x5e, 0x68, 0x6a, 0x6a, 0x6b, 0x64, 0x66, 0xb2, 0xff,
-  0xff, 0xfc, 0xb3, 0x73, 0x7c, 0xe4, 0xff, 0xff, 0xa4, 0xa4, 0xff,
-  0xff, 0xea, 0xa6, 0xa2, 0xd1, 0xff, 0xff, 0xff, 0xdb, 0xbc, 0xbc,
-  0xbf, 0xbf, 0xbf, 0xbe, 0xb9, 0xbe, 0xe9, 0xff, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xff, 0xf0, 0x72, 0x5f, 0x69, 0x6a, 0x69, 0x69, 0x5e,
-  0x6c, 0xc4, 0xff, 0xff, 0xff, 0xae, 0x74, 0x77, 0x86, 0xf8, 0xff,
-  0xee, 0x98, 0x98, 0xee, 0xff, 0xf9, 0xad, 0xa3, 0xa3, 0xce, 0xff,
-  0xff, 0xff, 0xe2, 0xbe, 0xba, 0xbe, 0xbe, 0xbf, 0xbe, 0xba, 0xc5,
-  0xfa, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xd3, 0x60, 0x63, 0x67,
-  0x66, 0x63, 0x5f, 0x87, 0xd8, 0xff, 0xff, 0xff, 0xb7, 0x74, 0x7d,
-  0x79, 0x9b, 0xfd, 0xff, 0xd0, 0x8f, 0x8f, 0xd0, 0xff, 0xfd, 0xba,
-  0xa5, 0xa7, 0xa3, 0xd3, 0xff, 0xff, 0xff, 0xec, 0xc9, 0xba, 0xbc,
-  0xbd, 0xbe, 0xbb, 0xbb, 0xef, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xf2, 0x7a, 0x5c, 0x62, 0x63, 0x78, 0xbd, 0xf7, 0xff, 0xff, 0xff,
-  0xc6, 0x7b, 0x7d, 0x80, 0x77, 0xbb, 0xff, 0xff, 0xb7, 0x8c, 0x8c,
-  0xb7, 0xff, 0xff, 0xcf, 0xa4, 0xaa, 0xa7, 0xa7, 0xdd, 0xff, 0xff,
-  0xff, 0xfa, 0xe0, 0xc4, 0xbb, 0xbb, 0xb9, 0xc8, 0xfb, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xe6, 0xae, 0xab, 0xca, 0xf6, 0xff,
-  0xff, 0xff, 0xff, 0xd8, 0x7e, 0x7b, 0x80, 0x7f, 0x7b, 0xdf, 0xff,
-  0xef, 0xa4, 0x90, 0x90, 0xa4, 0xef, 0xff, 0xe8, 0xa6, 0xa9, 0xaa,
-  0xa6, 0xab, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xe6, 0xda, 0xdb,
-  0xf5, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf5, 0x94, 0x77, 0x81, 0x81,
-  0x7a, 0x97, 0xf5, 0xff, 0xe0, 0x95, 0x94, 0x94, 0x95, 0xe0, 0xff,
-  0xf7, 0xb7, 0xa6, 0xaa, 0xaa, 0xa3, 0xba, 0xfa, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xc1,
-  0x76, 0x7f, 0x7f, 0x81, 0x74, 0xbf, 0xff, 0xff, 0xd5, 0x8c, 0x96,
-  0x96, 0x8c, 0xd5, 0xff, 0xff, 0xd1, 0xa1, 0xab, 0xaa, 0xa9, 0xa4,
-  0xd9, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xf5, 0x89, 0x7a, 0x7f, 0x80, 0x7b, 0x88, 0xe6, 0xff,
-  0xff, 0xc7, 0x8b, 0x96, 0x96, 0x8b, 0xc7, 0xff, 0xff, 0xec, 0xad,
-  0xa7, 0xaa, 0xa9, 0xa6, 0xb2, 0xfa, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xce, 0x79, 0x7d, 0x80, 0x7e,
-  0x79, 0xbb, 0xff, 0xff, 0xfe, 0xbd, 0x8d, 0x96, 0x96, 0x8d, 0xbd,
-  0xfe, 0xff, 0xff, 0xce, 0xa5, 0xa9, 0xaa, 0xa7, 0xa6, 0xe1, 0xff,
-  0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xb5,
-  0x78, 0x7e, 0x7f, 0x75, 0x92, 0xf5, 0xff, 0xff, 0xfd, 0xbb, 0x8d,
-  0x96, 0x96, 0x8d, 0xbb, 0xfd, 0xff, 0xff, 0xf6, 0xb4, 0xa3, 0xa9,
-  0xa8, 0xa5, 0xcf, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xb9, 0x74, 0x79, 0x76, 0x7e, 0xda, 0xff, 0xfe,
-  0xff, 0xfd, 0xbb, 0x8d, 0x96, 0x96, 0x8d, 0xbb, 0xfd, 0xff, 0xfe,
-  0xff, 0xe2, 0xa7, 0xa3, 0xa6, 0xa2, 0xd1, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xeb, 0x91, 0x78, 0x8c,
-  0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x8b, 0x96, 0x96, 0x8b,
-  0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xaf, 0xa4, 0xb3, 0xf0,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
-  0xff, 0xf7, 0xe0, 0xf6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xd1,
-  0x8c, 0x97, 0x97, 0x8c, 0xd1, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xf6, 0xe6, 0xf8, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xfe, 0xff, 0xee, 0x9e, 0x8c, 0x8c, 0x9e, 0xee, 0xff, 0xfe,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xd9, 0xa2, 0xa2,
-  0xd9, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xf4, 0xf4, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff
-};
-
-static const unsigned char twirl_60_data[] = {
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xe9, 0xe9, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xff, 0xfe, 0xfd, 0xff, 0xb4, 0x45, 0x45, 0xb4, 0xff, 0xfd, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xde, 0x3d, 0x1a, 0x1a,
-  0x3d, 0xde, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xff, 0xef, 0xc7, 0xeb, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xa3, 0x1a, 0x2f, 0x2f, 0x1a, 0xa3, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xff, 0xee, 0xc7, 0xf0, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xdf, 0x55, 0x34, 0x4b, 0xb6, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0x80, 0x18, 0x2e, 0x2e, 0x18, 0x80, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xb2, 0x2c, 0x07, 0x35, 0xda, 0xff, 0xff,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x97, 0x2e,
-  0x37, 0x32, 0x39, 0xbe, 0xff, 0xfe, 0xff, 0xfc, 0x78, 0x1c, 0x2d,
-  0x2d, 0x1c, 0x78, 0xfc, 0xff, 0xfe, 0xff, 0xba, 0x12, 0x03, 0x09,
-  0x00, 0x7e, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0x94, 0x35, 0x3d, 0x3f, 0x31, 0x57, 0xeb, 0xff, 0xff,
-  0xfb, 0x77, 0x1c, 0x2d, 0x2d, 0x1c, 0x77, 0xfb, 0xff, 0xff, 0xed,
-  0x37, 0x02, 0x14, 0x12, 0x07, 0x78, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xbb, 0x37, 0x3b, 0x40, 0x3e,
-  0x34, 0x92, 0xff, 0xff, 0xfd, 0x7c, 0x1c, 0x2d, 0x2d, 0x1c, 0x7c,
-  0xfd, 0xff, 0xff, 0x83, 0x08, 0x12, 0x15, 0x0f, 0x09, 0xa4, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf4,
-  0x52, 0x37, 0x3f, 0x40, 0x39, 0x47, 0xd5, 0xff, 0xff, 0x90, 0x18,
-  0x2e, 0x2e, 0x18, 0x90, 0xff, 0xff, 0xd2, 0x24, 0x0b, 0x15, 0x14,
-  0x0b, 0x26, 0xec, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xa9, 0x32, 0x3f, 0x40, 0x43, 0x2d, 0x9a,
-  0xff, 0xff, 0xab, 0x19, 0x2e, 0x2e, 0x19, 0xab, 0xff, 0xff, 0x8a,
-  0x00, 0x18, 0x14, 0x14, 0x03, 0x8d, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf4, 0x65, 0x31,
-  0x41, 0x41, 0x38, 0x5d, 0xed, 0xff, 0xc2, 0x2c, 0x29, 0x29, 0x2c,
-  0xc2, 0xff, 0xec, 0x40, 0x0a, 0x17, 0x17, 0x04, 0x39, 0xed, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xe4, 0xa2, 0x9c, 0xbd, 0xf1, 0xff, 0xff,
-  0xff, 0xff, 0xcd, 0x42, 0x38, 0x40, 0x3e, 0x36, 0xca, 0xff, 0xe0,
-  0x49, 0x22, 0x22, 0x49, 0xe0, 0xff, 0xc4, 0x0b, 0x13, 0x16, 0x0d,
-  0x12, 0xb8, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xa4, 0x70, 0x74, 0xd4,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x6d, 0x45, 0x4b,
-  0x4c, 0x61, 0xac, 0xf1, 0xff, 0xff, 0xff, 0xb3, 0x3b, 0x3a, 0x40,
-  0x33, 0x93, 0xff, 0xff, 0x70, 0x1a, 0x1a, 0x70, 0xff, 0xff, 0x81,
-  0x04, 0x15, 0x0f, 0x0c, 0x97, 0xff, 0xff, 0xff, 0xf1, 0x8e, 0x19,
-  0x00, 0x00, 0x00, 0x1c, 0xe9, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xd6, 0x4b, 0x4c, 0x52, 0x50, 0x4e, 0x47, 0x6f, 0xcc, 0xff, 0xff,
-  0xff, 0x9c, 0x31, 0x3b, 0x36, 0x64, 0xfb, 0xff, 0xa0, 0x20, 0x20,
-  0xa0, 0xff, 0xfc, 0x47, 0x09, 0x10, 0x00, 0x7b, 0xff, 0xff, 0xff,
-  0xbc, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xf3, 0x65, 0x47, 0x54, 0x55, 0x54, 0x54,
-  0x49, 0x53, 0xb3, 0xff, 0xff, 0xff, 0x90, 0x30, 0x32, 0x47, 0xf1,
-  0xff, 0xde, 0x31, 0x31, 0xde, 0xff, 0xf2, 0x21, 0x05, 0x00, 0x6b,
-  0xff, 0xff, 0xff, 0x99, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x0f, 0xe5, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xc7, 0x53,
-  0x47, 0x53, 0x55, 0x54, 0x56, 0x4f, 0x4e, 0x9f, 0xff, 0xff, 0xff,
-  0x97, 0x30, 0x38, 0xd0, 0xff, 0xff, 0x4a, 0x4a, 0xff, 0xff, 0xcc,
-  0x0e, 0x00, 0x74, 0xfa, 0xff, 0xff, 0x7b, 0x00, 0x00, 0x02, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x9e, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe,
-  0xfe, 0xff, 0xff, 0xcd, 0x6e, 0x4b, 0x4e, 0x57, 0x56, 0x55, 0x50,
-  0x46, 0x93, 0xfc, 0xff, 0xff, 0xaa, 0x38, 0x93, 0xff, 0xff, 0xac,
-  0xac, 0xff, 0xff, 0x83, 0x06, 0x8b, 0xff, 0xff, 0xff, 0x6a, 0x00,
-  0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x1e, 0xa8, 0xff, 0xff, 0xfe,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xf2, 0xa4, 0x60,
-  0x44, 0x4d, 0x53, 0x55, 0x51, 0x45, 0x9a, 0xff, 0xff, 0xff, 0xb1,
-  0xa8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x92, 0x95, 0xff, 0xff,
-  0xff, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0xe4,
-  0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff,
-  0xfe, 0xff, 0xff, 0xde, 0xa9, 0x74, 0x4e, 0x49, 0x4c, 0x49, 0x43,
-  0xab, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
-  0xfc, 0xff, 0xff, 0xff, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27,
-  0x77, 0xc7, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xd4,
-  0xa4, 0x79, 0x5d, 0x4f, 0x4b, 0xb2, 0xff, 0xfc, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfa, 0xff, 0x98, 0x00, 0x00, 0x0a,
-  0x2f, 0x6e, 0xb9, 0xe7, 0xff, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xf5, 0xda, 0xa5, 0xb0, 0xfd,
-  0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff,
-  0x8b, 0x70, 0xc0, 0xee, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xe8, 0xbe,
-  0xa5, 0xa0, 0xa0, 0xa3, 0xb1, 0xc4, 0xd4, 0xe9, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd,
-  0xf9, 0xf6, 0xf4, 0xf2, 0xf1, 0xf1, 0xf2, 0xf6, 0xfb, 0xff, 0xff,
-  0xff, 0xca, 0x76, 0x5e, 0x5d, 0x5f, 0x60, 0x5f, 0x5d, 0x5d, 0x6b,
-  0x7f, 0x9b, 0xbd, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfc, 0xf6, 0xf1, 0xed, 0xea, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
-  0xe8, 0xeb, 0xf7, 0xff, 0xef, 0x7c, 0x5e, 0x6d, 0x6c, 0x6b, 0x6b,
-  0x6b, 0x6c, 0x6c, 0x69, 0x63, 0x5e, 0x62, 0x6e, 0x7f, 0xc4, 0xff,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xf6, 0xed, 0xea, 0xe9, 0xe8, 0xe9, 0xe9, 0xea, 0xea,
-  0xea, 0xea, 0xea, 0xea, 0xea, 0xe8, 0xec, 0xfd, 0xef, 0x7c, 0x5e,
-  0x6d, 0x6c, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x69, 0x63, 0x5e, 0x62,
-  0x6e, 0x7f, 0xc4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf6, 0xed, 0xea, 0xe9, 0xe8,
-  0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xe8, 0xec,
-  0xfd, 0xff, 0xca, 0x76, 0x5e, 0x5d, 0x5f, 0x60, 0x5f, 0x5d, 0x5d,
-  0x6b, 0x7f, 0x9b, 0xbd, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xff, 0xfc, 0xf6, 0xf1, 0xed, 0xea, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
-  0xe8, 0xe8, 0xeb, 0xf7, 0xff, 0xff, 0xff, 0xe8, 0xbe, 0xa5, 0xa0,
-  0xa0, 0xa3, 0xb1, 0xc4, 0xd4, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf9, 0xf6,
-  0xf4, 0xf2, 0xf1, 0xf1, 0xf2, 0xf6, 0xfb, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xfc, 0xf6, 0xdf, 0xb7, 0xc5, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xeb, 0xe8, 0xf6, 0xfd, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xf3, 0xdc, 0xb7, 0x98, 0x85, 0x7a, 0x7a, 0xcb, 0xff, 0xfd,
-  0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xec,
-  0xd2, 0xd3, 0xd7, 0xde, 0xe8, 0xf4, 0xfb, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff,
-  0xfe, 0xff, 0xff, 0xe3, 0xbc, 0x94, 0x7a, 0x77, 0x79, 0x76, 0x75,
-  0xc7, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xea, 0xd0, 0xd2, 0xd2, 0xd2, 0xd3, 0xdc,
-  0xea, 0xf7, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xf1, 0xb6, 0x85, 0x73, 0x7a, 0x7f,
-  0x80, 0x7c, 0x75, 0xba, 0xff, 0xff, 0xff, 0xcf, 0xcd, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xe2, 0xe5, 0xff, 0xff, 0xff, 0xe6, 0xd1,
-  0xd4, 0xd5, 0xd4, 0xd3, 0xd1, 0xd8, 0xe8, 0xfb, 0xff, 0xff, 0xfe,
-  0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xd4, 0x8f, 0x78,
-  0x7b, 0x82, 0x80, 0x80, 0x7c, 0x76, 0xb5, 0xff, 0xff, 0xff, 0xcb,
-  0x8e, 0xc7, 0xff, 0xff, 0xde, 0xde, 0xff, 0xff, 0xda, 0xbc, 0xe3,
-  0xff, 0xff, 0xfe, 0xe4, 0xd1, 0xd3, 0xd5, 0xd5, 0xd5, 0xd3, 0xd2,
-  0xdb, 0xf2, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xcf, 0x7b, 0x76, 0x7e, 0x80, 0x80, 0x81, 0x7b, 0x7d, 0xbd, 0xff,
-  0xff, 0xfd, 0xc0, 0x8a, 0x91, 0xe8, 0xff, 0xff, 0xb6, 0xb6, 0xff,
-  0xff, 0xef, 0xbc, 0xb9, 0xdc, 0xff, 0xff, 0xff, 0xe7, 0xd3, 0xd3,
-  0xd5, 0xd4, 0xd5, 0xd4, 0xd1, 0xd4, 0xf1, 0xff, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xff, 0xf2, 0x87, 0x76, 0x7f, 0x80, 0x7f, 0x7f, 0x76,
-  0x81, 0xcc, 0xff, 0xff, 0xff, 0xbc, 0x8b, 0x8d, 0x9a, 0xf9, 0xff,
-  0xf1, 0xac, 0xac, 0xf1, 0xff, 0xfa, 0xc1, 0xba, 0xb9, 0xda, 0xff,
-  0xff, 0xff, 0xec, 0xd4, 0xd2, 0xd4, 0xd4, 0xd5, 0xd4, 0xd1, 0xd9,
-  0xfc, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xda, 0x78, 0x7a, 0x7e,
-  0x7c, 0x7a, 0x76, 0x98, 0xdd, 0xff, 0xff, 0xff, 0xc3, 0x8b, 0x92,
-  0x8f, 0xab, 0xfd, 0xff, 0xd9, 0xa5, 0xa5, 0xd9, 0xff, 0xfd, 0xcb,
-  0xbb, 0xbd, 0xba, 0xde, 0xff, 0xff, 0xff, 0xf2, 0xdb, 0xd1, 0xd3,
-  0xd4, 0xd4, 0xd2, 0xd2, 0xf5, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xf4, 0x8e, 0x74, 0x79, 0x7a, 0x8c, 0xc7, 0xf8, 0xff, 0xff, 0xff,
-  0xcf, 0x90, 0x92, 0x95, 0x8d, 0xc6, 0xff, 0xff, 0xc6, 0xa3, 0xa3,
-  0xc6, 0xff, 0xff, 0xda, 0xba, 0xbf, 0xbd, 0xbd, 0xe5, 0xff, 0xff,
-  0xff, 0xfb, 0xea, 0xd8, 0xd2, 0xd2, 0xd1, 0xda, 0xfc, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xea, 0xba, 0xb8, 0xd1, 0xf7, 0xff,
-  0xff, 0xff, 0xff, 0xdf, 0x93, 0x91, 0x95, 0x94, 0x91, 0xe5, 0xff,
-  0xf3, 0xb6, 0xa6, 0xa6, 0xb6, 0xf3, 0xff, 0xed, 0xbb, 0xbe, 0xbf,
-  0xbc, 0xbf, 0xee, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xee, 0xe6, 0xe8,
-  0xf8, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf6, 0xa5, 0x8d, 0x96, 0x95,
-  0x90, 0xa8, 0xf6, 0xff, 0xe6, 0xaa, 0xa9, 0xa9, 0xaa, 0xe6, 0xff,
-  0xf9, 0xc9, 0xbc, 0xbf, 0xbf, 0xba, 0xcb, 0xfb, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xcc,
-  0x8d, 0x94, 0x94, 0x96, 0x8a, 0xc9, 0xff, 0xff, 0xdd, 0xa2, 0xab,
-  0xab, 0xa2, 0xdd, 0xff, 0xff, 0xdd, 0xb9, 0xc0, 0xbf, 0xbe, 0xba,
-  0xe2, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xf6, 0x9c, 0x90, 0x94, 0x95, 0x90, 0x9b, 0xea, 0xff,
-  0xff, 0xd3, 0xa2, 0xab, 0xab, 0xa2, 0xd3, 0xff, 0xff, 0xf1, 0xc1,
-  0xbc, 0xbf, 0xbe, 0xbc, 0xc5, 0xfb, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xd6, 0x8f, 0x92, 0x95, 0x93,
-  0x8f, 0xc6, 0xff, 0xff, 0xfe, 0xca, 0xa4, 0xab, 0xab, 0xa4, 0xca,
-  0xfe, 0xff, 0xff, 0xda, 0xbb, 0xbe, 0xbf, 0xbd, 0xbc, 0xe8, 0xff,
-  0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xc1,
-  0x8f, 0x93, 0x94, 0x8c, 0xa4, 0xf7, 0xff, 0xff, 0xfd, 0xc8, 0xa4,
-  0xaa, 0xaa, 0xa4, 0xc8, 0xfd, 0xff, 0xff, 0xf8, 0xc6, 0xba, 0xbe,
-  0xbe, 0xbb, 0xdb, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xc5, 0x8b, 0x8f, 0x8c, 0x93, 0xe0, 0xff, 0xfe,
-  0xff, 0xfd, 0xc9, 0xa4, 0xab, 0xab, 0xa4, 0xc9, 0xfd, 0xff, 0xfe,
-  0xff, 0xe9, 0xbc, 0xba, 0xbb, 0xb9, 0xdc, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xee, 0xa3, 0x8f, 0x9f,
-  0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xa2, 0xab, 0xab, 0xa2,
-  0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xbb, 0xc6, 0xf4,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
-  0xff, 0xf8, 0xe5, 0xf7, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xda,
-  0xa3, 0xab, 0xab, 0xa3, 0xda, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xf8, 0xec, 0xf9, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
-  0xff, 0xfe, 0xff, 0xf1, 0xb1, 0xa3, 0xa3, 0xb1, 0xf1, 0xff, 0xfe,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xe1, 0xb4, 0xb4,
-  0xe1, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xf6, 0xf6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff
-};
-
-// Also, the "failed" icon.
-const unsigned char failed_data[] = {
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfb, 0xfb, 0xfe, 0xff, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfd, 0xff, 0xfa, 0x91, 0x93, 0xfd, 0xff, 0xfd, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xfe, 0xfc, 0xff, 0x8c, 0x00, 0x00, 0x8f, 0xff,
-  0xfc, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xcf, 0x0f,
-  0x00, 0x00, 0x0f, 0xcf, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xff, 0x51, 0x00, 0x01, 0x01, 0x00, 0x51, 0xff, 0xff, 0xfe,
-  0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0xa4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xef, 0x1e, 0x00,
-  0x00, 0x55, 0x54, 0x00, 0x00, 0x1f, 0xf0, 0xff, 0xfe, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xff, 0x6a, 0x00, 0x00, 0x1c, 0xbf, 0xbe, 0x1c, 0x00, 0x00, 0x68,
-  0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xfe, 0xfb, 0xff, 0xbb, 0x03, 0x00, 0x00, 0x7d, 0xd2, 0xd2,
-  0x7e, 0x00, 0x00, 0x03, 0xbc, 0xff, 0xfb, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xf7, 0x3c, 0x00, 0x00,
-  0x3f, 0xc6, 0xc4, 0xc4, 0xc6, 0x3e, 0x00, 0x00, 0x3d, 0xf8, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff,
-  0x8b, 0x00, 0x00, 0x0c, 0xa1, 0xcd, 0xc0, 0xc0, 0xcd, 0xa1, 0x0c,
-  0x00, 0x00, 0x89, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xcf, 0x09, 0x00, 0x00, 0x6c, 0xd4, 0xc2, 0xc0,
-  0xc0, 0xc2, 0xd4, 0x6d, 0x00, 0x00, 0x09, 0xd0, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
-  0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x4a, 0x00, 0x00, 0x29,
-  0xc3, 0xca, 0xc7, 0xd0, 0xd0, 0xc7, 0xca, 0xc3, 0x29, 0x00, 0x00,
-  0x4a, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xa5,
-  0x00, 0x00, 0x00, 0x90, 0xd1, 0xc7, 0xb2, 0x52, 0x52, 0xb2, 0xc7,
-  0xd1, 0x91, 0x00, 0x00, 0x00, 0xa6, 0xff, 0xfe, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfc, 0xff, 0xeb, 0x25, 0x00, 0x00, 0x53, 0xc9, 0xc4, 0xd2, 0x6b,
-  0x00, 0x00, 0x6b, 0xd2, 0xc4, 0xc9, 0x53, 0x00, 0x00, 0x25, 0xec,
-  0xff, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0x69, 0x00, 0x00, 0x20, 0xb3,
-  0xcb, 0xbe, 0xd5, 0x58, 0x00, 0x00, 0x58, 0xd5, 0xbe, 0xcb, 0xb3,
-  0x21, 0x00, 0x00, 0x69, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xb7, 0x01,
-  0x00, 0x00, 0x7e, 0xd3, 0xc0, 0xbf, 0xd4, 0x5d, 0x00, 0x00, 0x5d,
-  0xd4, 0xbf, 0xc0, 0xd3, 0x7f, 0x00, 0x00, 0x01, 0xb8, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xf5, 0x36, 0x00, 0x00, 0x3f, 0xc9, 0xc5, 0xc2, 0xbf, 0xd4,
-  0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf, 0xc2, 0xc5, 0xca, 0x3f, 0x00,
-  0x00, 0x37, 0xf6, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0x87, 0x00, 0x00, 0x07, 0xa6, 0xcd,
-  0xc2, 0xc3, 0xbf, 0xd4, 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf, 0xc3,
-  0xc2, 0xcd, 0xa6, 0x07, 0x00, 0x00, 0x85, 0xff, 0xff, 0xfe, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xd4, 0x06, 0x00,
-  0x00, 0x6c, 0xd3, 0xc5, 0xc2, 0xc3, 0xbf, 0xd4, 0x5d, 0x00, 0x00,
-  0x5d, 0xd4, 0xbf, 0xc2, 0xc2, 0xc5, 0xd3, 0x6d, 0x00, 0x00, 0x06,
-  0xd5, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
-  0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0x4e, 0x00, 0x00, 0x2e, 0xbe, 0xcd, 0xc2, 0xc2, 0xc2, 0xbf,
-  0xd4, 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf, 0xc3, 0xc2, 0xc2, 0xcd,
-  0xbe, 0x2e, 0x00, 0x00, 0x4f, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xfe, 0xfb, 0xff, 0xa2, 0x00, 0x00, 0x02, 0x8f, 0xcf, 0xc3,
-  0xc2, 0xc2, 0xc2, 0xbf, 0xd4, 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf,
-  0xc3, 0xc2, 0xc2, 0xc3, 0xcf, 0x8f, 0x02, 0x00, 0x00, 0xa1, 0xff,
-  0xfb, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xe6, 0x27, 0x00, 0x00,
-  0x53, 0xcb, 0xc2, 0xc2, 0xc3, 0xc2, 0xc3, 0xbf, 0xd4, 0x5c, 0x00,
-  0x00, 0x5c, 0xd4, 0xbf, 0xc3, 0xc2, 0xc3, 0xc2, 0xc1, 0xcc, 0x54,
-  0x00, 0x00, 0x27, 0xe8, 0xff, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0x66, 0x00, 0x00, 0x20, 0xb7, 0xcc, 0xc1, 0xc3, 0xc2, 0xc2, 0xc3,
-  0xbf, 0xd4, 0x59, 0x00, 0x00, 0x59, 0xd4, 0xbf, 0xc3, 0xc2, 0xc2,
-  0xc3, 0xc1, 0xcc, 0xb7, 0x20, 0x00, 0x00, 0x66, 0xff, 0xff, 0xfe,
-  0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xfe, 0xff, 0xff, 0xb7, 0x00, 0x00, 0x00, 0x83, 0xd3, 0xc2, 0xc3,
-  0xc3, 0xc2, 0xc2, 0xc2, 0xc0, 0xce, 0x86, 0x01, 0x01, 0x86, 0xce,
-  0xc0, 0xc3, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2, 0xd3, 0x84, 0x00, 0x00,
-  0x00, 0xb8, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0x30, 0x00, 0x00, 0x3e,
-  0xca, 0xc8, 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
-  0x90, 0x90, 0xc3, 0xc3, 0xc2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2,
-  0xc8, 0xca, 0x3d, 0x00, 0x00, 0x31, 0xfd, 0xff, 0xfe, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8a,
-  0x00, 0x00, 0x0a, 0xa2, 0xcf, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2,
-  0xc3, 0xc3, 0xc2, 0xc3, 0xd3, 0xd3, 0xc3, 0xc2, 0xc3, 0xc3, 0xc2,
-  0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xcf, 0xa2, 0x0a, 0x00, 0x00, 0x8a,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
-  0xfc, 0xff, 0xcf, 0x0e, 0x00, 0x00, 0x6b, 0xd0, 0xc5, 0xc2, 0xc2,
-  0xc3, 0xc3, 0xc2, 0xc2, 0xc3, 0xc3, 0xc1, 0xc9, 0xc5, 0xc5, 0xc9,
-  0xc1, 0xc3, 0xc3, 0xc2, 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc5, 0xd1,
-  0x6e, 0x00, 0x00, 0x0e, 0xcf, 0xff, 0xfb, 0xff, 0xfe, 0xfe, 0xfe,
-  0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x4e, 0x00, 0x00, 0x31, 0xbf,
-  0xc8, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc9,
-  0xab, 0x52, 0x52, 0xab, 0xc9, 0xc1, 0xc3, 0xc2, 0xc2, 0xc2, 0xc3,
-  0xc2, 0xc2, 0xc2, 0xc8, 0xc0, 0x30, 0x00, 0x00, 0x4f, 0xff, 0xff,
-  0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xa0, 0x00,
-  0x00, 0x04, 0x93, 0xd1, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2,
-  0xc2, 0xc2, 0xc0, 0xcf, 0x4c, 0x00, 0x00, 0x4c, 0xcf, 0xc0, 0xc3,
-  0xc2, 0xc2, 0xc2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc0, 0xd1, 0x92, 0x04,
-  0x00, 0x00, 0x9e, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xe5, 0x1e, 0x00, 0x00, 0x54, 0xcd, 0xc3, 0xc3, 0xc3, 0xc2,
-  0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc3, 0xc0, 0xd0, 0x4e, 0x00, 0x00,
-  0x4e, 0xd0, 0xc0, 0xc3, 0xc2, 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2,
-  0xc3, 0xc3, 0xce, 0x56, 0x00, 0x00, 0x20, 0xe6, 0xff, 0xfe, 0xfe,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x1b, 0xba, 0xcc,
-  0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1,
-  0xc7, 0xae, 0x4c, 0x4c, 0xae, 0xc7, 0xc1, 0xc2, 0xc2, 0xc2, 0xc2,
-  0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xcc, 0xbb, 0x19, 0x00, 0x00,
-  0x61, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xbd, 0x00, 0x00,
-  0x00, 0x85, 0xd6, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
-  0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xcb, 0xd2, 0xd2, 0xcb, 0xc5, 0xc6,
-  0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
-  0xd6, 0x84, 0x00, 0x00, 0x00, 0xbb, 0xff, 0xfe, 0xff, 0xfd, 0xff,
-  0xf7, 0x38, 0x00, 0x00, 0x42, 0xbf, 0xc6, 0xbe, 0xbe, 0xbe, 0xbe,
-  0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf,
-  0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
-  0xbe, 0xbe, 0xbe, 0xbe, 0xc6, 0xc0, 0x42, 0x00, 0x00, 0x38, 0xf9,
-  0xff, 0xfd, 0xfc, 0xff, 0x83, 0x00, 0x00, 0x00, 0x16, 0x24, 0x20,
-  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
-  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
-  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x24, 0x16,
-  0x00, 0x00, 0x00, 0x82, 0xff, 0xfd, 0xff, 0xfa, 0x34, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xf7, 0xff, 0xfe,
-  0xff, 0x86, 0x12, 0x11, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
-  0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
-  0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
-  0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x12, 0x18,
-  0x8a, 0xff, 0xfe, 0xfe, 0xff, 0xfd, 0xd2, 0xc4, 0xc8, 0xc8, 0xc8,
-  0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
-  0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
-  0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
-  0xc8, 0xc8, 0xc5, 0xd8, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
-  0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
-  0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
-  0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
-  0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
-  0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
-  0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
-  0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
-  0xff, 0xfe, 0xff, 0xff, 0xff
-};
-
-static const unsigned char * const twirl_data[4] = {
-  twirl_0_data,
-  twirl_30_data,
-  twirl_60_data,
-  failed_data
-};
-
-
-/**
- * Fills the indicated data array with a string of twirl_width * twirl_height
- * * 3 bytes, representing the RGB pixel values of the twirl_width x
- * twirl_height image at frame number step of twirl_num_steps frames.  The
- * specified fg and bg colors are applied to the array appropriately.
- *
- * Returns true on success, false on failure.  On false, the array is
- * initialized with zero.
- *
- * You must pass data_length = twirl_width * twirl_height * 3; this value is
- * passed as a sanity check on array size.  You should pass step so that 0 <=
- * step < twirl_num_steps + 1.  (The special value of step == twirl_num_steps
- * returns the "failed" icon.)
- */
-bool
-get_twirl_data(unsigned char data[], size_t data_length, int step,
-               int fg_r, int fg_g, int fg_b,
-               int bg_r, int bg_g, int bg_b) {
-
-  if (step < 0 || step >= twirl_num_steps + 1) {
-    memset(data, 0, data_length);
-    return false;
-  }
-  if (data_length != twirl_width * twirl_height) {
-    memset(data, 0, data_length);
-    return false;
-  }
-
-  twirl_flip &flip = twirl_flip_table[step];
-  const unsigned char *in_data = twirl_data[flip._index];
-
-  for (int yi = 0; yi < twirl_height; ++yi) {
-    const unsigned char *sp = &in_data[yi * twirl_width];
-    for (int xi = 0; xi < twirl_width; ++xi) {
-      int xo = xi;
-      int yo = yi;
-      if (flip._flip_x) {
-        xo = twirl_width - 1 - xo;
-      }
-      if (flip._flip_y) {
-        yo = twirl_width - 1 - yo;
-      }
-      if (flip._exchange) {
-        int t = xo;
-        xo = yo;
-        yo = t;
-      }
-      unsigned char *dp = &data[yo * twirl_width * 3];
-      dp += 3 * xo;
-
-      double t = (double)(*sp) / 255.0;
-      ++sp;
-
-      dp[0] = (unsigned char)(fg_r + t * (bg_r - fg_r));
-      dp[1] = (unsigned char)(fg_g + t * (bg_g - fg_g));
-      dp[2] = (unsigned char)(fg_b + t * (bg_b - fg_b));
-    }
-  }
-
-  return true;
-}

+ 0 - 27
direct/src/plugin/get_twirl_data.h

@@ -1,27 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file get_twirl_data.h
- * @author drose
- * @date 2011-08-24
- */
-
-#ifndef GET_TWIRL_DATA_H
-#define GET_TWIRL_DATA_H
-
-#include <stddef.h>
-
-static const int twirl_width = 48;
-static const int twirl_height = 48;
-static const int twirl_num_steps = 12;
-
-bool get_twirl_data(unsigned char data[], size_t data_length, int step,
-                    int fg_r, int fg_g, int fg_b,
-                    int bg_r, int bg_g, int bg_b);
-
-#endif

+ 0 - 88
direct/src/plugin/handleStream.I

@@ -1,88 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStream.I
- * @author drose
- * @date 2009-06-05
- */
-
-/**
- *
- */
-inline HandleStream::
-HandleStream() : std::iostream(&_buf) {
-}
-
-/**
- *
- */
-inline HandleStream::
-~HandleStream() {
-  close();
-}
-
-/**
- * Attempts to open the given handle for input.  The stream may not be
- * simultaneously open for input and output.
- */
-inline void HandleStream::
-open_read(FHandle handle) {
-  clear((std::ios::iostatetate)0);
-  _buf.open_read(handle);
-  if (!_buf.is_open_read()) {
-    clear(std::ios::failbit);
-  }
-}
-
-/**
- * Attempts to open the given handle for output.  The stream may not be
- * simultaneously open for input and output.
- */
-inline void HandleStream::
-open_write(FHandle handle) {
-  clear((std::ios::iostatetate)0);
-  _buf.open_write(handle);
-  if (!_buf.is_open_write()) {
-    clear(std::ios::failbit);
-  }
-}
-
-/**
- *
- */
-inline void HandleStream::
-close() {
-  _buf.close();
-}
-
-/**
- * Closes the underlying handle, *without* attempting to flush the stream.
- */
-inline void HandleStream::
-close_handle() {
-  _buf.close_handle();
-}
-
-/**
- * Returns the handle that was passed to open_read() or open_write().
- */
-inline FHandle HandleStream::
-get_handle() const {
-  return _buf.get_handle();
-}
-
-/**
- * Returns true if there is data in the stream's "get" buffer, meaning that at
- * least one character can be extracted from the stream without making an OS
- * read() call.  Returns false if the get buffer is empty, meaning the next
- * read call will hit the OS.
- */
-inline bool HandleStream::
-has_gdata() const {
-  return _buf.has_gdata();
-}

+ 0 - 14
direct/src/plugin/handleStream.cxx

@@ -1,14 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStream.cxx
- * @author drose
- * @date 2009-06-05
- */
-
-#include "handleStream.h"

+ 0 - 43
direct/src/plugin/handleStream.h

@@ -1,43 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStream.h
- * @author drose
- * @date 2009-06-05
- */
-
-#ifndef HANDLESTREAM_H
-#define HANDLESTREAM_H
-
-#include "handleStreamBuf.h"
-
-/**
- * Implements a C++ stream object suitable for reading from and writing to
- * Windows' HANDLE objects, or Posix file descriptors.  This is necessary to
- * map low-level pipes into an iostream for tinyxml.
- */
-class HandleStream : public std::iostream {
-public:
-  inline HandleStream();
-  inline ~HandleStream();
-
-  inline void open_read(FHandle handle);
-  inline void open_write(FHandle handle);
-  inline void close();
-  inline void close_handle();
-
-  inline FHandle get_handle() const;
-  inline bool has_gdata() const;
-
-private:
-  HandleStreamBuf _buf;
-};
-
-#include "handleStream.I"
-
-#endif

+ 0 - 31
direct/src/plugin/handleStreamBuf.I

@@ -1,31 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStreamBuf.I
- * @author drose
- * @date 2009-06-05
- */
-
-/**
- * Returns the handle that was passed to open_read() or open_write().
- */
-inline FHandle HandleStreamBuf::
-get_handle() const {
-  return _handle;
-}
-
-/**
- * Returns true if there is data in the stream's "get" buffer, meaning that at
- * least one character can be extracted from the stream without making an OS
- * read() call.  Returns false if the get buffer is empty, meaning the next
- * read call will hit the OS.
- */
-inline bool HandleStreamBuf::
-has_gdata() const {
-  return (egptr() != gptr());
-}

+ 0 - 342
direct/src/plugin/handleStreamBuf.cxx

@@ -1,342 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStreamBuf.cxx
- * @author drose
- * @date 2009-06-05
- */
-
-#include "handleStreamBuf.h"
-
-#include <assert.h>
-#include <string.h>
-
-#ifndef _WIN32
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#endif  // _WIN32
-
-#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__FreeBSD__)
-#include <libio.h>
-#endif // !_WIN32 && !__APPLE__ && !__FreeBSD__
-
-using std::cerr;
-using std::dec;
-using std::hex;
-
-static const size_t handle_buffer_size = 4096;
-
-/**
- *
- */
-HandleStreamBuf::
-HandleStreamBuf() {
-  _is_open_read = false;
-  _is_open_write = false;
-
-  _handle = invalid_fhandle;
-
-  INIT_LOCK(_lock);
-
-  _buffer = new char[handle_buffer_size];
-  char *ebuf = _buffer + handle_buffer_size;
-  setg(_buffer, ebuf, ebuf);
-  setp(_buffer, ebuf);
-}
-
-/**
- *
- */
-HandleStreamBuf::
-~HandleStreamBuf() {
-  close();
-
-  delete[] _buffer;
-
-  DESTROY_LOCK(_lock);
-}
-
-/**
- * Attempts to open the given handle for input.  The stream may not be
- * simultaneously open for input and output.
- */
-void HandleStreamBuf::
-open_read(FHandle handle) {
-  close();
-
-  _handle = handle;
-  _is_open_read = true;
-}
-
-/**
- * Attempts to open the given handle for output.  The stream may not be
- * simultaneously open for input and output.
- */
-void HandleStreamBuf::
-open_write(FHandle handle) {
-  close();
-
-  _handle = handle;
-  _is_open_write = true;
-}
-
-/**
- * Returns true if the file is open for input, false otherwise.
- */
-bool HandleStreamBuf::
-is_open_read() const {
-  return _is_open_read;
-}
-
-/**
- * Returns true if the file is open for output, false otherwise.
- */
-bool HandleStreamBuf::
-is_open_write() const {
-  return _is_open_write;
-}
-
-/**
- * Empties the buffer and closes the file.
- */
-void HandleStreamBuf::
-close() {
-  // Make sure the write buffer is flushed.
-  sync();
-
-  close_handle();
-
-  pbump(pbase() - pptr());
-  gbump(egptr() - gptr());
-}
-
-/**
- * Closes the underlying handle, *without* attempting to flush the stream.
- */
-void HandleStreamBuf::
-close_handle() {
-#ifdef _WIN32
-  if (_handle != nullptr) {
-    CloseHandle(_handle);
-  }
-  _handle = nullptr;
-#else
-  if (_handle != -1) {
-    ::close(_handle);
-  }
-  _handle = -1;
-#endif  // _WIN32
-
-  _is_open_read = false;
-  _is_open_write = false;
-}
-
-/**
- * Called by the system ostream implementation when its internal buffer is
- * filled, plus one character.
- */
-int HandleStreamBuf::
-overflow(int ch) {
-  ACQUIRE_LOCK(_lock);
-
-  bool okflag = true;
-
-  assert(pptr() >= pbase());
-  size_t n = pptr() - pbase();
-  if (n != 0) {
-    size_t wrote = write_chars(pbase(), n);
-    assert(wrote <= n);
-    pbump(-(int)wrote);
-    if (wrote != n) {
-      okflag = false;
-    }
-  }
-
-  if (okflag && ch != EOF) {
-    if (pptr() != epptr()) {
-      // Store the extra character back in the buffer.
-      *(pptr()) = ch;
-      pbump(1);
-    } else {
-      // No room to store ch.
-      okflag = false;
-    }
-  }
-
-  RELEASE_LOCK(_lock);
-
-  if (!okflag) {
-    return EOF;
-  }
-  return 0;
-}
-
-/**
- * Called by the system iostream implementation to implement a flush
- * operation.
- */
-int HandleStreamBuf::
-sync() {
-  ACQUIRE_LOCK(_lock);
-  assert(pptr() >= pbase());
-  size_t n = pptr() - pbase();
-
-  size_t wrote = write_chars(pbase(), n);
-  assert(wrote <= n);
-  pbump(-(int)wrote);
-
-  RELEASE_LOCK(_lock);
-
-  if (n != wrote) {
-    return EOF;
-  }
-  return 0;
-}
-
-/**
- * Called by the system istream implementation when its internal buffer needs
- * more characters.
- */
-int HandleStreamBuf::
-underflow() {
-  ACQUIRE_LOCK(_lock);
-  // Sometimes underflow() is called even if the buffer is not empty.
-  if (gptr() >= egptr()) {
-    // Mark the buffer filled (with buffer_size bytes).
-    size_t buffer_size = egptr() - eback();
-    gbump(-(int)buffer_size);
-
-    size_t num_bytes = buffer_size;
-    size_t read_count = read_chars(gptr(), buffer_size);
-
-    if (read_count != num_bytes) {
-      // Oops, we didn't read what we thought we would.
-      if (read_count == 0) {
-        gbump(num_bytes);
-        RELEASE_LOCK(_lock);
-        return EOF;
-      }
-
-      // Slide what we did read to the top of the buffer.
-      assert(read_count < num_bytes);
-      size_t delta = num_bytes - read_count;
-      memmove(gptr() + delta, gptr(), read_count);
-      gbump(delta);
-    }
-  }
-
-  unsigned char next = *gptr();
-  RELEASE_LOCK(_lock);
-
-  return next;
-}
-
-/**
- * Attempts to extract the indicated number of characters from the current
- * file position.  Returns the number of characters extracted.
- */
-size_t HandleStreamBuf::
-read_chars(char *start, size_t length) {
-  if (length == 0 || !_is_open_read) {
-    // Trivial no-op.
-    return 0;
-  }
-
-  // Make sure the write buffer is flushed.
-  sync();
-
-  if (length == 0) {
-    return 0;
-  }
-
-#ifdef _WIN32
-  // Windows case.
-  DWORD bytes_read = 0;
-  BOOL success = ReadFile(_handle, start, length, &bytes_read, nullptr);
-  if (!success) {
-    DWORD error = GetLastError();
-    if (error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE) {
-      cerr << "Error reading " << length
-           << " bytes, windows error code 0x" << hex
-           << error << dec << ".\n";
-      return 0;
-    }
-  }
-
-  length = bytes_read;
-
-#else
-  // Posix case.
-  ssize_t result = ::read(_handle, start, length);
-  if (result < 0) {
-    cerr << "Error reading " << length << " bytes\n";
-    return 0;
-  }
-
-  length = result;
-#endif  // _WIN32
-
-  return length;
-}
-
-/**
- * Outputs the indicated stream of characters to the current file position.
- */
-size_t HandleStreamBuf::
-write_chars(const char *start, size_t length) {
-  if (length == 0) {
-    // Trivial no-op.
-    return 0;
-  }
-
-  // Make sure the read buffer is flushed.
-  size_t n = egptr() - gptr();
-  gbump(n);
-
-  if (length == 0 || !_is_open_write) {
-    return 0;
-  }
-
-#ifdef _WIN32
-  // Windows case.
-  DWORD bytes_written = 0;
-  BOOL success = WriteFile(_handle, start, length, &bytes_written, nullptr);
-  if (!success) {
-    assert(bytes_written <= length);
-    DWORD error = GetLastError();
-    if (error != ERROR_NO_DATA && error != ERROR_BROKEN_PIPE) {
-      cerr << "Error writing " << length
-           << " bytes, windows error code 0x" << hex
-           << error << dec << ".\n";
-    }
-    return bytes_written;
-  }
-  assert(bytes_written == length);
-
-#else
-  // Posix case.
-  size_t remaining = length;
-  while (remaining > 0) {
-    ssize_t result = ::write(_handle, start, remaining);
-    if (result < 0) {
-      if (errno != EPIPE) {
-        cerr << "Error writing " << remaining << " bytes\n";
-      }
-      return length - remaining;
-    }
-
-    start += result;
-    remaining -= result;
-  }
-#endif  // _WIN32
-
-  return length;
-}

+ 0 - 60
direct/src/plugin/handleStreamBuf.h

@@ -1,60 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStreamBuf.h
- * @author drose
- * @date 2009-06-05
- */
-
-#ifndef HANDLESTREAMBUF_H
-#define HANDLESTREAMBUF_H
-
-#include "fhandle.h"
-#include "p3d_lock.h"
-#include <iostream>
-
-/**
- *
- */
-class HandleStreamBuf : public std::streambuf {
-public:
-  HandleStreamBuf();
-  virtual ~HandleStreamBuf();
-
-  void open_read(FHandle handle);
-  void open_write(FHandle handle);
-  bool is_open_read() const;
-  bool is_open_write() const;
-  void close();
-  void close_handle();
-
-  inline FHandle get_handle() const;
-  inline bool has_gdata() const;
-
-protected:
-  virtual int overflow(int c);
-  virtual int sync();
-  virtual int underflow();
-
-private:
-  size_t read_chars(char *start, size_t length);
-  size_t write_chars(const char *start, size_t length);
-
-private:
-  bool _is_open_read;
-  bool _is_open_write;
-
-  FHandle _handle;
-  LOCK _lock;
-
-  char *_buffer;
-};
-
-#include "handleStreamBuf.I"
-
-#endif

+ 0 - 29
direct/src/plugin/is_pathsep.I

@@ -1,29 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file is_pathsep.I
- * @author drose
- * @date 2009-07-07
- */
-
-/**
- * Returns true if the indicated character is a path separator character (e.g.
- * slash or backslash), false otherwise.
- */
-inline bool
-is_pathsep(int ch) {
-  if (ch == '/') {
-    return true;
-  }
-#ifdef _WIN32
-  if (ch == '\\') {
-    return true;
-  }
-#endif
-  return false;
-}

+ 0 - 22
direct/src/plugin/is_pathsep.h

@@ -1,22 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file is_pathsep.h
- * @author drose
- * @date 2009-07-07
- */
-
-#ifndef IS_PATHSEP_H
-#define IS_PATHSEP_H
-
-inline bool
-is_pathsep(int ch);
-
-#include "is_pathsep.I"
-
-#endif

+ 0 - 476
direct/src/plugin/load_plugin.cxx

@@ -1,476 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file load_plugin.cxx
- * @author drose
- * @date 2009-06-19
- */
-
-#include "load_plugin.h"
-#include "p3d_plugin_config.h"
-#include "is_pathsep.h"
-#include "wstring_encode.h"
-
-#include <assert.h>
-
-#include <iostream>
-
-#ifndef _WIN32
-#include <dlfcn.h>
-#endif
-
-using std::string;
-
-#ifdef _WIN32
-static const string dll_ext = ".dll";
-#elif defined(__APPLE__)
-static const string dll_ext = ".dylib";
-#else
-static const string dll_ext = ".so";
-#endif
-
-static const string default_plugin_filename = "p3d_plugin";
-
-P3D_initialize_func *P3D_initialize_ptr;
-P3D_finalize_func *P3D_finalize_ptr;
-P3D_set_plugin_version_func *P3D_set_plugin_version_ptr;
-P3D_set_super_mirror_func *P3D_set_super_mirror_ptr;
-P3D_new_instance_func *P3D_new_instance_ptr;
-P3D_instance_start_func *P3D_instance_start_ptr;
-P3D_instance_start_stream_func *P3D_instance_start_stream_ptr;
-P3D_instance_finish_func *P3D_instance_finish_ptr;
-P3D_instance_setup_window_func *P3D_instance_setup_window_ptr;
-
-P3D_object_get_type_func *P3D_object_get_type_ptr;
-P3D_object_get_bool_func *P3D_object_get_bool_ptr;
-P3D_object_get_int_func *P3D_object_get_int_ptr;
-P3D_object_get_float_func *P3D_object_get_float_ptr;
-P3D_object_get_string_func *P3D_object_get_string_ptr;
-P3D_object_get_repr_func *P3D_object_get_repr_ptr;
-P3D_object_get_property_func *P3D_object_get_property_ptr;
-P3D_object_set_property_func *P3D_object_set_property_ptr;
-P3D_object_has_method_func *P3D_object_has_method_ptr;
-P3D_object_call_func *P3D_object_call_ptr;
-P3D_object_eval_func *P3D_object_eval_ptr;
-P3D_object_incref_func *P3D_object_incref_ptr;
-P3D_object_decref_func *P3D_object_decref_ptr;
-
-P3D_make_class_definition_func *P3D_make_class_definition_ptr;
-P3D_new_undefined_object_func *P3D_new_undefined_object_ptr;
-P3D_new_none_object_func *P3D_new_none_object_ptr;
-P3D_new_bool_object_func *P3D_new_bool_object_ptr;
-P3D_new_int_object_func *P3D_new_int_object_ptr;
-P3D_new_float_object_func *P3D_new_float_object_ptr;
-P3D_new_string_object_func *P3D_new_string_object_ptr;
-P3D_instance_get_panda_script_object_func *P3D_instance_get_panda_script_object_ptr;
-P3D_instance_set_browser_script_object_func *P3D_instance_set_browser_script_object_ptr;
-
-P3D_instance_get_request_func *P3D_instance_get_request_ptr;
-P3D_check_request_func *P3D_check_request_ptr;
-P3D_request_finish_func *P3D_request_finish_ptr;
-P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream_ptr;
-P3D_instance_handle_event_func *P3D_instance_handle_event_ptr;
-
-#ifdef _WIN32
-static HMODULE module = nullptr;
-#else
-static void *module = nullptr;
-#endif
-
-static bool plugin_loaded = false;
-static bool dso_needs_unload = false;
-
-
-/**
- * Returns the default plugin filename, without any directory path (but
- * including the extension appropriate to this platform).
- */
-string
-get_plugin_basename() {
-  return default_plugin_filename + dll_ext;
-}
-
-/**
- * Returns the position in the string of the dot before the filename
- * extension; that is, the position of the rightmost dot that is right of the
- * rightmost slash (or backslash, on Windows).  Returns string::npos if there
- * is no extension.
- */
-static size_t
-find_extension_dot(const string &filename) {
-  size_t p = filename.length();
-  while (p > 0 && !is_pathsep(filename[p - 1])) {
-    --p;
-    if (filename[p] == '.') {
-      return p;
-    }
-  }
-
-  return string::npos;
-}
-
-// Forward reference for function defined below.
-static void unload_dso();
-
-/**
- * Loads the plugin and assigns all of the function pointers.  Returns true on
- * success, false on failure.  If load_plugin() has already been called
- * successfully, this returns true immediately, without parsing any
- * parameters.
- *
- * If p3d_plugin_filename is empty, the module is assumed to be already loaded
- * (or statically linked in), and the symbols are located within the current
- * address space.
- */
-bool
-load_plugin(const string &p3d_plugin_filename,
-            const string &contents_filename, const string &host_url,
-            P3D_verify_contents verify_contents, const string &platform,
-            const string &log_directory, const string &log_basename,
-            bool trusted_environment, bool console_environment,
-            const string &root_dir, const string &host_dir,
-            const string &start_dir, std::ostream &logfile) {
-  if (plugin_loaded) {
-    return true;
-  }
-  string filename = p3d_plugin_filename;
-
-#ifdef _WIN32
-  assert(module == nullptr);
-
-  if (filename.empty()) {
-    // If no filename is supplied, look within our existing address space.
-    module = GetModuleHandle(nullptr);
-    dso_needs_unload = false;
-
-  } else {
-    // If a filename is supplied, attempt to load it as a dynamic library.
-
-    // On Windows, the filename passed to LoadLibrary() must have an
-    // extension, or a default ".DLL" will be implicitly added.  If the file
-    // actually has no extension, we must add "." to avoid this.
-
-    // Check whether the filename has an extension.
-    size_t extension_dot = find_extension_dot(filename);
-    if (extension_dot == string::npos) {
-      // No extension.
-      filename += ".";
-    }
-
-    SetErrorMode(0);
-    std::wstring filename_w;
-    if (string_to_wstring(filename_w, filename)) {
-      module = LoadLibraryW(filename_w.c_str());
-    }
-    dso_needs_unload = true;
-  }
-
-  if (module == nullptr) {
-    // Couldn't load the DLL.
-    logfile
-      << "Couldn't load " << filename << ", error = "
-      << GetLastError() << "\n";
-    return false;
-  }
-
-  #define get_func GetProcAddress
-
-#else  // _WIN32
-  // Posix case.
-  assert(module == nullptr);
-  if (filename.empty()) {
-    module = dlopen(nullptr, RTLD_LAZY | RTLD_LOCAL);
-  } else {
-    module = dlopen(filename.c_str(), RTLD_LAZY | RTLD_LOCAL);
-  }
-  if (module == nullptr) {
-    // Couldn't load the .so.
-    const char *message = dlerror();
-    if (message == nullptr) {
-      message = "No error";
-    }
-    logfile << "Couldn't load " << filename << ": " << message << "\n";
-
-    return false;
-  }
-  dso_needs_unload = true;
-
-  #define get_func dlsym
-
-#endif  // _WIN32
-
-  // Now get all of the function pointers.
-  P3D_initialize_ptr = (P3D_initialize_func *)get_func(module, "P3D_initialize");
-  P3D_finalize_ptr = (P3D_finalize_func *)get_func(module, "P3D_finalize");
-  P3D_set_plugin_version_ptr = (P3D_set_plugin_version_func *)get_func(module, "P3D_set_plugin_version");
-  P3D_set_super_mirror_ptr = (P3D_set_super_mirror_func *)get_func(module, "P3D_set_super_mirror");
-  P3D_new_instance_ptr = (P3D_new_instance_func *)get_func(module, "P3D_new_instance");
-  P3D_instance_start_ptr = (P3D_instance_start_func *)get_func(module, "P3D_instance_start");
-  P3D_instance_start_stream_ptr = (P3D_instance_start_stream_func *)get_func(module, "P3D_instance_start_stream");
-  P3D_instance_finish_ptr = (P3D_instance_finish_func *)get_func(module, "P3D_instance_finish");
-  P3D_instance_setup_window_ptr = (P3D_instance_setup_window_func *)get_func(module, "P3D_instance_setup_window");
-
-  P3D_object_get_type_ptr = (P3D_object_get_type_func *)get_func(module, "P3D_object_get_type");
-  P3D_object_get_bool_ptr = (P3D_object_get_bool_func *)get_func(module, "P3D_object_get_bool");
-  P3D_object_get_int_ptr = (P3D_object_get_int_func *)get_func(module, "P3D_object_get_int");
-  P3D_object_get_float_ptr = (P3D_object_get_float_func *)get_func(module, "P3D_object_get_float");
-  P3D_object_get_string_ptr = (P3D_object_get_string_func *)get_func(module, "P3D_object_get_string");
-  P3D_object_get_repr_ptr = (P3D_object_get_repr_func *)get_func(module, "P3D_object_get_repr");
-  P3D_object_get_property_ptr = (P3D_object_get_property_func *)get_func(module, "P3D_object_get_property");
-  P3D_object_set_property_ptr = (P3D_object_set_property_func *)get_func(module, "P3D_object_set_property");
-  P3D_object_has_method_ptr = (P3D_object_has_method_func *)get_func(module, "P3D_object_has_method");
-  P3D_object_call_ptr = (P3D_object_call_func *)get_func(module, "P3D_object_call");
-  P3D_object_eval_ptr = (P3D_object_eval_func *)get_func(module, "P3D_object_eval");
-  P3D_object_incref_ptr = (P3D_object_incref_func *)get_func(module, "P3D_object_incref");
-  P3D_object_decref_ptr = (P3D_object_decref_func *)get_func(module, "P3D_object_decref");
-  P3D_make_class_definition_ptr = (P3D_make_class_definition_func *)get_func(module, "P3D_make_class_definition");
-  P3D_new_undefined_object_ptr = (P3D_new_undefined_object_func *)get_func(module, "P3D_new_undefined_object");
-  P3D_new_none_object_ptr = (P3D_new_none_object_func *)get_func(module, "P3D_new_none_object");
-  P3D_new_bool_object_ptr = (P3D_new_bool_object_func *)get_func(module, "P3D_new_bool_object");
-  P3D_new_int_object_ptr = (P3D_new_int_object_func *)get_func(module, "P3D_new_int_object");
-  P3D_new_float_object_ptr = (P3D_new_float_object_func *)get_func(module, "P3D_new_float_object");
-  P3D_new_string_object_ptr = (P3D_new_string_object_func *)get_func(module, "P3D_new_string_object");
-  P3D_instance_get_panda_script_object_ptr = (P3D_instance_get_panda_script_object_func *)get_func(module, "P3D_instance_get_panda_script_object");
-  P3D_instance_set_browser_script_object_ptr = (P3D_instance_set_browser_script_object_func *)get_func(module, "P3D_instance_set_browser_script_object");
-
-  P3D_instance_get_request_ptr = (P3D_instance_get_request_func *)get_func(module, "P3D_instance_get_request");
-  P3D_check_request_ptr = (P3D_check_request_func *)get_func(module, "P3D_check_request");
-  P3D_request_finish_ptr = (P3D_request_finish_func *)get_func(module, "P3D_request_finish");
-  P3D_instance_feed_url_stream_ptr = (P3D_instance_feed_url_stream_func *)get_func(module, "P3D_instance_feed_url_stream");
-  P3D_instance_handle_event_ptr = (P3D_instance_handle_event_func *)get_func(module, "P3D_instance_handle_event");
-
-  #undef get_func
-
-  // Successfully loaded.
-  plugin_loaded = true;
-
-  if (!init_plugin(contents_filename, host_url,
-                   verify_contents, platform,
-                   log_directory, log_basename,
-                   trusted_environment, console_environment,
-                   root_dir, host_dir, start_dir, logfile)) {
-    unload_dso();
-    return false;
-  }
-
-  return true;
-}
-
-/**
- * Ensures all the required function pointers have been set, and then calls
- * P3D_initialize() on the recently-loaded plugin.  Returns true on success,
- * false on failure.
- *
- * It is not necessary to call this after calling load_plugin(); it is called
- * implicitly.
- */
-bool
-init_plugin(const string &contents_filename, const string &host_url,
-            P3D_verify_contents verify_contents, const string &platform,
-            const string &log_directory, const string &log_basename,
-            bool trusted_environment, bool console_environment,
-            const string &root_dir, const string &host_dir,
-            const string &start_dir, std::ostream &logfile) {
-
-  // Ensure that all of the function pointers have been found.
-  if (P3D_initialize_ptr == nullptr ||
-      P3D_finalize_ptr == nullptr ||
-      P3D_set_plugin_version_ptr == nullptr ||
-      P3D_set_super_mirror_ptr == nullptr ||
-      P3D_new_instance_ptr == nullptr ||
-      P3D_instance_start_ptr == nullptr ||
-      P3D_instance_start_stream_ptr == nullptr ||
-      P3D_instance_finish_ptr == nullptr ||
-      P3D_instance_setup_window_ptr == nullptr ||
-
-      P3D_object_get_type_ptr == nullptr ||
-      P3D_object_get_bool_ptr == nullptr ||
-      P3D_object_get_int_ptr == nullptr ||
-      P3D_object_get_float_ptr == nullptr ||
-      P3D_object_get_string_ptr == nullptr ||
-      P3D_object_get_repr_ptr == nullptr ||
-      P3D_object_get_property_ptr == nullptr ||
-      P3D_object_set_property_ptr == nullptr ||
-      P3D_object_has_method_ptr == nullptr ||
-      P3D_object_call_ptr == nullptr ||
-      P3D_object_eval_ptr == nullptr ||
-      P3D_object_incref_ptr == nullptr ||
-      P3D_object_decref_ptr == nullptr ||
-
-      P3D_make_class_definition_ptr == nullptr ||
-      P3D_new_undefined_object_ptr == nullptr ||
-      P3D_new_none_object_ptr == nullptr ||
-      P3D_new_bool_object_ptr == nullptr ||
-      P3D_new_int_object_ptr == nullptr ||
-      P3D_new_float_object_ptr == nullptr ||
-      P3D_new_string_object_ptr == nullptr ||
-      P3D_instance_get_panda_script_object_ptr == nullptr ||
-      P3D_instance_set_browser_script_object_ptr == nullptr ||
-
-      P3D_instance_get_request_ptr == nullptr ||
-      P3D_check_request_ptr == nullptr ||
-      P3D_request_finish_ptr == nullptr ||
-      P3D_instance_feed_url_stream_ptr == nullptr ||
-      P3D_instance_handle_event_ptr == nullptr) {
-
-    logfile
-      << "Some function pointers not found:"
-      << "\nP3D_initialize_ptr = " << P3D_initialize_ptr
-      << "\nP3D_finalize_ptr = " << P3D_finalize_ptr
-      << "\nP3D_set_plugin_version_ptr = " << P3D_set_plugin_version_ptr
-      << "\nP3D_set_super_mirror_ptr = " << P3D_set_super_mirror_ptr
-      << "\nP3D_new_instance_ptr = " << P3D_new_instance_ptr
-      << "\nP3D_instance_start_ptr = " << P3D_instance_start_ptr
-      << "\nP3D_instance_start_stream_ptr = " << P3D_instance_start_stream_ptr
-      << "\nP3D_instance_finish_ptr = " << P3D_instance_finish_ptr
-      << "\nP3D_instance_setup_window_ptr = " << P3D_instance_setup_window_ptr
-
-      << "\nP3D_object_get_type_ptr = " << P3D_object_get_type_ptr
-      << "\nP3D_object_get_bool_ptr = " << P3D_object_get_bool_ptr
-      << "\nP3D_object_get_int_ptr = " << P3D_object_get_int_ptr
-      << "\nP3D_object_get_float_ptr = " << P3D_object_get_float_ptr
-      << "\nP3D_object_get_string_ptr = " << P3D_object_get_string_ptr
-      << "\nP3D_object_get_repr_ptr = " << P3D_object_get_repr_ptr
-      << "\nP3D_object_get_property_ptr = " << P3D_object_get_property_ptr
-      << "\nP3D_object_set_property_ptr = " << P3D_object_set_property_ptr
-      << "\nP3D_object_has_method_ptr = " << P3D_object_has_method_ptr
-      << "\nP3D_object_call_ptr = " << P3D_object_call_ptr
-      << "\nP3D_object_eval_ptr = " << P3D_object_eval_ptr
-      << "\nP3D_object_incref_ptr = " << P3D_object_incref_ptr
-      << "\nP3D_object_decref_ptr = " << P3D_object_decref_ptr
-
-      << "\nP3D_make_class_definition_ptr = " << P3D_make_class_definition_ptr
-      << "\nP3D_new_undefined_object_ptr = " << P3D_new_undefined_object_ptr
-      << "\nP3D_new_none_object_ptr = " << P3D_new_none_object_ptr
-      << "\nP3D_new_bool_object_ptr = " << P3D_new_bool_object_ptr
-      << "\nP3D_new_int_object_ptr = " << P3D_new_int_object_ptr
-      << "\nP3D_new_float_object_ptr = " << P3D_new_float_object_ptr
-      << "\nP3D_new_string_object_ptr = " << P3D_new_string_object_ptr
-      << "\nP3D_instance_get_panda_script_object_ptr = " << P3D_instance_get_panda_script_object_ptr
-      << "\nP3D_instance_set_browser_script_object_ptr = " << P3D_instance_set_browser_script_object_ptr
-
-      << "\nP3D_instance_get_request_ptr = " << P3D_instance_get_request_ptr
-      << "\nP3D_check_request_ptr = " << P3D_check_request_ptr
-      << "\nP3D_request_finish_ptr = " << P3D_request_finish_ptr
-      << "\nP3D_instance_feed_url_stream_ptr = " << P3D_instance_feed_url_stream_ptr
-      << "\nP3D_instance_handle_event_ptr = " << P3D_instance_handle_event_ptr
-      << "\n";
-    return false;
-  }
-
-  // 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(),
-                          log_directory.c_str(), log_basename.c_str(),
-                          trusted_environment, console_environment,
-                          root_dir.c_str(), host_dir.c_str(),
-                          start_dir.c_str())) {
-    // Oops, failure to initialize.
-    logfile
-      << "Failed to initialize plugin (passed API version "
-      << api_version << ")\n";
-    return false;
-  }
-
-  return true;
-}
-
-/**
- * Calls finalize, then removes the plugin from memory space and clears all of
- * the pointers.
- */
-void
-unload_plugin(std::ostream &logfile) {
-  if (!plugin_loaded) {
-    return;
-  }
-
-  P3D_finalize_ptr();
-  unload_dso();
-}
-
-/**
- * Removes the plugin from memory space and clears all of the pointers.  This
- * is only intended to be called by load_plugin(), above, in the specific case
- * that the plugin loaded but could not successfully initialize itself.  All
- * user code should call unload_plugin(), above, which first calls
- * P3D_finalize().
- */
-static void
-unload_dso() {
-  if (dso_needs_unload) {
-    assert(module != nullptr);
-#ifdef _WIN32
-    FreeLibrary(module);
-#else
-    dlclose(module);
-#endif
-    module = nullptr;
-    dso_needs_unload = false;
-  }
-
-  P3D_initialize_ptr = nullptr;
-  P3D_finalize_ptr = nullptr;
-  P3D_set_plugin_version_ptr = nullptr;
-  P3D_set_super_mirror_ptr = nullptr;
-  P3D_new_instance_ptr = nullptr;
-  P3D_instance_start_ptr = nullptr;
-  P3D_instance_start_stream_ptr = nullptr;
-  P3D_instance_finish_ptr = nullptr;
-  P3D_instance_setup_window_ptr = nullptr;
-
-  P3D_object_get_type_ptr = nullptr;
-  P3D_object_get_bool_ptr = nullptr;
-  P3D_object_get_int_ptr = nullptr;
-  P3D_object_get_float_ptr = nullptr;
-  P3D_object_get_string_ptr = nullptr;
-  P3D_object_get_repr_ptr = nullptr;
-  P3D_object_get_property_ptr = nullptr;
-  P3D_object_set_property_ptr = nullptr;
-  P3D_object_has_method_ptr = nullptr;
-  P3D_object_call_ptr = nullptr;
-  P3D_object_eval_ptr = nullptr;
-  P3D_object_incref_ptr = nullptr;
-  P3D_object_decref_ptr = nullptr;
-
-  P3D_make_class_definition_ptr = nullptr;
-  P3D_new_undefined_object_ptr = nullptr;
-  P3D_new_none_object_ptr = nullptr;
-  P3D_new_bool_object_ptr = nullptr;
-  P3D_new_int_object_ptr = nullptr;
-  P3D_new_float_object_ptr = nullptr;
-  P3D_new_string_object_ptr = nullptr;
-  P3D_instance_get_panda_script_object_ptr = nullptr;
-  P3D_instance_set_browser_script_object_ptr = nullptr;
-
-  P3D_instance_get_request_ptr = nullptr;
-  P3D_check_request_ptr = nullptr;
-  P3D_request_finish_ptr = nullptr;
-  P3D_instance_feed_url_stream_ptr = nullptr;
-  P3D_instance_handle_event_ptr = nullptr;
-
-  plugin_loaded = false;
-}
-
-/**
- * Returns true if the plugin has been loaded successfully by a previous call
- * to load_plugin(), false otherwise.
- */
-bool
-is_plugin_loaded() {
-  return plugin_loaded;
-}

+ 0 - 81
direct/src/plugin/load_plugin.h

@@ -1,81 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file load_plugin.h
- * @author drose
- * @date 2009-06-19
- */
-
-#ifndef LOAD_PLUGIN_H
-#define LOAD_PLUGIN_H
-
-#include "p3d_plugin.h"
-
-#include <string>
-
-extern P3D_initialize_func *P3D_initialize_ptr;
-extern P3D_finalize_func *P3D_finalize_ptr;
-extern P3D_set_plugin_version_func *P3D_set_plugin_version_ptr;
-extern P3D_set_super_mirror_func *P3D_set_super_mirror_ptr;
-extern P3D_new_instance_func *P3D_new_instance_ptr;
-extern P3D_instance_start_func *P3D_instance_start_ptr;
-extern P3D_instance_start_stream_func *P3D_instance_start_stream_ptr;
-extern P3D_instance_finish_func *P3D_instance_finish_ptr;
-extern P3D_instance_setup_window_func *P3D_instance_setup_window_ptr;
-
-extern P3D_object_get_type_func *P3D_object_get_type_ptr;
-extern P3D_object_get_bool_func *P3D_object_get_bool_ptr;
-extern P3D_object_get_int_func *P3D_object_get_int_ptr;
-extern P3D_object_get_float_func *P3D_object_get_float_ptr;
-extern P3D_object_get_string_func *P3D_object_get_string_ptr;
-extern P3D_object_get_repr_func *P3D_object_get_repr_ptr;
-extern P3D_object_get_property_func *P3D_object_get_property_ptr;
-extern P3D_object_set_property_func *P3D_object_set_property_ptr;
-extern P3D_object_has_method_func *P3D_object_has_method_ptr;
-extern P3D_object_call_func *P3D_object_call_ptr;
-extern P3D_object_eval_func *P3D_object_eval_ptr;
-extern P3D_object_incref_func *P3D_object_incref_ptr;
-extern P3D_object_decref_func *P3D_object_decref_ptr;
-
-extern P3D_make_class_definition_func *P3D_make_class_definition_ptr;
-extern P3D_new_undefined_object_func *P3D_new_undefined_object_ptr;
-extern P3D_new_none_object_func *P3D_new_none_object_ptr;
-extern P3D_new_bool_object_func *P3D_new_bool_object_ptr;
-extern P3D_new_int_object_func *P3D_new_int_object_ptr;
-extern P3D_new_float_object_func *P3D_new_float_object_ptr;
-extern P3D_new_string_object_func *P3D_new_string_object_ptr;
-extern P3D_instance_get_panda_script_object_func *P3D_instance_get_panda_script_object_ptr;
-extern P3D_instance_set_browser_script_object_func *P3D_instance_set_browser_script_object_ptr;
-
-extern P3D_instance_get_request_func *P3D_instance_get_request_ptr;
-extern P3D_check_request_func *P3D_check_request_ptr;
-extern P3D_request_finish_func *P3D_request_finish_ptr;
-extern P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream_ptr;
-extern P3D_instance_handle_event_func *P3D_instance_handle_event_ptr;
-
-std::string get_plugin_basename();
-bool
-load_plugin(const std::string &p3d_plugin_filename,
-            const std::string &contents_filename, const std::string &host_url,
-            P3D_verify_contents verify_contents, const std::string &platform,
-            const std::string &log_directory, const std::string &log_basename,
-            bool trusted_environment, bool console_environment,
-            const std::string &root_dir, const std::string &host_dir,
-            const std::string &start_dir, std::ostream &logfile);
-bool
-init_plugin(const std::string &contents_filename, const std::string &host_url,
-            P3D_verify_contents verify_contents, const std::string &platform,
-            const std::string &log_directory, const std::string &log_basename,
-            bool trusted_environment, bool console_environment,
-            const std::string &root_dir, const std::string &host_dir,
-            const std::string &start_dir, std::ostream &logfile);
-
-void unload_plugin(std::ostream &logfile);
-bool is_plugin_loaded();
-
-#endif

+ 0 - 224
direct/src/plugin/mkdir_complete.cxx

@@ -1,224 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file mkdir_complete.cxx
- * @author drose
- * @date 2009-06-29
- */
-
-#include "mkdir_complete.h"
-#include "is_pathsep.h"
-#include "wstring_encode.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#include <io.h>    // chmod()
-#else
-#include <fcntl.h>
-#include <sys/stat.h>  // for mkdir()
-#include <errno.h>
-#include <string.h>     // strerror()
-#include <unistd.h>
-#endif
-
-using std::ostream;
-using std::string;
-using std::wstring;
-
-/**
- * Returns the directory component of the indicated pathname, or the empty
- * string if there is no directory prefix.
- */
-static string
-get_dirname(const string &filename) {
-  size_t p = filename.length();
-  while (p > 0) {
-    --p;
-    if (is_pathsep(filename[p])) {
-      return filename.substr(0, p);
-    }
-  }
-
-  return string();
-}
-
-#ifdef _WIN32
-/**
- * The wide-character implementation of get_dirname(). Only implemented (and
- * needed) on Windows.
- */
-static wstring
-get_dirname_w(const wstring &filename) {
-  size_t p = filename.length();
-  while (p > 0) {
-    --p;
-    if (is_pathsep(filename[p])) {
-      return filename.substr(0, p);
-    }
-  }
-
-  return wstring();
-}
-#endif  // _WIN32
-
-
-
-/**
- * Creates a new directory, with normal access privileges.  Returns true on
- * success, false on failure.  Will create intervening directories if
- * necessary.
- */
-bool
-mkdir_complete(const string &dirname, ostream &logfile) {
-#ifdef _WIN32
-  wstring dirname_w;
-  if (!string_to_wstring(dirname_w, dirname)) {
-    return false;
-  }
-  return mkdir_complete_w(dirname_w, logfile);
-
-#else  //_WIN32
-  if (mkdir(dirname.c_str(), 0755) == 0) {
-    // Success!
-    return true;
-  }
-
-  // Failed.
-  if (errno == EEXIST) {
-    // Not really an error: the directory is already there.
-    return true;
-  }
-
-  if (errno == ENOENT || errno == EACCES) {
-    // We need to make the parent directory first.
-    string parent = get_dirname(dirname);
-    if (!parent.empty() && mkdir_complete(parent, logfile)) {
-      // Parent successfully created.  Try again to make the child.
-      if (mkdir(dirname.c_str(), 0755) == 0) {
-        // Got it!
-        return true;
-      }
-      // Couldn't create the directory.  :(
-      logfile
-        << "Couldn't create " << dirname << ": " << strerror(errno) << "\n";
-    }
-  }
-  return false;
-
-#endif  // _WIN32
-}
-
-/**
- * Creates a new file with normal access priviledges.  Returns true on
- * success, false on failure.  This will create intervening directories if
- * needed.
- */
-bool
-mkfile_complete(const string &filename, ostream &logfile) {
-#ifdef _WIN32
-  wstring filename_w;
-  if (!string_to_wstring(filename_w, filename)) {
-    return false;
-  }
-  return mkfile_complete_w(filename_w, logfile);
-#else  // _WIN32
-  // Make sure we delete any previously-existing file first.
-  unlink(filename.c_str());
-
-  int fd = creat(filename.c_str(), 0755);
-  if (fd == -1) {
-    // Try to make the parent directory first.
-    string parent = get_dirname(filename);
-    if (!parent.empty() && mkdir_complete(parent, logfile)) {
-      // Parent successfully created.  Try again to make the file.
-      fd = creat(filename.c_str(), 0755);
-    }
-    if (fd == -1) {
-      logfile
-        << "Couldn't create " << filename << ": " << strerror(errno) << "\n";
-      return false;
-    }
-  }
-  close(fd);
-  return true;
-
-#endif  // _WIN32
-}
-
-
-#ifdef _WIN32
-/**
- * The wide-character implementation of mkdir_complete().  Only implemented
- * (and needed) on Windows.
- */
-bool
-mkdir_complete_w(const wstring &dirname, ostream &logfile) {
-  if (CreateDirectoryW(dirname.c_str(), nullptr) != 0) {
-    // Success!
-    return true;
-  }
-
-  // Failed.
-  DWORD last_error = GetLastError();
-  if (last_error == ERROR_ALREADY_EXISTS) {
-    // Not really an error: the directory is already there.
-    return true;
-  }
-
-  if (last_error == ERROR_PATH_NOT_FOUND) {
-    // We need to make the parent directory first.
-    wstring parent = get_dirname_w(dirname);
-    if (!parent.empty() && mkdir_complete_w(parent, logfile)) {
-      // Parent successfully created.  Try again to make the child.
-      if (CreateDirectoryW(dirname.c_str(), nullptr) != 0) {
-        // Got it!
-        return true;
-      }
-      logfile
-        << "Couldn't create " << dirname << "\n";
-    }
-  }
-  return false;
-}
-#endif  // _WIN32
-
-#ifdef _WIN32
-/**
- * The wide-character implementation of mkfile_complete().  Only implemented
- * (and needed) on Windows.
- */
-bool
-mkfile_complete_w(const wstring &filename, ostream &logfile) {
-  // Make sure we delete any previously-existing file first.
-
-  // Windows can't delete a file if it's read-only.  Weird.
-  _wchmod(filename.c_str(), 0644);
-  _wunlink(filename.c_str());
-
-  HANDLE file = CreateFileW(filename.c_str(), GENERIC_READ | GENERIC_WRITE,
-                            FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
-                            nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
-  if (file == INVALID_HANDLE_VALUE) {
-    // Try to make the parent directory first.
-    wstring parent = get_dirname_w(filename);
-    if (!parent.empty() && mkdir_complete_w(parent, logfile)) {
-      // Parent successfully created.  Try again to make the file.
-      file = CreateFileW(filename.c_str(), GENERIC_READ | GENERIC_WRITE,
-                         FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
-                         nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
-    }
-    if (file == INVALID_HANDLE_VALUE) {
-      logfile
-        << "Couldn't create " << filename << "\n";
-      return false;
-    }
-  }
-  CloseHandle(file);
-  return true;
-}
-#endif  // _WIN32

+ 0 - 28
direct/src/plugin/mkdir_complete.h

@@ -1,28 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file mkdir_complete.h
- * @author drose
- * @date 2009-06-29
- */
-
-#ifndef MKDIR_COMPLETE_H
-#define MKDIR_COMPLETE_H
-
-#include <string>
-#include <iostream>
-
-bool mkdir_complete(const std::string &dirname, std::ostream &logfile);
-bool mkfile_complete(const std::string &dirname, std::ostream &logfile);
-
-#ifdef _WIN32
-bool mkdir_complete_w(const std::wstring &dirname, std::ostream &logfile);
-bool mkfile_complete_w(const std::wstring &dirname, std::ostream &logfile);
-#endif  // _WIN32
-
-#endif

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

@@ -1,12 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dAuthSession.I
- * @author drose
- * @date 2009-09-17
- */

+ 0 - 456
direct/src/plugin/p3dAuthSession.cxx

@@ -1,456 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dAuthSession.cxx
- * @author drose
- * @date 2009-09-17
- */
-
-#include "p3dAuthSession.h"
-#include "p3dInstance.h"
-#include "p3dInstanceManager.h"
-#include "p3dMultifileReader.h"
-#include "p3d_plugin_config.h"
-#include "mkdir_complete.h"
-#include "wstring_encode.h"
-
-#include <ctype.h>
-
-#ifndef _WIN32
-#include <fcntl.h>
-#include <sys/wait.h>
-#include <sys/select.h>
-#include <signal.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#endif
-
-using std::string;
-
-/**
- *
- */
-P3DAuthSession::
-P3DAuthSession(P3DInstance *inst) :
-  _inst(inst)
-{
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-#ifdef _WIN32
-  _p3dcert_handle = INVALID_HANDLE_VALUE;
-#else
-  _p3dcert_pid = -1;
-#endif
-  _p3dcert_started = false;
-  _p3dcert_running = false;
-  _started_wait_thread = false;
-  INIT_THREAD(_wait_thread);
-
-  // Write the cert chain to a temporary file.
-  _cert_filename = new P3DTemporaryFile(".crt");
-  string filename = _cert_filename->get_filename();
-  FILE *fp = fopen(filename.c_str(), "w");
-  if (fp == nullptr) {
-    nout << "Couldn't write temporary file\n";
-    return;
-  }
-
-  if (inst->_mf_reader.get_num_signatures() > 0) {
-    const P3DMultifileReader::CertChain &cert_chain =
-      inst->_mf_reader.get_signature(0);
-
-    if (cert_chain.size() > 0) {
-      // Save the cert_dir, this is where the p3dcert program will need to
-      // write the cert when it is approved.
-      P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-      _cert_dir = inst_mgr->get_cert_dir(cert_chain[0]._cert);
-    }
-
-    P3DMultifileReader::CertChain::const_iterator ci;
-    for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) {
-      X509 *c = (*ci)._cert;
-      PEM_write_X509(fp, c);
-    }
-  }
-  fclose(fp);
-
-  start_p3dcert();
-}
-
-
-/**
- *
- */
-P3DAuthSession::
-~P3DAuthSession() {
-  shutdown(false);
-
-  if (_cert_filename != nullptr) {
-    delete _cert_filename;
-  }
-}
-
-/**
- * Terminates the session by killing the subprocess.
- */
-void P3DAuthSession::
-shutdown(bool send_message) {
-  if (!send_message) {
-    // If we're not to send the instance the shutdown message as a result of
-    // this, then clear the _inst pointer now.
-    _inst = nullptr;
-  }
-
-  if (_p3dcert_running) {
-    nout << "Killing p3dcert process\n";
-#ifdef _WIN32
-    TerminateProcess(_p3dcert_handle, 2);
-    CloseHandle(_p3dcert_handle);
-
-#else  // _WIN32
-    kill(_p3dcert_pid, SIGKILL);
-
-    // Wait a few milliseconds for the process to exit, and then get its
-    // return status to clean up the zombie status.  If we don't wait long
-    // enough, don't sweat it.
-    struct timeval tv;
-    tv.tv_sec = 0;
-    tv.tv_usec = 100000;
-    select(0, nullptr, nullptr, nullptr, &tv);
-    int status;
-    waitpid(_p3dcert_pid, &status, WNOHANG);
-#endif  // _WIN32
-
-    _p3dcert_running = false;
-  }
-  _p3dcert_started = false;
-
-  // Now that the process has stopped, the thread should stop itself quickly
-  // too.
-  join_wait_thread();
-
-  // We're no longer bound to any particular instance.
-  _inst = nullptr;
-}
-
-/**
- * Starts the p3dcert program running in a child process.
- */
-void P3DAuthSession::
-start_p3dcert() {
-  if (_p3dcert_started) {
-    // Already started.
-    return;
-  }
-
-  if (_inst->_p3dcert_package == nullptr) {
-    nout << "Couldn't start Python: no p3dcert package.\n";
-    return;
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  _start_dir = inst_mgr->get_start_dir();
-
-  string root_dir = _inst->_p3dcert_package->get_package_dir();
-  mkdir_complete(_start_dir, nout);
-
-  _p3dcert_exe = root_dir + "/p3dcert";
-#ifdef _WIN32
-  _p3dcert_exe += ".exe";
-#endif
-#ifdef __APPLE__
-  // On OSX, run from the packaged bundle.
-  _p3dcert_exe = root_dir + "/P3DCert.app/Contents/MacOS/p3dcert";
-#endif
-
-  // Populate the new process' environment.
-
-#ifdef _WIN32
-  // These are the enviroment variables we forward from the current
-  // environment, if they are set.
-  const wchar_t *keep[] = {
-    L"TMP", L"TEMP", L"HOME", L"USER",
-    L"SYSTEMROOT", L"USERPROFILE", L"COMSPEC",
-    nullptr
-  };
-
-  std::wstring env_w;
-
-  for (int ki = 0; keep[ki] != nullptr; ++ki) {
-    wchar_t *value = _wgetenv(keep[ki]);
-    if (value != nullptr) {
-      env_w += keep[ki];
-      env_w += L"=";
-      env_w += value;
-      env_w += (wchar_t)'\0';
-    }
-  }
-
-  wstring_to_string(_env, env_w);
-
-#else  // _WIN32
-
-  _env = string();
-
-  // These are the enviroment variables we forward from the current
-  // environment, if they are set.
-  const char *keep[] = {
-    "TMP", "TEMP", "HOME", "USER",
-    "LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG",
-#ifdef HAVE_X11
-    "DISPLAY", "XAUTHORITY",
-#endif
-    nullptr
-  };
-  for (int ki = 0; keep[ki] != nullptr; ++ki) {
-    char *value = getenv(keep[ki]);
-    if (value != nullptr) {
-      _env += keep[ki];
-      _env += "=";
-      _env += value;
-      _env += '\0';
-    }
-  }
-#endif  // _WIN32
-
-  // Define some new environment variables.
-  _env += "PATH=";
-  _env += root_dir;
-  _env += '\0';
-
-  _env += "LD_LIBRARY_PATH=";
-  _env += root_dir;
-  _env += '\0';
-
-  _env += "DYLD_LIBRARY_PATH=";
-  _env += root_dir;
-  _env += '\0';
-
-  _env += "P3DCERT_ROOT=";
-  _env += root_dir;
-  _env += '\0';
-
-  nout << "Setting environment:\n";
-  write_env();
-
-  nout << "Attempting to start p3dcert from " << _p3dcert_exe << "\n";
-
-  bool started_p3dcert = false;
-#ifdef _WIN32
-  _p3dcert_handle = win_create_process();
-  started_p3dcert = (_p3dcert_handle != INVALID_HANDLE_VALUE);
-#else
-  _p3dcert_pid = posix_create_process();
-  started_p3dcert = (_p3dcert_pid > 0);
-#endif
-  if (!started_p3dcert) {
-    nout << "Failed to create process.\n";
-    return;
-  }
-
-  _p3dcert_started = true;
-  _p3dcert_running = true;
-
-  spawn_wait_thread();
-}
-
-/**
- * Starts the wait thread.  This thread is responsible for waiting for the
- * process to finish, and notifying the instance when it does.
- */
-void P3DAuthSession::
-spawn_wait_thread() {
-  SPAWN_THREAD(_wait_thread, wt_thread_run, this);
-  _started_wait_thread = true;
-}
-
-/**
- * Waits for the wait thread to stop.
- */
-void P3DAuthSession::
-join_wait_thread() {
-  if (!_started_wait_thread) {
-    return;
-  }
-
-  JOIN_THREAD(_wait_thread);
-  _started_wait_thread = false;
-}
-
-/**
- * Writes _env, which is formatted as a string containing zero-byte-terminated
- * environment defintions, to the nout stream, one definition per line.
- */
-void P3DAuthSession::
-write_env() const {
-  size_t p = 0;
-  size_t zero = _env.find('\0', p);
-  while (zero != string::npos) {
-    nout << "  ";
-    nout.write(_env.data() + p, zero - p);
-    nout << "\n";
-    p = zero + 1;
-    zero = _env.find('\0', p);
-  }
-}
-
-/**
- * The main function for the wait thread.
- */
-void P3DAuthSession::
-wt_thread_run() {
-  // All we do here is wait for the process to terminate.
-  nout << "wt_thread_run in " << this << " beginning\n";
-#ifdef _WIN32
-  DWORD result = WaitForSingleObject(_p3dcert_handle, INFINITE);
-  if (result != 0) {
-    nout << "Wait for process failed: " << GetLastError() << "\n";
-  }
-  CloseHandle(_p3dcert_handle);
-  _p3dcert_handle = INVALID_HANDLE_VALUE;
-  _p3dcert_running = false;
-  nout << "p3dcert process has successfully stopped.\n";
-#else
-  int status;
-  pid_t result = waitpid(_p3dcert_pid, &status, 0);
-  if (result == -1) {
-    perror("waitpid");
-  }
-  _p3dcert_pid = -1;
-  _p3dcert_running = false;
-
-  nout << "p3dcert process has successfully stopped.\n";
-  if (WIFEXITED(status)) {
-    nout << "  exited normally, status = "
-         << WEXITSTATUS(status) << "\n";
-  } else if (WIFSIGNALED(status)) {
-    nout << "  signalled by " << WTERMSIG(status) << ", core = "
-         << WCOREDUMP(status) << "\n";
-  } else if (WIFSTOPPED(status)) {
-    nout << "  stopped by " << WSTOPSIG(status) << "\n";
-  }
-#endif  // _WIN32
-
-  // Notify the instance that we're done.
-  P3DInstance *inst = _inst;
-  if (inst != nullptr) {
-    inst->auth_finished_sub_thread();
-  }
-
-  nout << "Exiting wt_thread_run in " << this << "\n";
-}
-
-#ifdef _WIN32
-/**
- * Creates a sub-process to run _p3dcert_exe, with the appropriate command-
- * line arguments, and the environment string defined in _env.
- *
- * Returns the handle to the created process on success, or
- * INVALID_HANDLE_VALUE on falure.
- */
-HANDLE P3DAuthSession::
-win_create_process() {
-  // Make sure we see an error dialog if there is a missing DLL.
-  SetErrorMode(0);
-
-  STARTUPINFO startup_info;
-  ZeroMemory(&startup_info, sizeof(startup_info));
-  startup_info.cb = sizeof(startup_info);
-
-  // Make sure the initial window is *shown* for this graphical app.
-  startup_info.wShowWindow = SW_SHOW;
-  startup_info.dwFlags |= STARTF_USESHOWWINDOW;
-
-  const char *start_dir_cstr = _start_dir.c_str();
-
-  // Construct the command-line string, containing the quoted command-line
-  // arguments.
-  std::ostringstream stream;
-  stream << "\"" << _p3dcert_exe << "\" \""
-         << _cert_filename->get_filename() << "\" \"" << _cert_dir << "\"";
-
-  // I'm not sure why CreateProcess wants a non-const char pointer for its
-  // command-line string, but I'm not taking chances.  It gets a non-const
-  // char array that it can modify.
-  string command_line_str = stream.str();
-  char *command_line = new char[command_line_str.size() + 1];
-  memcpy(command_line, command_line_str.c_str(), command_line_str.size() + 1);
-
-  nout << "Command line: " << command_line_str << "\n";
-
-  // Something about p3dCert_wx tends to become crashy when we call it from
-  // CreateProcessW().  Something about the way wx parses the command-line
-  // parameters?  Well, whatever, we don't really need the Unicode form
-  // anyway.
-  PROCESS_INFORMATION process_info;
-  BOOL result = CreateProcess
-    (_p3dcert_exe.c_str(), command_line, nullptr, nullptr, TRUE,
-     0, (void *)_env.c_str(),
-     start_dir_cstr, &startup_info, &process_info);
-  bool started_program = (result != 0);
-
-  delete[] command_line;
-
-  if (!started_program) {
-    nout << "CreateProcess failed, error: " << GetLastError() << "\n";
-    return INVALID_HANDLE_VALUE;
-  }
-
-  CloseHandle(process_info.hThread);
-  return process_info.hProcess;
-}
-#endif // _WIN32
-
-
-#ifndef _WIN32
-/**
- * Creates a sub-process to run _p3dcert_exe, with the appropriate command-
- * line arguments, and the environment string defined in _env.
- *
- * Returns the pid of the created process on success, or -1 on falure.
- */
-int P3DAuthSession::
-posix_create_process() {
-  // Fork and exec.
-  pid_t child = fork();
-  if (child < 0) {
-    perror("fork");
-    return -1;
-  }
-
-  if (child == 0) {
-    // Here we are in the child process.
-
-    if (chdir(_start_dir.c_str()) < 0) {
-      nout << "Could not chdir to " << _start_dir << "\n";
-      // This is a warning, not an error.  We don't actually care that much
-      // about the starting directory.
-    }
-
-    // build up an array of char strings for the environment.
-    std::vector<const char *> ptrs;
-    size_t p = 0;
-    size_t zero = _env.find('\0', p);
-    while (zero != string::npos) {
-      ptrs.push_back(_env.data() + p);
-      p = zero + 1;
-      zero = _env.find('\0', p);
-    }
-    ptrs.push_back(nullptr);
-
-    execle(_p3dcert_exe.c_str(), _p3dcert_exe.c_str(),
-           _cert_filename->get_filename().c_str(), _cert_dir.c_str(),
-           (char *)0, &ptrs[0]);
-    nout << "Failed to exec " << _p3dcert_exe << "\n";
-    _exit(1);
-  }
-
-  return child;
-}
-#endif  // _WIN32

+ 0 - 82
direct/src/plugin/p3dAuthSession.h

@@ -1,82 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dAuthSession.h
- * @author drose
- * @date 2009-09-17
- */
-
-#ifndef P3DAUTHSESSION_H
-#define P3DAUTHSESSION_H
-
-#include "p3d_plugin_common.h"
-#include "p3dPackage.h"
-#include "get_tinyxml.h"
-#include "p3dTemporaryFile.h"
-#include "p3dReferenceCount.h"
-
-class P3DInstance;
-
-/**
- * This is an instance of a p3dcert program running in a subprocess.  There's
- * no communication with the process, or none of that complicated stuff the
- * P3DSession has to do; all we do here is fire off the process, then wait for
- * it to exit.
- */
-class P3DAuthSession : public P3DReferenceCount {
-public:
-  P3DAuthSession(P3DInstance *inst);
-  ~P3DAuthSession();
-
-  void shutdown(bool send_message);
-
-private:
-  void start_p3dcert();
-
-  void spawn_wait_thread();
-  void join_wait_thread();
-
-  void write_env() const;
-
-private:
-  // These methods run only within the read thread.
-  THREAD_CALLBACK_DECLARATION(P3DAuthSession, wt_thread_run);
-  void wt_thread_run();
-
-#ifdef _WIN32
-  HANDLE win_create_process();
-#else
-  int posix_create_process();
-#endif
-
-private:
-  P3DInstance *_inst;
-  std::string _start_dir;
-
-  // This information is passed to create_process().
-  P3DTemporaryFile *_cert_filename;
-  std::string _cert_dir;
-  std::string _p3dcert_exe;
-  std::string _env;
-
-#ifdef _WIN32
-  HANDLE _p3dcert_handle;
-#else
-  int _p3dcert_pid;
-#endif
-  bool _p3dcert_started;
-  bool _p3dcert_running;
-
-  // The remaining members are manipulated by or for the read thread.
-  bool _started_wait_thread;
-  THREAD _wait_thread;
-};
-
-#include "p3dAuthSession.I"
-
-#endif

+ 0 - 68
direct/src/plugin/p3dBoolObject.cxx

@@ -1,68 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dBoolObject.cxx
- * @author drose
- * @date 2009-06-30
- */
-
-#include "p3dBoolObject.h"
-
-/**
- *
- */
-P3DBoolObject::
-P3DBoolObject(bool value) : _value(value) {
-}
-
-/**
- *
- */
-P3DBoolObject::
-P3DBoolObject(const P3DBoolObject &copy) :
-  P3DObject(copy),
-  _value(copy._value)
-{
-}
-
-/**
- * Returns the fundamental type of this kind of object.
- */
-P3D_object_type P3DBoolObject::
-get_type() {
-  return P3D_OT_bool;
-}
-
-/**
- * Returns the object value coerced to a boolean, if possible.
- */
-bool P3DBoolObject::
-get_bool() {
-  return _value;
-}
-
-/**
- * Returns the object value coerced to an integer, if possible.
- */
-int P3DBoolObject::
-get_int() {
-  return _value;
-}
-
-/**
- * Fills the indicated C++ string object with the value of this object coerced
- * to a string.
- */
-void P3DBoolObject::
-make_string(std::string &value) {
-  if (_value) {
-    value = "True";
-  } else {
-    value = "False";
-  }
-}

+ 0 - 38
direct/src/plugin/p3dBoolObject.h

@@ -1,38 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dBoolObject.h
- * @author drose
- * @date 2009-06-30
- */
-
-#ifndef P3DBOOLOBJECT_H
-#define P3DBOOLOBJECT_H
-
-#include "p3d_plugin_common.h"
-#include "p3dObject.h"
-
-/**
- * An object type that contains a boolean value.
- */
-class P3DBoolObject : public P3DObject {
-public:
-  P3DBoolObject(bool value);
-  P3DBoolObject(const P3DBoolObject &copy);
-
-public:
-  virtual P3D_object_type get_type();
-  virtual bool get_bool();
-  virtual int get_int();
-  virtual void make_string(std::string &value);
-
-private:
-  bool _value;
-};
-
-#endif

+ 0 - 20
direct/src/plugin/p3dCInstance.I

@@ -1,20 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCInstance.I
- * @author drose
- * @date 2009-06-08
- */
-
-/**
- * Returns a unique integer for each instance in the system.
- */
-inline int P3DCInstance::
-get_instance_id() const {
-  return _instance_id;
-}

+ 0 - 32
direct/src/plugin/p3dCInstance.cxx

@@ -1,32 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCInstance.cxx
- * @author drose
- * @date 2009-06-08
- */
-
-#include "p3dCInstance.h"
-
-
-/**
- * Constructs a new Instance from an XML description.
- */
-P3DCInstance::
-P3DCInstance(TiXmlElement *xinstance) :
-  _func(nullptr)
-{
-  xinstance->Attribute("instance_id", &_instance_id);
-}
-
-/**
- *
- */
-P3DCInstance::
-~P3DCInstance() {
-}

+ 0 - 50
direct/src/plugin/p3dCInstance.h

@@ -1,50 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCInstance.h
- * @author drose
- * @date 2009-06-08
- */
-
-#ifndef P3DCINSTANCE_H
-#define P3DCINSTANCE_H
-
-#include "pandabase.h"
-
-#include "p3d_plugin.h"
-#include "pvector.h"
-#include "get_tinyxml.h"
-#include "windowHandle.h"
-
-class P3DSession;
-
-/**
- * This is an instance of a Panda3D window, as seen in the child-level
- * process.
- */
-class P3DCInstance : public P3D_instance {
-public:
-  P3DCInstance(TiXmlElement *xinstance);
-  ~P3DCInstance();
-
-  inline int get_instance_id() const;
-
-public:
-  PT(WindowHandle) _parent_window_handle;
-
-private:
-  P3D_request_ready_func *_func;
-
-  int _instance_id;
-
-  friend class P3DPythonRun;
-};
-
-#include "p3dCInstance.I"
-
-#endif

+ 0 - 742
direct/src/plugin/p3dCert.cxx

@@ -1,742 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert.cxx
- * @author rdb
- * @date 2011-03-08
- */
-
-#include "p3dCert.h"
-#include "p3dCert_strings.h"
-#include "wstring_encode.h"
-#include "mkdir_complete.h"
-
-#include <FL/Fl_Box.H>
-#include <FL/Fl_Button.H>
-#include <FL/Fl_Return_Button.H>
-#include <FL/Fl_Text_Display.H>
-
-#include <cassert>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <limits.h>
-#include <locale.h>
-
-#define BUTTON_WIDTH 180 // fit the Russian text
-#define BUTTON_SPACE 10
-
-#include "ca_bundle_data_src.c"
-
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <shellapi.h>
-#include <malloc.h>
-
-#define snprintf sprintf_s
-#endif
-
-#ifdef __APPLE__
-#include <CoreFoundation/CoreFoundation.h>
-#endif
-
-using std::cerr;
-using std::string;
-using std::wstring;
-
-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 != nullptr) {
-    ULONG num_langs = 0;
-    ULONG buffer_size = 0;
-    pGetUserPreferredUILanguages(8, &num_langs, nullptr, &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 != nullptr && lang_code[0] == buffer[0] &&
-                                     lang_code[1] == buffer[1]) {
-              return (LanguageIndex)j;
-            }
-          }
-        }
-        buffer += len + 1;
-      }
-    }
-  }
-
-  // Fall back to the old Windows XP function.
-  LANGID lang = GetUserDefaultUILanguage() & 0x3ff;
-  if (lang == 0) {
-    return LI_default;
-  }
-
-  for (int i = 0; i < LI_COUNT; ++i) {
-    if (language_ids[i] != 0 && language_ids[i] == lang) {
-      return (LanguageIndex)i;
-    }
-  }
-  return LI_default;
-}
-
-#elif defined(__APPLE__)
-static LanguageIndex detect_language() {
-  // Get and iterate through the list of preferred languages.
-  CFArrayRef langs = CFLocaleCopyPreferredLanguages();
-  CFIndex num_langs = CFArrayGetCount(langs);
-
-  for (long i = 0; i < num_langs; ++i) {
-    CFStringRef lang = (CFStringRef)CFArrayGetValueAtIndex(langs, i);
-
-    CFIndex length = CFStringGetLength(lang);
-    if (length < 2) {
-      continue;
-    }
-
-    CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
-    char *buffer = (char *)alloca(max_size);
-    if (!CFStringGetCString(lang, buffer, max_size, kCFStringEncodingUTF8)) {
-      continue;
-    }
-
-    if (isalnum(buffer[2])) {
-      // It's not a two-letter code.
-      continue;
-    }
-
-    // See if we support this language.
-    for (int j = 0; j < LI_COUNT; ++j) {
-      const char *lang_code = language_codes[j];
-      if (lang_code != nullptr && strncasecmp(buffer, lang_code, 2) == 0) {
-        CFRelease(langs);
-        return (LanguageIndex)j;
-      }
-    }
-  }
-
-  CFRelease(langs);
-  return LI_default;
-}
-
-#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 != nullptr && lang[0] != 0) {
-    size_t len;
-    const char *next = strchr(lang, ':');
-    if (next == nullptr) {
-      len = strlen(lang);
-    } else {
-      len = (next - lang);
-      ++next;
-    }
-
-    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 != nullptr && 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, nullptr);
-
-  if (lang == nullptr || lang[0] == 0 || strcmp(lang, "C") == 0) {
-    // Try the LANG variable.
-    lang = getenv("LANG");
-  }
-
-  if (lang == nullptr || 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 != nullptr && strncasecmp(lang, lang_code, 2) == 0) {
-      return (LanguageIndex)i;
-    }
-  }
-  return LI_default;
-}
-#endif
-
-#ifdef _WIN32
-int WINAPI
-wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
-  OpenSSL_add_all_algorithms();
-
-  LPWSTR *argv;
-  int argc;
-  argv = CommandLineToArgvW(pCmdLine, &argc);
-  if (argv == nullptr || argc != 2) {
-    cerr << "usage: p3dcert cert_filename cert_dir\n";
-    return 1;
-  }
-
-  li = detect_language();
-
-  wstring cert_filename (argv[0]);
-  wstring cert_dir (argv[1]);
-  LocalFree(argv);
-
-  AuthDialog *dialog = new AuthDialog(cert_filename, cert_dir);
-  dialog->show();
-
-  return Fl::run();
-}
-
-#else // _WIN32
-int main(int argc, char **argv) {
-  OpenSSL_add_all_algorithms();
-
-  if (argc != 3) {
-    cerr << "usage: p3dcert cert_filename cert_dir\n";
-    return 1;
-  }
-
-  li = detect_language();
-
-  string cert_filename (argv[1]);
-  string cert_dir (argv[2]);
-
-  AuthDialog *dialog = new AuthDialog(cert_filename, cert_dir);
-  dialog->show(1, argv);
-
-  return Fl::run();
-}
-#endif // _WIN32
-
-/**
- *
- */
-#ifdef _WIN32
-AuthDialog::
-AuthDialog(const wstring &cert_filename, const wstring &cert_dir) :
-#else
-AuthDialog::
-AuthDialog(const string &cert_filename, const string &cert_dir) :
-#endif
-  Fl_Window(435, 242, new_application_title[li]),
-  _cert_dir(cert_dir)
-{
-  _view_cert_dialog = nullptr;
-
-  _cert = nullptr;
-  _stack = nullptr;
-  _verify_result = -1;
-
-  // Center the window on the screen.
-  position((Fl::w() - w()) / 2, (Fl::h() - h()) / 2);
-  set_modal();
-
-  read_cert_file(cert_filename);
-  get_friendly_name();
-  verify_cert();
-  layout();
-}
-
-/**
- *
- */
-AuthDialog::
-~AuthDialog() {
-  if (_view_cert_dialog != nullptr) {
-    _view_cert_dialog->hide();
-  }
-
-  if (_cert != nullptr) {
-    X509_free(_cert);
-    _cert = nullptr;
-  }
-  if (_stack != nullptr) {
-    sk_X509_free(_stack);
-    _stack = nullptr;
-  }
-}
-
-/**
- * The user clicks the "Run" button.
- */
-void AuthDialog::
-run_clicked(Fl_Widget *w, void *data) {
-  AuthDialog *dlg = (AuthDialog *) data;
-  dlg->approve_cert();
-}
-
-/**
- * The user clicks the "View Certificate" button.
- */
-void AuthDialog::
-view_cert_clicked(Fl_Widget *w, void *data) {
-  AuthDialog *dlg = (AuthDialog *) data;
-
-  if (dlg->_view_cert_dialog != nullptr) {
-    dlg->_view_cert_dialog->hide();
-  }
-  dlg->hide();
-  dlg->_view_cert_dialog = new ViewCertDialog(dlg, dlg->_cert);
-  dlg->_view_cert_dialog->show();
-}
-
-/**
- * The user clicks the "Cancel" button.
- */
-void AuthDialog::
-cancel_clicked(Fl_Widget *w, void *data) {
-  AuthDialog *dlg = (AuthDialog *) data;
-  dlg->hide();
-}
-
-/**
- * Writes the certificate into the _cert_dir, so that it will be found by the
- * P3DInstanceManager and known to be approved.
- */
-void AuthDialog::
-approve_cert() {
-  assert(_cert != nullptr);
-
-  // Make sure the directory exists.
-#ifdef _WIN32
-  mkdir_complete_w(_cert_dir, cerr);
-#else
-  mkdir_complete(_cert_dir, cerr);
-#endif
-
-  // Look for an unused filename.
-  int i = 1;
-  size_t buf_length = _cert_dir.length() + 100;
-
-  // Sure, there's a slight race condition right now: another process might
-  // attempt to create the same filename.  So what.
-  FILE *fp = nullptr;
-
-#ifdef _WIN32
-  wchar_t *buf = new wchar_t[buf_length];
-
-  while (true) {
-    swprintf(buf, L"%s/p%d.crt", _cert_dir.c_str(), i);
-    assert(wcslen(buf) < buf_length);
-
-    // Check if it already exists.  If not, take it.
-    if (GetFileAttributesW(buf) == -1) {
-      break;
-    }
-    ++i;
-  }
-  fp = _wfopen(buf, L"w");
-
-#else // _WIN32
-  char *buf = new char[buf_length];
-
-  while (true) {
-    sprintf(buf, "%s/p%d.crt", _cert_dir.c_str(), i);
-    assert(strlen(buf) < buf_length);
-
-    // Check if it already exists.  If not, take it.
-    struct stat statbuf;
-    if (stat(buf, &statbuf) != 0) {
-      break;
-    }
-    ++i;
-  }
-  fp = fopen(buf, "w");
-#endif // _WIN32
-
-  if (fp != nullptr) {
-    PEM_write_X509(fp, _cert);
-    fclose(fp);
-  }
-
-  hide();
-}
-
-/**
- * Reads the list of certificates in the pem filename passed on the command
- * line into _cert and _stack.
- */
-#ifdef _WIN32
-void AuthDialog::
-read_cert_file(const wstring &cert_filename) {
-#else
-void AuthDialog::
-read_cert_file(const string &cert_filename) {
-#endif
-
-  FILE *fp = nullptr;
-#ifdef _WIN32
-  fp = _wfopen(cert_filename.c_str(), L"r");
-#else // _WIN32
-  fp = fopen(cert_filename.c_str(), "r");
-#endif  // _WIN32
-
-  if (fp == nullptr) {
-#ifdef _WIN32
-    wcerr << L"Couldn't read " << cert_filename.c_str() << L"\n";
-#else
-    cerr << "Couldn't read " << cert_filename.c_str() << "\n";
-#endif
-    return;
-  }
-  _cert = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
-  if (_cert == nullptr) {
-#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";
-#endif
-    fclose(fp);
-    return;
-  }
-
-  // Build up a STACK of the remaining certificates in the file.
-  _stack = sk_X509_new(nullptr);
-  X509 *c = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
-  while (c != nullptr) {
-    sk_X509_push(_stack, c);
-    c = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
-  }
-
-  fclose(fp);
-}
-
-/**
- * Extracts the "friendly name" from the certificate: the common name or email
- * name.
- */
-void AuthDialog::
-get_friendly_name() {
-  if (_cert == nullptr) {
-    _friendly_name.clear();
-    return;
-  }
-
-
-  static const int nid_choices[] = {
-    NID_pkcs9_emailAddress,
-    NID_commonName,
-    -1,
-  };
-
-  // Choose the first NID that exists on the cert.
-  for (int ni = 0; nid_choices[ni] != -1; ++ni) {
-    int nid = nid_choices[ni];
-
-    // A complex OpenSSL interface to extract out the name in utf-8.
-    X509_NAME *xname = X509_get_subject_name(_cert);
-    if (xname != nullptr) {
-      int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
-      if (pos != -1) {
-        // We just get the first common name.  I guess it's possible to have
-        // more than one; not sure what that means in this context.
-        X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
-        if (xentry != nullptr) {
-          ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
-          if (data != nullptr) {
-            // We use "print" to dump the output to a memory BIO.  Is there an
-            // easier way to decode the ASN1_STRING?  Curse these incomplete
-            // docs.
-            BIO *mbio = BIO_new(BIO_s_mem());
-            ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
-
-            char *pp;
-            long pp_size = BIO_get_mem_data(mbio, &pp);
-            _friendly_name = string(pp, pp_size);
-            BIO_free(mbio);
-            return;
-          }
-        }
-      }
-    }
-  }
-}
-
-/**
- * Checks whether the certificate is valid by the chain and initializes
- * _verify_status accordingly.
- */
-void AuthDialog::
-verify_cert() {
-  if (_cert == nullptr) {
-    _verify_result = -1;
-    return;
-  }
-
-  // Create a new X509_STORE.
-  X509_STORE *store = X509_STORE_new();
-  X509_STORE_set_default_paths(store);
-
-  // Add in the well-known certificate authorities.
-  load_certificates_from_der_ram(store, (const char *)ca_bundle_data, ca_bundle_data_len);
-
-  // Create the X509_STORE_CTX for verifying the cert and chain.
-  X509_STORE_CTX *ctx = X509_STORE_CTX_new();
-  X509_STORE_CTX_init(ctx, store, _cert, _stack);
-  X509_STORE_CTX_set_cert(ctx, _cert);
-
-  if (X509_verify_cert(ctx)) {
-    _verify_result = 0;
-  } else {
-    _verify_result = X509_STORE_CTX_get_error(ctx);
-  }
-
-  X509_STORE_CTX_free(ctx);
-
-  X509_STORE_free(store);
-
-  cerr << "Got certificate from " << _friendly_name.c_str()
-       << ", verify_result = " << _verify_result << "\n";
-}
-
-/**
- * Reads a chain of trusted certificates from the indicated data buffer and
- * adds them to the X509_STORE object.  The data buffer should be DER-
- * formatted.  Returns the number of certificates read on success, or 0 on
- * failure.
- *
- * You should call this only with trusted, locally-stored certificates; not
- * with certificates received from an untrusted source.
- */
-int AuthDialog::
-load_certificates_from_der_ram(X509_STORE *store,
-                               const char *data, size_t data_size) {
-  int count = 0;
-
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
-  // Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **.
-  const unsigned char *bp, *bp_end;
-#else
-  // Prior to 0.9.8, d2i_X509() accepted an unsigned char **.
-  unsigned char *bp, *bp_end;
-#endif
-
-  bp = (unsigned char *)data;
-  bp_end = bp + data_size;
-  X509 *x509 = d2i_X509(nullptr, &bp, bp_end - bp);
-  while (x509 != nullptr) {
-    X509_STORE_add_cert(store, x509);
-    ++count;
-    x509 = d2i_X509(nullptr, &bp, bp_end - bp);
-  }
-
-  return count;
-}
-
-/**
- * Arranges the text and controls within the dialog.
- */
-void AuthDialog::
-layout() {
-  get_text(_header, sizeof _header, _text, sizeof _text);
-
-  // Now replicate out any @ signs in the text to avoid FlTk's escape
-  // sequences.
-  int j = 0;
-  for (int i = 0; _text[i] != '\0'; ++i) {
-    _text_clean[j++] = _text[i];
-    if (_text[i] == '@') {
-      _text_clean[j++] = _text[i];
-    }
-  }
-  _text_clean[j] = '\0';
-  assert(strlen(_text_clean) < sizeof(_text_clean));
-
-  int next_y = 35;
-  if (strlen(_header) > 0) {
-    Fl_Box *text0 = new Fl_Box(w() / 2, next_y, 0, 25, _header);
-    text0->align(FL_ALIGN_TOP | FL_ALIGN_CENTER);
-    text0->labelfont(FL_BOLD);
-    text0->labelsize(text0->labelsize() * 1.5);
-    next_y += 25;
-  }
-
-  Fl_Box *text1 = new Fl_Box(17, next_y, 400, 180, _text_clean);
-  text1->align(FL_ALIGN_TOP | FL_ALIGN_INSIDE | FL_ALIGN_WRAP);
-  next_y += 180;
-
-  short nbuttons = 1;
-  if (_cert != nullptr) {
-    nbuttons++;
-    if (_verify_result == 0) {
-      nbuttons++;
-    }
-  }
-  short bx = (w() - nbuttons * BUTTON_WIDTH - (nbuttons - 1) * BUTTON_SPACE) / 2;
-
-  if (_verify_result == 0 && _cert != nullptr) {
-    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);
-    bx += BUTTON_WIDTH + BUTTON_SPACE;
-  }
-
-  if (_cert != nullptr) {
-    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);
-    bx += BUTTON_WIDTH + BUTTON_SPACE;
-  }
-
-  Fl_Button *cancel_button;
-  cancel_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, cancel_title[li]);
-  cancel_button->callback(this->cancel_clicked, this);
-
-  next_y += 42;
-  size(435, next_y);
-
-  end();
-  set_modal();
-}
-
-/**
- * Fills in the text appropriate to display in the dialog box, based on the
- * certificate read so far.
- */
-void AuthDialog::
-get_text(char *header, size_t hlen, char *text, size_t tlen) {
-  switch (_verify_result) {
-  case -1:
-    strncpy(header, no_cert_title[li], hlen);
-    strncpy(text, no_cert_text[li], tlen);
-    break;
-
-  case 0:
-    snprintf(text, tlen, verified_cert_text[li], _friendly_name.c_str(),
-                        _friendly_name.c_str(), _friendly_name.c_str());
-    break;
-
-  case X509_V_ERR_CERT_NOT_YET_VALID:
-  case X509_V_ERR_CERT_HAS_EXPIRED:
-  case X509_V_ERR_CRL_NOT_YET_VALID:
-  case X509_V_ERR_CRL_HAS_EXPIRED:
-    strncpy(header, expired_cert_title[li], hlen);
-    snprintf(text, tlen, expired_cert_text[li], _friendly_name.c_str());
-    break;
-
-  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-    strncpy(header, unverified_cert_title[li], hlen);
-    strncpy(text, unknown_auth_cert_text[li], tlen);
-    break;
-
-  case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
-  case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
-    strncpy(header, unverified_cert_title[li], hlen);
-    strncpy(text, self_signed_cert_text[li], tlen);
-    break;
-
-  default:
-    strncpy(header, unverified_cert_title[li], hlen);
-    snprintf(text, tlen, generic_error_cert_text[li], _verify_result);
-  }
-}
-
-/**
- *
- */
-ViewCertDialog::
-ViewCertDialog(AuthDialog *auth_dialog, X509 *cert) :
-  Fl_Window(600, 400, show_cert_title[li]),
-  _auth_dialog(auth_dialog),
-  _cert(cert)
-{
-  // Center the window on the screen.
-  position((Fl::w() - w()) / 2, (Fl::h() - h()) / 2);
-  set_modal();
-
-  layout();
-}
-
-/**
- *
- */
-ViewCertDialog::
-~ViewCertDialog() {
-  if (_auth_dialog != nullptr) {
-    _auth_dialog->_view_cert_dialog = nullptr;
-  }
-}
-
-/**
- * The user clicks the "Run" button.
- */
-void ViewCertDialog::
-run_clicked(Fl_Widget *w, void *data) {
-  ViewCertDialog *dlg = (ViewCertDialog *) data;
-  if (dlg->_auth_dialog != nullptr){
-    dlg->_auth_dialog->approve_cert();
-  }
-  dlg->hide();
-}
-
-/**
- * The user clicks the "Cancel" button.
- */
-void ViewCertDialog::
-cancel_clicked(Fl_Widget *w, void *data) {
-  ViewCertDialog *dlg = (ViewCertDialog *) data;
-  if (dlg->_auth_dialog != nullptr){
-    dlg->_auth_dialog->hide();
-  }
-  dlg->hide();
-}
-
-/**
- * Arranges the text and controls within the dialog.
- */
-void ViewCertDialog::
-layout() {
-  // Format the certificate text for display in the dialog.
-  assert(_cert != nullptr);
-
-  BIO *mbio = BIO_new(BIO_s_mem());
-  X509_print(mbio, _cert);
-
-  char *pp;
-  long pp_size = BIO_get_mem_data(mbio, &pp);
-  string cert_body(pp, pp_size);
-  BIO_free(mbio);
-
-  Fl_Text_Buffer *buffer = new Fl_Text_Buffer;
-  buffer->append(cert_body.c_str());
-
-  Fl_Text_Display *text = new Fl_Text_Display(20, 20, 565, 320);
-  text->buffer(buffer);
-
-  short bx = (w() - BUTTON_WIDTH * 2 - BUTTON_SPACE) / 2;
-
-  Fl_Return_Button *run_button = new Fl_Return_Button(bx, 360, BUTTON_WIDTH, 25, run_title[li]);
-  run_button->callback(this->run_clicked, this);
-
-  bx += BUTTON_WIDTH + BUTTON_SPACE;
-
-  Fl_Button *cancel_button = new Fl_Button(bx, 360, BUTTON_WIDTH, 25, cancel_title[li]);
-  cancel_button->callback(this->cancel_clicked, this);
-
-  end();
-  set_modal();
-}

+ 0 - 117
direct/src/plugin/p3dCert.h

@@ -1,117 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert.h
- * @author rdb
- * @date 2011-03-08
- */
-
-#ifndef P3DCERT_H
-#define P3DCERT_H
-
-#include <FL/Fl.H>
-#include <FL/Fl_Window.H>
-
-#define OPENSSL_NO_KRB5
-#include <openssl/x509.h>
-#include <openssl/x509_vfy.h>
-#include <openssl/pem.h>
-
-#include <string>
-#include <iostream>
-#include <stdio.h>
-
-class ViewCertDialog;
-
-#ifndef STACK_OF
-  // At some point, presumably in 1.0.0, openssl went to the STACK_OF() macro
-  // system to typedef the contents of a stack.  Unfortunately, that new API
-  // is different.  We define some macros here here for backward compatiblity.
-  #define STACK_OF(type) STACK
-  #define sk_X509_push(stack, item) sk_push((stack), (char *)(item))
-  #define sk_X509_free(stack) sk_free(stack)
-  #define sk_X509_new(cmp) sk_new(cmp)
-#endif
-
-/**
- * This is the primary dialog of this application.
- *
- * This dialog is presented to the user when he/she clicks on the red
- * authorization button on the splash window.  It tells the user the status of
- * the application's signature, and invites the user to approve the signature
- * or cancel.
- */
-class AuthDialog : public Fl_Window {
-public:
-#ifdef _WIN32
-  AuthDialog(const std::wstring &cert_filename, const std::wstring &cert_dir);
-#else
-  AuthDialog(const std::string &cert_filename, const std::string &cert_dir);
-#endif
-  virtual ~AuthDialog();
-
-  static void run_clicked(Fl_Widget *w, void *data);
-  static void view_cert_clicked(Fl_Widget *w, void *data);
-  static void cancel_clicked(Fl_Widget *w, void *data);
-
-  void approve_cert();
-
-private:
-#ifdef _WIN32
-  void read_cert_file(const std::wstring &cert_filename);
-#else
-  void read_cert_file(const std::string &cert_filename);
-#endif
-  void get_friendly_name();
-  void verify_cert();
-  int load_certificates_from_der_ram(X509_STORE *store,
-                                     const char *data, size_t data_size);
-
-  void layout();
-  void get_text(char *header, size_t hlen, char *text, size_t tlen);
-
-public:
-  ViewCertDialog *_view_cert_dialog;
-
-private:
-#ifdef _WIN32
-  std::wstring _cert_dir;
-#else
-  std::string _cert_dir;
-#endif
-  X509 *_cert;
-  STACK_OF(X509) *_stack;
-
-  char _header[64];
-  char _text[1024];
-  char _text_clean[2048];
-
-  std::string _friendly_name;
-  int _verify_result;
-};
-
-/**
- * This is the detailed view of the particular certificate.
- */
-class ViewCertDialog : public Fl_Window {
-public:
-  ViewCertDialog(AuthDialog *auth_dialog, X509 *cert);
-  virtual ~ViewCertDialog();
-
-  static void run_clicked(Fl_Widget *w, void *data);
-  static void cancel_clicked(Fl_Widget *w, void *data);
-
-private:
-  void layout();
-
-private:
-  AuthDialog *_auth_dialog;
-  X509 *_cert;
-};
-
-#endif

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

@@ -1,567 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert_strings.cxx
- * @author rdb
- * @date 2015-03-25
- */
-
-#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.comen-
-// uslibrarywindowsdesktopdd318693%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.",
-};

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

@@ -1,44 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert_strings.h
- * @author rdb
- * @date 2015-03-25
- */
-
-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];

+ 0 - 618
direct/src/plugin/p3dCert_wx.cxx

@@ -1,618 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert_wx.cxx
- * @author drose
- * @date 2009-09-11
- */
-
-#include "p3dCert_wx.h"
-#include "wstring_encode.h"
-#include "mkdir_complete.h"
-
-#include <wx/cmdline.h>
-#include <wx/filename.h>
-
-#include "ca_bundle_data_src.c"
-
-using std::cerr;
-
-static const wxString
-self_signed_cert_text =
-  _T("This Panda3D application uses a self-signed certificate.  ")
-  _T("This means the author's name can't be verified, and you have ")
-  _T("no way of knowing for sure who wrote it.\n\n")
-
-  _T("We recommend you click Cancel to avoid running this application.");
-
-static const wxString
-unknown_auth_cert_text =
-  _T("This Panda3D application has been signed, but we don't recognize ")
-  _T("the authority that verifies the signature.  This means the author's ")
-  _T("name can't be trusted, and you have no way of knowing ")
-  _T("for sure who wrote it.\n\n")
-
-  _T("We recommend you click Cancel to avoid running this application.");
-
-static const wxString
-verified_cert_text =
-  _T("This Panda3D application has been signed by %s. ")
-  _T("If you trust %s, then click the Run button below ")
-  _T("to run this application on your computer.  This will also ")
-  _T("automatically approve this and any other applications signed by ")
-  _T("%s in the future.\n\n")
-
-  _T("If you are unsure about this application, ")
-  _T("you should click Cancel instead.");
-
-static const wxString
-expired_cert_text =
-  _T("This Panda3D application has been signed by %s, ")
-  _T("but the certificate has expired.\n\n")
-
-  _T("You should check the current date set on your computer's clock ")
-  _T("to make sure it is correct.\n\n")
-
-  _T("If your computer's date is correct, we recommend ")
-  _T("you click Cancel to avoid running this application.");
-
-static const wxString
-generic_error_cert_text =
-  _T("This Panda3D application has been signed, but there is a problem ")
-  _T("with the certificate (OpenSSL error code %d).\n\n")
-
-  _T("We recommend you click Cancel to avoid running this application.");
-
-static const wxString
-no_cert_text =
-  _T("This Panda3D application has not been signed.  This means you have ")
-  _T("no way of knowing for sure who wrote it.\n\n")
-
-  _T("Click Cancel to avoid running this application.");
-
-// wxWidgets boilerplate macro to define main() and start up the application.
-IMPLEMENT_APP(P3DCertApp)
-
-/**
- * The "main" of a wx application.  This is the first entry point.
- */
-bool P3DCertApp::
-OnInit() {
-  // call the base class initialization method, currently it only parses a few
-  // common command-line options but it could be do more in the future
-  if (!wxApp::OnInit()) {
-    return false;
-  }
-
-  OpenSSL_add_all_algorithms();
-
-  AuthDialog *dialog = new AuthDialog(_cert_filename, _cert_dir);
-  SetTopWindow(dialog);
-  dialog->Show(true);
-  dialog->SetFocus();
-  dialog->Raise();
-
-  // Return true to enter the main loop and wait for user input.
-  return true;
-}
-
-/**
- * A callback to initialize the parser with the command line options.
- */
-void P3DCertApp::
-OnInitCmdLine(wxCmdLineParser &parser) {
-  parser.AddParam();
-  parser.AddParam();
-}
-
-/**
- * A callback after the successful parsing of the command line.
- */
-bool P3DCertApp::
-OnCmdLineParsed(wxCmdLineParser &parser) {
-  _cert_filename = (const char *)parser.GetParam(0).mb_str();
-  _cert_dir = (const char *)parser.GetParam(1).mb_str();
-
-  return true;
-}
-
-
-// The event table for AuthDialog.
-#define VIEW_CERT_BUTTON  (wxID_HIGHEST + 1)
-BEGIN_EVENT_TABLE(AuthDialog, wxDialog)
-    EVT_BUTTON(wxID_OK, AuthDialog::run_clicked)
-    EVT_BUTTON(VIEW_CERT_BUTTON, AuthDialog::view_cert_clicked)
-    EVT_BUTTON(wxID_CANCEL, AuthDialog::cancel_clicked)
-END_EVENT_TABLE()
-
-/**
- *
- */
-AuthDialog::
-AuthDialog(const std::string &cert_filename, const std::string &cert_dir) :
-  // I hate stay-on-top dialogs, but if we don't set this flag, it doesn't
-  // come to the foreground on OSX, and might be lost behind the browser
-  // window.
-  wxDialog(nullptr, wxID_ANY, _T("New Panda3D Application"), wxDefaultPosition,
-           wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxSTAY_ON_TOP),
-  _cert_dir(cert_dir)
-{
-  _view_cert_dialog = nullptr;
-
-  _cert = nullptr;
-  _stack = nullptr;
-  _verify_result = -1;
-
-  read_cert_file(cert_filename);
-  get_friendly_name();
-  verify_cert();
-  layout();
-}
-
-/**
- *
- */
-AuthDialog::
-~AuthDialog() {
-  if (_view_cert_dialog != nullptr) {
-    _view_cert_dialog->Destroy();
-  }
-
-  if (_cert != nullptr) {
-    X509_free(_cert);
-    _cert = nullptr;
-  }
-  if (_stack != nullptr) {
-    sk_X509_free(_stack);
-    _stack = nullptr;
-  }
-}
-
-/**
- * The user clicks the "Run" button.
- */
-void AuthDialog::
-run_clicked(wxCommandEvent &event) {
-  approve_cert();
-}
-
-/**
- * The user clicks the "View Certificate" button.
- */
-void AuthDialog::
-view_cert_clicked(wxCommandEvent &event) {
-  if (_view_cert_dialog != nullptr) {
-    _view_cert_dialog->Destroy();
-  }
-  Hide();
-  _view_cert_dialog = new ViewCertDialog(this, _cert);
-  _view_cert_dialog->Show();
-}
-
-/**
- * The user clicks the "Cancel" button.
- */
-void AuthDialog::
-cancel_clicked(wxCommandEvent &event) {
-  Destroy();
-}
-
-/**
- * Writes the certificate into the _cert_dir, so that it will be found by the
- * P3DInstanceManager and known to be approved.
- */
-void AuthDialog::
-approve_cert() {
-  assert(_cert != nullptr);
-
-  // Make sure the directory exists.
-  mkdir_complete(_cert_dir, cerr);
-
-  // Look for an unused filename.
-  int i = 1;
-  size_t buf_length = _cert_dir.length() + 100;
-  char *buf = new char[buf_length];
-#ifdef _WIN32
-  std::wstring buf_w;
-#endif // _WIN32
-
-  while (true) {
-    sprintf(buf, "%s/p%d.crt", _cert_dir.c_str(), i);
-    assert(strlen(buf) < buf_length);
-
-    // Check if it already exists.  If not, take it.
-#ifdef _WIN32
-    DWORD results = 0;
-    if (string_to_wstring(buf_w, buf)) {
-      results = GetFileAttributesW(buf_w.c_str());
-    }
-    if (results == -1) {
-      break;
-    }
-#else
-    struct stat statbuf;
-    if (stat(buf, &statbuf) != 0) {
-      break;
-    }
-#endif
-    ++i;
-  }
-
-  // Sure, there's a slight race condition right now: another process might
-  // attempt to create the same filename.  So what.
-  FILE *fp = nullptr;
-#ifdef _WIN32
-  fp = _wfopen(buf_w.c_str(), L"w");
-#else // _WIN32
-  fp = fopen(buf, "w");
-#endif  // _WIN32
-  if (fp != nullptr) {
-    PEM_write_X509(fp, _cert);
-    fclose(fp);
-  }
-
-  Destroy();
-}
-
-/**
- * Reads the list of certificates in the pem filename passed on the command
- * line into _cert and _stack.
- */
-void AuthDialog::
-read_cert_file(const std::string &cert_filename) {
-  FILE *fp = nullptr;
-#ifdef _WIN32
-  std::wstring cert_filename_w;
-  if (string_to_wstring(cert_filename_w, cert_filename)) {
-    fp = _wfopen(cert_filename_w.c_str(), L"r");
-  }
-#else // _WIN32
-  fp = fopen(cert_filename.c_str(), "r");
-#endif  // _WIN32
-
-  if (fp == nullptr) {
-    cerr << "Couldn't read " << cert_filename << "\n";
-    return;
-  }
-  _cert = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
-  if (_cert == nullptr) {
-    cerr << "Could not read certificate in " << cert_filename << ".\n";
-    fclose(fp);
-    return;
-  }
-
-  // Build up a STACK of the remaining certificates in the file.
-  _stack = sk_X509_new(nullptr);
-  X509 *c = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
-  while (c != nullptr) {
-    sk_X509_push(_stack, c);
-    c = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
-  }
-
-  fclose(fp);
-}
-
-/**
- * Extracts the "friendly name" from the certificate: the common name or email
- * name.
- */
-void AuthDialog::
-get_friendly_name() {
-  if (_cert == nullptr) {
-    _friendly_name.clear();
-    return;
-  }
-
-
-  static const int nid_choices[] = {
-    NID_pkcs9_emailAddress,
-    NID_commonName,
-    -1,
-  };
-
-  // Choose the first NID that exists on the cert.
-  for (int ni = 0; nid_choices[ni] != -1; ++ni) {
-    int nid = nid_choices[ni];
-
-    // A complex OpenSSL interface to extract out the name in utf-8.
-    X509_NAME *xname = X509_get_subject_name(_cert);
-    if (xname != nullptr) {
-      int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
-      if (pos != -1) {
-        // We just get the first common name.  I guess it's possible to have
-        // more than one; not sure what that means in this context.
-        X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
-        if (xentry != nullptr) {
-          ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
-          if (data != nullptr) {
-            // We use "print" to dump the output to a memory BIO.  Is there an
-            // easier way to decode the ASN1_STRING?  Curse these incomplete
-            // docs.
-            BIO *mbio = BIO_new(BIO_s_mem());
-            ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
-
-            char *pp;
-            long pp_size = BIO_get_mem_data(mbio, &pp);
-            _friendly_name = wxString(pp, wxConvUTF8, pp_size);
-            BIO_free(mbio);
-            return;
-          }
-        }
-      }
-    }
-  }
-}
-
-/**
- * Checks whether the certificate is valid by the chain and initializes
- * _verify_status accordingly.
- */
-void AuthDialog::
-verify_cert() {
-  if (_cert == nullptr) {
-    _verify_result = -1;
-    return;
-  }
-
-  // Create a new X509_STORE.
-  X509_STORE *store = X509_STORE_new();
-  X509_STORE_set_default_paths(store);
-
-  // Add in the well-known certificate authorities.
-  load_certificates_from_der_ram(store, (const char *)ca_bundle_data, ca_bundle_data_len);
-
-  // Create the X509_STORE_CTX for verifying the cert and chain.
-  X509_STORE_CTX *ctx = X509_STORE_CTX_new();
-  X509_STORE_CTX_init(ctx, store, _cert, _stack);
-  X509_STORE_CTX_set_cert(ctx, _cert);
-
-  if (X509_verify_cert(ctx)) {
-    _verify_result = 0;
-  } else {
-    _verify_result = X509_STORE_CTX_get_error(ctx);
-  }
-
-  X509_STORE_CTX_free(ctx);
-
-  X509_STORE_free(store);
-
-  cerr << "Got certificate from " << _friendly_name.mb_str()
-       << ", verify_result = " << _verify_result << "\n";
-}
-
-/**
- * Reads a chain of trusted certificates from the indicated data buffer and
- * adds them to the X509_STORE object.  The data buffer should be DER-
- * formatted.  Returns the number of certificates read on success, or 0 on
- * failure.
- *
- * You should call this only with trusted, locally-stored certificates; not
- * with certificates received from an untrusted source.
- */
-int AuthDialog::
-load_certificates_from_der_ram(X509_STORE *store,
-                               const char *data, size_t data_size) {
-  int count = 0;
-
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
-  // Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **.
-  const unsigned char *bp, *bp_end;
-#else
-  // Prior to 0.9.8, d2i_X509() accepted an unsigned char **.
-  unsigned char *bp, *bp_end;
-#endif
-
-  bp = (unsigned char *)data;
-  bp_end = bp + data_size;
-  X509 *x509 = d2i_X509(nullptr, &bp, bp_end - bp);
-  while (x509 != nullptr) {
-    X509_STORE_add_cert(store, x509);
-    ++count;
-    x509 = d2i_X509(nullptr, &bp, bp_end - bp);
-  }
-
-  return count;
-}
-
-/**
- * Arranges the text and controls within the dialog.
- */
-void AuthDialog::
-layout() {
-  wxString header, text;
-  get_text(header, text);
-
-  wxPanel *panel = new wxPanel(this);
-  wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
-
-  wxFont font = panel->GetFont();
-  wxFont *bold_font = wxTheFontList->FindOrCreateFont
-    ((int)(font.GetPointSize() * 1.5),
-     font.GetFamily(), font.GetStyle(), wxFONTWEIGHT_BOLD);
-
-  if (!header.IsEmpty()) {
-    wxStaticText *text0 = new wxStaticText
-      (panel, wxID_ANY, header, wxDefaultPosition, wxDefaultSize,
-       wxALIGN_CENTER);
-    text0->SetFont(*bold_font);
-    vsizer->Add(text0, 0, wxCENTER | wxALL, 10);
-  }
-
-  wxStaticText *text1 = new wxStaticText
-    (panel, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
-  text1->Wrap(400);
-  vsizer->Add(text1, 0, wxCENTER | wxALL, 10);
-
-  // Create the run  cancel buttons.
-  wxBoxSizer *bsizer = new wxBoxSizer(wxHORIZONTAL);
-
-  if (_verify_result == 0 && _cert != nullptr) {
-    wxButton *run_button = new wxButton(panel, wxID_OK, _T("Run"));
-    bsizer->Add(run_button, 0, wxALIGN_CENTER | wxALL, 5);
-  }
-
-  if (_cert != nullptr) {
-    wxButton *view_button = new wxButton(panel, VIEW_CERT_BUTTON, _T("View Certificate"));
-    bsizer->Add(view_button, 0, wxALIGN_CENTER | wxALL, 5);
-  }
-
-  wxButton *cancel_button = new wxButton(panel, wxID_CANCEL, _T("Cancel"));
-  bsizer->Add(cancel_button, 0, wxALIGN_CENTER | wxALL, 5);
-
-  vsizer->Add(bsizer, 0, wxALIGN_CENTER | wxALL, 5);
-
-  panel->SetSizer(vsizer);
-  panel->SetAutoLayout(true);
-  vsizer->Fit(this);
-}
-
-/**
- * Fills in the text appropriate to display in the dialog box, based on the
- * certificate read so far.
- */
-void AuthDialog::
-get_text(wxString &header, wxString &text) {
-  switch (_verify_result) {
-  case -1:
-    header = _T("No signature!");
-    text = no_cert_text;
-    break;
-
-  case 0:
-    text.Printf(verified_cert_text, _friendly_name.c_str(), _friendly_name.c_str(), _friendly_name.c_str());
-    break;
-
-  case X509_V_ERR_CERT_NOT_YET_VALID:
-  case X509_V_ERR_CERT_HAS_EXPIRED:
-  case X509_V_ERR_CRL_NOT_YET_VALID:
-  case X509_V_ERR_CRL_HAS_EXPIRED:
-    header = _T("Expired signature!");
-    text.Printf(expired_cert_text, _friendly_name.c_str());
-    break;
-
-  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-    header = _T("Unverified signature!");
-    text.Printf(unknown_auth_cert_text, _friendly_name.c_str());
-    break;
-
-  case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
-  case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
-    header = _T("Unverified signature!");
-    text = self_signed_cert_text;
-    break;
-
-  default:
-    header = _T("Unverified signature!");
-    text.Printf(generic_error_cert_text, _verify_result);
-  }
-}
-
-
-// The event table for ViewCertDialog.
-BEGIN_EVENT_TABLE(ViewCertDialog, wxDialog)
-    EVT_BUTTON(wxID_OK, ViewCertDialog::run_clicked)
-    EVT_BUTTON(wxID_CANCEL, ViewCertDialog::cancel_clicked)
-END_EVENT_TABLE()
-
-/**
- *
- */
-ViewCertDialog::
-ViewCertDialog(AuthDialog *auth_dialog, X509 *cert) :
-wxDialog(nullptr, wxID_ANY, _T("View Certificate"), wxDefaultPosition,
-         wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
-  _auth_dialog(auth_dialog),
-  _cert(cert)
-{
-  layout();
-}
-
-/**
- *
- */
-ViewCertDialog::
-~ViewCertDialog() {
-  if (_auth_dialog != nullptr) {
-    _auth_dialog->_view_cert_dialog = nullptr;
-  }
-}
-
-/**
- * The user clicks the "Run" button.
- */
-void ViewCertDialog::
-run_clicked(wxCommandEvent &event) {
-  if (_auth_dialog != nullptr){
-    _auth_dialog->approve_cert();
-  }
-  Destroy();
-}
-
-/**
- * The user clicks the "Cancel" button.
- */
-void ViewCertDialog::
-cancel_clicked(wxCommandEvent &event) {
-  if (_auth_dialog != nullptr){
-    _auth_dialog->Destroy();
-  }
-  Destroy();
-}
-
-/**
- * Arranges the text and controls within the dialog.
- */
-void ViewCertDialog::
-layout() {
-  // Format the certificate text for display in the dialog.
-  assert(_cert != nullptr);
-
-  BIO *mbio = BIO_new(BIO_s_mem());
-  X509_print(mbio, _cert);
-
-  char *pp;
-  long pp_size = BIO_get_mem_data(mbio, &pp);
-  wxString cert_body(pp, wxConvUTF8, pp_size);
-  BIO_free(mbio);
-
-  wxPanel *panel = new wxPanel(this);
-  wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
-
-  wxScrolledWindow *slwin = new wxScrolledWindow
-    (panel, -1, wxDefaultPosition, wxDefaultSize, wxVSCROLL | wxHSCROLL | wxBORDER_SUNKEN);
-  slwin->SetScrollRate(20, 20);
-
-  wxBoxSizer *slsizer = new wxBoxSizer(wxVERTICAL);
-
-  wxStaticText *text1 = new wxStaticText
-    (slwin, wxID_ANY, cert_body, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
-  slsizer->Add(text1, 0, wxEXPAND, 0);
-  slwin->SetSizer(slsizer);
-
-  vsizer->Add(slwin, 1, wxEXPAND | wxALL, 10);
-
-  // Create the run  cancel buttons.
-  wxBoxSizer *bsizer = new wxBoxSizer(wxHORIZONTAL);
-
-  wxButton *run_button = new wxButton(panel, wxID_OK, _T("Run"));
-  bsizer->Add(run_button, 0, wxALIGN_CENTER | wxALL, 5);
-
-  wxButton *cancel_button = new wxButton(panel, wxID_CANCEL, _T("Cancel"));
-  bsizer->Add(cancel_button, 0, wxALIGN_CENTER | wxALL, 5);
-
-  vsizer->Add(bsizer, 0, wxALIGN_CENTER | wxALL, 5);
-
-  panel->SetSizer(vsizer);
-  panel->SetAutoLayout(true);
-  vsizer->Fit(this);
-
-  // Make sure the resulting window is at least a certain size.
-  int width, height;
-  GetSize(&width, &height);
-  SetSize(std::max(width, 600), std::max(height, 400));
-}

+ 0 - 119
direct/src/plugin/p3dCert_wx.h

@@ -1,119 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert_wx.h
- * @author drose
- * @date 2009-09-11
- */
-
-#ifndef P3DCERT_WX_H
-#define P3DCERT_WX_H
-
-#include <wx/wx.h>
-
-#define OPENSSL_NO_KRB5
-#include <openssl/x509.h>
-#include <openssl/x509_vfy.h>
-#include <openssl/pem.h>
-
-#include <string>
-#include <iostream>
-#include <stdio.h>
-
-class ViewCertDialog;
-
-#ifndef STACK_OF
-  // At some point, presumably in 1.0.0, openssl went to the STACK_OF() macro
-  // system to typedef the contents of a stack.  Unfortunately, that new API
-  // is different.  We define some macros here here for backward compatiblity.
-  #define STACK_OF(type) STACK
-  #define sk_X509_push(stack, item) sk_push((stack), (char *)(item))
-  #define sk_X509_free(stack) sk_free(stack)
-  #define sk_X509_new(cmp) sk_new(cmp)
-#endif
-
-/**
- * This is the wxApp that drives this application.
- */
-class P3DCertApp : public wxApp {
-public:
-  virtual bool OnInit();
-  virtual void OnInitCmdLine(wxCmdLineParser &parser);
-  virtual bool OnCmdLineParsed(wxCmdLineParser &parser);
-
-private:
-  std::string _cert_filename;
-  std::string _cert_dir;
-};
-
-/**
- * This is the primary dialog of this application.
- *
- * This dialog is presented to the user when he/she clicks on the red
- * authorization button on the splash window.  It tells the user the status of
- * the application's signature, and invites the user to approve the signature
- * or cancel.
- */
-class AuthDialog : public wxDialog {
-public:
-  AuthDialog(const std::string &cert_filename, const std::string &cert_dir);
-  virtual ~AuthDialog();
-
-  void run_clicked(wxCommandEvent &event);
-  void view_cert_clicked(wxCommandEvent &event);
-  void cancel_clicked(wxCommandEvent &event);
-
-  void approve_cert();
-
-private:
-  void read_cert_file(const std::string &cert_filename);
-  void get_friendly_name();
-  void verify_cert();
-  int load_certificates_from_der_ram(X509_STORE *store,
-                                     const char *data, size_t data_size);
-
-  void layout();
-  void get_text(wxString &header, wxString &text);
-
-public:
-  ViewCertDialog *_view_cert_dialog;
-
-private:
-  // any class wishing to process wxWidgets events must use this macro
-  DECLARE_EVENT_TABLE()
-
-  std::string _cert_dir;
-  X509 *_cert;
-  STACK_OF(X509) *_stack;
-
-  wxString _friendly_name;
-  int _verify_result;
-};
-
-/**
- * This is the detailed view of the particular certificate.
- */
-class ViewCertDialog : public wxDialog {
-public:
-  ViewCertDialog(AuthDialog *auth_dialog, X509 *cert);
-  virtual ~ViewCertDialog();
-
-  void run_clicked(wxCommandEvent &event);
-  void cancel_clicked(wxCommandEvent &event);
-
-private:
-  void layout();
-
-private:
-  DECLARE_EVENT_TABLE()
-
-  AuthDialog *_auth_dialog;
-  X509 *_cert;
-};
-
-#endif

+ 0 - 213
direct/src/plugin/p3dConcreteSequence.cxx

@@ -1,213 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConcreteSequence.cxx
- * @author drose
- * @date 2009-06-30
- */
-
-#include "p3dConcreteSequence.h"
-#include "p3dSession.h"
-
-/**
- *
- */
-P3DConcreteSequence::
-P3DConcreteSequence() {
-}
-
-/**
- *
- */
-P3DConcreteSequence::
-~P3DConcreteSequence() {
-  Elements::iterator ei;
-  for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
-    P3D_OBJECT_DECREF(*ei);
-  }
-}
-
-/**
- * Returns true if this is actually an instance of a P3DConcreteSequence,
- * false otherwise.
- */
-bool P3DConcreteSequence::
-is_sequence_object() {
-  return true;
-}
-
-/**
- * Returns the fundamental type of this kind of object.
- */
-P3D_object_type P3DConcreteSequence::
-get_type() {
-  return P3D_OT_object;
-}
-
-/**
- * Returns the object value coerced to a boolean, if possible.
- */
-bool P3DConcreteSequence::
-get_bool() {
-  return !_elements.empty();
-}
-
-/**
- * Fills the indicated C++ string object with the value of this object coerced
- * to a string.
- */
-void P3DConcreteSequence::
-make_string(std::string &value) {
-  std::ostringstream strm;
-  strm << "[";
-  if (!_elements.empty()) {
-    strm << *_elements[0];
-    for (size_t i = 1; i < _elements.size(); ++i) {
-      strm << ", " << *_elements[i];
-    }
-  }
-  strm << "]";
-
-  value = strm.str();
-}
-
-/**
- * Returns the named property element in the object.  The return value is a
- * new-reference P3D_object, or NULL on error.
- */
-P3D_object *P3DConcreteSequence::
-get_property(const std::string &property) {
-  // We only understand integer "property" names.
-  char *endptr;
-  int index = strtoul(property.c_str(), &endptr, 10);
-  if (*endptr != '\0') {
-    return nullptr;
-  }
-
-  return get_element(index);
-}
-
-/**
- * Modifies (or deletes, if value is NULL) the named property element in the
- * object.  Returns true on success, false on failure.
- */
-bool P3DConcreteSequence::
-set_property(const std::string &property, P3D_object *value) {
-  // We only understand integer "property" names.
-  char *endptr;
-  int index = strtoul(property.c_str(), &endptr, 10);
-  if (*endptr != '\0') {
-    return false;
-  }
-
-  return set_element(index, value);
-}
-
-/**
- * If this object has a valid XML representation for the indicated session
- * (that hasn't already been implemented by the generic code in P3DSession),
- * this method will apply it to the indicated "value" element and return true.
- * Otherwise, this method will leave the element unchanged and return false.
- */
-bool P3DConcreteSequence::
-fill_xml(TiXmlElement *xvalue, P3DSession *session) {
-  xvalue->SetAttribute("type", "concrete_sequence");
-  Elements::const_iterator ei;
-  for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
-    xvalue->LinkEndChild(session->p3dobj_to_xml(*ei));
-  }
-
-  return true;
-}
-
-/**
- * Returns a pointer to the array of objects represented by this object.  Most
- * objects represent only themselves, but a P3DConcreteSequence represents its
- * list.
- */
-P3D_object **P3DConcreteSequence::
-get_object_array() {
-  if (_elements.empty()) {
-    return nullptr;
-  }
-  return &_elements[0];
-}
-
-/**
- * Returns the number of elements in the array returned by get_object_array().
- */
-int P3DConcreteSequence::
-get_object_array_size() {
-  return _elements.size();
-}
-
-/**
- * Returns the number of items in the sequence.
- */
-int P3DConcreteSequence::
-get_length() const {
-  return _elements.size();
-}
-
-/**
- * Returns the nth item in the sequence.  The return value is a new-reference
- * P3DObject object, or NULL on error.
- */
-P3D_object *P3DConcreteSequence::
-get_element(int n) const {
-  if (n >= 0 && n < (int)_elements.size()) {
-    P3D_OBJECT_INCREF(_elements[n]);
-    return _elements[n];
-  }
-
-  return nullptr;
-}
-
-/**
- * Modifies (or deletes, if value is NULL) the nth item in the sequence.
- * Returns true on success, false on failure.
- */
-bool P3DConcreteSequence::
-set_element(int n, P3D_object *value) {
-  if (value == nullptr) {
-    // Delete an element.
-    if (n < 0 || n >= (int)_elements.size()) {
-      // Invalid index.
-      return false;
-    }
-    P3D_OBJECT_DECREF(_elements[n]);
-    _elements.erase(_elements.begin() + n);
-    return true;
-
-  } else if (n == _elements.size()) {
-    // Append an element.
-    append(value);
-    return true;
-
-  } else {
-    // Replace an element.
-    if (n < 0 || n >= (int)_elements.size()) {
-      // Invalid index.
-      return false;
-    }
-
-    P3D_OBJECT_INCREF(value);
-    P3D_OBJECT_DECREF(_elements[n]);
-    _elements[n] = value;
-    return true;
-  }
-}
-
-/**
- * Adds a new element to the end of the list.
- */
-void P3DConcreteSequence::
-append(P3D_object *value) {
-  _elements.push_back(value);
-  P3D_OBJECT_INCREF(value);
-}

+ 0 - 56
direct/src/plugin/p3dConcreteSequence.h

@@ -1,56 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConcreteSequence.h
- * @author drose
- * @date 2009-06-30
- */
-
-#ifndef P3DCONCRETESEQUENCE_H
-#define P3DCONCRETESEQUENCE_H
-
-#include "p3d_plugin_common.h"
-#include "p3dObject.h"
-
-/**
- * An object type that contains a sequence of objects, which is passed by
- * value between Python and JavaScript, so may be more optimal for small lists
- * that are accessed repeatedly.
- *
- * This is converted from a Python "tuple" object.
- */
-class P3DConcreteSequence : public P3DObject {
-public:
-  P3DConcreteSequence();
-  virtual ~P3DConcreteSequence();
-
-  virtual bool is_sequence_object();
-
-  virtual P3D_object_type get_type();
-  virtual bool get_bool();
-
-  virtual void make_string(std::string &value);
-
-  virtual P3D_object *get_property(const std::string &property);
-  virtual bool set_property(const std::string &property, P3D_object *value);
-
-  virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session);
-  virtual P3D_object **get_object_array();
-  virtual int get_object_array_size();
-
-  int get_length() const;
-  P3D_object *get_element(int n) const;
-  bool set_element(int n, P3D_object *value);
-  void append(P3D_object *value);
-
-private:
-  typedef std::vector<P3D_object *> Elements;
-  Elements _elements;
-};
-
-#endif

+ 0 - 178
direct/src/plugin/p3dConcreteStruct.cxx

@@ -1,178 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConcreteStruct.cxx
- * @author drose
- * @date 2009-07-14
- */
-
-#include "p3dConcreteStruct.h"
-
-using std::string;
-
-/**
- *
- */
-P3DConcreteStruct::
-P3DConcreteStruct() {
-}
-
-/**
- *
- */
-P3DConcreteStruct::
-~P3DConcreteStruct() {
-  Elements::iterator ei;
-  for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
-    P3D_OBJECT_DECREF((*ei).second);
-  }
-}
-
-/**
- * Returns the fundamental type of this kind of object.
- */
-P3D_object_type P3DConcreteStruct::
-get_type() {
-  return P3D_OT_object;
-}
-
-/**
- * Returns the object value coerced to a boolean, if possible.
- */
-bool P3DConcreteStruct::
-get_bool() {
-  return !_elements.empty();
-}
-
-/**
- * Fills the indicated C++ string object with the value of this object coerced
- * to a string.
- */
-void P3DConcreteStruct::
-make_string(string &value) {
-  std::ostringstream strm;
-  strm << "{";
-  if (!_elements.empty()) {
-    Elements::iterator ei;
-    ei = _elements.begin();
-    strm << (*ei).first << ": " << *(*ei).second;
-    ++ei;
-    while (ei != _elements.end()) {
-      strm << ", " << (*ei).first << ": " << *(*ei).second;
-      ++ei;
-    }
-  }
-  strm << "}";
-
-  value = strm.str();
-}
-
-/**
- * Returns the named property element in the object.  The return value is a
- * new-reference P3D_object, or NULL on error.
- */
-P3D_object *P3DConcreteStruct::
-get_property(const string &property) {
-  Elements::const_iterator ei = _elements.find(property);
-  if (ei != _elements.end()) {
-    P3D_OBJECT_INCREF((*ei).second);
-    return (*ei).second;
-  }
-
-  return nullptr;
-}
-
-/**
- * Modifies (or deletes, if value is NULL) the named property element in the
- * object.  Returns true on success, false on failure.
- */
-bool P3DConcreteStruct::
-set_property(const string &property, P3D_object *value) {
-  if (value == nullptr) {
-    // Delete an element.
-    Elements::iterator ei = _elements.find(property);
-    if (ei == _elements.end()) {
-      // Invalid property.
-      return false;
-    }
-    P3D_OBJECT_DECREF((*ei).second);
-    _elements.erase(ei);
-    return true;
-
-  } else {
-    // Replace or insert an element.
-    P3D_OBJECT_INCREF(value);
-    std::pair<Elements::iterator, bool> result = _elements.insert(Elements::value_type(property, value));
-    if (!result.second) {
-      // Replacing an element.
-      Elements::iterator ei = result.first;
-      P3D_OBJECT_DECREF((*ei).second);
-      (*ei).second = value;
-    }
-    return true;
-  }
-}
-
-/**
- * Returns true if the named method exists on this object, false otherwise.
- */
-bool P3DConcreteStruct::
-has_method(const string &method_name) {
-  if (method_name == "toString") {
-    return true;
-  }
-
-  return false;
-}
-
-/**
- * Invokes the named method on the object, passing the indicated parameters.
- * If the method name is empty, invokes the object itself.
- *
- * If needs_response is true, the return value is a new-reference P3D_object
- * on success, or NULL on failure.  If needs_response is false, the return
- * value is always NULL, and there is no way to determine success or failure.
- */
-P3D_object *P3DConcreteStruct::
-call(const string &method_name, bool needs_response,
-     P3D_object *params[], int num_params) {
-  P3D_object *result = nullptr;
-
-  if (method_name == "toString") {
-    string value;
-    make_string(value);
-    result = P3D_new_string_object(value.data(), value.length());
-  }
-
-  if (result != nullptr && !needs_response) {
-    P3D_OBJECT_DECREF(result);
-    result = nullptr;
-  }
-
-  return result;
-}
-
-
-/**
- * If this object has a valid XML representation for the indicated session
- * (that hasn't already been implemented by the generic code in P3DSession),
- * this method will apply it to the indicated "value" element and return true.
- * Otherwise, this method will leave the element unchanged and return false.
- */
-bool P3DConcreteStruct::
-fill_xml(TiXmlElement *xvalue, P3DSession *session) {
-  xvalue->SetAttribute("type", "concrete_struct");
-  Elements::const_iterator ei;
-  for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
-    TiXmlElement *xitem = session->p3dobj_to_xml((*ei).second);
-    xitem->SetAttribute("key", (*ei).first);
-    xvalue->LinkEndChild(xitem);
-  }
-
-  return true;
-}

+ 0 - 51
direct/src/plugin/p3dConcreteStruct.h

@@ -1,51 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConcreteStruct.h
- * @author drose
- * @date 2009-07-14
- */
-
-#ifndef P3DCONCRETESTRUCT_H
-#define P3DCONCRETESTRUCT_H
-
-#include "p3d_plugin_common.h"
-#include "p3dObject.h"
-
-/**
- * A simple object that contains a standard mapping of string -> element.  It
- * is passed by value between Python and Javascript, so it may be more optimal
- * for relatively small objects.
- *
- * Methods are not supported, other than built-in methods like toString().
- */
-class P3DConcreteStruct : public P3DObject {
-public:
-  P3DConcreteStruct();
-  virtual ~P3DConcreteStruct();
-
-  virtual P3D_object_type get_type();
-  virtual bool get_bool();
-
-  virtual void make_string(std::string &value);
-
-  virtual P3D_object *get_property(const std::string &property);
-  virtual bool set_property(const std::string &property, P3D_object *value);
-
-  virtual bool has_method(const std::string &method_name);
-  virtual P3D_object *call(const std::string &method_name, bool needs_response,
-                           P3D_object *params[], int num_params);
-
-  virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session);
-
-private:
-  typedef std::map<std::string, P3D_object *> Elements;
-  Elements _elements;
-};
-
-#endif

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

@@ -1,12 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConditionVar.I
- * @author drose
- * @date 2009-07-02
- */

+ 0 - 171
direct/src/plugin/p3dConditionVar.cxx

@@ -1,171 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConditionVar.cxx
- * @author drose
- * @date 2009-07-02
- */
-
-#include "p3dConditionVar.h"
-
-#ifndef _WIN32
-#include <sys/time.h>
-#include <math.h>
-#endif
-#include <errno.h>
-
-/**
- *
- */
-P3DConditionVar::
-P3DConditionVar() {
-#ifdef _WIN32
-  InitializeCriticalSection(&_lock);
-
-  // Create an auto-reset event.
-  _event_signal = CreateEvent(nullptr, false, false, nullptr);
-
-#else  // _WIN32
-  pthread_mutexattr_t attr;
-  pthread_mutexattr_init(&attr);
-  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
-  int result = pthread_mutex_init(&_lock, &attr);
-  pthread_mutexattr_destroy(&attr);
-  assert(result == 0);
-
-  result = pthread_cond_init(&_cvar, nullptr);
-  assert(result == 0);
-
-#endif  // _WIN32
-}
-
-/**
- *
- */
-P3DConditionVar::
-~P3DConditionVar() {
-#ifdef _WIN32
-  DeleteCriticalSection(&_lock);
-  CloseHandle(_event_signal);
-
-#else  // _WIN32
-  int result = pthread_mutex_destroy(&_lock);
-  assert(result == 0);
-
-  result = pthread_cond_destroy(&_cvar);
-  assert(result == 0);
-
-#endif  // _WIN32
-}
-
-/**
- * Acquires the internal lock.  The lock should be held during any calls to
- * wait() or notify().
- */
-void P3DConditionVar::
-acquire() {
-#ifdef _WIN32
-  EnterCriticalSection(&_lock);
-
-#else  // _WIN32
-  int result = pthread_mutex_lock(&_lock);
-  assert(result == 0);
-
-#endif  // _WIN32
-}
-
-/**
- * Requires the lock to be held on entry.  Releases the lock, waits for
- * another thread to call notify(), then reacquires the lock on exit.
- */
-void P3DConditionVar::
-wait() {
-#ifdef _WIN32
-  LeaveCriticalSection(&_lock);
-
-  DWORD result = WaitForSingleObject(_event_signal, INFINITE);
-  assert(result == WAIT_OBJECT_0);
-
-  EnterCriticalSection(&_lock);
-
-#else  // _WIN32
-  int result = pthread_cond_wait(&_cvar, &_lock);
-  assert(result == 0);
-
-#endif  // _WIN32
-}
-
-/**
- * As above, but waits no longer than timeout seconds before returning.
- */
-void P3DConditionVar::
-wait(double timeout) {
-#ifdef _WIN32
-  LeaveCriticalSection(&_lock);
-
-  DWORD result = WaitForSingleObject(_event_signal, (DWORD)(timeout * 1000.0));
-  assert(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT);
-
-  EnterCriticalSection(&_lock);
-#else  // _WIN32
-
-  struct timeval now;
-  gettimeofday(&now, nullptr);
-
-  // Convert from timeval to timespec
-  struct timespec ts;
-  ts.tv_sec  = now.tv_sec;
-  ts.tv_nsec = now.tv_usec * 1000;
-
-  int seconds = (int)floor(timeout);
-  ts.tv_sec += seconds;
-  ts.tv_nsec += (int)((timeout - seconds) * 1000000.0);
-  if (ts.tv_nsec > 1000000) {
-    ts.tv_nsec -= 1000000;
-    ++ts.tv_sec;
-  }
-
-  int result = pthread_cond_timedwait(&_cvar, &_lock, &ts);
-  if (result != 0 && result != ETIMEDOUT) {
-    errno = result;
-    perror("pthread_cond_timedwait");
-  }
-
-#endif  // _WIN32
-}
-
-/**
- * Waits a single thread blocked on wait(), if any.  If no threads are
- * waiting, the event is lost.  The lock should be held during this call.
- */
-void P3DConditionVar::
-notify() {
-#ifdef _WIN32
-  SetEvent(_event_signal);
-
-#else  // _WIN32
-  int result = pthread_cond_signal(&_cvar);
-  assert(result == 0);
-
-#endif  // _WIN32
-}
-
-/**
- * Releases the internal lock.
- */
-void P3DConditionVar::
-release() {
-#ifdef _WIN32
-  LeaveCriticalSection(&_lock);
-
-#else  // _WIN32
-  int result = pthread_mutex_unlock(&_lock);
-  assert(result == 0);
-
-#endif  // _WIN32
-}

+ 0 - 48
direct/src/plugin/p3dConditionVar.h

@@ -1,48 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConditionVar.h
- * @author drose
- * @date 2009-07-02
- */
-
-#ifndef P3DCONDITIONVAR_H
-#define P3DCONDITIONVAR_H
-
-#include "p3d_plugin_common.h"
-
-/**
- * A simple condition-variable like object.  It doesn't support the full
- * condition-var semantics, but it works well enough with one waiter and one
- * signaller.
- */
-class P3DConditionVar {
-public:
-  P3DConditionVar();
-  ~P3DConditionVar();
-
-  void acquire();
-  void wait();
-  void wait(double timeout);
-  void notify();
-  void release();
-
-private:
-#ifdef _WIN32
-  CRITICAL_SECTION _lock;
-  HANDLE _event_signal;
-
-#else  // _WIN32
-  pthread_mutex_t _lock;
-  pthread_cond_t _cvar;
-#endif  // _WIN32
-};
-
-#include "p3dConditionVar.I"
-
-#endif

+ 0 - 126
direct/src/plugin/p3dDownload.I

@@ -1,126 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dDownload.I
- * @author drose
- * @date 2009-06-11
- */
-
-/**
- * Returns the URL that we are querying.
- */
-const std::string &P3DDownload::
-get_url() const {
-  return _url;
-}
-
-/**
- * Specifies the particular P3DInstance that is responsible for downloading
- * this object.
- */
-inline void P3DDownload::
-set_instance(P3DInstance *instance) {
-  _instance = instance;
-}
-
-/**
- * Returns the particular P3DInstance that is responsible for downloading this
- * object.
- */
-inline P3DInstance *P3DDownload::
-get_instance() const {
-  return _instance;
-}
-
-/**
- * Returns an indication of the progress through the download file, 0.0 to
- * 1.0.  Returns 1.0 if the size of the file is not known.
- */
-inline double P3DDownload::
-get_download_progress() const {
-  if (_total_expected_data == 0) {
-    return 1.0;
-  }
-
-  return (double)_total_data / (double)_total_expected_data;
-}
-
-/**
- * Returns true if the download progress is known, or false if it is unknown
- * because the server hasn't told us the total size it will be feeding us.  If
- * this is false, get_download_progress() will generally always return 1.0;
- * use get_total_bytes() to measure progress in this case.
- */
-inline bool P3DDownload::
-is_download_progress_known() const {
-  return _progress_known;
-}
-
-/**
- * Returns the total number of bytes downloaded so far.
- */
-inline size_t P3DDownload::
-get_total_data() const {
-  return _total_data;
-}
-
-/**
- * Sets the total number of bytes expected to be downloaded.  This is used to
- * compute the progress.  Normally, this can be set from the download server,
- * but there may be cases when the download server doesn't accurately report
- * it.
- */
-inline void P3DDownload::
-set_total_expected_data(size_t expected_data) {
-  _total_expected_data = expected_data;
-  _progress_known = true;
-}
-
-/**
- * Returns true if the download has finished, either successfully or
- * otherwise, or false if it is still in progress.
- */
-inline bool P3DDownload::
-get_download_finished() const {
-  return _status != P3D_RC_in_progress;
-}
-
-/**
- * Returns true if the download has finished successfully, or false if it is
- * still in progress or if it has failed.
- */
-inline bool P3DDownload::
-get_download_success() const {
-  return _status == P3D_RC_done;
-}
-
-/**
- * Returns true if the download has failed because the instance is about to be
- * shut down, or false if it hasn't failed, or failed for some other reason.
- */
-inline bool P3DDownload::
-get_download_terminated() const {
-  return _status == P3D_RC_shutdown;
-}
-
-/**
- * Called only by P3DInstance to set a unique ID for this particular download
- * object.
- */
-inline void P3DDownload::
-set_download_id(int download_id) {
-  _download_id = download_id;
-}
-
-/**
- * Returns the unique ID set by the P3DInstance.
- */
-inline int P3DDownload::
-get_download_id() const {
-  return _download_id;
-}

+ 0 - 192
direct/src/plugin/p3dDownload.cxx

@@ -1,192 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dDownload.cxx
- * @author drose
- * @date 2009-06-11
- */
-
-#include "p3dDownload.h"
-
-/**
- *
- */
-P3DDownload::
-P3DDownload() {
-  _status = P3D_RC_in_progress;
-  _http_status_code = 0;
-  _total_data = 0;
-  _total_expected_data = 0;
-  _last_reported_time = 0;
-  _progress_known = false;
-
-  _canceled = false;
-  _download_id = 0;
-  _instance = nullptr;
-}
-
-/**
- *
- */
-P3DDownload::
-P3DDownload(const P3DDownload &copy) :
-  _url(copy._url),
-  _total_expected_data(copy._total_expected_data),
-  _progress_known(copy._progress_known)
-{
-  _status = P3D_RC_in_progress;
-  _http_status_code = 0;
-  _total_data = 0;
-  _last_reported_time = 0;
-
-  _canceled = false;
-  _download_id = 0;
-  _instance = nullptr;
-}
-
-/**
- *
- */
-P3DDownload::
-~P3DDownload() {
-}
-
-/**
- * Supplies the source URL for the download.
- */
-void P3DDownload::
-set_url(const std::string &url) {
-  _url = url;
-}
-
-/**
- * Cancels a running download.  download_finished() will not be called, but
- * the P3DDownload object itself will eventually be deleted by its owning
- * P3DInstance.
- */
-void P3DDownload::
-cancel() {
-  _canceled = true;
-  if (_status == P3D_RC_in_progress) {
-    _status = P3D_RC_generic_error;
-  }
-}
-
-/**
- * Resets the download to its initial state, for re-trying the same download.
- */
-void P3DDownload::
-clear() {
-  _status = P3D_RC_in_progress;
-  _http_status_code = 0;
-  _total_data = 0;
-  _last_reported_time = 0;
-
-  _canceled = false;
-  _download_id = 0;
-}
-
-/**
- * Called by P3DInstance as more data arrives from the host.  Returns true on
- * success, false if the download should be aborted.
- */
-bool P3DDownload::
-feed_url_stream(P3D_result_code result_code,
-                int http_status_code,
-                size_t total_expected_data,
-                const unsigned char *this_data,
-                size_t this_data_size) {
-  if (_canceled) {
-    return false;
-  }
-
-  _status = result_code;
-  _http_status_code = http_status_code;
-
-  if (this_data_size != 0) {
-    if (!receive_data(this_data, this_data_size)) {
-      // Failure writing to disk or some such.
-      _status = P3D_RC_generic_error;
-      download_finished(false);
-      return false;
-    }
-
-    _total_data += this_data_size;
-  }
-
-  total_expected_data = std::max(total_expected_data, _total_data);
-  if (total_expected_data > _total_expected_data) {
-    // If the expected data grows during the download, we don't really know
-    // how much we're getting.
-    _progress_known = false;
-    _total_expected_data = total_expected_data;
-  }
-  if (_total_expected_data > 0 &&
-      (double)_total_data / (double)_total_expected_data < 0.9) {
-    // But if we're not close to our target yet, let's say we do know (at
-    // least until we get there and the target moves again).
-    _progress_known = true;
-  }
-
-  download_progress();
-
-  if (_status != P3D_RC_in_progress) {
-    // We're done.
-    if (get_download_success()) {
-      _progress_known = true;
-    }
-    download_finished(get_download_success());
-  }
-
-  return true;
-}
-
-
-/**
- * Called as new data is downloaded.  Returns true on success, false on
- * failure.
- */
-bool P3DDownload::
-receive_data(const unsigned char *this_data, size_t this_data_size) {
-  return false;
-}
-
-/**
- * Intended to be overloaded to generate an occasional callback as new data
- * comes in.
- */
-void P3DDownload::
-download_progress() {
-  time_t now = time(nullptr);
-  if (now - _last_reported_time > 10) {
-    _last_reported_time = now;
-    nout << "Downloading " << get_url() << ": ";
-    if (_progress_known) {
-      nout << int(get_download_progress() * 1000.0) / 10.0 << "%";
-    } else {
-      nout << int((double)get_total_data() / 104857.6) / 10.0 << "M";
-    }
-    nout << ", " << this << "\n";
-  }
-}
-
-/**
- * Intended to be overloaded to generate a callback when the download
- * finishes, either successfully or otherwise.  The bool parameter is true if
- * the download was successful.
- */
-void P3DDownload::
-download_finished(bool success) {
-  nout << "Downloaded " << get_url() << ": ";
-  if (_progress_known) {
-    nout << int(get_download_progress() * 1000.0) / 10.0 << "%";
-  } else {
-    nout << int((double)get_total_data() / 104857.6) / 10.0 << "M";
-  }
-  nout << ", " << this << ", success = " << success << "\n";
-}

+ 0 - 87
direct/src/plugin/p3dDownload.h

@@ -1,87 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dDownload.h
- * @author drose
- * @date 2009-06-11
- */
-
-#ifndef P3DDOWNLOAD_H
-#define P3DDOWNLOAD_H
-
-#include "p3d_plugin_common.h"
-#include "p3dReferenceCount.h"
-class P3DInstance;
-
-#include <time.h>
-
-/**
- * This represents a request to download a single file from a URL, with no
- * particular destination.  It is intended to be used as an abstract base
- * class; to use it, subclass it and redefine the appropriate callback
- * methods.
- */
-class P3DDownload : public P3DReferenceCount {
-public:
-  P3DDownload();
-  P3DDownload(const P3DDownload &copy);
-  virtual ~P3DDownload();
-
-  void set_url(const std::string &url);
-  inline const std::string &get_url() const;
-
-  inline void set_instance(P3DInstance *instance);
-  inline P3DInstance *get_instance() const;
-
-  inline double get_download_progress() const;
-  inline bool is_download_progress_known() const;
-  inline bool get_download_finished() const;
-  inline bool get_download_success() const;
-  inline bool get_download_terminated() const;
-  inline size_t get_total_data() const;
-  inline void set_total_expected_data(size_t expected_data);
-
-  void cancel();
-  void clear();
-
-public:
-  // These are intended to be called only by P3DInstance.
-  inline void set_download_id(int download_id);
-  inline int get_download_id() const;
-
-  bool feed_url_stream(P3D_result_code result_code,
-                       int http_status_code,
-                       size_t total_expected_data,
-                       const unsigned char *this_data,
-                       size_t this_data_size);
-
-protected:
-  virtual bool receive_data(const unsigned char *this_data,
-                            size_t this_data_size);
-  virtual void download_progress();
-  virtual void download_finished(bool success);
-
-protected:
-  P3D_result_code _status;
-  int _http_status_code;
-
-  size_t _total_data;
-  size_t _total_expected_data;
-  time_t _last_reported_time;
-  bool _progress_known;
-
-private:
-  bool _canceled;
-  int _download_id;
-  std::string _url;
-  P3DInstance *_instance;
-};
-
-#include "p3dDownload.I"
-
-#endif

+ 0 - 20
direct/src/plugin/p3dFileDownload.I

@@ -1,20 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileDownload.I
- * @author drose
- * @date 2009-06-11
- */
-
-/**
- * Returns the filename that we are downloading into.
- */
-const std::string &P3DFileDownload::
-get_filename() const {
-  return _filename;
-}

+ 0 - 107
direct/src/plugin/p3dFileDownload.cxx

@@ -1,107 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileDownload.cxx
- * @author drose
- * @date 2009-06-11
- */
-
-#include "p3dFileDownload.h"
-#include "p3dInstanceManager.h"
-#include "mkdir_complete.h"
-#include "wstring_encode.h"
-
-/**
- *
- */
-P3DFileDownload::
-P3DFileDownload() {
-}
-
-/**
- *
- */
-P3DFileDownload::
-P3DFileDownload(const P3DFileDownload &copy) :
-  P3DDownload(copy)
-{
-  // We don't copy the filename.  You have to copy it yourself.
-}
-
-/**
- * Supplies the target local filename for the download.  Returns true on
- * success, false on failure.
- */
-bool P3DFileDownload::
-set_filename(const std::string &filename) {
-  _filename = filename;
-
-  return open_file();
-}
-
-/**
- * Opens the local file for receiving the download.  Returns true on success,
- * false on failure.
- */
-bool P3DFileDownload::
-open_file() {
-  if (!mkfile_complete(_filename, nout)) {
-    nout << "Failed to create " << _filename << "\n";
-    return false;
-  }
-
-  _file.clear();
-#ifdef _WIN32
-  std::wstring filename_w;
-  if (string_to_wstring(filename_w, _filename)) {
-    _file.open(filename_w.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
-  }
-#else // _WIN32
-  _file.open(_filename.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
-#endif  // _WIN32
-  if (!_file) {
-    nout << "Failed to open " << _filename << " in write mode\n";
-    return false;
-  }
-
-  return true;
-}
-
-/**
- * Closes the local file.
- */
-void P3DFileDownload::
-close_file() {
-  _file.close();
-}
-
-/**
- * Called as new data is downloaded.  Returns true on success, false on
- * failure.
- */
-bool P3DFileDownload::
-receive_data(const unsigned char *this_data, size_t this_data_size) {
-  _file.write((const char *)this_data, this_data_size);
-
-  if (!_file) {
-    return false;
-  }
-
-  return true;
-}
-
-/**
- * Intended to be overloaded to generate a callback when the download
- * finishes, either successfully or otherwise.  The bool parameter is true if
- * the download was successful.
- */
-void P3DFileDownload::
-download_finished(bool success) {
-  P3DDownload::download_finished(success);
-  _file.close();
-}

+ 0 - 50
direct/src/plugin/p3dFileDownload.h

@@ -1,50 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileDownload.h
- * @author drose
- * @date 2009-06-11
- */
-
-#ifndef P3DFILEDOWNLOAD_H
-#define P3DFILEDOWNLOAD_H
-
-#include "p3d_plugin_common.h"
-#include "p3dDownload.h"
-
-#include <fstream>
-
-/**
- * This is a specialization on P3DDownload that specifically writes a disk
- * file.
- */
-class P3DFileDownload : public P3DDownload {
-public:
-  P3DFileDownload();
-  P3DFileDownload(const P3DFileDownload &copy);
-
-  bool set_filename(const std::string &filename);
-  inline const std::string &get_filename() const;
-
-protected:
-  virtual bool open_file();
-  void close_file();
-  virtual bool receive_data(const unsigned char *this_data,
-                            size_t this_data_size);
-  virtual void download_finished(bool success);
-
-protected:
-  ofstream _file;
-
-private:
-  std::string _filename;
-};
-
-#include "p3dFileDownload.I"
-
-#endif

+ 0 - 63
direct/src/plugin/p3dFileParams.I

@@ -1,63 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileParams.I
- * @author drose
- * @date 2009-06-23
- */
-
-/**
- * Returns the filename that was passed to set_p3d_filename().
- */
-inline const std::string &P3DFileParams::
-get_p3d_filename() const {
-  return _p3d_filename;
-}
-
-/**
- * Returns the p3d file offset, the location in the file where the p3d data
- * starts.
- */
-inline int P3DFileParams::
-get_p3d_offset() const {
-  return _p3d_offset;
-}
-
-/**
- * Returns the string that was passed to set_p3d_url().
- */
-inline const std::string &P3DFileParams::
-get_p3d_url() const {
-  return _p3d_url;
-}
-
-/**
- * Returns the number of tokens in the params.
- */
-inline int P3DFileParams::
-get_num_tokens() const {
-  return _tokens.size();
-}
-
-/**
- * Returns the keyword of the nth token.
- */
-inline const std::string &P3DFileParams::
-get_token_keyword(int n) const {
-  assert(n >= 0 && n < (int)_tokens.size());
-  return _tokens[n]._keyword;
-}
-
-/**
- * Returns the value of the nth token.
- */
-inline const std::string &P3DFileParams::
-get_token_value(int n) const {
-  assert(n >= 0 && n < (int)_tokens.size());
-  return _tokens[n]._value;
-}

+ 0 - 193
direct/src/plugin/p3dFileParams.cxx

@@ -1,193 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileParams.cxx
- * @author drose
- * @date 2009-06-23
- */
-
-#include "p3dFileParams.h"
-#include <ctype.h>
-
-using std::string;
-
-/**
- *
- */
-P3DFileParams::
-P3DFileParams() : _p3d_offset(0) {
-}
-
-/**
- *
- */
-P3DFileParams::
-P3DFileParams(const P3DFileParams &copy) :
-  _p3d_filename(copy._p3d_filename),
-  _p3d_offset(copy._p3d_offset),
-  _p3d_url(copy._p3d_url),
-  _tokens(copy._tokens),
-  _args(copy._args)
-{
-}
-
-/**
- *
- */
-void P3DFileParams::
-operator = (const P3DFileParams &other) {
-  _p3d_filename = other._p3d_filename;
-  _p3d_offset = other._p3d_offset;
-  _p3d_url = other._p3d_url;
-  _tokens = other._tokens;
-  _args = other._args;
-}
-
-/**
- * Specifies the file that contains the instance data.
- */
-void P3DFileParams::
-set_p3d_filename(const string &p3d_filename) {
-  _p3d_filename = p3d_filename;
-}
-
-/**
- * Specifies the location in the file where the p3d file data starts.
- */
-void P3DFileParams::
-set_p3d_offset(const int &p3d_offset) {
-  _p3d_offset = p3d_offset;
-}
-
-/**
- * Specifies the original URL that hosted the p3d file, if any.  This is for
- * documentation purposes only; it is communicated to the child Panda3D
- * process.
- */
-void P3DFileParams::
-set_p3d_url(const string &p3d_url) {
-  _p3d_url = p3d_url;
-}
-
-/**
- * Replaces all the tokens associated with the instance.
- */
-void P3DFileParams::
-set_tokens(const P3D_token tokens[], size_t num_tokens) {
-  _tokens.clear();
-
-  for (size_t i = 0; i < num_tokens; ++i) {
-    set_token(tokens[i]._keyword, tokens[i]._value);
-  }
-}
-
-/**
- * Sets an individual token value.
- */
-void P3DFileParams::
-set_token(const char *keyword, const char *value) {
-  Token token;
-  if (keyword != nullptr) {
-    // Make the token lowercase, since HTML is case-insensitive but we're not.
-    for (const char *p = keyword; *p; ++p) {
-      token._keyword += tolower(*p);
-    }
-  }
-  if (value != nullptr) {
-    token._value = value;
-  }
-  _tokens.push_back(token);
-}
-
-/**
- * Specifies the command-line arguments associated with the instance.
- */
-void P3DFileParams::
-set_args(int argc, const char *argv[]) {
-  _args.clear();
-
-  for (int i = 0; i < argc; ++i) {
-    const char *arg = argv[i];
-    if (arg == nullptr) {
-      arg = "";
-    }
-    _args.push_back(arg);
-  }
-}
-
-/**
- * Returns the value associated with the first appearance of the named token,
- * or empty string if the token does not appear.
- */
-string P3DFileParams::
-lookup_token(const string &keyword) const {
-  Tokens::const_iterator ti;
-  for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
-    if ((*ti)._keyword == keyword) {
-      return (*ti)._value;
-    }
-  }
-
-  return string();
-}
-
-/**
- * Returns the integer value associated with the first appearance of the named
- * token, or zero if the token does not appear or is not an integer.
- */
-int P3DFileParams::
-lookup_token_int(const string &keyword) const {
-  string value = lookup_token(keyword);
-  return atoi(value.c_str());
-}
-
-/**
- * Returns true if the named token appears in the list, false otherwise.
- */
-bool P3DFileParams::
-has_token(const string &keyword) const {
-  Tokens::const_iterator ti;
-  for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
-    if ((*ti)._keyword == keyword) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-/**
- * Returns a newly-allocated XML structure that corresponds to the file
- * parameter data within this instance.
- */
-TiXmlElement *P3DFileParams::
-make_xml() {
-  TiXmlElement *xfparams = new TiXmlElement("fparams");
-
-  xfparams->SetAttribute("p3d_filename", _p3d_filename);
-  xfparams->SetAttribute("p3d_offset", _p3d_offset);
-  xfparams->SetAttribute("p3d_url", _p3d_url);
-
-  Tokens::const_iterator ti;
-  for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
-    const Token &token = (*ti);
-    TiXmlElement *xtoken = new TiXmlElement("token");
-    xtoken->SetAttribute("keyword", token._keyword);
-    xtoken->SetAttribute("value", token._value);
-    xfparams->LinkEndChild(xtoken);
-  }
-
-  Args::const_iterator ai;
-  for (ai = _args.begin(); ai != _args.end(); ++ai) {
-    TiXmlElement *xarg = new TiXmlElement("arg");
-    xarg->SetAttribute("value", (*ai));
-    xfparams->LinkEndChild(xarg);
-  }
-
-  return xfparams;
-}

+ 0 - 68
direct/src/plugin/p3dFileParams.h

@@ -1,68 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileParams.h
- * @author drose
- * @date 2009-06-23
- */
-
-#ifndef P3DFILEPARAMS_H
-#define P3DFILEPARAMS_H
-
-#include "p3d_plugin_common.h"
-#include "get_tinyxml.h"
-#include <vector>
-
-/**
- * Encapsulates the file parameters: the p3d_filename, and extra tokens.
- */
-class P3DFileParams {
-public:
-  P3DFileParams();
-  P3DFileParams(const P3DFileParams &copy);
-  void operator = (const P3DFileParams &other);
-
-  void set_p3d_filename(const std::string &p3d_filename);
-  void set_p3d_offset(const int &p3d_offset);
-  void set_p3d_url(const std::string &p3d_url);
-  void set_tokens(const P3D_token tokens[], size_t num_tokens);
-  void set_token(const char *keyword, const char *value);
-  void set_args(int argc, const char *argv[]);
-
-  inline const std::string &get_p3d_filename() const;
-  inline int get_p3d_offset() const;
-  inline const std::string &get_p3d_url() const;
-  std::string lookup_token(const std::string &keyword) const;
-  int lookup_token_int(const std::string &keyword) const;
-  bool has_token(const std::string &keyword) const;
-
-  inline int get_num_tokens() const;
-  inline const std::string &get_token_keyword(int n) const;
-  inline const std::string &get_token_value(int n) const;
-
-  TiXmlElement *make_xml();
-
-private:
-  class Token {
-  public:
-    std::string _keyword;
-    std::string _value;
-  };
-  typedef std::vector<Token> Tokens;
-  typedef std::vector<std::string> Args;
-
-  std::string _p3d_filename;
-  int _p3d_offset;
-  std::string _p3d_url;
-  Tokens _tokens;
-  Args _args;
-};
-
-#include "p3dFileParams.I"
-
-#endif

+ 0 - 74
direct/src/plugin/p3dFloatObject.cxx

@@ -1,74 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFloatObject.cxx
- * @author drose
- * @date 2009-06-30
- */
-
-#include "p3dFloatObject.h"
-
-/**
- *
- */
-P3DFloatObject::
-P3DFloatObject(double value) : _value(value) {
-}
-
-/**
- *
- */
-P3DFloatObject::
-P3DFloatObject(const P3DFloatObject &copy) :
-  P3DObject(copy),
-  _value(copy._value)
-{
-}
-
-/**
- * Returns the fundamental type of this kind of object.
- */
-P3D_object_type P3DFloatObject::
-get_type() {
-  return P3D_OT_float;
-}
-
-/**
- * Returns the object value coerced to a boolean, if possible.
- */
-bool P3DFloatObject::
-get_bool() {
-  return (_value != 0.0);
-}
-
-/**
- * Returns the object value coerced to an integer, if possible.
- */
-int P3DFloatObject::
-get_int() {
-  return (int)_value;
-}
-
-/**
- * Returns the object value coerced to a floating-point value, if possible.
- */
-double P3DFloatObject::
-get_float() {
-  return _value;
-}
-
-/**
- * Fills the indicated C++ string object with the value of this object coerced
- * to a string.
- */
-void P3DFloatObject::
-make_string(std::string &value) {
-  std::ostringstream strm;
-  strm << _value;
-  value = strm.str();
-}

+ 0 - 39
direct/src/plugin/p3dFloatObject.h

@@ -1,39 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFloatObject.h
- * @author drose
- * @date 2009-06-30
- */
-
-#ifndef P3DFLOATOBJECT_H
-#define P3DFLOATOBJECT_H
-
-#include "p3d_plugin_common.h"
-#include "p3dObject.h"
-
-/**
- * An object type that contains a floating-point value.
- */
-class P3DFloatObject : public P3DObject {
-public:
-  P3DFloatObject(double value);
-  P3DFloatObject(const P3DFloatObject &copy);
-
-public:
-  virtual P3D_object_type get_type();
-  virtual bool get_bool();
-  virtual int get_int();
-  virtual double get_float();
-  virtual void make_string(std::string &value);
-
-private:
-  double _value;
-};
-
-#endif

+ 0 - 106
direct/src/plugin/p3dHost.I

@@ -1,106 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dHost.I
- * @author drose
- * @date 2009-08-21
- */
-
-/**
- * Returns true if the host_dir has already been set, false if not.  If this
- * returns true it is safe to call get_host_dir().
- */
-inline bool P3DHost::
-has_host_dir() const {
-  return (!_host_dir.empty());
-}
-
-/**
- * Returns the local directory into which files downloaded from this host will
- * be installed.  It may not be safe to call this before the host has fully
- * bootstrapped; if there is some danger of calling this early in the
- * initialization process, you should check has_host_dir() first.
- */
-inline const std::string &P3DHost::
-get_host_dir() const {
-  assert(has_host_dir());
-  return _host_dir;
-}
-
-/**
- * Returns the root URL of this particular host, as passed from the package
- * file.  This is a unique string that identifies each host.
- */
-inline const std::string &P3DHost::
-get_host_url() const {
-  return _host_url;
-}
-
-/**
- * Returns the root URL of this host, for constructing the URL to download
- * contents.xml only.  This is the same as get_host_url(), except it is
- * guaranteed to end in a slash character.
- *
- * Also see get_download_url_prefix().
- */
-inline const std::string &P3DHost::
-get_host_url_prefix() const {
-  return _host_url_prefix;
-}
-
-/**
- * Returns the root URL of this host, for downloading everything other than
- * the contents.xml file.  This is often the same as get_host_url_prefix(),
- * but it may be different in the case of an https server for contents.xml.
- */
-inline const std::string &P3DHost::
-get_download_url_prefix() const {
-  return _download_url_prefix;
-}
-
-/**
- * Returns the descriptive name provided for this host, if any.  Returns the
- * url if no descriptive name is provided.  This will be available after
- * read_contents_file() has been called.
- */
-inline const std::string &P3DHost::
-get_descriptive_name() const {
-  return _descriptive_name;
-}
-
-/**
- * Returns true if a contents.xml file has been successfully read for this
- * host, false otherwise.
- */
-inline bool P3DHost::
-has_contents_file() const {
-  return (_xcontents != nullptr);
-}
-
-/**
- * Returns a number that increments whenever a new version of the contents.xml
- * file has been read.  This number is local to the session only; it has
- * nothing to do with the "seq" value written into the contents.xml file
- * itself.
- *
- * This can be used by packages to determine whether they need to redownload
- * from scratch.
- */
-inline int P3DHost::
-get_contents_iseq() const {
-  return _contents_iseq;
-}
-
-/**
- * Returns true if the indicated pathname has the same md5 hash as the
- * contents.xml file (as provided by the server), false otherwise.
- */
-inline bool P3DHost::
-check_contents_hash(const std::string &pathname) const {
-  return _contents_spec.check_hash(pathname);
-}

+ 0 - 1056
direct/src/plugin/p3dHost.cxx

@@ -1,1056 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dHost.cxx
- * @author drose
- * @date 2009-08-21
- */
-
-#include "p3dHost.h"
-#include "p3dInstanceManager.h"
-#include "p3dPackage.h"
-#include "mkdir_complete.h"
-#include "wstring_encode.h"
-#include "xml_helpers.h"
-#include <openssl/md5.h>
-
-#include <algorithm>
-
-#ifndef _WIN32
-#include <unistd.h>
-#endif
-
-using std::ios;
-using std::ostringstream;
-using std::string;
-using std::wstring;
-
-/**
- * Use P3DInstanceManager::get_host() to construct a new P3DHost.
- */
-P3DHost::
-P3DHost(const string &host_url, const string &host_dir) :
-  _host_url(host_url),
-  _host_dir(host_dir)
-{
-  // Ensure that the download URL ends with a slash.
-  _host_url_prefix = _host_url;
-  if (!_host_url_prefix.empty() && _host_url_prefix[_host_url_prefix.size() - 1] != '/') {
-    _host_url_prefix += "/";
-  }
-  _download_url_prefix = _host_url_prefix;
-
-  _descriptive_name = _host_url;
-
-  _xcontents = nullptr;
-  _contents_expiration = 0;
-  _contents_iseq = 0;
-}
-
-/**
- *
- */
-P3DHost::
-~P3DHost() {
-  if (_xcontents != nullptr) {
-    delete _xcontents;
-  }
-
-  Packages::iterator mi;
-  for (mi = _packages.begin(); mi != _packages.end(); ++mi) {
-    PackageMap &package_map = (*mi).second;
-    PackageMap::iterator pi;
-    for (pi = package_map.begin(); pi != package_map.end(); ++pi) {
-      PlatformPackages &ppackages = (*pi).second;
-      PlatformPackages::iterator ppi;
-      for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) {
-        delete (*ppi);
-      }
-    }
-  }
-  _packages.clear();
-
-  FailedPackages::iterator pi;
-  for (pi = _failed_packages.begin(); pi != _failed_packages.end(); ++pi) {
-    delete (*pi);
-  }
-  _failed_packages.clear();
-}
-
-/**
- * Returns the pre-defined alternate host with the indicated token, if one is
- * defined for this token, or the original host if there is no alternate host
- * defined for this token.
- *
- * This is intended to implement test versions and the like, for instance in
- * which a particular p3d file may reference a package on one particular host,
- * but there is an alternate version to be tested on a different host.  The
- * HTML code that embeds the p3d file can choose to set the alt_host token to
- * redirect the p3d file to the alternate host.
- *
- * The actual URL for the alternate host is embedded within the host's
- * contents.xml as a security measure, to prevent people from tricking a p3d
- * file into running untrusted code by redirecting it to an arbitrary URL.
- */
-P3DHost *P3DHost::
-get_alt_host(const string &alt_host) {
-  assert(_xcontents != nullptr);
-
-  AltHosts::iterator hi;
-  hi = _alt_hosts.find(alt_host);
-  if (hi != _alt_hosts.end() && (*hi).second != _host_url) {
-    P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-    return inst_mgr->get_host((*hi).second);
-  }
-  return this;
-}
-
-/**
- * Returns true if a contents.xml file has been successfully read for this
- * host and is still current, false otherwise.
- */
-bool P3DHost::
-has_current_contents_file(P3DInstanceManager *inst_mgr) const {
-  if (inst_mgr->get_verify_contents() == P3D_VC_never
-    || inst_mgr->get_verify_contents() == P3D_VC_none) {
-    // If we're not asking to verify contents, then contents.xml files never
-    // expire.
-    return has_contents_file();
-  }
-
-  time_t now = time(nullptr);
-  return now < _contents_expiration && (_xcontents != nullptr);
-}
-
-/**
- * Reads the contents.xml file in the standard filename, if possible.
- *
- * Returns true on success, false on failure.
- */
-bool P3DHost::
-read_contents_file() {
-  if (_host_dir.empty()) {
-    // If we haven't got a host_dir yet, we can't read the contents.
-    return false;
-  }
-
-  string standard_filename = _host_dir + "/contents.xml";
-  return read_contents_file(standard_filename, false);
-}
-
-/**
- * Reads the contents.xml file in the indicated filename.  On success, writes
- * the contents.xml file into the standard location (if it's not there
- * already).
- *
- * Returns true on success, false on failure.
- */
-bool P3DHost::
-read_contents_file(const string &contents_filename, bool fresh_download) {
-  TiXmlDocument doc(contents_filename.c_str());
-  if (!doc.LoadFile()) {
-    return false;
-  }
-
-  TiXmlElement *xcontents = doc.FirstChildElement("contents");
-  if (xcontents == nullptr) {
-    return false;
-  }
-
-  if (_xcontents != nullptr) {
-    delete _xcontents;
-  }
-  _xcontents = (TiXmlElement *)xcontents->Clone();
-  ++_contents_iseq;
-  _contents_spec = FileSpec();
-
-  int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE;
-  xcontents->Attribute("max_age", &max_age);
-
-  // Get the latest possible expiration time, based on the max_age indication.
-  // Any expiration time later than this is in error.
-  time_t now = time(nullptr);
-  _contents_expiration = now + (time_t)max_age;
-
-  if (fresh_download) {
-    _contents_spec.read_hash(contents_filename);
-
-    // Update the XML with the new download information.
-    TiXmlElement *xorig = xcontents->FirstChildElement("orig");
-    while (xorig != nullptr) {
-      xcontents->RemoveChild(xorig);
-      xorig = xcontents->FirstChildElement("orig");
-    }
-
-    xorig = new TiXmlElement("orig");
-    xcontents->LinkEndChild(xorig);
-    _contents_spec.store_xml(xorig);
-
-    xorig->SetAttribute("expiration", (int)_contents_expiration);
-
-  } else {
-    // Read the download hash and expiration time from the XML.
-    int expiration = 0;
-    TiXmlElement *xorig = xcontents->FirstChildElement("orig");
-    if (xorig != nullptr) {
-      _contents_spec.load_xml(xorig);
-      xorig->Attribute("expiration", &expiration);
-    }
-    if (!_contents_spec.has_hash()) {
-      _contents_spec.read_hash(contents_filename);
-    }
-
-    _contents_expiration = std::min(_contents_expiration, (time_t)expiration);
-  }
-
-  nout << "read contents.xml, max_age = " << max_age
-       << ", expires in " << std::max(_contents_expiration, now) - now
-       << " s\n";
-
-  TiXmlElement *xhost = _xcontents->FirstChildElement("host");
-  if (xhost != nullptr) {
-    const char *url = xhost->Attribute("url");
-    if (url != nullptr && _host_url == string(url)) {
-      // We're the primary host.  This is the normal case.
-      read_xhost(xhost);
-
-      // Build up the list of alternate hosts.
-      TiXmlElement *xalthost = xhost->FirstChildElement("alt_host");
-      while (xalthost != nullptr) {
-        const char *keyword = xalthost->Attribute("keyword");
-        const char *url = xalthost->Attribute("url");
-        if (keyword != nullptr && url != nullptr) {
-          _alt_hosts[keyword] = url;
-        }
-        xalthost = xalthost->NextSiblingElement("alt_host");
-      }
-
-    } else {
-      // We're not the primary host; perhaps we're an alternate host.
-      TiXmlElement *xalthost = xhost->FirstChildElement("alt_host");
-      while (xalthost != nullptr) {
-        const char *url = xalthost->Attribute("url");
-        if (url != nullptr && _host_url == string(url)) {
-          // Yep, we're this alternate host.
-          read_xhost(xalthost);
-          break;
-        }
-        xalthost = xalthost->NextSiblingElement("alt_host");
-      }
-    }
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  if (_host_dir.empty()) {
-    determine_host_dir("");
-  }
-  assert(!_host_dir.empty());
-
-  string standard_filename = _host_dir + "/contents.xml";
-
-  if (inst_mgr->get_verify_contents() != P3D_VC_never) {
-    mkdir_complete(_host_dir, nout);
-
-    if (fresh_download) {
-      if (!save_xml_file(&doc, standard_filename)) {
-        nout << "Couldn't save to " << standard_filename << "\n";
-      }
-    } else {
-      if (standardize_filename(standard_filename) !=
-          standardize_filename(contents_filename)) {
-        if (!copy_file(contents_filename, standard_filename)) {
-          nout << "Couldn't copy to " << standard_filename << "\n";
-        }
-      }
-    }
-
-    if (_host_url == inst_mgr->get_host_url()) {
-      // If this is also the plugin host, then copy the contents.xml file into
-      // the root Panda directory as well, for the next plugin iteration.
-      string top_filename = inst_mgr->get_root_dir() + "/contents.xml";
-      if (standardize_filename(top_filename) !=
-          standardize_filename(standard_filename)) {
-        if (!copy_file(standard_filename, top_filename)) {
-          nout << "Couldn't copy to " << top_filename << "\n";
-        }
-      }
-    }
-  }
-
-  return true;
-}
-
-/**
- * Reads the host data from the <host> (or <alt_host>) entry in the
- * contents.xml file, or from a p3d_info.xml file.
- */
-void P3DHost::
-read_xhost(TiXmlElement *xhost) {
-  const char *descriptive_name = xhost->Attribute("descriptive_name");
-  if (descriptive_name != nullptr && _descriptive_name.empty()) {
-    _descriptive_name = descriptive_name;
-  }
-
-  const char *host_dir_basename = xhost->Attribute("host_dir");
-  if (host_dir_basename == nullptr) {
-    host_dir_basename = "";
-  }
-  if (_host_dir.empty()) {
-    determine_host_dir(host_dir_basename);
-  }
-
-  // Get the "download" URL, which is the source from which we download
-  // everything other than the contents.xml file.
-  const char *download_url = xhost->Attribute("download_url");
-  if (download_url != nullptr) {
-    _download_url_prefix = download_url;
-  }
-  if (!_download_url_prefix.empty()) {
-    if (_download_url_prefix[_download_url_prefix.size() - 1] != '/') {
-      _download_url_prefix += "/";
-    }
-  } else {
-    _download_url_prefix = _host_url_prefix;
-  }
-
-  TiXmlElement *xmirror = xhost->FirstChildElement("mirror");
-  while (xmirror != nullptr) {
-    const char *url = xmirror->Attribute("url");
-    if (url != nullptr) {
-      add_mirror(url);
-    }
-    xmirror = xmirror->NextSiblingElement("mirror");
-  }
-}
-
-/**
- * Returns a (possibly shared) pointer to the indicated package.
- *
- * The package_seq value should be the expected minimum package_seq value for
- * the indicated package.  If the given seq value is higher than the
- * package_seq value in the contents.xml file cached for the host, it is a
- * sign that the contents.xml file is out of date and needs to be
- * redownloaded.
- */
-P3DPackage *P3DHost::
-get_package(const string &package_name, const string &package_version,
-            const string &package_platform, const string &package_seq,
-            const string &alt_host) {
-  if (!alt_host.empty()) {
-    if (_xcontents != nullptr) {
-      // If we're asking for an alt host and we've already read our
-      // contents.xml file, then we already know all of our hosts, and we can
-      // start the package off with the correct host immediately.
-      P3DHost *new_host = get_alt_host(alt_host);
-      return new_host->get_package(package_name, package_version,
-                                   package_platform, package_seq);
-    }
-
-    // If we haven't read contents.xml yet, we need to create the package
-    // first, then let it be responsible for downloading our contents.xml, and
-    // it can migrate to its alt_host after that.
-  }
-
-  string key = package_name + "_" + package_version;
-  PlatformPackages &ppackages = _packages[alt_host][key];
-  PlatformPackages::iterator ppi;
-  P3DPackage *package = nullptr;
-
-  // First, look for an exact match of the platform.
-  for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) {
-    if ((*ppi)->get_package_platform() == package_platform) {
-      package = *ppi;
-      break;
-    }
-  }
-
-  // If an exact match isn't found, look for a generic platform.
-  if (package == nullptr) {
-    for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) {
-      if ((*ppi)->get_package_platform().empty()) {
-        package = *ppi;
-        break;
-      }
-    }
-  }
-
-  if (package != nullptr) {
-    if (package->get_failed()) {
-      // If the package has previously failed, move it aside and try again
-      // (maybe it just failed because the user interrupted it).
-      nout << "Package " << key << " has previously failed; trying again.\n";
-      _failed_packages.push_back(package);
-      ppackages.erase(ppi);
-      package = nullptr;
-    }
-  }
-
-  if (package == nullptr) {
-    package =
-      new P3DPackage(this, package_name, package_version, package_platform, alt_host);
-    ppackages.push_back(package);
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  if (!package_seq.empty() && has_current_contents_file(inst_mgr)) {
-    // If we were given a specific package_seq file to verify, and we believe
-    // we have a valid contents.xml file, then check the seq value in the
-    // contents.
-    FileSpec desc_file;
-    string seq;
-    bool solo;
-    if (get_package_desc_file(desc_file, seq, solo,
-                              package_name, package_version, package_platform)) {
-      nout << package_name << ": asked for seq " << package_seq
-           << ", we have seq " << seq << "\n";
-      if (compare_seq(package_seq, seq) > 0) {
-        // The requested seq value is higher than the one we have on file; our
-        // contents.xml file must be out of date after all.
-        nout << "expiring contents.xml for " << get_host_url() << "\n";
-        _contents_expiration = 0;
-      }
-    }
-  }
-
-  return package;
-}
-
-/**
- * Chooses the most appropriate platform for the indicated package based on
- * what this hardware supports and what is actually available.  Also fills in
- * per_platform, which is a boolean value indicating whether the directory
- * structure contains the platform directory or not.
- */
-bool P3DHost::
-choose_suitable_platform(string &selected_platform,
-                         bool &per_platform,
-                         const string &package_name,
-                         const string &package_version,
-                         const string &package_platform) {
-  if (_xcontents == nullptr) {
-    return false;
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  TiXmlElement *xpackage;
-
-  // If the platform is initially unspecified, we allow searching for any
-  // available supported platform.
-  if (package_platform == "") {
-    int num_supported_platforms = inst_mgr->get_num_supported_platforms();
-    for (int pi = 0; pi < num_supported_platforms; ++pi) {
-      string supported_platform = inst_mgr->get_supported_platform(pi);
-      xpackage = _xcontents->FirstChildElement("package");
-      while (xpackage != nullptr) {
-        const char *name = xpackage->Attribute("name");
-        const char *platform = xpackage->Attribute("platform");
-        const char *version = xpackage->Attribute("version");
-        if (platform == nullptr) {
-          platform = "";
-        }
-        if (version == nullptr) {
-          version = "";
-        }
-        if (name != nullptr &&
-            package_name == name &&
-            supported_platform == platform &&
-            package_version == version) {
-          // Here's the matching package definition.
-          selected_platform = platform;
-          per_platform = parse_bool_attrib(xpackage, "per_platform", false);
-          return true;
-        }
-
-        xpackage = xpackage->NextSiblingElement("package");
-      }
-    }
-  }
-
-  // Now, we look for an exact match for the expected platform.
-  xpackage = _xcontents->FirstChildElement("package");
-  while (xpackage != nullptr) {
-    const char *name = xpackage->Attribute("name");
-    const char *platform = xpackage->Attribute("platform");
-    const char *version = xpackage->Attribute("version");
-    if (platform == nullptr) {
-      platform = "";
-    }
-    if (version == nullptr) {
-      version = "";
-    }
-    if (name != nullptr &&
-        package_name == name &&
-        package_platform == platform &&
-        package_version == version) {
-      // Here's the matching package definition.
-      selected_platform = platform;
-      per_platform = parse_bool_attrib(xpackage, "per_platform", false);
-      return true;
-    }
-
-    xpackage = xpackage->NextSiblingElement("package");
-  }
-
-  // Look one more time, this time looking for a non-platform-specific
-  // version.
-  xpackage = _xcontents->FirstChildElement("package");
-  while (xpackage != nullptr) {
-    const char *name = xpackage->Attribute("name");
-    const char *platform = xpackage->Attribute("platform");
-    const char *version = xpackage->Attribute("version");
-    if (platform == nullptr) {
-      platform = "";
-    }
-    if (version == nullptr) {
-      version = "";
-    }
-    if (name != nullptr &&
-        package_name == name &&
-        *platform == '\0' &&
-        package_version == version) {
-      selected_platform = platform;
-      per_platform = parse_bool_attrib(xpackage, "per_platform", false);
-      return true;
-    }
-
-    xpackage = xpackage->NextSiblingElement("package");
-  }
-
-  // Couldn't find a suitable platform.
-  return false;
-}
-
-/**
- * Fills the indicated FileSpec with the hash information for the package's
- * desc file, and also determines the package's platform.  Returns true if
- * successful, false if the package is unknown.  This requires
- * has_contents_file() to return true in order to be successful.
- */
-bool P3DHost::
-get_package_desc_file(FileSpec &desc_file,              // out
-                      string &package_seq,              // out
-                      bool &package_solo,               // out
-                      const string &package_name,       // in
-                      const string &package_version,    // in
-                      const string &package_platform) { // in
-  if (_xcontents == nullptr) {
-    return false;
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  // Scan the contents data for the indicated package.  We expect to match the
-  // platform precisely, because we previously called
-  // choose_suitable_platform().
-  TiXmlElement *xpackage = _xcontents->FirstChildElement("package");
-  while (xpackage != nullptr) {
-    const char *name = xpackage->Attribute("name");
-    const char *platform = xpackage->Attribute("platform");
-    const char *version = xpackage->Attribute("version");
-    const char *seq = xpackage->Attribute("seq");
-    const char *solo = xpackage->Attribute("solo");
-    if (platform == nullptr) {
-      platform = "";
-    }
-    if (version == nullptr) {
-      version = "";
-    }
-    if (seq == nullptr) {
-      seq = "";
-    }
-    if (name != nullptr && platform != nullptr &&
-        package_name == name &&
-        package_platform == platform &&
-        package_version == version) {
-      // Here's the matching package definition.
-      desc_file.load_xml(xpackage);
-      package_seq = seq;
-      package_solo = false;
-      if (solo != nullptr) {
-        package_solo = (atoi(solo) != 0);
-      }
-      return true;
-    }
-
-    xpackage = xpackage->NextSiblingElement("package");
-  }
-
-  // Couldn't find the named package.
-  return false;
-}
-
-/**
- * Removes the indicated package from the cache of packages known by this
- * host.  This is invoked from the Python side by AppRunner.deletePackages(),
- * so that we remove the package before deleting its files.
- */
-void P3DHost::
-forget_package(P3DPackage *package, const string &alt_host) {
-  string key = package->get_package_name() + "_" + package->get_package_version();
-  nout << "Forgetting package " << key << "\n";
-
-  PlatformPackages &ppackages = _packages[alt_host][key];
-
-  PlatformPackages::iterator ppi = find(ppackages.begin(), ppackages.end(), package);
-  if (ppi != ppackages.end()) {
-    // Hmm, this is a memory leak.  But we allow it to remain, since it's an
-    // unusual circumstance (uninstalling), and it's safer to leak than to
-    // risk a floating pointer.
-    ppackages.erase(ppi);
-  }
-}
-
-/**
- * This is called by P3DPackage when it migrates from this host to its final
- * alt_host, after downloading the contents.xml file for this file and
- * learning the true URL for its target alt_host.
- */
-void P3DHost::
-migrate_package_host(P3DPackage *package, const string &alt_host, P3DHost *new_host) {
-  assert(new_host != this);
-  assert(new_host == get_alt_host(alt_host));
-
-  string key = package->get_package_name() + "_" + package->get_package_version();
-
-  PlatformPackages &ppackages = _packages[alt_host][key];
-
-  PlatformPackages::iterator ppi = find(ppackages.begin(), ppackages.end(), package);
-
-  assert(ppi != ppackages.end());
-  ppackages.erase(ppi);
-
-  PlatformPackages &new_ppackages = new_host->_packages[""][key];
-  new_ppackages.push_back(package);
-}
-
-/**
- * Selects num_mirrors elements, chosen at random, from the _mirrors list.
- * Adds the selected mirrors to result.  If there are fewer than num_mirrors
- * elements in the list, adds only as many mirrors as we can get.
- */
-void P3DHost::
-choose_random_mirrors(std::vector<string> &result, int num_mirrors) {
-  std::vector<size_t> selected;
-
-  size_t num_to_select = std::min(_mirrors.size(), (size_t)num_mirrors);
-  while (num_to_select > 0) {
-    size_t i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size());
-    while (find(selected.begin(), selected.end(), i) != selected.end()) {
-      // Already found this i, find a new one.
-      i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size());
-    }
-    selected.push_back(i);
-    result.push_back(_mirrors[i]);
-    --num_to_select;
-  }
-}
-
-/**
- * Adds a new URL to serve as a mirror for this host.  The mirrors will be
- * consulted first, before consulting the host directly.
- */
-void P3DHost::
-add_mirror(string mirror_url) {
-  // Ensure the URL ends in a slash.
-  if (!mirror_url.empty() && mirror_url[mirror_url.size() - 1] != '/') {
-    mirror_url += '/';
-  }
-
-  // Add it to the _mirrors list, but only if it's not already there.
-  if (find(_mirrors.begin(), _mirrors.end(), mirror_url) == _mirrors.end()) {
-    _mirrors.push_back(mirror_url);
-  }
-}
-
-/**
- * Removes the host directory and all its contents from the user's hard disk.
- */
-void P3DHost::
-uninstall() {
-  if (_host_dir.empty()) {
-    nout << "Cannot uninstall " << _descriptive_name << ": host directory not yet known.\n";
-    return;
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  // Check if we're even allowed to.
-  if (inst_mgr->get_verify_contents() == P3D_VC_never) {
-    nout << "Not allowed to uninstall " << _descriptive_name << ".\n";
-    return;
-  }
-
-  // First, explicitly uninstall each of our packages.
-  Packages::iterator mi;
-  for (mi = _packages.begin(); mi != _packages.end(); ++mi) {
-    PackageMap &package_map = (*mi).second;
-    PackageMap::iterator pi;
-    for (pi = package_map.begin(); pi != package_map.end(); ++pi) {
-      PlatformPackages &ppackages = (*pi).second;
-      PlatformPackages::iterator ppi;
-      for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) {
-        P3DPackage *package = (*ppi);
-        package->uninstall();
-      }
-    }
-  }
-
-  // Then, uninstall the host itself.
-  nout << "Uninstalling " << _descriptive_name << " from " << _host_dir << "\n";
-  inst_mgr->delete_directory_recursively(_host_dir);
-  inst_mgr->forget_host(this);
-}
-
-/**
- * Hashes the host_url into a (mostly) unique directory string, which will be
- * the root of the host's install tree.  Stores the result in _host_dir.
- *
- * This code is duplicated in Python, in HostInfo.determineHostDir().
- */
-void P3DHost::
-determine_host_dir(const string &host_dir_basename) {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  _host_dir = inst_mgr->get_root_dir();
-  _host_dir += "/hosts";
-
-  if (!host_dir_basename.empty()) {
-    // If the contents.xml specified a host_dir parameter, use it.
-    inst_mgr->append_safe_dir(_host_dir, host_dir_basename);
-    return;
-  }
-
-  // If we didn't get a host_dir parameter, we have to make one up.
-  _host_dir += "/";
-  string hostname;
-
-  // Look for a server name in the URL.  Including this string in the
-  // directory name makes it friendlier for people browsing the directory.
-
-  // We can't use URLSpec here, because we don't link with Panda3D. We have to
-  // do it by hand.
-  size_t p = _host_url.find("://");
-  if (p != string::npos) {
-    size_t start = p + 3;
-    size_t end = _host_url.find("/",  start);
-    // Now start .. end is something like "username@host:port".
-
-    size_t at = _host_url.find("@", start);
-    if (at < end) {
-      start = at + 1;
-    }
-
-    size_t colon = _host_url.find(":", start);
-    if (colon < end) {
-      end = colon;
-    }
-
-    // Now start .. end is just the hostname.
-    hostname = _host_url.substr(start, end - start);
-  }
-
-/*
- * Now build a hash string of the whole URL.  We'll use MD5 to get a pretty
- * good hash, with a minimum chance of collision.  Even if there is a hash
- * collision, though, it's not the end of the world; it just means that both
- * hosts will dump their packages into the same directory, and they'll fight
- * over the toplevel contents.xml file.  Assuming they use different version
- * numbers (which should be safe since they have the same hostname), there
- * will be minimal redownloading.
- */
-
-  static const size_t hash_size = 16;
-  unsigned char md[hash_size];
-
-  size_t keep_hash = hash_size;
-
-  if (!hostname.empty()) {
-    _host_dir += hostname;
-    _host_dir += "_";
-
-    // If we successfully got a hostname, we don't really need the full hash.
-    // We'll keep half of it.
-    keep_hash = keep_hash / 2;
-  }
-
-  MD5_CTX ctx;
-  MD5_Init(&ctx);
-  MD5_Update(&ctx, _host_url.data(), _host_url.size());
-  MD5_Final(md, &ctx);
-
-  for (size_t i = 0; i < keep_hash; ++i) {
-    int high = (md[i] >> 4) & 0xf;
-    int low = md[i] & 0xf;
-    _host_dir += P3DInstanceManager::encode_hexdigit(high);
-    _host_dir += P3DInstanceManager::encode_hexdigit(low);
-  }
-}
-
-
-/**
- * Attempts to change the filename into some standard form for comparison with
- * other filenames.  On a case-insensitive filesystem, this converts the
- * filename to lowercase.  On Windows, it further replaces forward slashes
- * with backslashes.
- */
-string P3DHost::
-standardize_filename(const string &filename) {
-#if defined(_WIN32) || defined(__APPLE__)
-  string new_filename;
-  for (string::const_iterator si = filename.begin();
-       si != filename.end();
-       ++si) {
-    char ch = *si;
-#ifdef _WIN32
-    if (ch == '/') {
-      ch = '\\';
-    }
-#endif  // _WIN32
-    new_filename += tolower(ch);
-  }
-  return new_filename;
-#else  // _WIN32 || __APPLE__
-  return filename;
-#endif  // _WIN32 || __APPLE__
-}
-
-/**
- * Copies the data in the file named by from_filename into the file named by
- * to_filename.
- */
-bool P3DHost::
-copy_file(const string &from_filename, const string &to_filename) {
-#ifdef _WIN32
-  ifstream in;
-  wstring from_filename_w;
-  if (string_to_wstring(from_filename_w, from_filename)) {
-    in.open(from_filename_w.c_str(), ios::in | ios::binary);
-  }
-
-  // Copy to a temporary file first, in case (a) the filenames actually refer
-  // to the same file, or (b) in case we have different processes writing to
-  // the same file, and (c) to prevent partially overwriting the file should
-  // something go wrong.
-  ostringstream strm;
-  strm << to_filename << ".t";
-  strm << GetCurrentProcessId() << "_" << GetCurrentThreadId();
-  string temp_filename = strm.str();
-  ofstream out;
-  wstring temp_filename_w;
-  if (string_to_wstring(temp_filename_w, temp_filename)) {
-    out.open(temp_filename_w.c_str(), ios::out | ios::binary);
-  }
-
-  static const size_t buffer_size = 4096;
-  char buffer[buffer_size];
-
-  in.read(buffer, buffer_size);
-  std::streamsize count = in.gcount();
-  while (count != 0) {
-    out.write(buffer, count);
-    if (out.fail()) {
-      unlink(temp_filename.c_str());
-      return false;
-    }
-    in.read(buffer, buffer_size);
-    count = in.gcount();
-  }
-  out.close();
-
-  wstring to_filename_w;
-  string_to_wstring(to_filename_w, to_filename);
-
-  if (!in.eof()) {
-    _wunlink(temp_filename_w.c_str());
-    return false;
-  }
-
-  if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) {
-    return true;
-  }
-
-  _wunlink(to_filename_w.c_str());
-  if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) {
-    return true;
-  }
-
-  _wunlink(temp_filename_w.c_str());
-  return false;
-
-#else  // _WIN32
-
-  ifstream in;
-  in.open(from_filename.c_str(), ios::in | ios::binary);
-
-  // Copy to a temporary file first, in case (a) the filenames actually refer
-  // to the same file, or (b) in case we have different processes writing to
-  // the same file, and (c) to prevent partially overwriting the file should
-  // something go wrong.
-  ostringstream strm;
-  strm << to_filename << ".t";
-  strm << getpid();
-
-  string temp_filename = strm.str();
-  ofstream out;
-  out.open(temp_filename.c_str(), ios::out | ios::binary);
-
-  static const size_t buffer_size = 4096;
-  char buffer[buffer_size];
-
-  in.read(buffer, buffer_size);
-  size_t count = in.gcount();
-  while (count != 0) {
-    out.write(buffer, count);
-    if (out.fail()) {
-      unlink(temp_filename.c_str());
-      return false;
-    }
-    in.read(buffer, buffer_size);
-    count = in.gcount();
-  }
-  out.close();
-
-  if (!in.eof()) {
-    unlink(temp_filename.c_str());
-    return false;
-  }
-
-  if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
-    return true;
-  }
-
-  unlink(to_filename.c_str());
-  if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
-    return true;
-  }
-
-  unlink(temp_filename.c_str());
-  return false;
-#endif  // _WIN32
-}
-
-/**
- * Stores the XML document to the file named by to_filename, safely.
- */
-bool P3DHost::
-save_xml_file(TiXmlDocument *doc, const string &to_filename) {
-  // Save to a temporary file first, in case (a) we have different processes
-  // writing to the same file, and (b) to prevent partially overwriting the
-  // file should something go wrong.
-
-#ifdef _WIN32
-  ostringstream strm;
-  strm << to_filename << ".t";
-  strm << GetCurrentProcessId() << "_" << GetCurrentThreadId();
-  string temp_filename = strm.str();
-
-  wstring temp_filename_w;
-  string_to_wstring(temp_filename_w, temp_filename);
-  wstring to_filename_w;
-  string_to_wstring(to_filename_w, to_filename);
-
-  if (!doc->SaveFile(temp_filename.c_str())) {
-    _wunlink(temp_filename_w.c_str());
-    return false;
-  }
-
-  if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) {
-    return true;
-  }
-
-  _wunlink(to_filename_w.c_str());
-  if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) {
-    return true;
-  }
-
-  _wunlink(temp_filename_w.c_str());
-  return false;
-
-#else  // _WIN32
-  ostringstream strm;
-  strm << to_filename << ".t";
-  strm << getpid();
-  string temp_filename = strm.str();
-
-  if (!doc->SaveFile(temp_filename.c_str())) {
-    unlink(temp_filename.c_str());
-    return false;
-  }
-
-  if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
-    return true;
-  }
-
-  unlink(to_filename.c_str());
-  if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
-    return true;
-  }
-
-  unlink(temp_filename.c_str());
-  return false;
-#endif  // _WIN32
-}
-
-/**
- * Compares the two dotted-integer sequence values numerically.  Returns -1 if
- * seq_a sorts first, 1 if seq_b sorts first, 0 if they are equivalent.
- */
-int P3DHost::
-compare_seq(const string &seq_a, const string &seq_b) {
-  const char *num_a = seq_a.c_str();
-  const char *num_b = seq_b.c_str();
-  int comp = compare_seq_int(num_a, num_b);
-  while (comp == 0) {
-    if (*num_a != '.') {
-      if (*num_b != '.') {
-        // Both strings ran out together.
-        return 0;
-      }
-      // a ran out first.
-      return -1;
-    } else if (*num_b != '.') {
-      // b ran out first.
-      return 1;
-    }
-
-    // Increment past the dot.
-    ++num_a;
-    ++num_b;
-    comp = compare_seq(num_a, num_b);
-  }
-
-  return comp;
-}
-
-/**
- * Numerically compares the formatted integer value at num_a with num_b.
- * Increments both num_a and num_b to the next character following the valid
- * integer.
- */
-int P3DHost::
-compare_seq_int(const char *&num_a, const char *&num_b) {
-  long int a;
-  char *next_a;
-  long int b;
-  char *next_b;
-
-  a = strtol((char *)num_a, &next_a, 10);
-  b = strtol((char *)num_b, &next_b, 10);
-
-  num_a = next_a;
-  num_b = next_b;
-
-  if (a < b) {
-    return -1;
-  } else if (b < a) {
-    return 1;
-  } else {
-    return 0;
-  }
-}

+ 0 - 115
direct/src/plugin/p3dHost.h

@@ -1,115 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dHost.h
- * @author drose
- * @date 2009-08-21
- */
-
-#ifndef P3DHOST_H
-#define P3DHOST_H
-
-#include "p3d_plugin_common.h"
-#include "fileSpec.h"
-#include <map>
-
-class FileSpec;
-class P3DInstanceManager;
-class P3DPackage;
-
-/**
- * Represents a particular download host serving up Panda3D packages.
- */
-class P3DHost {
-private:
-  P3DHost(const std::string &host_url, const std::string &host_dir = "");
-  ~P3DHost();
-
-public:
-  inline bool has_host_dir() const;
-  inline const std::string &get_host_dir() const;
-  inline const std::string &get_host_url() const;
-  inline const std::string &get_host_url_prefix() const;
-  inline const std::string &get_download_url_prefix() const;
-  inline const std::string &get_descriptive_name() const;
-
-  P3DHost *get_alt_host(const std::string &alt_host);
-
-  inline bool has_contents_file() const;
-  bool has_current_contents_file(P3DInstanceManager *inst_mgr) const;
-  inline int get_contents_iseq() const;
-  inline bool check_contents_hash(const std::string &pathname) const;
-
-  bool read_contents_file();
-  bool read_contents_file(const std::string &contents_filename, bool fresh_download);
-  void read_xhost(TiXmlElement *xhost);
-
-  P3DPackage *get_package(const std::string &package_name,
-                          const std::string &package_version,
-                          const std::string &package_platform,
-                          const std::string &package_seq,
-                          const std::string &alt_host = "");
-  bool choose_suitable_platform(std::string &selected_platform,
-                                bool &per_platform,
-                                const std::string &package_name,
-                                const std::string &package_version,
-                                const std::string &package_platform);
-  bool get_package_desc_file(FileSpec &desc_file,
-                             std::string &package_seq,
-                             bool &package_solo,
-                             const std::string &package_name,
-                             const std::string &package_version,
-                             const std::string &package_platform);
-
-  void forget_package(P3DPackage *package, const std::string &alt_host = "");
-  void migrate_package_host(P3DPackage *package, const std::string &alt_host, P3DHost *new_host);
-
-  void choose_random_mirrors(std::vector<std::string> &result, int num_mirrors);
-  void add_mirror(std::string mirror_url);
-
-  void uninstall();
-
-private:
-  void determine_host_dir(const std::string &host_dir_basename);
-
-  static std::string standardize_filename(const std::string &filename);
-  static bool copy_file(const std::string &from_filename, const std::string &to_filename);
-  static bool save_xml_file(TiXmlDocument *doc, const std::string &to_filename);
-  static int compare_seq(const std::string &seq_a, const std::string &seq_b);
-  static int compare_seq_int(const char *&num_a, const char *&num_b);
-
-private:
-  std::string _host_dir;
-  std::string _host_url;
-  std::string _host_url_prefix;
-  std::string _download_url_prefix;
-  std::string _descriptive_name;
-  TiXmlElement *_xcontents;
-  time_t _contents_expiration;
-  int _contents_iseq;
-  FileSpec _contents_spec;
-
-  typedef std::vector<std::string> Mirrors;
-  Mirrors _mirrors;
-
-  typedef std::map<std::string, std::string> AltHosts;
-  AltHosts _alt_hosts;
-
-  typedef std::vector<P3DPackage *> PlatformPackages;
-  typedef std::map<std::string, PlatformPackages> PackageMap;
-  typedef std::map<std::string, PackageMap> Packages;
-  Packages _packages;
-  typedef std::vector<P3DPackage *> FailedPackages;
-  FailedPackages _failed_packages;
-
-  friend class P3DInstanceManager;
-};
-
-#include "p3dHost.I"
-
-#endif

+ 0 - 147
direct/src/plugin/p3dInstance.I

@@ -1,147 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dInstance.I
- * @author drose
- * @date 2009-05-29
- */
-
-/**
- * Returns the current file parameters.
- */
-inline const P3DFileParams &P3DInstance::
-get_fparams() const {
-  return _fparams;
-}
-
-/**
- * Returns the current window parameters.
- */
-inline const P3DWindowParams &P3DInstance::
-get_wparams() const {
-  return _wparams;
-}
-
-/**
- * Returns a unique integer for each instance in the system.
- */
-inline int P3DInstance::
-get_instance_id() const {
-  return _instance_id;
-}
-
-/**
- * Returns a string that uniquely identifies this session.  This is a
- * constructed string that includes the supplied session_name, the python and
- * panda version, and the publisher, as well as any other relevant details; it
- * is guaranteed to be unique for each unique session required for different
- * P3DInstances.
- */
-inline const std::string &P3DInstance::
-get_session_key() const {
-  return _session_key;
-}
-
-/**
- * Returns the platform of this particular session.  Before the panda3d
- * package has been seen, this is the empty string; once we have downloaded
- * the info file for the panda3d package, it is filled in with whatever
- * platform is provided (that we're also runtime-compatible with).
- *
- * Presumably all of the platform-specific packages that are downloaded
- * subsequently must be of the exact same platform.
- */
-inline const std::string &P3DInstance::
-get_session_platform() const {
-  return _session_platform;
-}
-
-/**
- * Returns the P3DSession that is hosting this instance, or NULL if the
- * instance is not running.
- */
-inline P3DSession *P3DInstance::
-get_session() const {
-  return _session;
-}
-
-/**
- * Returns a pointer to the asynchronous notification function that was passed
- * to the constructor, if any, or NULL if asynchronous notifications are not
- * required.
- */
-inline P3D_request_ready_func *P3DInstance::
-get_request_ready_func() const {
-  return _func;
-}
-
-/**
- * Returns true if this instance's p3d file is trusted and ready to launch,
- * false if it needs to be approved by the user.
- */
-inline bool P3DInstance::
-is_trusted() const {
-  return _p3d_trusted;
-}
-
-/**
- * Returns true if this instance is allowed to be scripted by its embedding
- * web page, false otherwise.  This may not be known until the p3d file has
- * been fully downloaded and opened.
- */
-inline bool P3DInstance::
-get_matches_script_origin() const {
-  return _matches_script_origin;
-}
-
-/**
- * Returns true if this instance has already been started within some session,
- * false otherwise.
- */
-inline bool P3DInstance::
-is_started() const {
-  return (_session != nullptr);
-}
-
-/**
- * Returns true if this instance has tried and failed to launch for some
- * reason.
- */
-inline bool P3DInstance::
-is_failed() const {
-  return _failed;
-}
-
-/**
- *
- */
-inline P3DInstance::ImageFile::
-ImageFile() {
-  _use_standard_image = true;
-  _temp_filename = nullptr;
-  _image_placement = P3DSplashWindow::IP_none;
-}
-
-/**
- *
- */
-inline P3DInstance::ImageFile::
-~ImageFile() {
-  cleanup();
-}
-
-/**
- * Removes the temporary file, if any.
- */
-inline void P3DInstance::ImageFile::
-cleanup() {
-  if (_temp_filename != nullptr) {
-    delete _temp_filename;
-    _temp_filename = nullptr;
-  }
-}

+ 0 - 3936
direct/src/plugin/p3dInstance.cxx

@@ -1,3936 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dInstance.cxx
- * @author drose
- * @date 2009-05-29
- */
-
-#include "p3dInstance.h"
-#include "p3dInstanceManager.h"
-#include "p3dDownload.h"
-#include "p3dSession.h"
-#include "p3dPackage.h"
-#include "p3dSplashWindow.h"
-#include "p3dWinSplashWindow.h"
-#include "p3dOsxSplashWindow.h"
-#include "p3dX11SplashWindow.h"
-#include "p3dObject.h"
-#include "p3dMainObject.h"
-#include "p3dUndefinedObject.h"
-#include "p3dMultifileReader.h"
-#include "p3dTemporaryFile.h"
-#include "parse_color.h"
-
-#include <sstream>
-#include <algorithm>
-
-#ifdef __APPLE__
-#include <sys/mman.h>
-#include <ApplicationServices/ApplicationServices.h>
-
-using std::max;
-using std::min;
-using std::ostream;
-using std::ostringstream;
-using std::stringstream;
-using std::string;
-using std::vector;
-
-// Lifted from NSEvent.h (which is Objective-C).
-enum {
-   NSAlphaShiftKeyMask = 1 << 16,
-   NSShiftKeyMask      = 1 << 17,
-   NSControlKeyMask    = 1 << 18,
-   NSAlternateKeyMask  = 1 << 19,
-   NSCommandKeyMask    = 1 << 20,
-   NSNumericPadKeyMask = 1 << 21,
-   NSHelpKeyMask       = 1 << 22,
-   NSFunctionKeyMask   = 1 << 23,
-   NSDeviceIndependentModifierFlagsMask = 0xffff0000U
-};
-
-#endif  // __APPLE__
-
-#ifdef _WIN32
-typedef P3DWinSplashWindow SplashWindowType;
-#elif defined(__APPLE__) && !__LP64__
-typedef P3DOsxSplashWindow SplashWindowType;
-#elif defined(HAVE_X11)
-typedef P3DX11SplashWindow SplashWindowType;
-#else
-typedef P3DSplashWindow SplashWindowType;
-#endif
-
-// The amount of time (in seconds) over which we average the total download
-// time, for smoothing out the time estimate.
-static const double time_average = 10.0;
-
-// These are the various image files we might download for use in the splash
-// window.  This list must match the ImageType enum.
-const char *P3DInstance::_image_type_names[P3DInstance::IT_num_image_types] = {
-  "download",
-  "unauth",
-  "ready",
-  "failed",
-  "launch",
-  "active",
-  "auth_ready",
-  "auth_rollover",
-  "auth_click",
-  "play_ready",
-  "play_rollover",
-  "play_click",
-  "none",  // Not really used.
-};
-
-static void
-write_str(ostream &out, const wchar_t *str) {
-  const wchar_t *p = str;
-  while (*p != 0) {
-    out << (int)*p << ' ';
-    ++p;
-  }
-}
-
-/**
- *
- */
-P3DInstance::
-P3DInstance(P3D_request_ready_func *func,
-            const P3D_token tokens[], size_t num_tokens,
-            int argc, const char *argv[], void *user_data) :
-  _func(func)
-{
-  _dom_object = nullptr;
-  _main_object = new P3DMainObject;
-  _main_object->set_instance(this);
-  _user_data = user_data;
-  _request_pending = false;
-  _total_time_reports = 0;
-  _temp_p3d_filename = nullptr;
-  _image_package = nullptr;
-  _current_background_image = IT_none;
-  _current_button_image = IT_none;
-  _got_fparams = false;
-  _got_wparams = false;
-  _p3d_trusted = false;
-  _xpackage = nullptr;
-  _certlist_package = nullptr;
-  _p3dcert_package = nullptr;
-
-  _fparams.set_tokens(tokens, num_tokens);
-  _fparams.set_args(argc, argv);
-
-  nout << "Creating P3DInstance " << this << ": ";
-  for (int i = 0; i < _fparams.get_num_tokens(); ++i) {
-    nout << " " << _fparams.get_token_keyword(i)
-         << "=\"" << _fparams.get_token_value(i) << "\"";
-  }
-  nout << "\n";
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  _instance_id = inst_mgr->get_unique_id();
-  _hidden = (_fparams.lookup_token_int("hidden") != 0);
-  _matches_run_origin = true;
-  _matches_script_origin = false;
-  _allow_python_dev = false;
-  _keep_user_env = (_fparams.lookup_token_int("keep_user_env") != 0);
-  _auto_start = (_fparams.lookup_token_int("auto_start") != 0);
-  _stop_on_ready = (_fparams.lookup_token_int("stop_on_ready") != 0);
-  _auto_install = true;
-  if (_fparams.has_token("auto_install")) {
-    _auto_install = (_fparams.lookup_token_int("auto_install") != 0);
-  }
-  _auth_button_clicked = false;
-  _failed = false;
-  _session = nullptr;
-  _auth_session = nullptr;
-  _panda3d_package = nullptr;
-  _splash_window = nullptr;
-  _instance_window_opened = false;
-  _instance_window_attached = false;
-  _stuff_to_download = false;
-  _download_package_index = 0;
-  _prev_downloaded = 0;
-  _total_download_size = 0;
-  _total_downloaded = 0;
-  _packages_specified = false;
-  _download_started = false;
-  _download_complete = false;
-  _instance_started = false;
-
-  INIT_LOCK(_request_lock);
-  _requested_stop = false;
-
-#ifdef __APPLE__
-  _shared_fd = -1;
-  _shared_mmap_size = 0;
-  _swbuffer = nullptr;
-  _reversed_buffer = nullptr;
-  _buffer_data = nullptr;
-  _data_provider = nullptr;
-  _buffer_color_space = nullptr;
-  _buffer_image = nullptr;
-
-  // We have to start with _mouse_active true; firefox doesn't send activate
-  // events.
-  _mouse_active = true;
-  _modifiers = 0;
-  _frame_timer = nullptr;
-#endif  // __APPLE__
-
-  // Set some initial properties.
-  _main_object->set_float_property("instanceDownloadProgress", 0.0);
-  _main_object->set_float_property("downloadProgress", 0.0);
-  _main_object->set_undefined_property("downloadElapsedSeconds");
-  _main_object->set_undefined_property("downloadElapsedFormatted");
-  _main_object->set_undefined_property("downloadRemainingSeconds");
-  _main_object->set_undefined_property("downloadRemainingFormatted");
-  _main_object->set_string_property("downloadPackageName", "");
-  _main_object->set_string_property("downloadPackageDisplayName", "");
-  _main_object->set_bool_property("downloadComplete", false);
-  _main_object->set_string_property("status", "initial");
-
-  ostringstream stream;
-  stream << inst_mgr->get_plugin_major_version() << "."
-         << inst_mgr->get_plugin_minor_version() << "."
-         << inst_mgr->get_plugin_sequence_version();
-  if (!inst_mgr->get_plugin_official_version()) {
-    stream << "c";
-  }
-
-  // The plugin version as a single number, with three digits reserved for
-  // each component.
-  int numeric_version =
-    inst_mgr->get_plugin_major_version() * 1000000 +
-    inst_mgr->get_plugin_minor_version() * 1000 +
-    inst_mgr->get_plugin_sequence_version();
-  if (!inst_mgr->get_plugin_official_version()) {
-    // Subtract 1 if we are not an official version.
-    --numeric_version;
-  }
-
-  _main_object->set_string_property("pluginVersionString", stream.str());
-  _main_object->set_int_property("pluginMajorVersion", inst_mgr->get_plugin_major_version());
-  _main_object->set_int_property("pluginMinorVersion", inst_mgr->get_plugin_minor_version());
-  _main_object->set_int_property("pluginSequenceVersion", inst_mgr->get_plugin_sequence_version());
-  _main_object->set_bool_property("pluginOfficialVersion", inst_mgr->get_plugin_official_version());
-  _main_object->set_int_property("pluginNumericVersion", numeric_version);
-  _main_object->set_string_property("pluginDistributor", inst_mgr->get_plugin_distributor());
-  _main_object->set_string_property("coreapiHostUrl", inst_mgr->get_coreapi_host_url());
-  time_t timestamp = inst_mgr->get_coreapi_timestamp();
-  _main_object->set_int_property("coreapiTimestamp", (int)timestamp);
-  const char *timestamp_string = ctime(&timestamp);
-  if (timestamp_string == nullptr) {
-    timestamp_string = "";
-  }
-  _main_object->set_string_property("coreapiTimestampString", timestamp_string);
-  _main_object->set_string_property("coreapiVersionString", inst_mgr->get_coreapi_set_ver());
-
-  _main_object->set_bool_property("trustedEnvironment", (int)inst_mgr->get_trusted_environment());
-  _main_object->set_bool_property("consoleEnvironment", (int)inst_mgr->get_console_environment());
-
-  // We'll start off with the "download" image displayed in the splash window
-  // (when it opens), until we get stuff downloaded.
-  set_background_image(IT_download);
-
-  // We'd better ask for the image package up front, even if it turns out we
-  // don't need it for this particular app.  We'll probably use it eventually,
-  // and it's good to have it loaded early, so we can put up a splash image
-  // (for instance, the above IT_download image) while we download the real
-  // contents.
-  P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url());
-  _image_package = host->get_package("images", "", "", "");
-  if (_image_package != nullptr) {
-    _image_package->add_instance(this);
-  }
-
-  // Check if the window size has been explicitly set to 0.  This means we
-  // have an explicitly hidden plugin, and we should be prepared not to get a
-  // wparams from the browser.
-  if (_fparams.has_token("width") && _fparams.has_token("height") &&
-      (_fparams.lookup_token_int("width") == 0 ||
-       _fparams.lookup_token_int("height") == 0)) {
-    P3D_window_handle dummy_handle;
-    memset(&dummy_handle, 0, sizeof(dummy_handle));
-    P3DWindowParams wparams(P3D_WT_hidden, 0, 0, 0, 0, dummy_handle);
-    set_wparams(wparams);
-  }
-}
-
-/**
- *
- */
-P3DInstance::
-~P3DInstance() {
-  assert(_session == nullptr);
-  cleanup();
-
-  if (_dom_object != nullptr) {
-    P3D_OBJECT_DECREF(_dom_object);
-    _dom_object = nullptr;
-  }
-
-  if (_main_object != nullptr) {
-    nout << "panda_script_object ref = "
-         << _main_object->_ref_count << "\n";
-    _main_object->set_instance(nullptr);
-    P3D_OBJECT_DECREF(_main_object);
-    _main_object = nullptr;
-  }
-
-  Downloads::iterator di;
-  for (di = _downloads.begin(); di != _downloads.end(); ++di) {
-    P3DDownload *download = (*di).second;
-    if (download->get_instance() == this) {
-      download->set_instance(nullptr);
-    }
-    p3d_unref_delete(download);
-  }
-  _downloads.clear();
-
-  DESTROY_LOCK(_request_lock);
-
-  // TODO: Is it possible for someone to delete an instance while a download
-  // is still running?  Who will crash when this happens?
-}
-
-/**
- * Invalidates the instance and removes any structures prior to deleting.
- */
-void P3DInstance::
-cleanup() {
-  _failed = true;
-
-  if (_auth_session != nullptr) {
-    _auth_session->shutdown(false);
-    p3d_unref_delete(_auth_session);
-    _auth_session = nullptr;
-  }
-
-  for (int i = 0; i < (int)IT_num_image_types; ++i) {
-    _image_files[i].cleanup();
-  }
-
-  // Tell all of the packages that we're no longer in business for them.
-  Packages::iterator pi;
-  for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
-    (*pi)->remove_instance(this);
-  }
-  _packages.clear();
-  if (_image_package != nullptr) {
-    _image_package->remove_instance(this);
-    _image_package = nullptr;
-  }
-
-  if (_certlist_package != nullptr) {
-    _certlist_package->remove_instance(this);
-    _certlist_package = nullptr;
-  }
-
-  if (_p3dcert_package != nullptr) {
-    _p3dcert_package->remove_instance(this);
-    _p3dcert_package = nullptr;
-  }
-
-  if (_splash_window != nullptr) {
-    delete _splash_window;
-    _splash_window = nullptr;
-  }
-
-  if (_temp_p3d_filename != nullptr) {
-    delete _temp_p3d_filename;
-    _temp_p3d_filename = nullptr;
-  }
-
-  if (_xpackage != nullptr) {
-    delete _xpackage;
-    _xpackage = nullptr;
-  }
-
-#ifdef __APPLE__
-  if (_frame_timer != nullptr) {
-    CFRunLoopTimerInvalidate(_frame_timer);
-    CFRelease(_frame_timer);
-    _frame_timer = nullptr;
-  }
-
-  free_swbuffer();
-#endif
-
-  TiXmlDocument *doc = nullptr;
-  ACQUIRE_LOCK(_request_lock);
-  RawRequests::iterator ri;
-  for (ri = _raw_requests.begin(); ri != _raw_requests.end(); ++ri) {
-    doc = (*ri);
-    delete doc;
-  }
-  _raw_requests.clear();
-  RELEASE_LOCK(_request_lock);
-
-  BakedRequests::iterator bi;
-  for (bi = _baked_requests.begin(); bi != _baked_requests.end(); ++bi) {
-    P3D_request *request = (*bi);
-    finish_request(request, false);
-  }
-  _baked_requests.clear();
-
-  Downloads::iterator di;
-  for (di = _downloads.begin(); di != _downloads.end(); ++di) {
-    P3DDownload *download = (*di).second;
-    download->cancel();
-  }
-}
-
-
-/**
- * Specifies a URL that should be contacted to download the instance data.
- * Normally this, or set_p3d_filename() or make_p3d_stream(), is only called
- * once.
- *
- * The instance data at the other end of this URL is key.  We can't start the
- * instance until we have downloaded the instance file and examined the
- * p3d_info.xml, and we know what Python version we need and so forth.
- */
-void P3DInstance::
-set_p3d_url(const string &p3d_url) {
-  if (p3d_url.empty()) {
-    nout << "No p3d URL specified.  Cannot run.\n";
-    set_failed();
-    return;
-  }
-  _fparams.set_p3d_url(p3d_url);
-
-  // Save the last part of the URL as the p3d_basename, for reporting purposes
-  // or whatever.
-  determine_p3d_basename(p3d_url);
-
-  // Make a temporary file to receive the instance data.
-  assert(_temp_p3d_filename == nullptr);
-  _temp_p3d_filename = new P3DTemporaryFile(".p3d");
-  _stuff_to_download = true;
-
-  // Maybe it's time to open a splash window now.
-  make_splash_window();
-
-  // Mark the time we started downloading, so we'll know when to reveal the
-  // progress bar, and we can predict the total download time.
-#ifdef _WIN32
-  _start_dl_tick = GetTickCount();
-#else
-  gettimeofday(&_start_dl_timeval, nullptr);
-#endif
-  _show_dl_instance_progress = false;
-
-  // Start downloading the data.
-  InstanceDownload *download = new InstanceDownload(this);
-  download->set_url(p3d_url);
-  download->set_filename(_temp_p3d_filename->get_filename());
-  if (_fparams.has_token("p3d_size")) {
-    download->set_total_expected_data(_fparams.lookup_token_int("p3d_size"));
-  }
-
-  _main_object->set_string_property("status", "downloading_instance");
-  start_download(download);
-}
-
-/**
- * Indicates an intention to transmit the p3d data as a stream.  Should return
- * a new unique stream ID to receive it.
- */
-int P3DInstance::
-make_p3d_stream(const string &p3d_url) {
-  _fparams.set_p3d_url(p3d_url);
-
-  // Save the last part of the URL as the p3d_basename, for reporting purposes
-  // or whatever.
-  determine_p3d_basename(p3d_url);
-
-  // Make a temporary file to receive the instance data.
-  assert(_temp_p3d_filename == nullptr);
-  _temp_p3d_filename = new P3DTemporaryFile(".p3d");
-  _stuff_to_download = true;
-
-  // Maybe it's time to open a splash window now.
-  make_splash_window();
-
-  // Mark the time we started downloading, so we'll know when to reveal the
-  // progress bar.
-#ifdef _WIN32
-  _start_dl_tick = GetTickCount();
-#else
-  gettimeofday(&_start_dl_timeval, nullptr);
-#endif
-  _show_dl_instance_progress = false;
-
-  // Start downloading the data.
-  InstanceDownload *download = new InstanceDownload(this);
-  download->set_url(p3d_url);
-  download->set_filename(_temp_p3d_filename->get_filename());
-  if (_fparams.has_token("p3d_size")) {
-    download->set_total_expected_data(_fparams.lookup_token_int("p3d_size"));
-  }
-
-  _main_object->set_string_property("status", "downloading_instance");
-  return start_download(download, false);
-}
-
-/**
- * Specifies the file that contains the instance data.  Normally this is only
- * called once.
- */
-void P3DInstance::
-set_p3d_filename(const string &p3d_filename, const int &p3d_offset) {
-  determine_p3d_basename(p3d_filename);
-  priv_set_p3d_filename(p3d_filename, p3d_offset);
-}
-
-/**
- * Changes the window parameters, e.g.  to resize or reposition the window; or
- * sets the parameters for the first time, creating the initial window.
- */
-void P3DInstance::
-set_wparams(const P3DWindowParams &wparams) {
-  bool prev_got_wparams = _got_wparams;
-  _got_wparams = true;
-  _wparams = wparams;
-
-  nout << "set_wparams: " << wparams.get_window_type()
-       << " " << wparams.get_win_width() << " " << wparams.get_win_height()
-       << "\n";
-
-  if (_hidden || _wparams.get_win_width() == 0 || _wparams.get_win_height() == 0) {
-    // If we're a hidden app, or if the window has no size, then it is really
-    // a hidden window, regardless of what type it claims to be.
-    _wparams.set_window_type(P3D_WT_hidden);
-  }
-
-  if (_wparams.get_window_type() != P3D_WT_hidden) {
-    // Update or create the splash window.
-    if (_splash_window != nullptr) {
-      _splash_window->set_wparams(_wparams);
-    } else {
-      make_splash_window();
-    }
-  }
-
-  // It doesn't make much sense to go further than this point if the instance
-  // is already in the failed state.
-  if (is_failed()) {
-    return;
-  }
-
-  if (_wparams.get_window_type() != P3D_WT_hidden) {
-#ifdef __APPLE__
-    // On Mac, we have to communicate the results of the rendering back via
-    // shared memory, instead of directly parenting windows to the browser.
-    // Set up this mechanism.
-    int x_size = _wparams.get_win_width();
-    int y_size = _wparams.get_win_height();
-    if (x_size != 0 && y_size != 0) {
-      if (_swbuffer == nullptr || _swbuffer->get_x_size() != x_size ||
-          _swbuffer->get_y_size() != y_size) {
-        // We need to open a new shared buffer.
-        alloc_swbuffer();
-      }
-
-      if (_swbuffer == nullptr) {
-        nout << "Could not open swbuffer\n";
-      }
-    }
-#endif   // __APPLE__
-  }
-
-  // Update the instance in the sub-process.
-  if (_session != nullptr) {
-    TiXmlDocument *doc = new TiXmlDocument;
-    TiXmlElement *xcommand = new TiXmlElement("command");
-    xcommand->SetAttribute("cmd", "setup_window");
-    xcommand->SetAttribute("instance_id", get_instance_id());
-    TiXmlElement *xwparams = _wparams.make_xml(this);
-
-    doc->LinkEndChild(xcommand);
-    xcommand->LinkEndChild(xwparams);
-
-    _session->send_command(doc);
-  }
-
-  if (!prev_got_wparams) {
-    // If this was the first set_wparams call, try to start the app.
-    if (_p3d_trusted && get_packages_ready()) {
-      ready_to_start();
-    }
-  }
-}
-
-/**
- * Returns a pointer to the top-level scriptable object of the instance, to be
- * used by JavaScript code in the browser to control this program.
- */
-P3D_object *P3DInstance::
-get_panda_script_object() const {
-  nout << "get_panda_script_object\n";
-  return _main_object;
-}
-
-/**
- * Stores a pointer to the top-level window object of the browser, to be used
- * by Panda code to control JavaScript.  The new object's reference count is
- * incremented, and the previous object's is decremented.
- */
-void P3DInstance::
-set_browser_script_object(P3D_object *browser_script_object) {
-  nout << "set_browser_script_object\n";
-  if (browser_script_object != _dom_object) {
-    P3D_OBJECT_XDECREF(_dom_object);
-    _dom_object = browser_script_object;
-    if (_dom_object != nullptr) {
-      P3D_OBJECT_INCREF(_dom_object);
-    }
-
-    if (_session != nullptr) {
-      send_browser_script_object();
-    }
-  }
-
-  // Query the origin: protocol, hostname, and port.  We'll use this to limit
-  // access to the scripting interfaces for a particular p3d file.
-  _origin_protocol.clear();
-  _origin_hostname.clear();
-  _origin_port.clear();
-  if (_dom_object != nullptr) {
-    P3D_object *location = P3D_OBJECT_GET_PROPERTY(_dom_object, "location");
-    if (location != nullptr) {
-      P3D_object *protocol = P3D_OBJECT_GET_PROPERTY(location, "protocol");
-      if (protocol != nullptr) {
-        int size = P3D_OBJECT_GET_STRING(protocol, nullptr, 0);
-        char *buffer = new char[size];
-        P3D_OBJECT_GET_STRING(protocol, buffer, size);
-        _origin_protocol = string(buffer, size);
-        delete [] buffer;
-        P3D_OBJECT_DECREF(protocol);
-      }
-
-      P3D_object *hostname = P3D_OBJECT_GET_PROPERTY(location, "hostname");
-      if (hostname != nullptr) {
-        int size = P3D_OBJECT_GET_STRING(hostname, nullptr, 0);
-        char *buffer = new char[size];
-        P3D_OBJECT_GET_STRING(hostname, buffer, size);
-        _origin_hostname = string(buffer, size);
-        delete [] buffer;
-        P3D_OBJECT_DECREF(hostname);
-      }
-
-      P3D_object *port = P3D_OBJECT_GET_PROPERTY(location, "port");
-      if (port != nullptr) {
-        int size = P3D_OBJECT_GET_STRING(port, nullptr, 0);
-        char *buffer = new char[size];
-        P3D_OBJECT_GET_STRING(port, buffer, size);
-        _origin_port = string(buffer, size);
-        delete [] buffer;
-        P3D_OBJECT_DECREF(port);
-      }
-
-      if (_origin_hostname.empty() && _origin_protocol == "file:") {
-        _origin_hostname = "localhost";
-      }
-
-      if (_origin_port.empty()) {
-        // Maybe the actual URL doesn't include the port, in which case it is
-        // implicit.
-        if (_origin_protocol == "http:") {
-          _origin_port = "80";
-        } else if (_origin_protocol == "https:") {
-          _origin_port = "443";
-        }
-      }
-
-      P3D_OBJECT_DECREF(location);
-    }
-  }
-
-  nout << "origin is " << _origin_protocol << "//" << _origin_hostname;
-  if (!_origin_port.empty()) {
-    nout << ":" << _origin_port;
-  }
-  nout << "\n";
-}
-
-
-/**
- * Returns true if the instance has any pending requests at the time of this
- * call, false otherwise.
- */
-bool P3DInstance::
-has_request() {
-  return _request_pending;
-}
-
-/**
- * Returns a newly-allocated P3D_request corresponding to the pending request
- * for the host, or NULL if there is no pending request.  If the return value
- * is non-NULL, it should eventually be passed back to finish_request() for
- * cleanup.
- */
-P3D_request *P3DInstance::
-get_request() {
-  bake_requests();
-  if (_baked_requests.empty()) {
-    // No requests ready.
-    _request_pending = false;
-    return nullptr;
-  }
-
-  P3D_request *request = _baked_requests.front();
-  _baked_requests.pop_front();
-  _request_pending = !_baked_requests.empty();
-
-  if (request != nullptr) {
-    switch (request->_request_type) {
-    case P3D_RT_notify:
-      {
-        // Also eval the associated HTML token, if any.
-        string message = request->_request._notify._message;
-        string expression = _fparams.lookup_token(message);
-        nout << "notify: " << message << " " << expression << "\n";
-        if (!expression.empty() && _dom_object != nullptr) {
-          P3D_object *result = P3D_OBJECT_EVAL(_dom_object, expression.c_str());
-          P3D_OBJECT_XDECREF(result);
-        }
-      }
-      break;
-
-    case P3D_RT_stop:
-      {
-        // We also send an implicit message when Python requests itself to
-        // shutdown.
-        _main_object->set_pyobj(nullptr);
-        _main_object->set_string_property("status", "stopped");
-
-        string message = "onpythonstop";
-        string expression = _fparams.lookup_token(message);
-        nout << "notify: " << message << " " << expression << "\n";
-        if (!expression.empty() && _dom_object != nullptr) {
-          P3D_object *result = P3D_OBJECT_EVAL(_dom_object, expression.c_str());
-          P3D_OBJECT_XDECREF(result);
-        }
-      }
-      break;
-
-    case P3D_RT_callback:
-      {
-        // And when the callback request is extracted, we make the callback.
-        P3D_callback_func *func = request->_request._callback._func;
-        void *data = request->_request._callback._data;
-        (*func)(data);
-      }
-      break;
-
-    case P3D_RT_forget_package:
-      {
-        // We're being asked to forget a particular package.
-        const char *host_url = request->_request._forget_package._host_url;
-        const char *package_name = request->_request._forget_package._package_name;
-        const char *package_version = request->_request._forget_package._package_version;
-        if (package_version == nullptr) {
-          package_version = "";
-        }
-
-        P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-        P3DHost *host = inst_mgr->get_host(host_url);
-        if (package_name != nullptr) {
-          P3DPackage *package = host->get_package(package_name, package_version, _session_platform, "");
-          host->forget_package(package);
-        } else {
-          // If a NULL package name is given, forget the whole host.
-          inst_mgr->forget_host(host);
-        }
-      }
-      break;
-
-    default:
-      break;
-    }
-  }
-
-  return request;
-}
-
-/**
- * Copies requests from the _raw_requests queue, which is built up in one or
- * more sub-threads, into the _baked_requests queue, which is publicly
- * presented to the browser.  Along the way, some requests (like script
- * requests) are handled immediately.
- *
- * At the end of this call, _baked_requests will contain the current set of
- * requests pending for the browser.
- *
- * This method should only be called in the main thread.
- */
-void P3DInstance::
-bake_requests() {
-  while (true) {
-    // Get the latest request from the read thread.
-    TiXmlDocument *doc = nullptr;
-    ACQUIRE_LOCK(_request_lock);
-    if (!_raw_requests.empty()) {
-      doc = _raw_requests.front();
-      _raw_requests.pop_front();
-    }
-    RELEASE_LOCK(_request_lock);
-
-    if (doc == nullptr) {
-      // No more requests to process right now.
-      return;
-    }
-
-    // Now we've got a request in XML form; convert it to P3D_request form.
-    TiXmlElement *xrequest = doc->FirstChildElement("request");
-    assert(xrequest != nullptr);
-    P3D_request *request = make_p3d_request(xrequest);
-    delete doc;
-
-    if (request != nullptr) {
-      _baked_requests.push_back(request);
-    }
-  }
-}
-
-/**
- * May be called in any thread to add a new XML request to the pending_request
- * queue for this instance.  The XML document will be deleted when the request
- * is eventually handled.
- */
-void P3DInstance::
-add_raw_request(TiXmlDocument *doc) {
-  ACQUIRE_LOCK(_request_lock);
-  _raw_requests.push_back(doc);
-  _request_pending = true;
-  RELEASE_LOCK(_request_lock);
-
-  // We don't decode the XML yet, since we might be running in any thread
-  // here.  We'll decode it in the main thread, where it's safe.
-
-  // Tell the world we've got a new request.
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  inst_mgr->signal_request_ready(this);
-  if (_session != nullptr) {
-    _session->signal_request_ready(this);
-  }
-}
-
-/**
- * May be called in the main thread only to add a new request to the
- * baked_request queue.  This request queue is directly passed on the browser
- * without further processing at this level.
- */
-void P3DInstance::
-add_baked_request(P3D_request *request) {
-  assert(request->_instance == nullptr);
-  request->_instance = this;
-  ref();
-
-  _baked_requests.push_back(request);
-  _request_pending = true;
-
-  // Tell the world we've got a new request.
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  inst_mgr->signal_request_ready(this);
-}
-
-/**
- * Deallocates a previously-returned request from get_request().  If handled
- * is true, the request has been handled by the host; otherwise, it has been
- * ignored.
- */
-void P3DInstance::
-finish_request(P3D_request *request, bool handled) {
-  assert(request != nullptr);
-  if (request->_instance == nullptr) {
-    nout << "Ignoring empty request " << request << "\n";
-    return;
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  if (inst_mgr->validate_instance(request->_instance) == nullptr) {
-    // nout << "Ignoring unknown request " << request << "\n";
-    return;
-  }
-
-  switch (request->_request_type) {
-  case P3D_RT_stop:
-    break;
-
-  case P3D_RT_get_url:
-    if (request->_request._get_url._url != nullptr) {
-      free((char *)request->_request._get_url._url);
-      request->_request._get_url._url = nullptr;
-    }
-    break;
-
-  case P3D_RT_notify:
-    if (request->_request._notify._message != nullptr) {
-      free((char *)request->_request._notify._message);
-      request->_request._notify._message = nullptr;
-    }
-    break;
-
-  case P3D_RT_forget_package:
-    if (request->_request._forget_package._host_url != nullptr) {
-      free((char *)request->_request._forget_package._host_url);
-      request->_request._forget_package._host_url = nullptr;
-    }
-    if (request->_request._forget_package._package_name != nullptr) {
-      free((char *)request->_request._forget_package._package_name);
-      request->_request._forget_package._package_name = nullptr;
-    }
-    if (request->_request._forget_package._package_version != nullptr) {
-      free((char *)request->_request._forget_package._package_version);
-      request->_request._forget_package._package_version = nullptr;
-    }
-    break;
-
-  default:
-    break;
-  }
-
-  p3d_unref_delete(((P3DInstance *)request->_instance));
-  request->_instance = nullptr;
-
-  delete request;
-}
-
-/**
- * Called by the host in response to a get_url request, this sends the data
- * retrieved from the requested URL, a piece at a time.
- */
-bool P3DInstance::
-feed_url_stream(int unique_id,
-                P3D_result_code result_code,
-                int http_status_code,
-                size_t total_expected_data,
-                const unsigned char *this_data,
-                size_t this_data_size) {
-  Downloads::iterator di = _downloads.find(unique_id);
-  if (di == _downloads.end()) {
-    nout << "Unexpected feed_url_stream for " << unique_id << "\n";
-    // Don't know this request.
-    return false;
-  }
-
-  P3DDownload *download = (*di).second;
-  assert(download->get_instance() == this);
-  bool download_ok = download->feed_url_stream
-    (result_code, http_status_code, total_expected_data,
-     this_data, this_data_size);
-
-  if (!download_ok || download->get_download_finished()) {
-    // All done.
-    if (download->get_instance() == this) {
-      download->set_instance(nullptr);
-    }
-    _downloads.erase(di);
-    p3d_unref_delete(download);
-  }
-
-  return download_ok;
-}
-
-/**
- * Responds to the os-generated window event.  Returns true if the event is
- * handled, false if ignored.
- */
-bool P3DInstance::
-handle_event(const P3D_event_data &event) {
-  bool retval = false;
-  if (_splash_window != nullptr) {
-    if (_splash_window->handle_event(event)) {
-      retval = true;
-    }
-  }
-
-#if defined(__APPLE__)
-  if (event._event_type == P3D_ET_osx_event_record) {
-    retval = handle_event_osx_event_record(event);
-  } else if (event._event_type == P3D_ET_osx_cocoa) {
-    retval = handle_event_osx_cocoa(event);
-  } else {
-    assert(false);
-  }
-#endif  // __APPLE__
-
-  return retval;
-}
-
-/**
- * Returns the log filename for this particular session, if the session was
- * started and if it has a log file.  Returns empty string if the session
- * never started or if it lacks a log file.
- *
- * This is the same value returned by P3DSession::get_log_pathname(), except
- * that it remains valid even after the session has closed.
- */
-const string &P3DInstance::
-get_log_pathname() const {
-  if (_session != nullptr) {
-    return _session->_log_pathname;
-  }
-  return _log_pathname;
-}
-
-/**
- * Adds the package to the list of packages used by this instance.  The
- * instance will share responsibility for downloading the package with any of
- * the other instances that use the same package.
- *
- * The seq value should be the expected minimum package_seq value for the
- * indicated package.  If the given seq value is higher than the package_seq
- * value in the contents.xml file cached for the host, it is a sign that the
- * contents.xml file is out of date and needs to be redownloaded.
- */
-void P3DInstance::
-add_package(const string &name, const string &version, const string &seq,
-            P3DHost *host) {
-  string alt_host = _fparams.lookup_token("alt_host");
-
-  // Look up in the p3d_info.xml file to see if this p3d file has a specific
-  // alt_host indication for this host_url.
-  string alt_host_url = find_alt_host_url(host->get_host_url(), alt_host);
-  if (!alt_host_url.empty()) {
-    // If it does, we go ahead and switch to that host now, instead of
-    // bothering to contact the original host.
-    P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-    host = inst_mgr->get_host(alt_host_url);
-    alt_host.clear();
-  }
-
-  if (!host->has_contents_file()) {
-    // Since we haven't downloaded this host's contents.xml file yet, get its
-    // additional host information.
-    get_host_info(host);
-  }
-
-  P3DPackage *package = host->get_package(name, version, _session_platform,
-                                          seq, alt_host);
-  add_package(package);
-}
-
-/**
- * Adds the package to the list of packages used by this instance.  The
- * instance will share responsibility for downloading the package with any of
- * the other instances that use the same package.
- */
-void P3DInstance::
-add_package(P3DPackage *package) {
-  if (find(_packages.begin(), _packages.end(), package) != _packages.end()) {
-    // Already have this package.
-    return;
-  }
-
-  if (package->get_package_name() == "panda3d") {
-    _panda3d_package = package;
-  }
-
-  _packages.push_back(package);
-
-  // This call must be at the end of this method, because it might ultimately
-  // start the application before it returns (if this was the last required
-  // package).
-  package->add_instance(this);
-}
-
-/**
- * Indicates that the given package is destructing and this instance should no
- * longer retain a pointer to it.  This is normally called only by the
- * P3DPackage destructor, and it invalidates the instance.
- */
-void P3DInstance::
-remove_package(P3DPackage *package) {
-  Packages::iterator pi = find(_packages.begin(), _packages.end(), package);
-  if (pi != _packages.end()) {
-    _packages.erase(pi);
-  }
-  pi = find(_downloading_packages.begin(), _downloading_packages.end(), package);
-  if (pi != _downloading_packages.end()) {
-    _downloading_packages.erase(pi);
-  }
-  if (package == _image_package) {
-    _image_package = nullptr;
-  }
-  if (package == _certlist_package) {
-    _certlist_package = nullptr;
-  }
-  if (package == _p3dcert_package) {
-    _p3dcert_package = nullptr;
-  }
-  if (package == _panda3d_package) {
-    _panda3d_package = nullptr;
-  }
-
-  set_failed();
-}
-
-/**
- * Returns true if all of the packages required by the instance have their
- * information available and are ready to be downloaded, false if one or more
- * of them is still waiting for information (or has failed).
- */
-bool P3DInstance::
-get_packages_info_ready() const {
-  if (!_packages_specified) {
-    // We haven't even specified the full set of required packages yet.
-    return false;
-  }
-
-  Packages::const_iterator pi;
-  for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
-    if (!(*pi)->get_info_ready()) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-/**
- * Returns true if all of the packages required by the instance (as specified
- * in previous calls to add_package()) have been fully downloaded and are
- * ready to run, or false if one or more of them still requires downloading.
- */
-bool P3DInstance::
-get_packages_ready() const {
-  if (!_packages_specified) {
-    // We haven't even specified the full set of required packages yet.
-    return false;
-  }
-
-  Packages::const_iterator pi;
-  for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
-    if (!(*pi)->get_ready()) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-/**
- * Returns true if any of the packages required by the instance have failed to
- * download (and thus we will never be ready).
- */
-bool P3DInstance::
-get_packages_failed() const {
-  Packages::const_iterator pi;
-  for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
-    if ((*pi)->get_failed()) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-/**
- * Adds a newly-allocated P3DDownload object to the download queue, and issues
- * the request to start it downloading.  As the download data comes in, it
- * will be fed to the download object.
- *
- * This increments the P3DDownload object's reference count, and will
- * decrement it (and possibly delete the object) after download_finished() has
- * been called.
- *
- * add_request should be true to actually request the URL from the plugin, or
- * false not to.  Normally, this should always be set true, except in the one
- * special case of make_p3d_stream(), in which case the plugin is already
- * prepared to send the stream and doesn't need to have it requested.
- *
- * Returns the unique ID of this stream.
- */
-int P3DInstance::
-start_download(P3DDownload *download, bool add_request) {
-  assert(download->get_download_id() == 0);
-  assert(!download->get_url().empty());
-
-  if (is_failed()) {
-    // Can't download anything more after failure.
-    download->cancel();
-    return 0;
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  int download_id = inst_mgr->get_unique_id();
-  download->set_download_id(download_id);
-  download->set_instance(this);
-
-  download->ref();
-  bool inserted = _downloads.insert(Downloads::value_type(download_id, download)).second;
-  assert(inserted);
-
-  // add_request will be false only for the initial p3d stream, which the
-  // plugin already knows about.  For all other download streams, add_request
-  // is true in order to ask the plugin for the stream.
-  if (add_request) {
-    P3D_request *request = new P3D_request;
-    request->_instance = nullptr;
-    request->_request_type = P3D_RT_get_url;
-    request->_request._get_url._url = strdup(download->get_url().c_str());
-    request->_request._get_url._unique_id = download_id;
-
-    add_baked_request(request);
-  }
-
-  return download_id;
-}
-
-/**
- * Asks the host to shut down this particular instance, presumably because the
- * user has indicated it should exit.  This call may be made in any thread.
- */
-void P3DInstance::
-request_stop_sub_thread() {
-  // Atomically check _requested_stop.
-  bool add_request = false;
-  ACQUIRE_LOCK(_request_lock);
-  if (!_requested_stop) {
-    _requested_stop = true;
-    add_request = true;
-  }
-  RELEASE_LOCK(_request_lock);
-
-  // If we haven't requested a stop already, do it now.
-  if (add_request) {
-    TiXmlDocument *doc = new TiXmlDocument;
-    TiXmlElement *xrequest = new TiXmlElement("request");
-    xrequest->SetAttribute("rtype", "stop");
-    doc->LinkEndChild(xrequest);
-
-    add_raw_request(doc);
-  }
-}
-
-/**
- * Asks the host to shut down this particular instance, presumably because the
- * user has indicated it should exit.  This call may only be made in the main
- * thread.
- */
-void P3DInstance::
-request_stop_main_thread() {
-  // Atomically check _requested_stop.
-  bool add_request = false;
-  ACQUIRE_LOCK(_request_lock);
-  if (!_requested_stop) {
-    _requested_stop = true;
-    add_request = true;
-  }
-  RELEASE_LOCK(_request_lock);
-
-  // If we haven't requested a stop already, do it now.
-  if (add_request) {
-    _requested_stop = true;
-    P3D_request *request = new P3D_request;
-    request->_instance = nullptr;
-    request->_request_type = P3D_RT_stop;
-    add_baked_request(request);
-  }
-}
-
-/**
- * Asks the host to refresh the plugin window.  This is only relevant for
- * windowless plugins, for instance, the way OSX plugins always run.
- */
-void P3DInstance::
-request_refresh() {
-  P3D_request *request = new P3D_request;
-  request->_instance = nullptr;
-  request->_request_type = P3D_RT_refresh;
-  add_baked_request(request);
-}
-
-/**
- * Asks the host to make a callback later.
- */
-void P3DInstance::
-request_callback(P3D_callback_func *func, void *data) {
-  P3D_request *request = new P3D_request;
-  request->_instance = nullptr;
-  request->_request_type = P3D_RT_callback;
-  request->_request._callback._func = func;
-  request->_request._callback._data = data;
-  add_baked_request(request);
-}
-
-/**
- * Returns a newly-allocated XML structure that corresponds to the data within
- * this instance.
- */
-TiXmlElement *P3DInstance::
-make_xml() {
-  assert(_got_fparams);
-
-  TiXmlElement *xinstance = new TiXmlElement("instance");
-  xinstance->SetAttribute("instance_id", _instance_id);
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  xinstance->SetAttribute("root_dir", inst_mgr->get_root_dir());
-  xinstance->SetAttribute("log_directory", inst_mgr->get_log_directory());
-  xinstance->SetAttribute("verify_contents", (int)inst_mgr->get_verify_contents());
-
-  // Tell the Panda process that it was started by a plugin that knows about
-  // the new per_platform flag.
-  xinstance->SetAttribute("respect_per_platform", 1);
-
-  if (!inst_mgr->get_super_mirror().empty()) {
-    xinstance->SetAttribute("super_mirror", inst_mgr->get_super_mirror());
-  }
-
-
-  TiXmlElement *xfparams = _fparams.make_xml();
-  xinstance->LinkEndChild(xfparams);
-
-  if (_got_wparams) {
-    TiXmlElement *xwparams = _wparams.make_xml(this);
-    xinstance->LinkEndChild(xwparams);
-  }
-
-  Packages::const_iterator pi;
-  for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
-    TiXmlElement *xpackage = (*pi)->make_xml();
-    xinstance->LinkEndChild(xpackage);
-  }
-
-  TiXmlElement *xmain = _session->p3dobj_to_xml(_main_object);
-  xmain->SetValue("main");
-  xinstance->LinkEndChild(xmain);
-
-  return xinstance;
-}
-
-/**
- * Called by the P3DSplashWindow code (maybe in a sub-thread) when the user
- * clicks the button visible on the splash window.  This will forward the
- * event to the main thread via the request callback mechanism.
- */
-void P3DInstance::
-splash_button_clicked_sub_thread() {
-  TiXmlDocument *doc = new TiXmlDocument;
-  TiXmlElement *xrequest = new TiXmlElement("request");
-  xrequest->SetAttribute("rtype", "notify");
-  xrequest->SetAttribute("message", "buttonclick");
-  doc->LinkEndChild(xrequest);
-
-  add_raw_request(doc);
-}
-
-/**
- * Called only in the main thread, indirectly from
- * splash_button_clicked_sub_thread(), as the result of the user clicking on
- * the button visible in the splash window.
- */
-void P3DInstance::
-splash_button_clicked_main_thread() {
-  if (is_failed()) {
-    // Can't click the button after we've failed.
-    nout << "Ignoring click for failed instance\n";
-    return;
-  }
-
-  if (!_p3d_trusted) {
-    auth_button_clicked();
-  } else if (_session == nullptr) {
-    play_button_clicked();
-  } else {
-    nout << "Ignoring click for already-started instance\n";
-  }
-}
-
-/**
- * Called to authorize the p3d file by the user clicking the red "auth"
- * button.
- */
-void P3DInstance::
-auth_button_clicked() {
-  // Delete the previous session and create a new one.
-  if (_auth_session != nullptr) {
-    _auth_session->shutdown(false);
-    p3d_unref_delete(_auth_session);
-    _auth_session = nullptr;
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  _auth_session = inst_mgr->authorize_instance(this);
-  _auth_session->ref();
-}
-
-/**
- * Called to start the game by the user clicking the green "play" button, or
- * by JavaScript calling play().
- */
-void P3DInstance::
-play_button_clicked() {
-  if (_session == nullptr && _p3d_trusted) {
-    set_button_image(IT_none);
-    if (!_download_started) {
-      // Now we initiate the download.
-      _auto_install = true;
-      _auto_start = true;
-      if (get_packages_info_ready()) {
-        ready_to_install();
-      }
-
-    } else {
-      set_background_image(IT_launch);
-      P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-      inst_mgr->start_instance(this);
-    }
-  }
-}
-
-/**
- * Called by the P3DAuthSession code in a sub-thread when the auth dialog
- * exits (for instance, because the user approved the certificate, or
- * cancelled).
- */
-void P3DInstance::
-auth_finished_sub_thread() {
-  TiXmlDocument *doc = new TiXmlDocument;
-  TiXmlElement *xrequest = new TiXmlElement("request");
-  xrequest->SetAttribute("rtype", "notify");
-  xrequest->SetAttribute("message", "authfinished");
-  doc->LinkEndChild(xrequest);
-
-  add_raw_request(doc);
-}
-
-/**
- * Called only in the main thread, indirectly from auth_finished_sub_thread(),
- * as the result of the user closing the auth dialog.
- */
-void P3DInstance::
-auth_finished_main_thread() {
-  // Set this flag to indicate that the user has clicked on the red "auth"
-  // button.  This eliminates the need to click on the green "start" button.
-  _auth_button_clicked = true;
-
-  // After the authorization program has returned, check the signature again.
-  check_p3d_signature();
-}
-
-/**
- * Stops the instance (if it is running) and deletes any packages referenced
- * by the instance.  This is normally called by JavaScript, via
- * P3DMainObject::call_uninstall().
- */
-bool P3DInstance::
-uninstall_packages() {
-  if (_packages.empty()) {
-    // If we have no packages (for instance, because we're untrusted), we
-    // can't uninstall anything.
-    nout << "Uninstall failed: no packages.\n";
-    return false;
-  }
-
-  nout << "Uninstalling " << _packages.size() << " packages\n";
-
-  Packages::const_iterator pi;
-  for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
-    P3DPackage *package = (*pi);
-    if (package != _image_package && package != _certlist_package &&
-        package != _p3dcert_package) {
-      package->uninstall();
-    }
-  }
-
-  // Also clean up the start directory, if we have a custom start dir.  We
-  // won't do this if verify_contents is 'none'.
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  if (inst_mgr->get_verify_contents() != P3D_VC_never) {
-    string start_dir_suffix = get_start_dir_suffix();
-    if (!start_dir_suffix.empty()) {
-      string start_dir = inst_mgr->get_start_dir() + start_dir_suffix;
-      nout << "Cleaning up start directory " << start_dir << "\n";
-      inst_mgr->delete_directory_recursively(start_dir);
-    }
-  }
-
-  return true;
-}
-
-/**
- * Stops the instance (if it is running) and deletes all packages downloaded
- * from any of the host(s) referenced by the instance.  This is a more
- * aggressive uninstall than uninstall_packages().  This is normally called by
- * JavaScript, via P3DMainObject::call_uninstall().
- */
-bool P3DInstance::
-uninstall_host() {
-  if (_packages.empty()) {
-    // If we have no packages (for instance, because we're untrusted), we
-    // can't uninstall anything.
-    nout << "Uninstall failed: no packages.\n";
-    return false;
-  }
-
-  uninstall_packages();
-
-  // Collect the set of hosts referenced by this instance.
-  std::set<P3DHost *> hosts;
-  Packages::const_iterator pi;
-  for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
-    P3DPackage *package = (*pi);
-    if (package != _image_package && package != _certlist_package &&
-        package != _p3dcert_package) {
-      hosts.insert(package->get_host());
-    }
-  }
-  nout << "Uninstalling " << hosts.size() << " hosts\n";
-
-  // Uninstall all of them.
-  std::set<P3DHost *>::iterator hi;
-  for (hi = hosts.begin(); hi != hosts.end(); ++hi) {
-    P3DHost *host = (*hi);
-    host->uninstall();
-  }
-
-  return true;
-}
-
-/**
- * The private implementation of set_p3d_filename(), this does all the work
- * except for updating p3d_basename.  It is intended to be called internally,
- * and might be passed a temporary filename.
- */
-void P3DInstance::
-priv_set_p3d_filename(const string &p3d_filename, const int &p3d_offset) {
-  if (!_fparams.get_p3d_filename().empty()) {
-    nout << "p3d_filename already set to: " << _fparams.get_p3d_filename()
-         << ", trying to set to " << p3d_filename << "\n";
-    return;
-  }
-
-  _fparams.set_p3d_filename(p3d_filename);
-  // The default for p3d_offset is -1, which means not to change it.
-  if (p3d_offset >= 0) {
-    _fparams.set_p3d_offset(p3d_offset);
-  }
-  _got_fparams = true;
-
-  _main_object->set_float_property("instanceDownloadProgress", 1.0);
-
-  // Generate a special notification: onpluginload, indicating the plugin has
-  // read its parameters and is ready to be queried (even if Python has not
-  // yet started).
-  send_notify("onpluginload");
-
-  if (!_mf_reader.open_read(_fparams.get_p3d_filename(), _fparams.get_p3d_offset())) {
-    if (_fparams.get_p3d_offset() == 0) {
-      nout << "Couldn't read " << _fparams.get_p3d_filename() << "\n";
-    } else {
-      nout << "Couldn't read " << _fparams.get_p3d_filename()
-           << " at offset " << _fparams.get_p3d_offset() << "\n";
-    }
-    set_failed();
-    return;
-  }
-
-  check_p3d_signature();
-}
-
-/**
- * Determines _p3d_basename from the indicated URL.
- */
-void P3DInstance::
-determine_p3d_basename(const string &p3d_url) {
-  string file_part = p3d_url;
-  size_t question = file_part.find('?');
-  if (question != string::npos) {
-    file_part = file_part.substr(0, question);
-  }
-  size_t slash = file_part.rfind('/');
-  if (slash != string::npos) {
-    file_part = file_part.substr(slash + 1);
-  }
-  _p3d_basename = file_part;
-
-  nout << "p3d_basename = " << _p3d_basename << "\n";
-}
-
-/**
- * Returns true if the indicated origin_match string, one of either run_origin
- * or script_origin from the p3d_info.xml file, matches the origin of the page
- * that embedded the p3d file.
- */
-bool P3DInstance::
-check_matches_origin(const string &origin_match) {
-  // First, separate the string up at the semicolons.
-  size_t p = 0;
-  size_t semicolon = origin_match.find(';');
-  while (semicolon != string::npos) {
-    if (check_matches_origin_one(origin_match.substr(p, semicolon - p))) {
-      return true;
-    }
-    p = semicolon + 1;
-    semicolon = origin_match.find(';', p);
-  }
-  if (check_matches_origin_one(origin_match.substr(p))) {
-    return true;
-  }
-
-  // It doesn't match any of the semicolon-delimited strings within
-  // origin_match.
-  return false;
-}
-
-/**
- * Called for each semicolon-delimited string within origin_match passed to
- * check_matches_origin().
- */
-bool P3DInstance::
-check_matches_origin_one(const string &origin_match) {
-  // Do we have a protocol?
-  size_t p = 0;
-  size_t colon = origin_match.find(':');
-  if (colon + 1 < origin_match.length() && origin_match[colon + 1] == '/') {
-    // Yes.  It should therefore match the protocol we have in the origin.
-    string protocol = origin_match.substr(0, colon + 1);
-    if (!check_matches_component(_origin_protocol, protocol)) {
-      return false;
-    }
-    p = colon + 2;
-    // We'll support both http:hostname and http:hostname, in case the user is
-    // sloppy.
-    if (p < origin_match.length() && origin_match[p] == '/') {
-      ++p;
-    }
-    colon = origin_match.find(':', p);
-  }
-
-  // Do we have a port?
-  if (colon < origin_match.length() && isdigit(origin_match[colon + 1])) {
-    // Yes.  It should therefore match the port we have in the origin.
-    string port = origin_match.substr(colon + 1);
-    if (!check_matches_component(_origin_port, port)) {
-      return false;
-    }
-  }
-
-  // The hostname should also match what we have in the origin.
-  string hostname = origin_match.substr(p, colon - p);
-  if (!check_matches_hostname(_origin_hostname, hostname)) {
-    return false;
-  }
-
-  // Everything matches.
-  return true;
-}
-
-/**
- * Matches the hostname of check_matches_origin: the individual components of
- * the hostname are matched independently, with '**.' allowed at the beginning
- * to indicate zero or more prefixes.  Returns true on match, false on
- * failure.
- */
-bool P3DInstance::
-check_matches_hostname(const string &orig, const string &match) {
-  // First, separate both strings up at the dots.
-  vector<string> orig_components;
-  separate_components(orig_components, orig);
-
-  vector<string> match_components;
-  separate_components(match_components, match);
-
-  // If the first component of match is "**", it means we accept any number,
-  // zero or more, of components at the beginning of the hostname.
-  if (!match_components.empty() && match_components[0] == "**") {
-    // Remove the leading "**"
-    match_components.erase(match_components.begin());
-    // Then remove any extra components from the beginning of orig_components;
-    // we won't need to check them.
-    if (orig_components.size() > match_components.size()) {
-      size_t num_to_remove = orig_components.size() - match_components.size();
-      orig_components.erase(orig_components.begin(), orig_components.begin() + num_to_remove);
-    }
-  }
-
-  // Now match the remaining components one-to-one.
-  if (match_components.size() != orig_components.size()) {
-    return false;
-  }
-
-  vector<string>::const_iterator p = orig_components.begin();
-  vector<string>::const_iterator p2 = match_components.begin();
-
-  while (p != orig_components.end()) {
-    assert(p2 != match_components.end());
-    if (!check_matches_component(*p, *p2)) {
-      return false;
-    }
-    ++p;
-    ++p2;
-  }
-
-  assert(p2 == match_components.end());
-  return true;
-}
-
-/**
- * Separates the indicated hostname into its components at the dots.
- */
-void P3DInstance::
-separate_components(vector<string> &components, const string &str) {
-  size_t p = 0;
-  size_t dot = str.find('.');
-  while (dot != string::npos) {
-    components.push_back(str.substr(p, dot - p));
-    p = dot + 1;
-    dot = str.find('.', p);
-  }
-  components.push_back(str.substr(p));
-}
-
-/**
- * Matches a single component of check_matches_origin: either protocol or
- * port, or a single component of the hostname.  Case-insensitive, and
- * supports the '*' wildcard operator to match the entire component.  Returns
- * true on match, false on failure.
- */
-bool P3DInstance::
-check_matches_component(const string &orig, const string &match) {
-  if (match == "*") {
-    return true;
-  }
-
-  // Case-insensitive compare.
-  if (orig.length() != match.length()) {
-    return false;
-  }
-
-  string::const_iterator p = orig.begin();
-  string::const_iterator p2 = match.begin();
-
-  while (p != orig.end()) {
-    assert(p2 != match.end());
-    if (tolower(*p) != tolower(*p2)) {
-      return false;
-    }
-    ++p;
-    ++p2;
-  }
-
-  assert(p2 == match.end());
-  return true;
-}
-
-/**
- * Checks the signature(s) encoded in the p3d file, and looks to see if any of
- * them are recognized.
- *
- * If the signature is recognized, calls mark_p3d_trusted(); otherwise, calls
- * mark_p3d_untrusted().
- */
-void P3DInstance::
-check_p3d_signature() {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  if (inst_mgr->get_trusted_environment()) {
-    // If we're in a trusted environment (e.g.  the panda3d command line,
-    // where we've already downloaded the p3d file separately), then
-    // everything is approved.
-    mark_p3d_trusted();
-    return;
-  }
-
-  // See if we've previously approved the certificate--any certificate--that's
-  // signing this p3d file.
-  int num_signatures = _mf_reader.get_num_signatures();
-  for (int i = 0; i < num_signatures; ++i) {
-    const P3DMultifileReader::CertChain &chain = _mf_reader.get_signature(i);
-
-    // Here's a certificate that has signed this multifile.
-    X509 *cert = chain[0]._cert;
-
-    // Look up the certificate to see if we've stored a copy in our certs dir.
-    if (inst_mgr->find_cert(cert)) {
-      mark_p3d_trusted();
-      return;
-    }
-  }
-
-  // Check the list of pre-approved certificates.
-  if (_certlist_package == nullptr) {
-    // We have to go download this package.
-    P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-    P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url());
-    _certlist_package = host->get_package("certlist", "", "", "");
-    if (_certlist_package != nullptr) {
-      _certlist_package->add_instance(this);
-    }
-
-    // When the package finishes downloading, we will come back here.
-    return;
-  }
-
-  if (!_certlist_package->get_ready() && !_certlist_package->get_failed()) {
-    // Wait for it to finish downloading.
-    return;
-  }
-
-  // Couldn't find any approved certificates.
-  mark_p3d_untrusted();
-  return;
-}
-
-/**
- * This is called internally when it has been determined that the p3d file
- * can't (yet) be trusted, for instance because it lacks a signature, or
- * because it is signed by an unrecognized certificate.  This puts up the red
- * "auth" button and waits for the user to approve the app before continuing.
- */
-void P3DInstance::
-mark_p3d_untrusted() {
-  // Failed test.
-  nout << "p3d untrusted\n";
-  if (is_failed()) {
-    return;
-  }
-
-  if (_p3dcert_package == nullptr) {
-    // We have to go download this package.
-    P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-    P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url());
-    _p3dcert_package = host->get_package("p3dcert", "", "", "");
-    if (_p3dcert_package != nullptr) {
-      _p3dcert_package->add_instance(this);
-    }
-
-    // When the package finishes downloading, we will come back here.
-    return;
-  }
-
-  if (_p3dcert_package->get_failed()) {
-    // Oh, too bad for us.  We're dependent on this package which we weren't
-    // able to download for some reason.
-    set_failed();
-  }
-
-  if (!_p3dcert_package->get_ready()) {
-    // Wait for it to finish downloading.
-    return;
-  }
-
-  // OK, we've got the authorization program; we can put up the red button
-  // now.
-
-  // Notify JS that we've got no trust of the p3d file.
-  _main_object->set_bool_property("trusted", false);
-  send_notify("onunauth");
-  set_background_image(IT_unauth);
-  set_button_image(IT_auth_ready);
-  make_splash_window();
-}
-
-/**
- * This is called internally when it has been determined that the p3d file can
- * be trusted and started.  When this is called, the p3d file will be examined
- * and made ready to start; it will not be started until this is called.
- */
-void P3DInstance::
-mark_p3d_trusted() {
-  nout << "p3d trusted\n";
-  // Only call this once.
-  if (_p3d_trusted) {
-    nout << "mark_p3d_trusted() called twice on " << _fparams.get_p3d_filename()
-         << "\n";
-    return;
-  }
-
-  // Extract the application desc file from the p3d file.
-  stringstream sstream;
-  if (!_mf_reader.extract_one(sstream, "p3d_info.xml")) {
-    nout << "No p3d_info.xml file found in " << _fparams.get_p3d_filename() << "\n";
-    set_failed();
-
-  } else {
-    sstream.seekg(0);
-    TiXmlDocument doc;
-    sstream >> doc;
-
-    scan_app_desc_file(&doc);
-  }
-
-  // Now we've got no further need to keep the _mf_reader open.
-  _mf_reader.close();
-
-  // For the moment, all sessions will be unique.  TODO: support multiple
-  // instances per session.
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  ostringstream strm;
-  strm << inst_mgr->get_unique_id();
-  _session_key = strm.str();
-
-  // Notify JS that we've accepted the trust of the p3d file.
-  _main_object->set_bool_property("trusted", true);
-  send_notify("onauth");
-
-  // Now that we're all set up, grab the panda3d package.  We need to examine
-  // this before we can start to download the remaining packages.
-  add_panda3d_package();
-}
-
-/**
- * Reads the p3d_info.xml file at instance startup, to determine the set of
- * required packages and so forth.
- */
-void P3DInstance::
-scan_app_desc_file(TiXmlDocument *doc) {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  TiXmlElement *xpackage = doc->FirstChildElement("package");
-  if (xpackage == nullptr) {
-    return;
-  }
-  assert(_xpackage == nullptr);
-  _xpackage = (TiXmlElement *)xpackage->Clone();
-
-  TiXmlElement *xconfig = _xpackage->FirstChildElement("config");
-  if (xconfig != nullptr) {
-    int hidden = 0;
-    if (xconfig->QueryIntAttribute("hidden", &hidden) == TIXML_SUCCESS) {
-      if (hidden != 0) {
-        _hidden = true;
-      }
-    }
-
-    const char *log_basename = xconfig->Attribute("log_basename");
-    if (log_basename != nullptr) {
-      _log_basename = log_basename;
-    }
-
-    const char *prc_name = xconfig->Attribute("prc_name");
-    if (prc_name != nullptr) {
-      _prc_name = prc_name;
-    }
-
-    const char *start_dir = xconfig->Attribute("start_dir");
-    if (start_dir != nullptr) {
-      _start_dir = start_dir;
-    }
-
-    const char *run_origin = xconfig->Attribute("run_origin");
-    if (run_origin != nullptr) {
-      _matches_run_origin = check_matches_origin(run_origin);
-    }
-
-    const char *script_origin = xconfig->Attribute("script_origin");
-    if (script_origin != nullptr) {
-      _matches_script_origin = check_matches_origin(script_origin);
-    }
-
-    int allow_python_dev = 0;
-    if (xconfig->QueryIntAttribute("allow_python_dev", &allow_python_dev) == TIXML_SUCCESS) {
-      _allow_python_dev = (allow_python_dev != 0);
-    }
-
-    int keep_user_env = 0;
-    if (xconfig->QueryIntAttribute("keep_user_env", &keep_user_env) == TIXML_SUCCESS) {
-      _keep_user_env = (keep_user_env != 0);
-    }
-
-    int auto_start = 0;
-    if (xconfig->QueryIntAttribute("auto_start", &auto_start) == TIXML_SUCCESS) {
-      _auto_start = (auto_start != 0);
-    }
-
-    int auto_install = 0;
-    if (xconfig->QueryIntAttribute("auto_install", &auto_install) == TIXML_SUCCESS) {
-      _auto_install = (auto_install != 0);
-    }
-  }
-
-  nout << "_matches_run_origin = " << _matches_run_origin << "\n";
-  nout << "_matches_script_origin = " << _matches_script_origin << "\n";
-
-  if (inst_mgr->get_trusted_environment()) {
-    // If we're in a trusted environment, it is as if the origin always
-    // matches.
-    _matches_run_origin = true;
-    _matches_script_origin = true;
-  }
-
-  if (_auth_button_clicked) {
-    // But finally, if the user has already clicked through the red "auth"
-    // button, no need to present himher with another green "play" button as
-    // well.
-    _auto_install = true;
-    _auto_start = true;
-  }
-
-  nout << "_auto_install = " << _auto_install
-       << ", _auto_start = " << _auto_start
-       << ", _stop_on_ready = " << _stop_on_ready
-       << "\n";
-
-  if (_hidden && _got_wparams) {
-    _wparams.set_window_type(P3D_WT_hidden);
-  }
-
-  if (!_matches_run_origin) {
-    nout << "Cannot run " << _p3d_basename << " from origin "
-         << _origin_protocol << "//" << _origin_hostname
-         << ":" << _origin_port << "\n";
-    set_failed();
-  }
-}
-
-/**
- * Adds the "panda3d" package only.  This package must be downloaded first,
- * and its desc file examined, before we can begin downloading the other
- * packages.
- */
-void P3DInstance::
-add_panda3d_package() {
-  assert(!_packages_specified);
-  assert(_panda3d_package == nullptr);
-  if (_xpackage == nullptr) {
-    return;
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  TiXmlElement *xrequires = _xpackage->FirstChildElement("requires");
-  while (xrequires != nullptr) {
-    const char *name = xrequires->Attribute("name");
-    const char *host_url = xrequires->Attribute("host");
-    if (name != nullptr && host_url != nullptr && strcmp(name, "panda3d") == 0) {
-      const char *version = xrequires->Attribute("version");
-      if (version == nullptr) {
-        version = "";
-      }
-      const char *seq = xrequires->Attribute("seq");
-      if (seq == nullptr) {
-        seq = "";
-      }
-      P3DHost *host = inst_mgr->get_host(host_url);
-      add_package(name, version, seq, host);
-      return;
-    }
-
-    xrequires = xrequires->NextSiblingElement("requires");
-  }
-
-  nout << "No panda3d package found in " << _fparams.get_p3d_filename() << "\n";
-  set_failed();
-}
-
-/**
- * Adds the set of packages required by this p3d file to the _packages member.
- * If _auto_install is true, this will also start downloading them.
- */
-void P3DInstance::
-add_packages() {
-  assert(!_packages_specified);
-  if (_xpackage == nullptr) {
-    return;
-  }
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  TiXmlElement *xrequires = _xpackage->FirstChildElement("requires");
-  while (xrequires != nullptr) {
-    const char *name = xrequires->Attribute("name");
-    const char *host_url = xrequires->Attribute("host");
-    if (name != nullptr && host_url != nullptr) {
-      const char *version = xrequires->Attribute("version");
-      if (version == nullptr) {
-        version = "";
-      }
-      const char *seq = xrequires->Attribute("seq");
-      if (seq == nullptr) {
-        seq = "";
-      }
-      P3DHost *host = inst_mgr->get_host(host_url);
-      add_package(name, version, seq, host);
-    }
-
-    xrequires = xrequires->NextSiblingElement("requires");
-  }
-
-  _packages_specified = true;
-
-  consider_start_download();
-
-  // Now that we've scanned the p3d file, and prepared the list of packages,
-  // it's safe to set the trusted flag.
-  _p3d_trusted = true;
-
-  // If the packages are already downloaded, start the instance rolling.
-  if (get_packages_ready()) {
-    mark_download_complete();
-  }
-}
-
-
-/**
- * Looks in the p3d_info.xml file for the alt_host associated with the
- * indicated host_url, if any.  Returns empty string if there is no match.
- */
-string P3DInstance::
-find_alt_host_url(const string &host_url, const string &alt_host) {
-  TiXmlElement *xhost = _xpackage->FirstChildElement("host");
-  while (xhost != nullptr) {
-    const char *url = xhost->Attribute("url");
-    if (url != nullptr && host_url == url) {
-      // This matches the host.  Now do we have a matching alt_host keyword
-      // for this host?
-      TiXmlElement *xalt_host = xhost->FirstChildElement("alt_host");
-      while (xalt_host != nullptr) {
-        const char *keyword = xalt_host->Attribute("keyword");
-        if (keyword != nullptr && alt_host == keyword) {
-          const char *alt_host_url = xalt_host->Attribute("url");
-          if (alt_host_url != nullptr) {
-            return alt_host_url;
-          }
-        }
-        xalt_host = xalt_host->NextSiblingElement("alt_host");
-      }
-    }
-    xhost = xhost->NextSiblingElement("host");
-  }
-
-  return string();
-}
-
-/**
- * Looks in the p3d_info.xml file for the auxiliary host information for the
- * selected host.  Some of this information is helpful to have before the host
- * has read its own contents.xml file (particularly the host_dir
- * specification).
- */
-void P3DInstance::
-get_host_info(P3DHost *host) {
-  // We should only call this function if we haven't already read the host's
-  // more-authoritative contents.xml file.
-  assert(!host->has_contents_file());
-
-  TiXmlElement *xhost = _xpackage->FirstChildElement("host");
-  while (xhost != nullptr) {
-    const char *url = xhost->Attribute("url");
-    if (url != nullptr && host->get_host_url() == url) {
-      // Found the entry for this particular host.
-      host->read_xhost(xhost);
-      return;
-    }
-    xhost = xhost->NextSiblingElement("host");
-  }
-
-  // Didn't find an entry for this host; oh well.
-}
-
-/**
- * Determines the local path to the appropriate start directory for this
- * instance, within the generic "start" directory.  Returns empty string if
- * this instance doesn't specify a custom start directory.
- *
- * If this is nonempty, it will begin with a slash--the intention is to append
- * this to the end of the generic start_dir path.
- */
-string P3DInstance::
-get_start_dir_suffix() const {
-  string start_dir_suffix;
-
-  string start_dir = get_fparams().lookup_token("start_dir");
-  if (start_dir.empty()) {
-    start_dir = _start_dir;
-
-    if (!start_dir.empty()) {
-      // If the start_dir is taken from the p3d file (and not from the HTML
-      // tokens), then we also append the alt_host name to the start_dir, so
-      // that each alt_host variant will run in a different directory.
-      string alt_host = get_fparams().lookup_token("alt_host");
-      if (!alt_host.empty()) {
-        start_dir += "_";
-        start_dir += alt_host;
-      }
-    }
-  }
-  if (!start_dir.empty()) {
-    P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-    inst_mgr->append_safe_dir(start_dir_suffix, start_dir);
-  }
-
-  return start_dir_suffix;
-}
-
-/**
- * Sends the XML sequence to inform the session of our browser's toplevel
- * window object.
- */
-void P3DInstance::
-send_browser_script_object() {
-  TiXmlDocument *doc = new TiXmlDocument;
-  TiXmlElement *xcommand = new TiXmlElement("command");
-  xcommand->SetAttribute("cmd", "pyobj");
-  xcommand->SetAttribute("op", "set_browser_script_object");
-  if (_dom_object != nullptr) {
-    xcommand->LinkEndChild(_session->p3dobj_to_xml(_dom_object));
-  }
-
-  doc->LinkEndChild(xcommand);
-
-  _session->send_command(doc);
-}
-
-/**
- * Creates a new P3D_request structure from the XML. Returns NULL if no
- * request is needed.
- */
-P3D_request *P3DInstance::
-make_p3d_request(TiXmlElement *xrequest) {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  P3D_request *request = nullptr;
-
-  const char *rtype = xrequest->Attribute("rtype");
-  if (rtype != nullptr) {
-    if (strcmp(rtype, "notify") == 0) {
-      const char *message = xrequest->Attribute("message");
-      if (message != nullptr) {
-        // A notify message from Python code.
-        request = new P3D_request;
-        request->_instance = nullptr;
-        request->_request_type = P3D_RT_notify;
-        request->_request._notify._message = strdup(message);
-        handle_notify_request(message);
-      }
-
-    } else if (strcmp(rtype, "script") == 0) {
-      // We don't actually build a P3D_request for a script request; we always
-      // just handle it immediately.
-      const char *operation = xrequest->Attribute("operation");
-      TiXmlElement *xobject = xrequest->FirstChildElement("object");
-      const char *property_name = xrequest->Attribute("property_name");
-      int needs_response = 0;
-      xrequest->Attribute("needs_response", &needs_response);
-      int unique_id = 0;
-      xrequest->Attribute("unique_id", &unique_id);
-
-      P3D_object *value = nullptr;
-      TiXmlElement *xvalue = xrequest->FirstChildElement("value");
-      if (xvalue != nullptr) {
-        value = _session->xml_to_p3dobj(xvalue);
-      }
-      if (value == nullptr) {
-        value = inst_mgr->new_none_object();
-      }
-
-      if (operation != nullptr && xobject != nullptr) {
-        P3D_object *object = _session->xml_to_p3dobj(xobject);
-
-        if (property_name == nullptr) {
-          property_name = "";
-        }
-
-        handle_script_request(operation, object, property_name, value,
-                              (needs_response != 0), unique_id);
-      }
-
-      P3D_OBJECT_DECREF(value);
-
-    } else if (strcmp(rtype, "drop_p3dobj") == 0) {
-      int object_id;
-      if (xrequest->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) {
-        // We no longer need to keep this reference.
-        _session->drop_p3dobj(object_id);
-      }
-
-    } else if (strcmp(rtype, "stop") == 0) {
-      // A stop request from Python code.  This is kind of weird, but OK.
-      request = new P3D_request;
-      request->_instance = nullptr;
-      request->_request_type = P3D_RT_stop;
-
-    } else if (strcmp(rtype, "forget_package") == 0) {
-      const char *host_url = xrequest->Attribute("host_url");
-      if (host_url != nullptr) {
-        // A Python-level request to remove a package from the cache.
-        request = new P3D_request;
-        request->_instance = nullptr;
-        request->_request_type = P3D_RT_forget_package;
-        request->_request._forget_package._host_url = strdup(host_url);
-        request->_request._forget_package._package_name = nullptr;
-        request->_request._forget_package._package_version = nullptr;
-
-        const char *package_name = xrequest->Attribute("package_name");
-        const char *package_version = xrequest->Attribute("package_version");
-        if (package_name != nullptr) {
-          request->_request._forget_package._package_name = strdup(package_name);
-          if (package_version != nullptr) {
-            request->_request._forget_package._package_version = strdup(package_version);
-          }
-        }
-      }
-
-    } else {
-      nout << "Ignoring request of type " << rtype << "\n";
-    }
-  }
-
-  if (request != nullptr) {
-    assert(request->_instance == nullptr);
-    request->_instance = this;
-    ref();
-  }
-  return request;
-}
-
-/**
- * Called (in the main thread) when a notify request is received from the
- * subprocess.
- */
-void P3DInstance::
-handle_notify_request(const string &message) {
-  // We look for certain notify events that have particular meaning to this
-  // instance.
-  if (message == "onpythonload") {
-    // Once Python is up and running, we can get the actual main object from
-    // the Python side, and merge it with our own.
-
-    TiXmlDocument *doc = new TiXmlDocument;
-    TiXmlElement *xcommand = new TiXmlElement("command");
-    xcommand->SetAttribute("cmd", "pyobj");
-    xcommand->SetAttribute("op", "get_panda_script_object");
-    doc->LinkEndChild(xcommand);
-    TiXmlDocument *response = _session->command_and_response(doc);
-
-    P3D_object *result = nullptr;
-    if (response != nullptr) {
-      TiXmlElement *xresponse = response->FirstChildElement("response");
-      if (xresponse != nullptr) {
-        TiXmlElement *xvalue = xresponse->FirstChildElement("value");
-        if (xvalue != nullptr) {
-          result = _session->xml_to_p3dobj(xvalue);
-        }
-      }
-      delete response;
-    }
-
-    if (result != nullptr) {
-      if (_matches_script_origin) {
-        // We only actually merge the objects if this web page is allowed to
-        // call our scripting functions.
-        _main_object->set_pyobj(result);
-      } else {
-        // Otherwise, we just do a one-time application of the toplevel
-        // properties down to Python.
-        _main_object->apply_properties(result);
-      }
-      P3D_OBJECT_DECREF(result);
-    }
-
-    _main_object->set_string_property("status", "starting");
-
-  } else if (message == "onwindowopen") {
-    // The process told us that it just successfully opened its window, for
-    // the first time.  Hide the splash window.
-    _instance_window_opened = true;
-    if (_splash_window != nullptr) {
-      _splash_window->set_visible(false);
-    }
-
-    // Guess we won't be using (most of) these images any more.
-    for (int i = 0; i < (int)IT_num_image_types; ++i) {
-      if (i != IT_active) {
-        _image_files[i].cleanup();
-      }
-    }
-
-    _main_object->set_string_property("status", "open");
-
-  } else if (message == "onwindowattach") {
-    // The graphics window has been attached to the browser frame (maybe
-    // initially, maybe later).  Hide the splash window.
-
-    // We don't actually hide the splash window immediately on OSX, because on
-    // that platform, we can hide it as soon as we render the first frame,
-    // avoiding an empty frame in that little period of time between the
-    // window opening and the first frame being drawn.
-    _instance_window_attached = true;
-#ifndef __APPLE__
-    if (_splash_window != nullptr) {
-      _splash_window->set_visible(false);
-    }
-#endif  // __APPLE__
-
-#ifdef __APPLE__
-    // Start a timer to update the frame repeatedly.  This seems to be
-    // steadier than waiting for nullEvent.
-    if (_frame_timer == nullptr) {
-      CFRunLoopTimerContext timer_context;
-      memset(&timer_context, 0, sizeof(timer_context));
-      timer_context.info = this;
-      _frame_timer = CFRunLoopTimerCreate
-        (nullptr, 0, 1.0 / 60.0, 0, 0, timer_callback, &timer_context);
-      CFRunLoopRef run_loop = CFRunLoopGetCurrent();
-      CFRunLoopAddTimer(run_loop, _frame_timer, kCFRunLoopCommonModes);
-    }
-#endif  // __APPLE__
-
-  } else if (message == "onwindowdetach") {
-    // The graphics window has been removed from the browser frame.  Restore
-    // the splash window.
-    _instance_window_opened = true;
-    _instance_window_attached = false;
-    set_background_image(IT_active);
-    if (_splash_window != nullptr) {
-      _splash_window->set_visible(true);
-    }
-
-#ifdef __APPLE__
-    // Stop the frame timer; we don't need it any more.
-    if (_frame_timer != nullptr) {
-      CFRunLoopTimerInvalidate(_frame_timer);
-      CFRelease(_frame_timer);
-      _frame_timer = nullptr;
-    }
-#endif  // __APPLE__
-
-  } else if (message == "buttonclick") {
-    // We just got a special "button click" message from the sub-thread.  This
-    // case is a little unusual, as it came from the splash window and not
-    // from Python (we presumably haven't even started Python yet).  We use
-    // this as a sneaky way to forward the event from the sub-thread to the
-    // main thread.
-    splash_button_clicked_main_thread();
-
-  } else if (message == "authfinished") {
-    // Similarly for the "auth finished" message.
-    auth_finished_main_thread();
-
-  } else if (message == "keyboardfocus") {
-    if (_splash_window != nullptr) {
-      _splash_window->request_keyboard_focus();
-    }
-  }
-}
-
-/**
- * Called (in the main thread) when a script request is received from the
- * subprocess.
- */
-void P3DInstance::
-handle_script_request(const string &operation, P3D_object *object,
-                      const string &property_name, P3D_object *value,
-                      bool needs_response, int unique_id) {
-
-  TiXmlDocument *doc = new TiXmlDocument;
-  TiXmlElement *xcommand = new TiXmlElement("command");
-  xcommand->SetAttribute("cmd", "script_response");
-  xcommand->SetAttribute("unique_id", unique_id);
-
-  doc->LinkEndChild(xcommand);
-
-  if (operation == "get_property") {
-    P3D_object *result = P3D_OBJECT_GET_PROPERTY(object, property_name.c_str());
-
-    // We've got the property value; feed it back down to the subprocess.
-
-    if (result != nullptr) {
-      xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
-      P3D_OBJECT_DECREF(result);
-    }
-
-  } else if (operation == "set_property") {
-    bool result =
-      P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), true, value);
-
-    TiXmlElement *xvalue = new TiXmlElement("value");
-    xvalue->SetAttribute("type", "bool");
-    xvalue->SetAttribute("value", (int)result);
-    xcommand->LinkEndChild(xvalue);
-
-  } else if (operation == "del_property") {
-    bool result = P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), true, nullptr);
-
-    TiXmlElement *xvalue = new TiXmlElement("value");
-    xvalue->SetAttribute("type", "bool");
-    xvalue->SetAttribute("value", (int)result);
-    xcommand->LinkEndChild(xvalue);
-
-  } else if (operation == "has_method") {
-    bool result = P3D_OBJECT_HAS_METHOD(object, property_name.c_str());
-
-    TiXmlElement *xvalue = new TiXmlElement("value");
-    xvalue->SetAttribute("type", "bool");
-    xvalue->SetAttribute("value", (int)result);
-    xcommand->LinkEndChild(xvalue);
-
-  } else if (operation == "call") {
-    // Convert the single value parameter into an array of parameters.
-    P3D_object **values = &value;
-    int num_values = 1;
-    if (value->_class == &P3DObject::_object_class) {
-      P3DObject *p3dobj = (P3DObject *)value;
-      if (p3dobj->get_object_array_size() != -1) {
-        values = p3dobj->get_object_array();
-        num_values = p3dobj->get_object_array_size();
-      }
-    }
-
-    P3D_object *result =
-      P3D_OBJECT_CALL(object, property_name.c_str(), needs_response,
-                      values, num_values);
-
-    if (result != nullptr) {
-      xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
-      P3D_OBJECT_DECREF(result);
-    }
-
-  } else if (operation == "eval") {
-    P3D_object *result;
-    int size = P3D_OBJECT_GET_STRING(value, nullptr, 0);
-    char *buffer = new char[size + 1];
-    P3D_OBJECT_GET_STRING(value, buffer, size + 1);
-    result = P3D_OBJECT_EVAL(object, buffer);
-    delete[] buffer;
-
-    if (result != nullptr) {
-      xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
-      P3D_OBJECT_DECREF(result);
-    }
-  }
-
-  if (needs_response) {
-    _session->send_command(doc);
-  } else {
-    delete doc;
-  }
-}
-
-/**
- * Sets the "failed" indication to display sadness to the user--we're unable
- * to launch the instance for some reason.
- */
-void P3DInstance::
-set_failed() {
-  set_button_image(IT_none);
-  set_background_image(IT_failed);
-  _main_object->set_string_property("status", "failed");
-
-  if (!_failed) {
-    _failed = true;
-    make_splash_window();
-    send_notify("onfail");
-  }
-}
-
-/**
- * Creates the splash window to be displayed at startup, if it's time.
- */
-void P3DInstance::
-make_splash_window() {
-  // Should we make the splash window visible?
-  bool make_visible = true;
-  if (_instance_window_opened) {
-    // Not once we've opened the main window.
-    make_visible = false;
-  }
-
-  if (_wparams.get_window_type() != P3D_WT_embedded &&
-      !_stuff_to_download && _auto_install &&
-      (_auto_start || _stop_on_ready) && _p3d_trusted) {
-    // If it's a toplevel or fullscreen window, then we don't want a splash
-    // window unless we have stuff to download, or a button to display.
-    make_visible = false;
-  }
-
-  if (is_failed()) {
-    // But, if we've failed to launch somehow, we need to let the user know.
-    make_visible = true;
-  }
-
-  if (_splash_window != nullptr) {
-    // Already got one.
-    _splash_window->set_visible(make_visible);
-    return;
-  }
-  if (!_got_wparams) {
-    // Don't know where to put it yet.
-    return;
-  }
-  if (_wparams.get_window_type() == P3D_WT_hidden && !is_failed()) {
-    // We're hidden, and so is the splash window.  (But if we've got a failure
-    // case to report, we don't care and create the splash window anyway.)
-    return;
-  }
-
-  _splash_window = new SplashWindowType(this, make_visible);
-
-  // Get the splash window colors.  We must set these *before* we call
-  // set_wparams.
-  if (_fparams.has_token("fgcolor")) {
-    int r, g, b;
-    if (parse_color(r, g, b, _fparams.lookup_token("fgcolor"))) {
-      _splash_window->set_fgcolor(r, g, b);
-    } else {
-      nout << "parse failure on fgcolor " << _fparams.lookup_token("fgcolor") << "\n";
-    }
-  }
-  if (_fparams.has_token("bgcolor")) {
-    int r, g, b;
-    if (parse_color(r, g, b, _fparams.lookup_token("bgcolor"))) {
-      _splash_window->set_bgcolor(r, g, b);
-      _splash_window->set_bar_bgcolor(r, g, b);
-    } else {
-      nout << "parse failure on bgcolor " << _fparams.lookup_token("bgcolor") << "\n";
-    }
-  }
-  if (_fparams.has_token("barcolor")) {
-    int r, g, b;
-    if (parse_color(r, g, b, _fparams.lookup_token("barcolor"))) {
-      _splash_window->set_barcolor(r, g, b);
-    } else {
-      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_install_label(_install_label);
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  // Go get the required images.
-  for (int i = 0; i < (int)IT_none; ++i) {
-    string token_keyword = string(_image_type_names[i]) + "_img";
-    if (!_fparams.has_token(token_keyword) && i < (int)IT_auth_ready) {
-      token_keyword = "splash_img";
-    }
-
-    string image_url = _fparams.lookup_token(token_keyword);
-    if (image_url.empty()) {
-      // No specific image for this type is specified; get the default image.
-      // We do this via the P3DPackage interface, so we can use the cached
-      // version on disk if it's good.
-      _image_files[i]._use_standard_image = true;
-
-    } else {
-      // We have an explicit image specified for this slot, so just download
-      // it directly.  This one won't be cached locally (though the browser
-      // might be free to cache it).
-      _image_files[i]._use_standard_image = false;
-      _image_files[i]._filename.clear();
-
-      // Make a temporary file to receive the splash image.
-      assert(_image_files[i]._temp_filename == nullptr);
-      _image_files[i]._temp_filename = new P3DTemporaryFile(".jpg");
-
-      // Start downloading the requested image.
-      ImageDownload *download = new ImageDownload(this, i);
-      download->set_url(image_url);
-      download->set_filename(_image_files[i]._temp_filename->get_filename());
-
-      start_download(download);
-    }
-  }
-
-  if (_current_background_image != IT_none) {
-    _splash_window->set_image_filename(_image_files[_current_background_image]._filename, P3DSplashWindow::IP_background);
-  }
-
-  if (_current_button_image != IT_none) {
-    _splash_window->set_image_filename(_image_files[_current_button_image]._filename, P3DSplashWindow::IP_button_ready);
-    _splash_window->set_image_filename(_image_files[_current_button_image + 1]._filename, P3DSplashWindow::IP_button_rollover);
-    _splash_window->set_image_filename(_image_files[_current_button_image + 2]._filename, P3DSplashWindow::IP_button_click);
-    _splash_window->set_button_active(true);
-  }
-}
-
-/**
- * Specifies the particular image that should be displayed as the background
- * image in the splash window.  Specify IT_none to take the background image
- * away.
- */
-void P3DInstance::
-set_background_image(ImageType image_type) {
-  if (is_failed()) {
-    // Can't change the background again after we've failed.
-    return;
-  }
-
-  if (image_type != _current_background_image) {
-    nout << "setting background to " << _image_type_names[image_type]
-         << ", splash_window = " << _splash_window << "\n";
-    // Remove the previous image.
-    _image_files[_current_background_image]._image_placement = P3DSplashWindow::IP_none;
-
-    // Install the new image.
-    _current_background_image = image_type;
-    if (_current_background_image != IT_none) {
-      _image_files[_current_background_image]._image_placement = P3DSplashWindow::IP_background;
-    }
-
-    // Update the splash window.
-    if (_splash_window != nullptr) {
-      _splash_window->set_image_filename(_image_files[_current_background_image]._filename, P3DSplashWindow::IP_background);
-    }
-  }
-}
-
-/**
- * Specifies the particular image that should be displayed as the button image
- * in the splash window.  Specify IT_none to take the button image away.
- *
- * This actually defines a trilogy of button images: ready, rollover, click.
- */
-void P3DInstance::
-set_button_image(ImageType image_type) {
-  if (is_failed()) {
-    // Can't set the button again after we've failed.
-    return;
-  }
-
-  if (image_type != _current_button_image) {
-    nout << "setting button to " << _image_type_names[image_type] << "\n";
-    // Remove the previous image.
-    _image_files[_current_button_image]._image_placement = P3DSplashWindow::IP_none;
-    if (_current_button_image != IT_none) {
-      _image_files[_current_button_image + 1]._image_placement = P3DSplashWindow::IP_none;
-      _image_files[_current_button_image + 2]._image_placement = P3DSplashWindow::IP_none;
-    }
-
-    // Install the new image.
-    _current_button_image = image_type;
-    if (_current_button_image != IT_none) {
-      _image_files[_current_button_image]._image_placement = P3DSplashWindow::IP_button_ready;
-      _image_files[_current_button_image + 1]._image_placement = P3DSplashWindow::IP_button_rollover;
-      _image_files[_current_button_image + 2]._image_placement = P3DSplashWindow::IP_button_click;
-    }
-
-    // Update the splash window.
-    if (_splash_window != nullptr) {
-      if (_current_button_image != IT_none) {
-        _splash_window->set_image_filename(_image_files[_current_button_image]._filename, P3DSplashWindow::IP_button_ready);
-        _splash_window->set_image_filename(_image_files[_current_button_image + 1]._filename, P3DSplashWindow::IP_button_rollover);
-        _splash_window->set_image_filename(_image_files[_current_button_image + 2]._filename, P3DSplashWindow::IP_button_click);
-        _splash_window->set_button_active(true);
-      } else {
-        _splash_window->set_button_active(false);
-      }
-    }
-
-  } else {
-    // We're not changing the button graphic, but we might be re-activating
-    // it.
-    if (_splash_window != nullptr) {
-      if (_current_button_image != IT_none) {
-        _splash_window->set_button_active(true);
-      } else {
-        _splash_window->set_button_active(false);
-      }
-    }
-  }
-}
-
-/**
- * Notified when a package information has been successfully downloaded and
- * the package is idle, waiting for activate_download() to be called.
- */
-void P3DInstance::
-report_package_info_ready(P3DPackage *package) {
-  nout << "report_package_info_ready: " << package->get_package_name() << "\n";
-  if (package == _image_package || package == _certlist_package ||
-      package == _p3dcert_package) {
-    // A special case: these packages get immediately downloaded, without
-    // waiting for anything else.
-    if (package == _certlist_package || package == _p3dcert_package) {
-      // If we're downloading one of the two cert packages, though, put up a
-      // progress bar.
-      make_splash_window();
-      if (_splash_window != nullptr) {
-        _splash_window->set_install_progress(0.0, true, 0);
-      }
-      if (package == _certlist_package) {
-        set_install_label("Getting Certificates");
-      } else {
-        set_install_label("Getting Authorization Dialog");
-      }
-    }
-
-    package->activate_download();
-    return;
-  }
-
-  if (package == _panda3d_package && !_packages_specified) {
-    // Another special case.  Once the special panda3d package is ready to
-    // download (and we know what platform it belongs to), we can begin to
-    // download the remaining required packages.
-    string package_platform = package->get_package_platform();
-    if (!package_platform.empty() && _session_platform.empty()) {
-      // From now on, all platform-specific files downloaded by this session
-      // will be for this platform.
-      _session_platform = package_platform;
-    }
-    if (_session_platform != package_platform) {
-      nout << "Error: session is " << _session_platform
-           << ", but we somehow got panda3d for " << package_platform << "\n";
-      set_failed();
-      return;
-    }
-
-    add_packages();
-  }
-
-  // Now that the package's info is ready, we know its set of required
-  // packages, and we can add these to the list.
-  P3DPackage::Requires::const_iterator ri;
-  for (ri = package->_requires.begin(); ri != package->_requires.end(); ++ri) {
-    const P3DPackage::RequiredPackage &rp = (*ri);
-    add_package(rp._package_name, rp._package_version, rp._package_seq,
-                rp._host);
-  }
-
-  consider_start_download();
-}
-
-/**
- * When all package info files have been obtained, begins downloading stuff.
- */
-void P3DInstance::
-consider_start_download() {
-  if (get_packages_info_ready()) {
-    // All packages are ready to go.  Let's start some download action.
-    _downloading_packages.clear();
-    _prev_downloaded = 0;
-    _total_download_size = 0;
-    Packages::const_iterator pi;
-    for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
-      P3DPackage *package = (*pi);
-      if (package->get_info_ready()) {
-        if (!package->get_ready()) {
-          _downloading_packages.push_back(package);
-          _total_download_size += package->get_download_size();
-        } else {
-          _prev_downloaded += package->get_download_size();
-        }
-      }
-    }
-    ready_to_install();
-  }
-}
-
-/**
- * Called when it's time to start the package download process.
- */
-void P3DInstance::
-ready_to_install() {
-  if (_downloading_packages.empty() && _download_complete) {
-    // We have already been here.  Ignore it.
-
-  } else if (!_auto_install && !_download_started) {
-    // Not authorized to download yet.  We're waiting for the user to
-    // acknowledge the download.
-    set_background_image(IT_ready);
-    set_button_image(IT_play_ready);
-
-  } else {
-    _download_started = true;
-    _download_complete = false;
-    _download_package_index = 0;
-    _total_downloaded = 0;
-
-    // Record the time we started the package download, so we can report
-    // downloadElapsedTime and predict downloadRemainingTime.
-#ifdef _WIN32
-    _start_dl_tick = GetTickCount();
-#else
-    gettimeofday(&_start_dl_timeval, nullptr);
-#endif
-
-    nout << "Beginning install of " << _downloading_packages.size()
-         << " packages, total " << _total_download_size
-         << " bytes required (" << _prev_downloaded
-         << " previously downloaded).\n";
-
-    if (_downloading_packages.size() > 0) {
-      _stuff_to_download = true;
-
-      // Maybe it's time to open a splash window now.
-      make_splash_window();
-    }
-
-    _main_object->set_string_property("status", "downloading");
-    _main_object->set_int_property("numDownloadingPackages", _downloading_packages.size());
-    _main_object->set_int_property("totalDownloadSize", _total_download_size);
-    _main_object->set_int_property("downloadElapsedSeconds", 0);
-    _main_object->set_undefined_property("downloadRemainingSeconds");
-
-    double progress = 0.0;
-    if (_prev_downloaded != 0) {
-      // We might start off with more than 0 progress, if we've already
-      // downloaded some of it previously.
-      progress = (_prev_downloaded) / (_total_download_size + _prev_downloaded);
-      progress = min(progress, 1.0);
-
-      _main_object->set_float_property("downloadProgress", progress);
-    }
-    if (_splash_window != nullptr) {
-      _splash_window->set_install_progress(progress, true, 0);
-    }
-
-    send_notify("ondownloadbegin");
-
-    start_next_download();
-  }
-}
-
-/**
- * Checks whether all packages are ready and waiting to be downloaded; if so,
- * starts the next package in sequence downloading.
- */
-void P3DInstance::
-start_next_download() {
-  while (_download_package_index < (int)_downloading_packages.size()) {
-    P3DPackage *package = _downloading_packages[_download_package_index];
-    if (package->get_failed()) {
-      send_notify("ondownloadfail");
-      set_failed();
-      return;
-    }
-
-    if (!package->get_ready()) {
-      // This package is ready to download.  Begin.
-      string name = package->get_formatted_name();
-      _main_object->set_string_property("downloadPackageName", package->get_package_name());
-      _main_object->set_string_property("downloadPackageDisplayName", name);
-      _main_object->set_int_property("downloadPackageNumber", _download_package_index + 1);
-      _main_object->set_int_property("downloadPackageSize", package->get_download_size());
-      set_install_label("Installing " + name);
-
-      nout << "Installing " << package->get_package_name()
-           << ", package " << _download_package_index + 1
-           << " of " << _downloading_packages.size()
-           << ", " << package->get_download_size()
-           << " bytes.\n";
-
-      package->activate_download();
-      send_notify("ondownloadnext");
-      return;
-    }
-
-    // This package has been downloaded.  Move to the next.
-    _total_downloaded += package->get_download_size();
-    ++_download_package_index;
-  }
-
-  // Looks like we're all done downloading.  Launch!
-  _downloading_packages.clear();
-
-  if (get_packages_ready()) {
-    mark_download_complete();
-  }
-}
-
-/**
- * Called internally when all files needed to launch have been downloaded.
- */
-void P3DInstance::
-mark_download_complete() {
-  if (_failed) {
-    return;
-  }
-
-  if (!_download_complete) {
-    _download_complete = true;
-    _main_object->set_bool_property("downloadComplete", true);
-    _main_object->set_string_property("status", "downloadcomplete");
-    send_notify("ondownloadcomplete");
-  }
-
-  // Take down the download progress bar.
-  if (_splash_window != nullptr) {
-    _splash_window->set_install_progress(0.0, true, 0);
-  }
-  set_install_label("");
-
-  if (_got_wparams && _p3d_trusted) {
-    ready_to_start();
-  }
-}
-
-
-/**
- * Called internally when we have got the wparams and fparams and we have
- * downloaded all required packages.
- */
-void P3DInstance::
-ready_to_start() {
-  if (_instance_started || is_failed()) {
-    // Already started--or never mind.
-    return;
-  }
-
-  _main_object->set_string_property("status", "ready");
-  send_notify("onready");
-
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  if (_stop_on_ready) {
-    // If we've got the "stop_on_ready" token, then exit abruptly now, instead
-    // of displaying the splash window.
-    request_stop_main_thread();
-    return;
-  }
-
-  if (_auto_start) {
-    set_background_image(IT_launch);
-    inst_mgr->start_instance(this);
-
-  } else {
-    // We're fully downloaded, and waiting for the user to click play.
-    set_background_image(IT_ready);
-    set_button_image(IT_play_ready);
-  }
-}
-
-/**
- * Notified as the instance file is downloaded.
- */
-void P3DInstance::
-report_instance_progress(double progress, bool is_progress_known,
-                         size_t received_data) {
-  if (!_show_dl_instance_progress) {
-    // If we haven't yet set the download label, set it after a full second
-    // has elapsed.  We don't want to set it too soon, because we're not
-    // really sure how long it will take to download (the instance file might
-    // be already in the browser cache).
-#ifdef _WIN32
-    int now = GetTickCount();
-    double elapsed = (double)(now - _start_dl_tick) * 0.001;
-#else
-    struct timeval now;
-    gettimeofday(&now, nullptr);
-    double elapsed = (double)(now.tv_sec - _start_dl_timeval.tv_sec) +
-      (double)(now.tv_usec - _start_dl_timeval.tv_usec) / 1000000.0;
-#endif
-
-    // Put up the progress bar after 2 seconds have elapsed, if we've still
-    // got some distance to go; or after 5 seconds have elapsed regardless.
-    if ((elapsed > 2.0 && progress < 0.7) ||
-        (elapsed > 5.0)) {
-      _show_dl_instance_progress = true;
-      if (_fparams.has_token("p3d_install_label")) {
-        set_install_label(_fparams.lookup_token("p3d_install_label"));
-      } else {
-        set_install_label("Getting " + _p3d_basename);
-      }
-    }
-  }
-
-  if (_splash_window != nullptr && _show_dl_instance_progress) {
-    _splash_window->set_install_progress(progress, is_progress_known, received_data);
-  }
-  _main_object->set_float_property("instanceDownloadProgress", progress);
-}
-
-/**
- * Notified as the packages required by the instance file are downloaded.
- */
-void P3DInstance::
-report_package_progress(P3DPackage *package, double progress) {
-  if (package == _image_package) {
-    // Ignore this.
-    return;
-  }
-  if (package == _certlist_package || package == _p3dcert_package) {
-    // This gets its own progress bar.
-    if (_splash_window != nullptr) {
-      _splash_window->set_install_progress(progress, true, 0);
-    }
-    return;
-  }
-
-  if (_download_package_index >= (int)_downloading_packages.size() ||
-      package != _downloading_packages[_download_package_index]) {
-    // Quietly ignore a download progress report from an unexpected package.
-    return;
-  }
-
-  // Scale the progress into the range appropriate to this package.
-  progress = (progress * package->get_download_size() + _total_downloaded + _prev_downloaded) / (_total_download_size + _prev_downloaded);
-  progress = min(progress, 1.0);
-
-  if (_splash_window != nullptr) {
-    _splash_window->set_install_progress(progress, true, 0);
-  }
-  _main_object->set_float_property("downloadProgress", progress);
-
-  static const size_t buffer_size = 256;
-  char buffer[buffer_size];
-
-  // Get the floating-point elapsed time.
-#ifdef _WIN32
-  int now = GetTickCount();
-  double elapsed = (double)(now - _start_dl_tick) * 0.001;
-#else
-  struct timeval now;
-  gettimeofday(&now, nullptr);
-  double elapsed = (double)(now.tv_sec - _start_dl_timeval.tv_sec) +
-    (double)(now.tv_usec - _start_dl_timeval.tv_usec) / 1000000.0;
-#endif
-
-  int ielapsed = (int)elapsed;
-  _main_object->set_int_property("downloadElapsedSeconds", ielapsed);
-
-  sprintf(buffer, "%d:%02d", ielapsed / 60, ielapsed % 60);
-  _main_object->set_string_property("downloadElapsedFormatted", buffer);
-
-  if (progress > 0 && (elapsed > 5.0 || progress > 0.2)) {
-    double this_total = elapsed / progress;
-    double this_remaining = max(this_total - elapsed, 0.0);
-
-    // Age out any old time reports.
-    double old = elapsed - min(time_average, this_remaining);
-    while (!_time_reports.empty() && _time_reports.front()._report_time < old) {
-      TimeReport &tr0 = _time_reports.front();
-      _total_time_reports -= tr0._total;
-      _time_reports.pop_front();
-    }
-    if (_time_reports.empty()) {
-      _total_time_reports = 0.0;
-    }
-
-    // Add a new time report.
-    TimeReport tr;
-    tr._total = this_total;
-    tr._report_time = elapsed;
-    _time_reports.push_back(tr);
-    _total_time_reports += tr._total;
-
-    // Now get the average report.
-    if (!_time_reports.empty()) {
-      double total = _total_time_reports / (double)_time_reports.size();
-      double remaining = max(total - elapsed, 0.0);
-      int iremaining = (int)(remaining + 0.5);
-      _main_object->set_int_property("downloadRemainingSeconds", iremaining);
-      sprintf(buffer, "%d:%02d", iremaining / 60, iremaining % 60);
-      _main_object->set_string_property("downloadRemainingFormatted", buffer);
-    }
-  }
-}
-
-/**
- * Notified when a required package is fully downloaded, or failed.
- */
-void P3DInstance::
-report_package_done(P3DPackage *package, bool success) {
-  nout << "Done installing " << package->get_package_name()
-       << ": success = " << success << "\n";
-
-  if (package == _image_package) {
-    // A special case: we just downloaded the image package, so get the image
-    // files out of it and point them to the splash window.
-    string package_dir = package->get_package_dir();
-    const TiXmlElement *xconfig = package->get_xconfig();
-    if (xconfig == nullptr) {
-      nout << "No <config> entry in image package\n";
-      return;
-    }
-    package->mark_used();
-
-    for (int i = 0; i < (int)IT_none; ++i) {
-      if (_image_files[i]._use_standard_image) {
-        // This image indexes into the package.  Go get the standard image
-        // filename.
-        string token = string(_image_type_names[i]) + "_img";
-        const string *basename = xconfig->Attribute(token);
-        if (basename == nullptr) {
-          nout << "No entry in image package for " << token << "\n";
-        } else {
-          string image_filename = package_dir + "/" + *basename;
-          _image_files[i]._filename = image_filename;
-
-          // If the image should be on the window now, and the window still
-          // exists, put it up.
-          if (_splash_window != nullptr &&
-              _image_files[i]._image_placement != P3DSplashWindow::IP_none) {
-            P3DSplashWindow::ImagePlacement image_placement = _image_files[i]._image_placement;
-            _splash_window->set_image_filename(image_filename, image_placement);
-          }
-        }
-      }
-    }
-    return;
-  }
-
-  if (package == _certlist_package) {
-    // Another special case: successfully downloading certlist (or failing to
-    // download it) means we can finish checking the authenticity of the p3d
-    // file.
-
-    package->mark_used();
-
-    // Take down the download progress.
-    if (_splash_window != nullptr) {
-      _splash_window->set_install_progress(0.0, true, 0);
-    }
-    set_install_label("");
-
-    P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-    inst_mgr->read_certlist(package);
-    check_p3d_signature();
-    return;
-  }
-
-  if (package == _p3dcert_package) {
-    // Another special case: successfully downloading p3dcert means we can
-    // enable the auth button.
-
-    package->mark_used();
-
-    // Take down the download progress.
-    if (_splash_window != nullptr) {
-      _splash_window->set_install_progress(0.0, true, 0);
-    }
-    set_install_label("");
-    mark_p3d_untrusted();
-    return;
-  }
-
-  if (success) {
-    report_package_progress(package, 1.0);
-    start_next_download();
-  } else {
-    send_notify("ondownloadfail");
-    set_failed();
-  }
-}
-
-/**
- * Sets the install label that will be displayed on the splash window, if it
- * is present.
- */
-void P3DInstance::
-set_install_label(const string &install_label) {
-  _install_label = install_label;
-  if (_splash_window != nullptr) {
-    _splash_window->set_install_label(_install_label);
-  }
-}
-
-/**
- * Actually paints the rendered image to the browser window.  This is only
- * needed for OSX, where the child process isn't allowed to do it directly.
- */
-void P3DInstance::
-paint_window() {
-#ifdef __APPLE__
-  const P3D_window_handle &handle = _wparams.get_parent_window();
-  if (handle._window_handle_type == P3D_WHT_osx_port) {
-#if !__LP64__
-    paint_window_osx_port();
-#endif
-
-  } else if (handle._window_handle_type == P3D_WHT_osx_cgcontext) {
-    const P3D_window_handle &handle = _wparams.get_parent_window();
-    assert(handle._window_handle_type == P3D_WHT_osx_cgcontext);
-    CGContextRef context = handle._handle._osx_cgcontext._context;
-
-    paint_window_osx_cgcontext(context);
-  }
-#endif  // __APPLE__
-}
-
-#if defined(__APPLE__) && !__LP64__
-/**
- * Fills _reversed_buffer with the pixels from the current frame, suitable for
- * rendering via the old QuickDraw interface.  Returns true on success, or
- * false if there is no Panda3D window visible.  Only needed on OSX.
- */
-bool P3DInstance::
-get_framebuffer_osx_port() {
-  if (_swbuffer == nullptr || !_instance_window_attached) {
-    // We don't have a Panda3D window yet.
-    return false;
-  }
-
-  // blit rendered framebuffer into window backing store
-  int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size());
-  int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size());
-  size_t rowsize = _swbuffer->get_row_size();
-
-  if (_swbuffer->ready_for_read()) {
-    // Copy the new framebuffer image from the child process.
-    const void *framebuffer = _swbuffer->open_read_framebuffer();
-
-    // We have to reverse the image vertically first (different conventions
-    // between Panda and Mac).
-    for (int yi = 0; yi < y_size; ++yi) {
-#ifndef __BIG_ENDIAN__
-      // On a little-endian machine, we only have to reverse the order of the
-      // rows.
-      memcpy(_reversed_buffer + (y_size - 1 - yi) * rowsize,
-             (char *)framebuffer + yi * rowsize,
-             rowsize);
-
-#else  // __BIG_ENDIAN__
-      // On a big-endian machine, we need to do more work.
-
-      // It appears that kBGRAPixelFormat, below, is ignored on big-endian
-      // machines, and it is treated as KARGBPixelFormat regardless of what we
-      // specify.  Vexing.  To compensate for this, we have to reverse the
-      // color channels ourselves on big-endian machines.
-
-      const char *source = (const char *)framebuffer + yi * rowsize;
-      const char *stop = source + x_size * 4;
-      char *dest = (_reversed_buffer + (y_size - 1 - yi) * rowsize);
-      while (source < stop) {
-        char b = source[0];
-        char g = source[1];
-        char r = source[2];
-        char a = source[3];
-        dest[0] = a;
-        dest[1] = r;
-        dest[2] = g;
-        dest[3] = b;
-        dest += 4;
-        source += 4;
-      }
-#endif
-    }
-
-    _swbuffer->close_read_framebuffer();
-
-    if (_splash_window != nullptr && _splash_window->get_visible()) {
-      // If the splash window is up, time to hide it.  We've just rendered a
-      // real frame.
-      _splash_window->set_visible(false);
-    }
-
-  } else {
-    // No frame ready.  Just re-paint the frame we had saved last time.
-  }
-
-  return true;
-}
-#endif  // __APPLE__
-
-#ifdef __APPLE__
-/**
- * Fills _reversed_buffer with the pixels from the current frame, suitable for
- * rendering via the new CoreGraphics interface.  Returns true on success, or
- * false if there is no Panda3D window visible.  Only needed on OSX.
- */
-bool P3DInstance::
-get_framebuffer_osx_cgcontext() {
-  if (_swbuffer == nullptr || !_instance_window_attached) {
-    // We don't have a Panda3D window yet.
-    return false;
-  }
-
-  // blit rendered framebuffer into window backing store
-  int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size());
-  int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size());
-  size_t rowsize = _swbuffer->get_row_size();
-
-  if (_swbuffer->ready_for_read()) {
-    // Copy the new framebuffer image from the child process.
-    const void *framebuffer = _swbuffer->open_read_framebuffer();
-    memcpy(_reversed_buffer, framebuffer, y_size * rowsize);
-    _swbuffer->close_read_framebuffer();
-
-    if (_splash_window != nullptr && _splash_window->get_visible()) {
-      // If the splash window is up, time to hide it.  We've just rendered a
-      // real frame.
-      _splash_window->set_visible(false);
-    }
-
-  } else {
-    // No frame ready.  Just re-paint the frame we had saved last time.
-  }
-
-  return true;
-}
-#endif  // __APPLE__
-
-#if defined(__APPLE__) && !__LP64__
-/**
- * Actually paints the rendered image to the browser window, using the OSX
- * deprecated QuickDraw interfaces.
- */
-void P3DInstance::
-paint_window_osx_port() {
-  if (!get_framebuffer_osx_port()) {
-    // No Panda3D window is showing.
-    return;
-  }
-
-  int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size());
-  int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size());
-  size_t rowsize = _swbuffer->get_row_size();
-
-  Rect src_rect = {0, 0, (short)y_size, (short)x_size};
-  Rect ddrc_rect = {0, 0, (short)y_size, (short)x_size};
-
-  QDErr err;
-
-  GWorldPtr pGWorld;
-  err = NewGWorldFromPtr(&pGWorld, k32BGRAPixelFormat, &src_rect, 0, 0, 0,
-                         _reversed_buffer, rowsize);
-  if (err != noErr) {
-    nout << " error in NewGWorldFromPtr, called from paint_window()\n";
-    return;
-  }
-
-  const P3D_window_handle &handle = _wparams.get_parent_window();
-  assert(handle._window_handle_type == P3D_WHT_osx_port);
-  GrafPtr out_port = handle._handle._osx_port._port;
-  GrafPtr port_save = nullptr;
-  Boolean port_changed = QDSwapPort(out_port, &port_save);
-
-  // Make sure the clipping rectangle isn't in the way.  Is there a better way
-  // to eliminate the cliprect from consideration?
-  Rect r = { 0, 0, 0x7fff, 0x7fff };
-  ClipRect(&r);
-
-  CopyBits(GetPortBitMapForCopyBits(pGWorld),
-           GetPortBitMapForCopyBits(out_port),
-           &src_rect, &ddrc_rect, srcCopy, 0);
-
-  if (port_changed) {
-    QDSwapPort(port_save, nullptr);
-  }
-
-  DisposeGWorld(pGWorld);
-}
-#endif  // __APPLE__
-
-#ifdef __APPLE__
-/**
- * Actually paints the rendered image to the browser window.  This is the
- * newer CoreGraphics implementation on OSX.
- */
-void P3DInstance::
-paint_window_osx_cgcontext(CGContextRef context) {
-  if (!get_framebuffer_osx_cgcontext()) {
-    // No Panda3D window is showing.
-    return;
-  }
-
-  int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size());
-  int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size());
-
-  if (_buffer_image != nullptr) {
-    CGRect region = { { 0, 0 }, { (CGFloat)x_size, (CGFloat)y_size } };
-    CGContextDrawImage(context, region, _buffer_image);
-  }
-}
-#endif  // __APPLE__
-
-/**
- * Responds to the deprecated Carbon event types in Mac OSX.
- */
-bool P3DInstance::
-handle_event_osx_event_record(const P3D_event_data &event) {
-  bool retval = false;
-
-#if defined(__APPLE__) && !__LP64__
-  assert(event._event_type == P3D_ET_osx_event_record);
-  EventRecord *er = event._event._osx_event_record._event;
-
-  Point pt = er->where;
-
-  // Need to ensure we have the correct port set, in order to convert the
-  // mouse coordinates successfully via GlobalToLocal().
-  const P3D_window_handle &handle = _wparams.get_parent_window();
-  if (handle._window_handle_type == P3D_WHT_osx_port) {
-    GrafPtr out_port = handle._handle._osx_port._port;
-    GrafPtr port_save = nullptr;
-    Boolean port_changed = QDSwapPort(out_port, &port_save);
-
-    GlobalToLocal(&pt);
-
-    if (port_changed) {
-      QDSwapPort(port_save, nullptr);
-    }
-  } else {
-    // First, convert the coordinates from screen coordinates to browser
-    // window coordinates.
-    WindowRef window = handle._handle._osx_cgcontext._window;
-    CGPoint cgpt = { (CGFloat)pt.h, (CGFloat)pt.v };
-    HIPointConvert(&cgpt, kHICoordSpaceScreenPixel, nullptr,
-                   kHICoordSpaceWindow, window);
-
-    // Then convert to plugin coordinates.
-    pt.h = (short)(cgpt.x - _wparams.get_win_x());
-    pt.v = (short)(cgpt.y - _wparams.get_win_y());
-  }
-
-  SubprocessWindowBuffer::Event swb_event;
-  swb_event._source = SubprocessWindowBuffer::ES_none;
-  swb_event._type = SubprocessWindowBuffer::ET_none;
-  swb_event._code = 0;
-  swb_event._flags = 0;
-  add_carbon_modifier_flags(swb_event._flags, er->modifiers);
-
-  bool trust_mouse_data = true;
-
-  switch (er->what) {
-  case mouseDown:
-    swb_event._source = SubprocessWindowBuffer::ES_mouse;
-    swb_event._type = SubprocessWindowBuffer::ET_button_down;
-    retval = true;
-    break;
-
-  case mouseUp:
-    swb_event._source = SubprocessWindowBuffer::ES_mouse;
-    swb_event._type = SubprocessWindowBuffer::ET_button_up;
-    retval = true;
-    break;
-
-  case keyDown:
-  case keyUp:
-  case autoKey:
-    if (_swbuffer != nullptr) {
-      swb_event._source = SubprocessWindowBuffer::ES_keyboard;
-      swb_event._code = er->message;
-      if (er->what == keyUp) {
-        swb_event._type = SubprocessWindowBuffer::ET_button_up;
-      } else if (er->what == keyDown) {
-        swb_event._type = SubprocessWindowBuffer::ET_button_down;
-      } else {
-        swb_event._type = SubprocessWindowBuffer::ET_button_again;
-      }
-      retval = true;
-    }
-    break;
-
-  case updateEvt:
-    paint_window();
-    retval = true;
-    break;
-
-  case activateEvt:
-    _mouse_active = ((er->modifiers & 1) != 0);
-    break;
-
-  case osEvt:
-    // The mouse data sent with an "os event" seems to be in an indeterminate
-    // space.
-    trust_mouse_data = false;
-    break;
-
-  default:
-    break;
-  }
-
-  if (_mouse_active) {
-    swb_event._flags |= SubprocessWindowBuffer::EF_has_mouse;
-    if (trust_mouse_data) {
-      swb_event._x = pt.h;
-      swb_event._y = pt.v;
-      swb_event._flags |= SubprocessWindowBuffer::EF_mouse_position;
-    }
-  }
-
-  if (_swbuffer != nullptr) {
-    _swbuffer->add_event(swb_event);
-  }
-#endif  // __APPLE__
-
-  return retval;
-}
-
-/**
- * Responds to the new Cocoa event types in Mac OSX.
- */
-bool P3DInstance::
-handle_event_osx_cocoa(const P3D_event_data &event) {
-  bool retval = false;
-
-#ifdef __APPLE__
-  assert(event._event_type == P3D_ET_osx_cocoa);
-  const P3DCocoaEvent &ce = event._event._osx_cocoa._event;
-
-  SubprocessWindowBuffer::Event swb_event;
-  swb_event._source = SubprocessWindowBuffer::ES_none;
-  swb_event._type = SubprocessWindowBuffer::ET_none;
-  swb_event._code = 0;
-  swb_event._flags = 0;
-
-  switch (ce.type) {
-  case P3DCocoaEventDrawRect:
-    {
-      CGContextRef context = ce.data.draw.context;
-      paint_window_osx_cgcontext(context);
-      retval = true;
-    }
-    break;
-
-  case P3DCocoaEventMouseDown:
-    swb_event._source = SubprocessWindowBuffer::ES_mouse;
-    swb_event._type = SubprocessWindowBuffer::ET_button_down;
-    retval = true;
-    break;
-
-  case P3DCocoaEventMouseUp:
-    swb_event._source = SubprocessWindowBuffer::ES_mouse;
-    swb_event._type = SubprocessWindowBuffer::ET_button_up;
-    retval = true;
-    break;
-
-  case P3DCocoaEventKeyDown:
-    swb_event._source = SubprocessWindowBuffer::ES_keyboard;
-    swb_event._code = ce.data.key.keyCode << 8;
-    if (ce.data.key.isARepeat) {
-      swb_event._type = SubprocessWindowBuffer::ET_button_again;
-    } else {
-      swb_event._type = SubprocessWindowBuffer::ET_button_down;
-      if (ce.data.key.characters[0] > 0 & ce.data.key.characters[0] < 0x100) {
-        swb_event._code |= ce.data.key.characters[0];
-      }
-    }
-    _modifiers = ce.data.key.modifierFlags;
-    retval = true;
-    break;
-
-  case P3DCocoaEventKeyUp:
-    swb_event._source = SubprocessWindowBuffer::ES_keyboard;
-    swb_event._type = SubprocessWindowBuffer::ET_button_up;
-    swb_event._code = ce.data.key.keyCode << 8;
-    _modifiers = ce.data.key.modifierFlags;
-    retval = true;
-    break;
-
-  case P3DCocoaEventFlagsChanged:
-    _modifiers = ce.data.key.modifierFlags;
-    retval = true;
-    break;
-
-  case P3DCocoaEventFocusChanged:
-    _mouse_active = (ce.data.focus.hasFocus != 0);
-    retval = true;
-    break;
-  }
-
-  add_cocoa_modifier_flags(swb_event._flags, _modifiers);
-
-  switch (ce.type) {
-  case P3DCocoaEventMouseDown:
-  case P3DCocoaEventMouseMoved:
-  case P3DCocoaEventMouseDragged:
-    swb_event._x = (int)ce.data.mouse.pluginX;
-    swb_event._y = (int)ce.data.mouse.pluginY;
-    swb_event._flags |= SubprocessWindowBuffer::EF_mouse_position;
-  }
-
-  if (_mouse_active) {
-    swb_event._flags |= SubprocessWindowBuffer::EF_has_mouse;
-  }
-
-  if (_swbuffer != nullptr) {
-    _swbuffer->add_event(swb_event);
-  }
-#endif  // __APPLE__
-
-  return retval;
-}
-
-/**
- * OSX only: adds the appropriate bits to the Event flag bitmask to correspond
- * to the modifier buttons held in the MacOS-style EventRecord::modifiers
- * mask.
- */
-void P3DInstance::
-add_carbon_modifier_flags(unsigned int &swb_flags, int modifiers) {
-#if defined(__APPLE__) && !__LP64__
-  if (modifiers & cmdKey) {
-    swb_flags |= SubprocessWindowBuffer::EF_meta_held;
-  }
-  if (modifiers & shiftKey) {
-    swb_flags |= SubprocessWindowBuffer::EF_shift_held;
-  }
-  if (modifiers & optionKey) {
-    swb_flags |= SubprocessWindowBuffer::EF_alt_held;
-  }
-  if (modifiers & controlKey) {
-    swb_flags |= SubprocessWindowBuffer::EF_control_held;
-  }
-#endif  // __APPLE__
-}
-
-/**
- * OSX only: adds the appropriate bits to the Event flag bitmask to correspond
- * to the modifier buttons held in the P3DCocoaEvent modifierFlags mask.
- */
-void P3DInstance::
-add_cocoa_modifier_flags(unsigned int &swb_flags, int modifiers) {
-#ifdef __APPLE__
-  if (modifiers & NSCommandKeyMask) {
-    swb_flags |= SubprocessWindowBuffer::EF_meta_held;
-  }
-  if (modifiers & NSShiftKeyMask) {
-    swb_flags |= SubprocessWindowBuffer::EF_shift_held;
-  }
-  if (modifiers & NSAlternateKeyMask) {
-    swb_flags |= SubprocessWindowBuffer::EF_alt_held;
-  }
-  if (modifiers & NSControlKeyMask) {
-    swb_flags |= SubprocessWindowBuffer::EF_control_held;
-  }
-#endif  // __APPLE__
-}
-
-/**
- * Generates a synthetic notify message here at the C++ level.
- *
- * Most notify messages are generated from within the Python code, and don't
- * use this method; but a few have to be sent before Python has started, and
- * those come through this method.
- */
-void P3DInstance::
-send_notify(const string &message) {
-  nout << "send_notify(" << message << ")\n";
-  P3D_request *request = new P3D_request;
-  request->_instance = nullptr;
-  request->_request_type = P3D_RT_notify;
-  request->_request._notify._message = strdup(message.c_str());
-  add_baked_request(request);
-}
-
-#ifdef __APPLE__
-/**
- * OSX only: allocates the _swbuffer and associated support objects.  If it
- * was already allocated, deallocates the previous one first.
- */
-void P3DInstance::
-alloc_swbuffer() {
-  free_swbuffer();
-
-  int x_size = _wparams.get_win_width();
-  int y_size = _wparams.get_win_height();
-
-  _swbuffer = SubprocessWindowBuffer::new_buffer
-    (_shared_fd, _shared_mmap_size, _shared_filename, x_size, y_size);
-  if (_swbuffer != nullptr) {
-    _reversed_buffer = new char[_swbuffer->get_framebuffer_size()];
-    memset(_reversed_buffer, 0, _swbuffer->get_row_size());
-    size_t rowsize = _swbuffer->get_row_size();
-
-    _buffer_data = CFDataCreateWithBytesNoCopy(nullptr, (const UInt8 *)_reversed_buffer,
-                                               y_size * rowsize, kCFAllocatorNull);
-
-    _data_provider = CGDataProviderCreateWithCFData(_buffer_data);
-    _buffer_color_space = CGColorSpaceCreateDeviceRGB();
-
-    _buffer_image = CGImageCreate(x_size, y_size, 8, 32, rowsize, _buffer_color_space,
-                                  kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
-                                  _data_provider, nullptr, false, kCGRenderingIntentDefault);
-
-  }
-}
-#endif  // __APPLE__
-
-#ifdef __APPLE__
-/**
- * OSX only: releases the _swbuffer and associated support objects previously
- * allocated by alloc_swbuffer().
- */
-void P3DInstance::
-free_swbuffer() {
-  if (_swbuffer != nullptr) {
-    SubprocessWindowBuffer::destroy_buffer(_shared_fd, _shared_mmap_size,
-                                           _shared_filename, _swbuffer);
-    _swbuffer = nullptr;
-  }
-  if (_reversed_buffer != nullptr) {
-    delete[] _reversed_buffer;
-    _reversed_buffer = nullptr;
-  }
-
-  if (_buffer_image != nullptr) {
-    CGImageRelease(_buffer_image);
-    CGColorSpaceRelease(_buffer_color_space);
-    CGDataProviderRelease(_data_provider);
-    CFRelease(_buffer_data);
-
-    _buffer_data = nullptr;
-    _data_provider = nullptr;
-    _buffer_color_space = nullptr;
-    _buffer_image = nullptr;
-  }
-}
-#endif  // __APPLE__
-
-
-#ifdef __APPLE__
-/**
- * OSX only: this callback is associated with a CFRunLoopTimer, to be called
- * periodically for updating the frame.
- */
-void P3DInstance::
-timer_callback(CFRunLoopTimerRef timer, void *info) {
-  P3DInstance *self = (P3DInstance *)info;
-  self->request_refresh();
-}
-#endif  // __APPLE__
-
-/**
- *
- */
-P3DInstance::ImageDownload::
-ImageDownload(P3DInstance *inst, int index) :
-  _inst(inst),
-  _index(index)
-{
-}
-
-/**
- * Intended to be overloaded to generate a callback when the download
- * finishes, either successfully or otherwise.  The bool parameter is true if
- * the download was successful.
- */
-void P3DInstance::ImageDownload::
-download_finished(bool success) {
-  P3DFileDownload::download_finished(success);
-  if (success) {
-    // We've successfully downloaded the image (directly, not via the package
-    // interface).
-    _inst->_image_files[_index]._filename = get_filename();
-
-    // Put it onscreen if it's supposed to be onscreen now, and our splash
-    // window still exists.
-    if (_inst->_splash_window != nullptr &&
-        _inst->_image_files[_index]._image_placement != P3DSplashWindow::IP_none) {
-      P3DSplashWindow::ImagePlacement image_placement = _inst->_image_files[_index]._image_placement;
-      _inst->_splash_window->set_image_filename(get_filename(), image_placement);
-    }
-  }
-}
-
-/**
- *
- */
-P3DInstance::InstanceDownload::
-InstanceDownload(P3DInstance *inst) :
-  _inst(inst)
-{
-}
-
-/**
- * Intended to be overloaded to generate an occasional callback as new data
- * comes in.
- */
-void P3DInstance::InstanceDownload::
-download_progress() {
-  P3DFileDownload::download_progress();
-  _inst->report_instance_progress(get_download_progress(), is_download_progress_known(), get_total_data());
-}
-
-/**
- * Intended to be overloaded to generate a callback when the download
- * finishes, either successfully or otherwise.  The bool parameter is true if
- * the download was successful.
- */
-void P3DInstance::InstanceDownload::
-download_finished(bool success) {
-  P3DFileDownload::download_finished(success);
-  if (success) {
-    // We've successfully downloaded the instance data.
-    _inst->report_instance_progress(1.0, true, 0);
-    _inst->priv_set_p3d_filename(get_filename());
-  } else {
-    // Oops, no joy on the instance data.
-    _inst->send_notify("ondownloadfail");
-    _inst->set_failed();
-  }
-}

+ 0 - 385
direct/src/plugin/p3dInstance.h

@@ -1,385 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dInstance.h
- * @author drose
- * @date 2009-05-29
- */
-
-#ifndef P3DINSTANCE_H
-#define P3DINSTANCE_H
-
-#include "p3d_plugin_common.h"
-#include "p3dFileDownload.h"
-#include "p3dFileParams.h"
-#include "p3dWindowParams.h"
-#include "p3dReferenceCount.h"
-#include "p3dSplashWindow.h"
-#include "p3dTemporaryFile.h"
-#include "p3dMultifileReader.h"
-#include "get_tinyxml.h"
-
-#ifdef __APPLE__
-#include "subprocessWindowBuffer.h"
-#include <CoreFoundation/CoreFoundation.h>
-#endif
-
-#ifndef _WIN32
-#include <sys/time.h>
-#endif
-
-#include <deque>
-#include <map>
-
-class P3DSession;
-class P3DSplashWindow;
-class P3DDownload;
-class P3DPackage;
-class P3DObject;
-class P3DMainObject;
-class P3DTemporaryFile;
-
-/**
- * This is an instance of a Panda3D window, as seen in the parent-level
- * process.
- */
-class P3DInstance : public P3D_instance, public P3DReferenceCount {
-public:
-  P3DInstance(P3D_request_ready_func *func,
-              const P3D_token tokens[], size_t num_tokens,
-              int argc, const char *argv[], void *user_data);
-  ~P3DInstance();
-  void cleanup();
-
-  void set_p3d_url(const std::string &p3d_url);
-  void set_p3d_filename(const std::string &p3d_filename, const int &p3d_offset = 0);
-  int make_p3d_stream(const std::string &p3d_url);
-  inline const P3DFileParams &get_fparams() const;
-
-  void set_wparams(const P3DWindowParams &wparams);
-  inline const P3DWindowParams &get_wparams() const;
-
-  P3D_object *get_panda_script_object() const;
-  void set_browser_script_object(P3D_object *object);
-
-  bool has_request();
-  P3D_request *get_request();
-  void bake_requests();
-  void add_raw_request(TiXmlDocument *doc);
-  void add_baked_request(P3D_request *request);
-  static void finish_request(P3D_request *request, bool handled);
-
-  bool feed_url_stream(int unique_id,
-                       P3D_result_code result_code,
-                       int http_status_code,
-                       size_t total_expected_data,
-                       const unsigned char *this_data,
-                       size_t this_data_size);
-
-  bool handle_event(const P3D_event_data &event);
-
-  inline int get_instance_id() const;
-  inline const std::string &get_session_key() const;
-  const std::string &get_log_pathname() const;
-  inline const std::string &get_session_platform() const;
-
-  inline P3DSession *get_session() const;
-
-  inline P3D_request_ready_func *get_request_ready_func() const;
-
-  void add_package(const std::string &name, const std::string &version,
-                   const std::string &seq, P3DHost *host);
-  void add_package(P3DPackage *package);
-  void remove_package(P3DPackage *package);
-  bool get_packages_info_ready() const;
-  bool get_packages_ready() const;
-  bool get_packages_failed() const;
-
-  inline bool is_trusted() const;
-  inline bool get_matches_script_origin() const;
-  int start_download(P3DDownload *download, bool add_request = true);
-  inline bool is_started() const;
-  inline bool is_failed() const;
-  void request_stop_sub_thread();
-  void request_stop_main_thread();
-  void request_refresh();
-  void request_callback(P3D_callback_func *func, void *data);
-
-  TiXmlElement *make_xml();
-  void splash_button_clicked_sub_thread();
-  void splash_button_clicked_main_thread();
-  void auth_button_clicked();
-  void play_button_clicked();
-
-  void auth_finished_sub_thread();
-  void auth_finished_main_thread();
-
-  bool uninstall_packages();
-  bool uninstall_host();
-
-private:
-  class ImageDownload : public P3DFileDownload {
-  public:
-    ImageDownload(P3DInstance *inst, int index);
-  protected:
-    virtual void download_finished(bool success);
-  private:
-    P3DInstance *_inst;
-    int _index;
-  };
-  class InstanceDownload : public P3DFileDownload {
-  public:
-    InstanceDownload(P3DInstance *inst);
-  protected:
-    virtual void download_progress();
-    virtual void download_finished(bool success);
-  private:
-    P3DInstance *_inst;
-  };
-
-  // The different kinds of image files we download for the splash window.
-  enum ImageType {
-    // Also update _image_type_names when you update this list.
-    IT_download,
-    IT_unauth,
-    IT_ready,
-    IT_failed,
-    IT_launch,
-    IT_active,
-    IT_auth_ready,
-    IT_auth_rollover,
-    IT_auth_click,
-    IT_play_ready,
-    IT_play_rollover,
-    IT_play_click,
-    IT_none,                // Must be the last value
-    IT_num_image_types,     // Not a real value
-  };
-
-  void priv_set_p3d_filename(const std::string &p3d_filename, const int &p3d_offset = -1);
-  void determine_p3d_basename(const std::string &p3d_url);
-
-  bool check_matches_origin(const std::string &origin_match);
-  bool check_matches_origin_one(const std::string &origin_match);
-  bool check_matches_hostname(const std::string &orig, const std::string &match);
-  void separate_components(std::vector<std::string> &components, const std::string &str);
-  bool check_matches_component(const std::string &orig, const std::string &match);
-
-  void check_p3d_signature();
-  void mark_p3d_untrusted();
-  void mark_p3d_trusted();
-  void scan_app_desc_file(TiXmlDocument *doc);
-  void add_panda3d_package();
-  void add_packages();
-  std::string find_alt_host_url(const std::string &host_url, const std::string &alt_host);
-  void get_host_info(P3DHost *host);
-  std::string get_start_dir_suffix() const;
-
-  void send_browser_script_object();
-  P3D_request *make_p3d_request(TiXmlElement *xrequest);
-  void handle_notify_request(const std::string &message);
-  void handle_script_request(const std::string &operation, P3D_object *object,
-                             const std::string &property_name, P3D_object *value,
-                             bool needs_response, int unique_id);
-
-  void set_failed();
-  void make_splash_window();
-  void set_background_image(ImageType image_type);
-  void set_button_image(ImageType image_type);
-  void report_package_info_ready(P3DPackage *package);
-  void consider_start_download();
-  void ready_to_install();
-  void start_next_download();
-  void mark_download_complete();
-  void ready_to_start();
-  void report_instance_progress(double progress, bool is_progress_known,
-                                size_t received_data);
-  void report_package_progress(P3DPackage *package, double progress);
-  void report_package_done(P3DPackage *package, bool success);
-  void set_install_label(const std::string &install_label);
-
-  void paint_window();
-
-#ifdef __APPLE__
-  bool get_framebuffer_osx_port();
-  bool get_framebuffer_osx_cgcontext();
-  void paint_window_osx_port();
-  void paint_window_osx_cgcontext(CGContextRef context);
-#endif  //  __APPLE__
-
-  bool handle_event_osx_event_record(const P3D_event_data &event);
-  bool handle_event_osx_cocoa(const P3D_event_data &event);
-  void add_carbon_modifier_flags(unsigned int &swb_flags, int modifiers);
-  void add_cocoa_modifier_flags(unsigned int &swb_flags, int modifiers);
-
-  void send_notify(const std::string &message);
-
-#ifdef __APPLE__
-  void alloc_swbuffer();
-  void free_swbuffer();
-  static void timer_callback(CFRunLoopTimerRef timer, void *info);
-#endif  // __APPLE__
-
-  P3D_request_ready_func *_func;
-  P3D_object *_dom_object;
-  P3DMainObject *_main_object;
-  std::string _p3d_basename;
-  std::string _origin_protocol;
-  std::string _origin_hostname;
-  std::string _origin_port;
-
-  // We need a list of previous time reports so we can average the predicted
-  // download time over the past few seconds.
-  class TimeReport {
-  public:
-    double _total;
-    double _report_time;
-  };
-  typedef std::deque<TimeReport> TimeReports;
-  TimeReports _time_reports;
-  double _total_time_reports;
-
-  P3DTemporaryFile *_temp_p3d_filename;
-
-  // For downloading the various images used by the splash window.
-  P3DPackage *_image_package;
-  static const char *_image_type_names[IT_num_image_types];
-
-  class ImageFile {
-  public:
-    inline ImageFile();
-    inline ~ImageFile();
-    inline void cleanup();
-
-    bool _use_standard_image;
-    P3DTemporaryFile *_temp_filename;
-    std::string _filename;
-    P3DSplashWindow::ImagePlacement _image_placement;
-  };
-  ImageFile _image_files[IT_num_image_types];
-  ImageType _current_background_image;
-  ImageType _current_button_image;
-
-  bool _got_fparams;
-  P3DFileParams _fparams;
-  P3DMultifileReader _mf_reader;
-
-  bool _got_wparams;
-  P3DWindowParams _wparams;
-
-  bool _p3d_trusted;
-  TiXmlElement *_xpackage;
-
-  // Holds the list of certificates that are pre-approved by the plugin
-  // vendor.
-  P3DPackage *_certlist_package;
-
-  // For downloading the p3dcert authorization program.
-  P3DPackage *_p3dcert_package;
-
-  int _instance_id;
-  std::string _session_key;
-  std::string _log_basename;
-  std::string _session_platform;
-  std::string _prc_name;
-  std::string _start_dir;
-  bool _hidden;
-  bool _matches_run_origin;
-  bool _matches_script_origin;
-  bool _allow_python_dev;
-  bool _keep_user_env;
-  bool _auto_install;
-  bool _auto_start;
-  bool _stop_on_ready;
-  bool _auth_button_clicked;
-  bool _failed;
-
-  P3DSession *_session;
-  P3DAuthSession *_auth_session;
-  std::string _log_pathname;
-
-#ifdef __APPLE__
-  // On OSX, we have to get a copy of the framebuffer data back from the child
-  // process, and draw it to the window, here in the parent process.  Crazy!
-  int _shared_fd;
-  size_t _shared_mmap_size;
-  std::string _shared_filename;
-  SubprocessWindowBuffer *_swbuffer;
-  char *_reversed_buffer;
-  CFDataRef _buffer_data;
-  CGDataProviderRef _data_provider;
-  CGColorSpaceRef _buffer_color_space;
-  CGImageRef _buffer_image;
-
-  bool _mouse_active;
-  unsigned int _modifiers;
-
-  CFRunLoopTimerRef _frame_timer;
-#endif  // __APPLE__
-
-  P3DSplashWindow *_splash_window;
-  std::string _install_label;
-  bool _instance_window_opened;
-  bool _instance_window_attached;
-  bool _stuff_to_download;
-
-  // Keep track of when the download was started, for reporting purposes.
-  // These members are used both for the instance download, and for the later
-  // package download.
-#ifdef _WIN32
-  int _start_dl_tick;
-#else
-  struct timeval _start_dl_timeval;
-#endif
-
-  // This is set false initially, but true if the instance download continues
-  // for more than a couple of seconds.
-  bool _show_dl_instance_progress;
-
-  typedef std::vector<P3DPackage *> Packages;
-  Packages _packages;
-  Packages _downloading_packages;
-  int _download_package_index;
-  size_t _prev_downloaded;
-  size_t _total_download_size;
-  size_t _total_downloaded;
-  bool _packages_specified;
-  bool _download_started;
-  bool _download_complete;
-  bool _instance_started;
-
-  // We keep the _panda3d pointer separately because it's so important, but
-  // it's in the above vector also.
-  P3DPackage *_panda3d_package;
-
-  typedef std::map<int, P3DDownload *> Downloads;
-  Downloads _downloads;
-
-  // The _raw_requests queue might be filled up by the read thread, so we
-  // protect it in a lock.
-  LOCK _request_lock;
-  typedef std::deque<TiXmlDocument *> RawRequests;
-  RawRequests _raw_requests;
-  bool _requested_stop;
-
-  // The _baked_requests queue is only touched in the main thread; no lock
-  // needed.
-  typedef std::deque<P3D_request *> BakedRequests;
-  BakedRequests _baked_requests;
-
-  friend class P3DSession;
-  friend class P3DAuthSession;
-  friend class ImageDownload;
-  friend class InstanceDownload;
-  friend class P3DWindowParams;
-  friend class P3DPackage;
-};
-
-#include "p3dInstance.I"
-
-#endif

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

@@ -1,322 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dInstanceManager.I
- * @author drose
- * @date 2009-05-29
- */
-
-/**
- * Returns true if the instance manager is successfully initialized, false
- * otherwise.
- */
-inline bool P3DInstanceManager::
-is_initialized() const {
-  return _is_initialized;
-}
-
-/**
- * Recreates the runtime environment if a previous call to uninstall_all()
- * removed it.  Does nothing if the runtime environment is already correctly
- * set up.
- */
-inline void P3DInstanceManager::
-reconsider_runtime_environment() {
-  assert(_is_initialized);
-  if (!_created_runtime_environment) {
-    create_runtime_environment();
-  }
-}
-
-/**
- * Returns the verify_contents setting.  When this is set to P3D_VC_none, it
- * indicates that we don't need to contact the server to verify that a
- * contents.xml file is fresh before using it; we should just use it as it is.
- */
-inline P3D_verify_contents P3DInstanceManager::
-get_verify_contents() const {
-  return _verify_contents;
-}
-
-/**
- * Resets the verify_contents flag to P3D_VC_normal, if it is P3D_VC_none.
- * This should be done whenever we discover anything needs to be downloaded.
- * At this point, we might as well verify everything.
- */
-inline void P3DInstanceManager::
-reset_verify_contents() {
-  if (_verify_contents == P3D_VC_none) {
-    _verify_contents = P3D_VC_normal;
-  }
-}
-
-
-/**
- * Returns the api_version number which was passed to P3D_initialize().
- * Client code may use this to determine how to interpret parameters to
- * various functions whose interface may have changed over different versions.
- */
-inline int P3DInstanceManager::
-get_api_version() const {
-  return _api_version;
-}
-
-/**
- * Returns the standard host_url which the instances should attempt to contact
- * to download auxiliary packages associated with the core API, such as the
- * p3dcert and images packages.  This is normally the compiled-in
- * PANDA_PACKAGE_HOST_URL, but it might be set to something different by the
- * -u parameter on the panda3d executable.
- */
-inline const std::string &P3DInstanceManager::
-get_host_url() const {
-  return _host_url;
-}
-
-/**
- * Returns the root directory into which all the P3D runtime files are
- * downloaded and installed.  This must be a writable directory or nothing
- * will work.
- */
-inline const std::string &P3DInstanceManager::
-get_root_dir() const {
-  return _root_dir;
-}
-
-/**
- * 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 std::string &P3DInstanceManager::
-get_start_dir() const {
-  return _start_dir;
-}
-
-/**
- * Returns the string that corresponds to the platform on which we are
- * running.  This string will be used to determine the appropriate packages to
- * download.
- */
-inline const std::string &P3DInstanceManager::
-get_platform() const {
-  return _platform;
-}
-
-/**
- * Returns the pathname of the directory into which temporary files should be
- * written.  This filename will end with a slash, so that full pathnames may
- * be made by concatenting directly with this string.
- */
-inline const std::string &P3DInstanceManager::
-get_temp_directory() const {
-  return _temp_directory;
-}
-
-/**
- * Returns the pathname of the directory into which all log files should be
- * written.  This filename will end with a slash, so that full pathnames may
- * be made by concatenting directly with this string.
- */
-inline const std::string &P3DInstanceManager::
-get_log_directory() const {
-  return _log_directory;
-}
-
-/**
- * Returns the filename of the system log file; this file is responsible for
- * downloading and installing updates, and launching applications.  This is
- * different from the session log file(s), which represent the output from a
- * particular Python session.
- */
-inline const std::string &P3DInstanceManager::
-get_log_pathname() const {
-  return _log_pathname;
-}
-
-/**
- * Returns the value of the trusted_environment flag passed to the
- * constructor.  If this is true, it means the environment we are running in
- * is trusted and the p3d file is already vetted.  This means the current
- * working directory will remain unchanged, and the p3d file will be run
- * without checking its signature.
- *
- * This should generally be true only when run by panda3d.exe or panda3dw.exe,
- * and not when run by the web plugin.
- */
-inline bool P3DInstanceManager::
-get_trusted_environment() const {
-  return _trusted_environment;
-}
-
-/**
- * Returns the value of the console_environment flag passed to the
- * constructor.  If this is true, it means we are running from a text-based
- * console window, and not from a desktop environment.
- *
- * This should generally be true only when run by panda3d.exe, and not when
- * run by the web plugin or by panda3dw.exe.
- */
-inline bool P3DInstanceManager::
-get_console_environment() const {
-  return _console_environment;
-}
-
-/**
- * Returns the number of different supported platforms available in
- * get_supported_platform().
- */
-inline int P3DInstanceManager::
-get_num_supported_platforms() const {
-  return (int)_supported_platforms.size();
-}
-
-/**
- * Returns the nth supported platform, where 0 <= n <
- * get_num_supported_platforms().
- *
- * A given runtime environment may support multiple different platforms, e.g.
- * win32 or win64, with the restriction that all platform-specific packages
- * (beginning from panda3d), must be the same platform.
- *
- * This function enumerates the different platforms that the current runtime
- * environment will support, in order of preference--preferred platforms
- * appear first in the list.
- */
-inline const std::string &P3DInstanceManager::
-get_supported_platform(int n) const {
-  return _supported_platforms.at(n);
-}
-
-/**
- * Returns the plugin's reported major version number.
- */
-inline int P3DInstanceManager::
-get_plugin_major_version() const {
-  return _plugin_major_version;
-}
-
-/**
- * Returns the plugin's reported minor version number.
- */
-inline int P3DInstanceManager::
-get_plugin_minor_version() const {
-  return _plugin_minor_version;
-}
-
-/**
- * Returns the plugin's reported sequence version number.
- */
-inline int P3DInstanceManager::
-get_plugin_sequence_version() const {
-  return _plugin_sequence_version;
-}
-
-/**
- * Returns true if the plugin claims to be from an "official" build, and the
- * its version number is authoritative; or false if it makes no such claim
- * (for instance, it was built by someone checking out from cvs).
- */
-inline bool P3DInstanceManager::
-get_plugin_official_version() const {
-  return _plugin_official_version;
-}
-
-/**
- * Returns the "distributor" reported by the plugin.  This should represent
- * the entity that built and hosted the plugin.
- */
-inline const std::string &P3DInstanceManager::
-get_plugin_distributor() const {
-  return _plugin_distributor;
-}
-
-/**
- * Returns the host URL from which this Core API was downloaded (according to
- * the plugin).  This is for reporting purposes only; see get_host_url() for
- * the URL to contact to actually download content.
- */
-inline const std::string &P3DInstanceManager::
-get_coreapi_host_url() const {
-  return _coreapi_host_url;
-}
-
-/**
- * Returns the timestamp associated with this Core API DLL (according to the
- * plugin).  This is the timestamp shown in the contents.xml for this host,
- * and is usually the time at which the plugin was built.
- */
-inline time_t P3DInstanceManager::
-get_coreapi_timestamp() const {
-  return _coreapi_timestamp;
-}
-
-/**
- * Returns the version number associated with the Core API, if provided.  Some
- * early versions of the Core API, and some early versions of the plugin, did
- * not provide a number here.  If provided, this will be a string of dot-
- * separated integers.
- */
-inline const std::string &P3DInstanceManager::
-get_coreapi_set_ver() const {
-  return _coreapi_set_ver;
-}
-
-/**
- * Returns the "super mirror" URL.  See p3d_plugin.h.
- */
-inline const std::string &P3DInstanceManager::
-get_super_mirror() const {
-  return _super_mirror_url;
-}
-
-/**
- * Returns the number of instances currently running within the world.
- */
-inline int P3DInstanceManager::
-get_num_instances() const {
-  return _instances.size();
-}
-
-/**
- * Returns the singleton "undefined" object, as a new reference.
- */
-inline P3D_object *P3DInstanceManager::
-new_undefined_object() {
-  P3D_OBJECT_INCREF(_undefined_object);
-  return _undefined_object;
-}
-
-/**
- * Returns the singleton "none" object, as a new reference.
- */
-inline P3D_object *P3DInstanceManager::
-new_none_object() {
-  P3D_OBJECT_INCREF(_none_object);
-  return _none_object;
-}
-
-/**
- * Returns the singleton "true" or "false" object, as a new reference.
- */
-inline P3D_object *P3DInstanceManager::
-new_bool_object(bool value) {
-  P3D_object *obj = (value) ? _true_object : _false_object;
-  P3D_OBJECT_INCREF(obj);
-  return obj;
-}
-
-/**
- * Returns the hex digit corresponding to the indicated integer value.
- */
-inline char P3DInstanceManager::
-encode_hexdigit(int c) {
-  if (c >= 10) {
-    return c - 10 + 'a';
-  }
-  return c + '0';
-}

+ 0 - 1519
direct/src/plugin/p3dInstanceManager.cxx

@@ -1,1519 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dInstanceManager.cxx
- * @author drose
- * @date 2009-05-29
- */
-
-#include "p3dInstanceManager.h"
-#include "p3dInstance.h"
-#include "p3dSession.h"
-#include "p3dAuthSession.h"
-#include "p3dHost.h"
-#include "p3d_plugin_config.h"
-#include "p3dWinSplashWindow.h"
-#include "p3dUndefinedObject.h"
-#include "p3dNoneObject.h"
-#include "p3dBoolObject.h"
-#include "p3dPackage.h"
-#include "find_root_dir.h"
-#include "fileSpec.h"
-#include "get_tinyxml.h"
-#include "binaryXml.h"
-#include "mkdir_complete.h"
-#include "wstring_encode.h"
-
-// We can include this header file to get the DTOOL_PLATFORM definition, even
-// though we don't link with dtool.
-#include "dtool_platform.h"
-
-#ifdef _WIN32
-#include <shlobj.h>
-#include <io.h>      // chmod()
-#include <direct.h>  // rmdir()
-#include <windows.h> // GetModuleHandle() etc.
-#else
-#include <sys/stat.h>
-#include <signal.h>
-#include <dirent.h>
-#endif
-
-#ifdef __APPLE__
-#include <sys/sysctl.h>
-#endif
-
-#include <stdio.h>
-
-using std::string;
-using std::vector;
-using std::wstring;
-
-static ofstream logfile;
-std::ostream *nout_stream = &logfile;
-
-P3DInstanceManager *P3DInstanceManager::_global_ptr;
-
-/**
- *
- */
-P3DInstanceManager::
-P3DInstanceManager() {
-  init_xml();
-
-  _is_initialized = false;
-  _created_runtime_environment = false;
-  _api_version = 0;
-  _next_temp_filename_counter = 1;
-  _unique_id = 0;
-  _trusted_environment = false;
-  _console_environment = false;
-
-  _plugin_major_version = 0;
-  _plugin_minor_version = 0;
-  _plugin_sequence_version = 0;
-  _plugin_official_version = false;
-  _coreapi_timestamp = 0;
-
-  _notify_thread_continue = false;
-  _started_notify_thread = false;
-  INIT_THREAD(_notify_thread);
-
-  // Initialize the singleton objects.
-  _undefined_object = new P3DUndefinedObject();
-  _none_object = new P3DNoneObject();
-  _true_object = new P3DBoolObject(true);
-  _false_object = new P3DBoolObject(false);
-
-  _auth_session = nullptr;
-
-  // Seed the lame random number generator in rand(); we use it to select a
-  // mirror for downloading.
-  srand((unsigned int)time(nullptr));
-
-#ifdef _WIN32
-  // Ensure the appropriate Windows common controls are available to this
-  // application.
-  INITCOMMONCONTROLSEX icc;
-  icc.dwSize = sizeof(icc);
-  icc.dwICC = ICC_PROGRESS_CLASS;
-  InitCommonControlsEx(&icc);
-#endif
-
-#ifndef _WIN32
-  // On Mac or Linux, we'd better ignore SIGPIPE, or this signal will shut
-  // down the browser if the plugin exits unexpectedly.
-  struct sigaction ignore;
-  memset(&ignore, 0, sizeof(ignore));
-  ignore.sa_handler = SIG_IGN;
-  sigaction(SIGPIPE, &ignore, &_old_sigpipe);
-#endif  // _WIN32
-}
-
-/**
- *
- */
-P3DInstanceManager::
-~P3DInstanceManager() {
-  if (_started_notify_thread) {
-    _notify_ready.acquire();
-    _notify_thread_continue = false;
-    _notify_ready.notify();
-    _notify_ready.release();
-    JOIN_THREAD(_notify_thread);
-    _started_notify_thread = false;
-  }
-
-#ifndef _WIN32
-  // Restore the original SIGPIPE handler.
-  sigaction(SIGPIPE, &_old_sigpipe, nullptr);
-#endif  // _WIN32
-
-  // force-finish any remaining instances.
-  while (!_instances.empty()) {
-    P3DInstance *inst = *(_instances.begin());
-    finish_instance(inst);
-  }
-
-  assert(_sessions.empty());
-  assert(_instances.empty());
-
-  if (_auth_session != nullptr) {
-    p3d_unref_delete(_auth_session);
-    _auth_session = nullptr;
-  }
-
-  Hosts::iterator hi;
-  for (hi = _hosts.begin(); hi != _hosts.end(); ++hi) {
-    delete (*hi).second;
-  }
-  _hosts.clear();
-
-  // Delete any remaining temporary files.
-  TempFilenames::iterator ti;
-  for (ti = _temp_filenames.begin(); ti != _temp_filenames.end(); ++ti) {
-    const string &filename = (*ti);
-    nout << "Removing delinquent temp file " << filename << "\n";
-    unlink(filename.c_str());
-  }
-  _temp_filenames.clear();
-
-  nout << "counts: " << _undefined_object->_ref_count
-       << " " << _none_object->_ref_count
-       << " " << _true_object->_ref_count
-       << " " << _false_object->_ref_count
-       << "\n";
-
-  /*
-  assert(_undefined_object->_ref_count == 1);
-  assert(_none_object->_ref_count == 1);
-  assert(_true_object->_ref_count == 1);
-  assert(_false_object->_ref_count == 1);
-  */
-
-  P3D_OBJECT_DECREF(_undefined_object);
-  P3D_OBJECT_DECREF(_none_object);
-  P3D_OBJECT_DECREF(_true_object);
-  P3D_OBJECT_DECREF(_false_object);
-
-#ifdef _WIN32
-  P3DWinSplashWindow::unregister_window_class();
-#endif
-}
-
-/**
- * Called by the plugin host at application startup.  It returns true if the
- * DLL is successfully initialized, false if it should be immediately shut
- * down and redownloaded.
- */
-bool P3DInstanceManager::
-initialize(int api_version, const string &contents_filename,
-           const string &host_url, P3D_verify_contents verify_contents,
-           const string &platform, const string &log_directory,
-           const string &log_basename, bool trusted_environment,
-           bool console_environment,
-           const string &root_dir, const string &host_dir,
-           const string &start_dir) {
-  _api_version = api_version;
-  _host_url = host_url;
-  _verify_contents = verify_contents;
-  _platform = platform;
-  _log_directory = log_directory;
-  _log_basename = log_basename;
-  _trusted_environment = trusted_environment;
-  _console_environment = console_environment;
-
-  if (_host_url.empty()) {
-    _host_url = PANDA_PACKAGE_HOST_URL;
-  }
-
-  if (_platform.empty()) {
-    // If the platform is compiled in (as opposed to passed in by the caller),
-    // we might in fact support multiple platforms.
-    _platform = DTOOL_PLATFORM;
-#ifdef _WIN32
-    if (_platform == "win_amd64") {
-      _supported_platforms.push_back("win_amd64");
-      _supported_platforms.push_back("win_i386");
-      _supported_platforms.push_back("win32");
-
-    } else if (_platform == "win_i386" || _platform == "win32") {
-      // This is a WIN32 process, but determine if the underlying OS actually
-      // supports WIN64.
-      if (supports_win64()) {
-        _supported_platforms.push_back("win_amd64");
-      }
-      _supported_platforms.push_back("win_i386");
-      _supported_platforms.push_back("win32");
-    }
-#elif defined(__APPLE__)
-    if (_platform == "osx_amd64") {
-      _supported_platforms.push_back("osx_amd64");
-      _supported_platforms.push_back("osx_i386");
-
-    } else if (_platform == "osx_i386") {
-      // This is a 32-bit process, but determine if the underlying OS supports
-      // 64-bit.
-
-      int mib[2] = { CTL_HW, HW_MACHINE };
-      char machine[512];
-      size_t len = 511;
-      if (sysctl(mib, 2, (void *)machine, &len, nullptr, 0) == 0) {
-        if (strcmp(machine, "x86_64") == 0) {
-          _supported_platforms.push_back("osx_amd64");
-        }
-      }
-
-      _supported_platforms.push_back("osx_i386");
-    }
-#endif  // _WIN32
-
-    // TODO: Linux multiplatform support.  Just add the appropriate platform
-    // strings to _supported_platforms.
-  } else {
-    nout << "Platform string was set by plugin to " << _platform << "\n";
-  }
-
-  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
-    // running.
-    _supported_platforms.push_back(_platform);
-  }
-
-#ifdef P3D_PLUGIN_LOG_DIRECTORY
-  if (_log_directory.empty()) {
-    _log_directory = P3D_PLUGIN_LOG_DIRECTORY;
-  }
-#endif
-
-#ifdef P3D_PLUGIN_LOG_BASENAME2
-  if (_log_basename.empty()) {
-    _log_basename = P3D_PLUGIN_LOG_BASENAME2;
-  }
-#endif
-  if (_log_basename.empty()) {
-    _log_basename = "p3dcore";
-  }
-
-  if (root_dir.empty()) {
-    _root_dir = find_root_dir();
-    if (_root_dir.empty()) {
-      std::cerr << "Could not find root directory.\n";
-      return false;
-    }
-  } else {
-    _root_dir = root_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.  Or,
-  // allow the developer to compile one in.  Failing that, we write logfiles
-  // to Panda3Dlog.
-  if (_log_directory.empty()) {
-    _log_directory = _root_dir + "/log";
-  }
-
-  // Ensure that the log directory ends with a slash.
-  if (!_log_directory.empty() && _log_directory[_log_directory.size() - 1] != '/') {
-#ifdef _WIN32
-    if (_log_directory[_log_directory.size() - 1] != '\\')
-#endif
-      _log_directory += "/";
-  }
-
-  // Construct the logfile pathname.
-  _log_pathname = _log_directory;
-  _log_pathname += _log_basename;
-  _log_pathname += ".log";
-
-  create_runtime_environment();
-  _is_initialized = true;
-
-  if (!host_url.empty() && !contents_filename.empty()) {
-    // Attempt to pre-read the supplied contents.xml file, to avoid an
-    // unnecessary download later.
-    P3DHost *host = get_host(host_url);
-    if (!host->read_contents_file(contents_filename, false)) {
-      nout << "Couldn't read " << contents_filename << "\n";
-    }
-  }
-
-  nout << "Supported platforms:";
-  for (size_t pi = 0; pi < _supported_platforms.size(); ++pi) {
-    nout << " " << _supported_platforms[pi];
-  }
-  nout << "\n";
-
-  return true;
-}
-
-/**
- * Specifies the version of the calling plugin, for reporting to JavaScript
- * and the like.
- */
-void P3DInstanceManager::
-set_plugin_version(int major, int minor, int sequence,
-                   bool official, const string &distributor,
-                   const string &coreapi_host_url,
-                   time_t coreapi_timestamp,
-                   const string &coreapi_set_ver) {
-  reconsider_runtime_environment();
-  _plugin_major_version = major;
-  _plugin_minor_version = minor;
-  _plugin_sequence_version = sequence;
-  _plugin_official_version = official;
-  _plugin_distributor = distributor;
-
-  // The Core API "host URL" is both compiled in, and comes in externally; we
-  // trust the external source in the case of a conflict.
-  string internal_host_url = PANDA_PACKAGE_HOST_URL;
-  if (coreapi_host_url != internal_host_url) {
-    nout << "Warning!  Downloaded Core API from " << coreapi_host_url
-         << ", but its internal URL was " << internal_host_url << "\n";
-  }
-  _coreapi_host_url = coreapi_host_url;
-  if (_coreapi_host_url.empty()) {
-    _coreapi_host_url = internal_host_url;
-  }
-
-  // The Core API timestamp is only available externally.
-  _coreapi_timestamp = coreapi_timestamp;
-
-  // The Core API "set ver", or version, is both compiled in and comes in
-  // externally; for this one we trust the internal version in the case of a
-  // conflict.
-  string internal_set_ver = P3D_COREAPI_VERSION_STR;
-  if (coreapi_set_ver != internal_set_ver && !coreapi_set_ver.empty() && !internal_set_ver.empty()) {
-    nout << "Warning!  contents.xml reports Core API version number "
-         << coreapi_set_ver << ", but its actual version number is "
-         << internal_set_ver << "\n";
-  }
-  _coreapi_set_ver = internal_set_ver;
-  if (_coreapi_set_ver.empty()) {
-    _coreapi_set_ver = coreapi_set_ver;
-  }
-
-  nout << "Plugin version: "
-       << _plugin_major_version << "."
-       << _plugin_minor_version << "."
-       << _plugin_sequence_version;
-  if (!_plugin_official_version) {
-    nout << "c";
-  }
-  nout << "\n";
-  nout << "Plugin distributor: " << _plugin_distributor << "\n";
-  nout << "Core API host URL: " <<  _coreapi_host_url << "\n";
-  nout << "Core API version: " << _coreapi_set_ver << "\n";
-
-  const char *timestamp_string = ctime(&_coreapi_timestamp);
-  if (timestamp_string == nullptr) {
-    timestamp_string = "";
-  }
-  nout << "Core API date: " << timestamp_string << "\n";
-}
-
-/**
- * Specifies the "super mirror" URL.  See p3d_plugin.h.
- */
-void P3DInstanceManager::
-set_super_mirror(const string &super_mirror_url) {
-  reconsider_runtime_environment();
-  if (!super_mirror_url.empty()) {
-    nout << "super_mirror = " << super_mirror_url << "\n";
-  }
-  _super_mirror_url = super_mirror_url;
-
-  // Make sure it ends with a slash.
-  if (!_super_mirror_url.empty() && _super_mirror_url[_super_mirror_url.size() - 1] != '/') {
-    _super_mirror_url += '/';
-  }
-}
-
-/**
- * Returns a newly-allocated P3DInstance with the indicated startup
- * information.
- */
-P3DInstance *P3DInstanceManager::
-create_instance(P3D_request_ready_func *func,
-                const P3D_token tokens[], size_t num_tokens,
-                int argc, const char *argv[], void *user_data) {
-  reconsider_runtime_environment();
-  P3DInstance *inst = new P3DInstance(func, tokens, num_tokens, argc, argv,
-                                      user_data);
-  inst->ref();
-  _instances.insert(inst);
-
-  return inst;
-}
-
-/**
- * Sets the p3d_filename (or p3d_url) on a particular instance.
- */
-bool P3DInstanceManager::
-set_p3d_filename(P3DInstance *inst, bool is_local,
-                 const string &p3d_filename, const int &p3d_offset) {
-  if (inst->is_started()) {
-    nout << "Instance started twice: " << inst << "\n";
-    return false;
-  }
-  if (is_local) {
-    inst->set_p3d_filename(p3d_filename, p3d_offset);
-  } else {
-    inst->set_p3d_url(p3d_filename);
-  }
-
-  return true;
-}
-
-/**
- * Indicates an intention to transmit the p3d data as a stream.  Should return
- * a new unique stream ID to receive it.
- */
-int P3DInstanceManager::
-make_p3d_stream(P3DInstance *inst, const string &p3d_url) {
-  if (inst->is_started()) {
-    nout << "Instance started twice: " << inst << "\n";
-    return -1;
-  }
-  return inst->make_p3d_stream(p3d_url);
-}
-
-
-/**
- * Actually starts the instance running on a particular session.  This is
- * called by the P3DInstance when it successfully loads its instance file.
- */
-bool P3DInstanceManager::
-start_instance(P3DInstance *inst) {
-  if (inst->is_failed()) {
-    // Don't bother trying again.
-    return false;
-  }
-
-  if (inst->is_started()) {
-    // Already started.
-    return true;
-  }
-
-  P3DSession *session;
-  Sessions::iterator si = _sessions.find(inst->get_session_key());
-  if (si == _sessions.end()) {
-    session = new P3DSession(inst);
-    session->ref();
-    bool inserted = _sessions.insert(Sessions::value_type(session->get_session_key(), session)).second;
-    assert(inserted);
-  } else {
-    session = (*si).second;
-  }
-
-  session->start_instance(inst);
-
-  return inst->is_started();
-}
-
-/**
- * Terminates and removes a previously-returned instance.
- */
-void P3DInstanceManager::
-finish_instance(P3DInstance *inst) {
-  nout << "finish_instance: " << inst << "\n";
-  Instances::iterator ii;
-  ii = _instances.find(inst);
-  if (ii != _instances.end()) {
-    _instances.erase(ii);
-  }
-
-  Sessions::iterator si = _sessions.find(inst->get_session_key());
-  if (si != _sessions.end()) {
-    P3DSession *session = (*si).second;
-    session->terminate_instance(inst);
-
-    // If that was the last instance in this session, terminate the session.
-    if (session->get_num_instances() == 0) {
-      _sessions.erase(session->get_session_key());
-      session->shutdown();
-      p3d_unref_delete(session);
-    }
-  }
-
-  inst->cleanup();
-  p3d_unref_delete(inst);
-}
-
-/**
- * Creates a new P3DAuthSession object, to pop up a window for the user to
- * authorize the certificate on this instance.  Automatically terminates any
- * previously-created P3DAuthSession.
- */
-P3DAuthSession *P3DInstanceManager::
-authorize_instance(P3DInstance *inst) {
-  if (_auth_session != nullptr) {
-    // We only want one auth_session window open at a time, to minimize user
-    // confusion, so close any previous window.
-    _auth_session->shutdown(true);
-    p3d_unref_delete(_auth_session);
-    _auth_session = nullptr;
-  }
-
-  _auth_session = new P3DAuthSession(inst);
-  _auth_session->ref();
-  return _auth_session;
-}
-
-/**
- * Returns the P3DInstance pointer corresponding to the indicated P3D_instance
- * if it is valid, or NULL if it is not.
- */
-P3DInstance *P3DInstanceManager::
-validate_instance(P3D_instance *instance) {
-  Instances::iterator ii;
-  ii = _instances.find((P3DInstance *)instance);
-  if (ii != _instances.end()) {
-    return (*ii);
-  }
-
-  return nullptr;
-}
-
-/**
- * If a request is currently pending on any instance, returns its pointer.
- * Otherwise, returns NULL.
- */
-P3DInstance *P3DInstanceManager::
-check_request() {
-  Instances::iterator ii;
-  for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
-    P3DInstance *inst = (*ii);
-    if (inst->has_request()) {
-      return inst;
-    }
-  }
-
-  return nullptr;
-}
-
-/**
- * Does not return until a request is pending on some instance, or until no
- * instances remain, or until the indicated time in seconds has elapsed.  Use
- * check_request to retrieve the pending request.  Due to the possibility of
- * race conditions, it is possible for this function to return when there is
- * in fact no request pending (another thread may have extracted the request
- * first).
- */
-void P3DInstanceManager::
-wait_request(double timeout) {
-#ifdef _WIN32
-  int stop_tick = int(GetTickCount() + timeout * 1000.0);
-#else
-  struct timeval stop_time;
-  gettimeofday(&stop_time, nullptr);
-
-  int seconds = (int)floor(timeout);
-  stop_time.tv_sec += seconds;
-  stop_time.tv_usec += (int)((timeout - seconds) * 1000.0);
-  if (stop_time.tv_usec > 1000) {
-    stop_time.tv_usec -= 1000;
-    ++stop_time.tv_sec;
-  }
-#endif
-
-  _request_ready.acquire();
-  if (check_request() != nullptr) {
-    _request_ready.release();
-    return;
-  }
-  if (_instances.empty()) {
-    _request_ready.release();
-    return;
-  }
-
-  // No pending requests; go to sleep.
-  _request_ready.wait(timeout);
-
-  while (true) {
-#ifdef _WIN32
-    int remaining_ticks = stop_tick - GetTickCount();
-    if (remaining_ticks <= 0) {
-      break;
-    }
-    timeout = remaining_ticks * 0.001;
-#else
-    struct timeval now;
-    gettimeofday(&now, nullptr);
-
-    struct timeval remaining;
-    remaining.tv_sec = stop_time.tv_sec - now.tv_sec;
-    remaining.tv_usec = stop_time.tv_usec - now.tv_usec;
-
-    if (remaining.tv_usec < 0) {
-      remaining.tv_usec += 1000;
-      --remaining.tv_sec;
-    }
-    if (remaining.tv_sec < 0) {
-      break;
-    }
-    timeout = remaining.tv_sec + remaining.tv_usec * 0.001;
-#endif
-
-    if (check_request() != nullptr) {
-      _request_ready.release();
-      return;
-    }
-    if (_instances.empty()) {
-      _request_ready.release();
-      return;
-    }
-
-    // No pending requests; go to sleep.
-    _request_ready.wait(timeout);
-  }
-  _request_ready.release();
-}
-
-/**
- * Returns a (possibly shared) pointer to the indicated download host.
- */
-P3DHost *P3DInstanceManager::
-get_host(const string &host_url) {
-  Hosts::iterator pi = _hosts.find(host_url);
-  if (pi != _hosts.end()) {
-    return (*pi).second;
-  }
-
-  P3DHost *host = new P3DHost(host_url, _host_dir);
-  bool inserted = _hosts.insert(Hosts::value_type(host_url, host)).second;
-  assert(inserted);
-
-  return host;
-}
-
-/**
- * Removes the indicated host from the cache.
- */
-void P3DInstanceManager::
-forget_host(P3DHost *host) {
-  const string &host_url = host->get_host_url();
-
-  nout << "Forgetting host " << host_url << "\n";
-
-  // Hmm, this is a memory leak.  But we allow it to remain, since it's an
-  // unusual circumstance (uninstalling), and it's safer to leak than to risk
-  // a floating pointer.
-  _hosts.erase(host_url);
-}
-
-/**
- * Returns a number used to uniquify different instances.  This number is
- * guaranteed to be different at each call, at least until the int space rolls
- * over.
- */
-int P3DInstanceManager::
-get_unique_id() {
-  ++_unique_id;
-  return _unique_id;
-}
-
-/**
- * May be called in any thread to indicate that a new P3D_request is available
- * in the indicated instance.
- */
-void P3DInstanceManager::
-signal_request_ready(P3DInstance *inst) {
-  if (inst->get_request_ready_func() != nullptr) {
-    // This instance requires asynchronous notifications of requests.  Thus,
-    // we should tell the notify thread to wake up and make the callback.
-    _notify_ready.acquire();
-    _notify_instances.push_back(inst);
-    _notify_ready.notify();
-    _notify_ready.release();
-
-    // Oh, and we should spawn the thread if we haven't already.
-    if (!_started_notify_thread) {
-      _notify_thread_continue = true;
-      SPAWN_THREAD(_notify_thread, nt_thread_run, this);
-      _started_notify_thread = true;
-    }
-  }
-
-  // Then, wake up the main thread, in case it's sleeping on wait_request().
-  _request_ready.acquire();
-  _request_ready.notify();
-  _request_ready.release();
-}
-
-/**
- *
- */
-P3D_class_definition *P3DInstanceManager::
-make_class_definition() const {
-  P3D_class_definition *new_class = new P3D_class_definition(P3DObject::_generic_class);
-  // TODO: save this pointer so we can delete it on destruction.
-  return new_class;
-}
-
-/**
- * Constructs a new, unique temporary filename with the indicated extension.
- * You should use the P3DTemporaryFilename interface instead of calling this
- * method directly.
- */
-string P3DInstanceManager::
-make_temp_filename(const string &extension) {
-  string result;
-  bool exists;
-
-  do {
-    int tid;
-#ifdef _WIN32
-    tid = GetCurrentProcessId();
-#else
-    tid = getpid();
-#endif
-    if (tid == 0) {
-      tid = 1;
-    }
-    int hash = ((clock() + _next_temp_filename_counter) * ((time(nullptr) * tid) >> 8)) & 0xffffff;
-    ++_next_temp_filename_counter;
-    char hex_code[10];
-    sprintf(hex_code, "%06x", hash);
-
-    result = _temp_directory;
-    result += "p3d_";
-    result += hex_code;
-    result += extension;
-
-    exists = false;
-    if (_temp_filenames.find(result) != _temp_filenames.end()) {
-      // We've previously allocated this file.
-      exists = true;
-
-    } else {
-
-      // Check if the file exists on disk.
-#ifdef _WIN32
-      DWORD results = GetFileAttributes(result.c_str());
-      if (results != -1) {
-        exists = true;
-      }
-
-#else  // _WIN32
-      struct stat this_buf;
-      if (stat(result.c_str(), &this_buf) == 0) {
-        exists = true;
-      }
-#endif
-    }
-
-  } while (exists);
-
-  nout << "make_temp_filename: " << result << "\n";
-  return result;
-}
-
-/**
- * Releases a temporary filename assigned earlier via make_temp_filename().
- * If the file exists, it will be removed.  You should use the
- * P3DTemporaryFilename interface instead of calling this method directly.
- */
-void P3DInstanceManager::
-release_temp_filename(const string &filename) {
-  nout << "release_temp_filename: " << filename << "\n";
-  _temp_filenames.erase(filename);
-  unlink(filename.c_str());
-}
-
-/**
- * Looks for the particular certificate in the cache of recognized
- * certificates.  Returns true if it is found, false if not.
- */
-bool P3DInstanceManager::
-find_cert(X509 *cert) {
-  // First, we need the DER representation.
-  string der = cert_to_der(cert);
-
-  // If we've previously found this certificate, we don't have to hit disk
-  // again.
-  ApprovedCerts::iterator ci = _approved_certs.find(der);
-  if (ci != _approved_certs.end()) {
-    return true;
-  }
-
-  // Well, we haven't found it already.  Look for it on disk.  For this, we
-  // hash the cert into a hex string.  This is similar to OpenSSL's
-  // get_by_subject() approach, except we hash the whole cert, not just the
-  // subject.  (Since we also store self-signed certs in this list, we can't
-  // trust the subject name alone.)
-  string this_cert_dir = get_cert_dir(cert);
-  nout << "looking in " << this_cert_dir << "\n";
-
-  vector<string> contents;
-  scan_directory(this_cert_dir, contents);
-
-  // Now look at each of the files in this directory and see if any of them
-  // matches the certificate.
-  vector<string>::iterator si;
-  for (si = contents.begin(); si != contents.end(); ++si) {
-    string filename = this_cert_dir + "/" + (*si);
-    X509 *x509 = nullptr;
-    FILE *fp = nullptr;
-#ifdef _WIN32
-    wstring filename_w;
-    if (string_to_wstring(filename_w, filename)) {
-      fp = _wfopen(filename_w.c_str(), L"r");
-    }
-#else // _WIN32
-    fp = fopen(filename.c_str(), "r");
-#endif  // _WIN32
-    if (fp != nullptr) {
-      x509 = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
-      fclose(fp);
-    }
-
-    if (x509 != nullptr) {
-      string der2 = cert_to_der(x509);
-      // We might as well save this cert in the table for next time, even if
-      // it's not the one we're looking for right now.
-      _approved_certs.insert(der2);
-
-      if (der == der2) {
-        return true;
-      }
-    }
-  }
-
-  // Nothing matched.
-  return false;
-}
-
-/**
- * Reads the pre-approved certificates in the certlist package and adds them
- * to the in-memory cache.
- */
-void P3DInstanceManager::
-read_certlist(P3DPackage *package) {
-  nout << "reading certlist in " << package->get_package_dir() << "\n";
-
-  vector<string> contents;
-  scan_directory(package->get_package_dir(), contents);
-
-  vector<string>::iterator si;
-  for (si = contents.begin(); si != contents.end(); ++si) {
-    const string &basename = (*si);
-    if (basename.length() > 4) {
-      string suffix = basename.substr(basename.length() - 4);
-      if (suffix == ".pem" || suffix == ".crt") {
-        string filename = package->get_package_dir() + "/" + basename;
-        X509 *x509 = nullptr;
-        FILE *fp = nullptr;
-#ifdef _WIN32
-        wstring filename_w;
-        if (string_to_wstring(filename_w, filename)) {
-          fp = _wfopen(filename_w.c_str(), L"r");
-        }
-#else // _WIN32
-        fp = fopen(filename.c_str(), "r");
-#endif  // _WIN32
-        if (fp != nullptr) {
-          x509 = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
-          fclose(fp);
-        }
-
-        if (x509 != nullptr) {
-          string der2 = cert_to_der(x509);
-          _approved_certs.insert(der2);
-        }
-      }
-    }
-  }
-}
-
-/**
- * Returns the directory searched for this particular certificate.
- */
-string P3DInstanceManager::
-get_cert_dir(X509 *cert) {
-  string der = cert_to_der(cert);
-
-  static const size_t hash_size = 16;
-  unsigned char md[hash_size];
-
-  MD5_CTX ctx;
-  MD5_Init(&ctx);
-  MD5_Update(&ctx, der.data(), der.size());
-  MD5_Final(md, &ctx);
-
-  string basename;
-  static const size_t keep_hash = 6;
-  for (size_t i = 0; i < keep_hash; ++i) {
-    int high = (md[i] >> 4) & 0xf;
-    int low = md[i] & 0xf;
-    basename += P3DInstanceManager::encode_hexdigit(high);
-    basename += P3DInstanceManager::encode_hexdigit(low);
-  }
-
-  return _certs_dir + "/" + basename;
-}
-
-/**
- * Converts the indicated certificate to its binary DER representation.
- */
-string P3DInstanceManager::
-cert_to_der(X509 *cert) {
-  int buffer_size = i2d_X509(cert, nullptr);
-  unsigned char *buffer = new unsigned char[buffer_size];
-  unsigned char *p = buffer;
-  i2d_X509(cert, &p);
-
-  string result((char *)buffer, buffer_size);
-  delete[] buffer;
-
-  return result;
-}
-
-/**
- * Stops all active instances and removes *all* downloaded files from all
- * hosts, and empties the current user's Panda3D directory as much as
- * possible.
- *
- * This cannot remove the coreapi dll or directory on Windows.
- */
-void P3DInstanceManager::
-uninstall_all() {
-  Instances::iterator ii;
-  for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
-    P3DInstance *inst = (*ii);
-    inst->uninstall_host();
-  }
-
-  Hosts::iterator hi;
-  for (hi = _hosts.begin(); hi != _hosts.end(); ++hi) {
-    P3DHost *host = (*hi).second;
-    host->uninstall();
-  }
-
-  // Close the logfile so we can remove that too.
-  logfile.close();
-
-  if (!_root_dir.empty()) {
-    // This won't be able to delete the coreapi directory on Windows, because
-    // we're running that DLL right now.  But it will delete everything else.
-    delete_directory_recursively(_root_dir);
-  }
-
-  _created_runtime_environment = false;
-}
-
-/**
- *
- */
-P3DInstanceManager *P3DInstanceManager::
-get_global_ptr() {
-  if (_global_ptr == nullptr) {
-    _global_ptr = new P3DInstanceManager;
-  }
-  return _global_ptr;
-}
-
-/**
- * This is called only at plugin shutdown time; it deletes the global instance
- * manager pointer and clears it to NULL.
- */
-void P3DInstanceManager::
-delete_global_ptr() {
-  if (_global_ptr != nullptr) {
-    delete _global_ptr;
-    _global_ptr = nullptr;
-  }
-}
-
-/**
- * Attempts to open the named filename as if it were a directory and looks for
- * the non-hidden files within the directory.  Fills the given vector up with
- * the sorted list of filenames that are local to this directory.
- *
- * It is the user's responsibility to ensure that the contents vector is empty
- * before making this call; otherwise, the new files will be appended to it.
- *
- * Returns true on success, false if the directory could not be read for some
- * reason.
- */
-bool P3DInstanceManager::
-scan_directory(const string &dirname, vector<string> &contents) {
-#ifdef _WIN32
-  // Use Windows' FindFirstFile()  FindNextFile() to walk through the list of
-  // files in a directory.
-  size_t orig_size = contents.size();
-
-  string match = dirname + "\\*.*";
-  WIN32_FIND_DATAW find_data;
-
-  wstring match_w;
-  string_to_wstring(match_w, match);
-
-  HANDLE handle = FindFirstFileW(match_w.c_str(), &find_data);
-  if (handle == INVALID_HANDLE_VALUE) {
-    if (GetLastError() == ERROR_NO_MORE_FILES) {
-      // No matching files is not an error.
-      return true;
-    }
-    return false;
-  }
-
-  do {
-    string filename;
-    wstring_to_string(filename, find_data.cFileName);
-    if (filename != "." && filename != "..") {
-      contents.push_back(filename);
-    }
-  } while (FindNextFileW(handle, &find_data));
-
-  bool scan_ok = (GetLastError() == ERROR_NO_MORE_FILES);
-  FindClose(handle);
-
-  sort(contents.begin() + orig_size, contents.end());
-  return scan_ok;
-
-#else  // _WIN32
-  // Use Posix's opendir()  readdir() to walk through the list of files in a
-  // directory.
-  size_t orig_size = contents.size();
-
-  DIR *root = opendir(dirname.c_str());
-  if (root == nullptr) {
-    return false;
-  }
-
-  struct dirent *d;
-  d = readdir(root);
-  while (d != nullptr) {
-    if (d->d_name[0] != '.') {
-      contents.push_back(d->d_name);
-    }
-    d = readdir(root);
-  }
-
-  closedir(root);
-
-  sort(contents.begin() + orig_size, contents.end());
-  return true;
-
-#endif  // _WIN32
-}
-
-/**
- * Fills up filename_contents with the list of all files (but not
- * directories), and dirname_contents with the list of all directories, rooted
- * at the indicated dirname and below.  The filenames generated are relative
- * to the root of the dirname, with slashes (not backslashes) as the directory
- * separator character.
- *
- * Returns true on success, false if the original dirname wasn't a directory
- * or something like that.
- */
-bool P3DInstanceManager::
-scan_directory_recursively(const string &dirname,
-                           vector<string> &filename_contents,
-                           vector<string> &dirname_contents,
-                           const string &prefix) {
-  vector<string> dir_contents;
-  if (!scan_directory(dirname, dir_contents)) {
-    // Apparently dirname wasn't a directory.
-    return false;
-  }
-
-  // Walk through the contents of dirname.
-  vector<string>::const_iterator si;
-  for (si = dir_contents.begin(); si != dir_contents.end(); ++si) {
-    // Here's a particular file within dirname.  Is it another directory, or
-    // is it a regular file?
-    string pathname = dirname + "/" + (*si);
-    string rel_filename = prefix + (*si);
-    if (scan_directory_recursively(pathname, filename_contents,
-                                   dirname_contents, rel_filename + "/")) {
-      // It's a directory, and it's just added its results to the contents.
-      dirname_contents.push_back(rel_filename);
-
-    } else {
-      // It's not a directory, so assume it's an ordinary file, and add it to
-      // the contents.
-      filename_contents.push_back(rel_filename);
-    }
-  }
-
-  return true;
-}
-
-/**
- * Deletes all of the files and directories in the named directory and below,
- * like rm -rf.  Use with extreme caution.
- */
-void P3DInstanceManager::
-delete_directory_recursively(const string &root_dir) {
-  vector<string> contents, dirname_contents;
-  if (!scan_directory_recursively(root_dir, contents, dirname_contents)) {
-    // Maybe it's just a single file, not a directory.  Delete it.
-#ifdef _WIN32
-    wstring root_dir_w;
-    string_to_wstring(root_dir_w, root_dir);
-    // Windows can't delete a file if it's read-only.
-    _wchmod(root_dir_w.c_str(), 0644);
-    int result = _wunlink(root_dir_w.c_str());
-#else  // _WIN32
-    int result = unlink(root_dir.c_str());
-#endif  // _WIN32
-    if (result == 0) {
-      nout << "Deleted " << root_dir << "\n";
-    } else {
-#ifdef _WIN32
-      result = _waccess(root_dir_w.c_str(), 0);
-#else  // _WIN32
-      result = access(root_dir.c_str(), 0);
-#endif  // _WIN32
-      if (result == 0) {
-        nout << "Could not delete " << root_dir << "\n";
-      }
-    }
-    return;
-  }
-
-  vector<string>::iterator ci;
-  for (ci = contents.begin(); ci != contents.end(); ++ci) {
-    string filename = (*ci);
-    string pathname = root_dir + "/" + filename;
-
-#ifdef _WIN32
-    wstring pathname_w;
-    string_to_wstring(pathname_w, pathname);
-    // Windows can't delete a file if it's read-only.
-    _wchmod(pathname_w.c_str(), 0644);
-    int result = _wunlink(pathname_w.c_str());
-#else  // _WIN32
-    int result = unlink(pathname.c_str());
-#endif  // _WIN32
-    if (result == 0) {
-      nout << "  Deleted " << filename << "\n";
-    } else {
-      nout << "  Could not delete " << filename << "\n";
-    }
-  }
-
-  // Now delete all of the directories too.  They're already in reverse order,
-  // so we remove deeper directories first.
-  for (ci = dirname_contents.begin(); ci != dirname_contents.end(); ++ci) {
-    string filename = (*ci);
-    string pathname = root_dir + "/" + filename;
-
-#ifdef _WIN32
-    wstring pathname_w;
-    string_to_wstring(pathname_w, pathname);
-    _wchmod(pathname_w.c_str(), 0755);
-    int result = _wrmdir(pathname_w.c_str());
-#else  // _WIN32
-    int result = rmdir(pathname.c_str());
-#endif  // _WIN32
-    if (result == 0) {
-      nout << "  Removed directory " << filename << "\n";
-    } else {
-      nout << "  Could not remove directory " << filename << "\n";
-    }
-  }
-
-  // Finally, delete the root directory itself.
-  string pathname = root_dir;
-#ifdef _WIN32
-  wstring pathname_w;
-  string_to_wstring(pathname_w, pathname);
-  _wchmod(pathname_w.c_str(), 0755);
-  int result = _wrmdir(pathname_w.c_str());
-#else  // _WIN32
-  int result = rmdir(pathname.c_str());
-#endif  // _WIN32
-  if (result == 0) {
-    nout << "Removed directory " << root_dir << "\n";
-  } else {
-#ifdef _WIN32
-    result = _waccess(pathname_w.c_str(), 0);
-#else  // _WIN32
-    result = access(pathname.c_str(), 0);
-#endif  // _WIN32
-    if (result == 0) {
-      nout << "Could not remove directory " << root_dir << "\n";
-    }
-  }
-}
-
-/**
- * Removes the first instance of the indicated file from the given list.
- * Returns true if removed, false if it was not found.
- *
- * On Windows, the directory separator characters are changed from backslash
- * to forward slash before searching in the list; so it is assumed that the
- * list contains filenames with a forward slash used as a separator.
- */
-bool P3DInstanceManager::
-remove_file_from_list(vector<string> &contents, const string &filename) {
-#ifdef _WIN32
-  // Convert backslashes to slashes.
-  string clean_filename;
-  for (string::const_iterator pi = filename.begin();
-       pi != filename.end();
-       ++pi) {
-    if ((*pi) == '\\') {
-      clean_filename += '/';
-    } else {
-      clean_filename += (*pi);
-    }
-  }
-#else
-  const string &clean_filename = filename;
-#endif  // _WIN32
-
-  vector<string>::iterator ci;
-  for (ci = contents.begin(); ci != contents.end(); ++ci) {
-    if ((*ci) == clean_filename) {
-      contents.erase(ci);
-      return true;
-    }
-  }
-
-  return false;
-}
-
-/**
- * Appends the indicated basename to the root directory name, which is
- * modified in-place.  The basename is allowed to contain nested slashes, but
- * no directory component of the basename may begin with a ".", thus
- * precluding ".." and hidden files.
- */
-void P3DInstanceManager::
-append_safe_dir(string &root, const string &basename) {
-  if (basename.empty()) {
-    return;
-  }
-
-  size_t p = 0;
-  while (p < basename.length()) {
-    size_t q = basename.find('/', p);
-    if (q == string::npos) {
-      if (q != p) {
-        append_safe_dir_component(root, basename.substr(p));
-      }
-      return;
-    }
-    if (q != p) {
-      append_safe_dir_component(root, basename.substr(p, q - p));
-    }
-    p = q + 1;
-  }
-}
-
-/**
- * Called during initialize, or after a previous call to uninstall_all(), to
- * make sure all needed directories exist and the logfile is open.
- */
-void P3DInstanceManager::
-create_runtime_environment() {
-  mkdir_complete(_log_directory, std::cerr);
-
-  logfile.close();
-  logfile.clear();
-#ifdef _WIN32
-  wstring log_pathname_w;
-  string_to_wstring(log_pathname_w, _log_pathname);
-  logfile.open(log_pathname_w.c_str(), std::ios::out | std::ios::trunc);
-#else
-  logfile.open(_log_pathname.c_str(), std::ios::out | std::ios::trunc);
-#endif  // _WIN32
-  if (logfile) {
-    logfile.setf(std::ios::unitbuf);
-    nout_stream = &logfile;
-  }
-
-  // Determine the temporary directory.
-#ifdef _WIN32
-  wchar_t buffer_1[MAX_PATH];
-  wstring temp_directory_w;
-
-  // Figuring out the correct path for temporary files is a real mess on
-  // Windows.  We should be able to use GetTempPath(), but that relies on $TMP
-  // or $TEMP being defined, and it appears that Mozilla clears these
-  // environment variables for the plugin, which forces GetTempPath() into
-  // $USERPROFILE instead.  This is really an inappropriate place for
-  // temporary files, so, GetTempPath() isn't a great choice.
-
-  // We could use SHGetSpecialFolderPath() instead to get us the path to
-  // "Temporary Internet Files", which is acceptable.  The trouble is, if we
-  // happen to be running in "Protected Mode" on Vista, this folder isn't
-  // actually writable by us!  On Vista, we're supposed to use
-  // IEGetWriteableFolderPath() instead, but *this* function doesn't exist on
-  // XP and below.  Good Lord.
-
-  // We could go through a bunch of LoadLibrary() calls to try to find the
-  // right path, like we do in find_root_dir(), but I'm just tired of doing
-  // all that nonsense.  We'll use a two-stage trick instead.  We'll check for
-  // $TEMP or $TMP being defined specifically, and if they are, we'll use
-  // GetTempPath(); otherwise, we'll fall back to SHGetSpecialFolderPath().
-
-  if (getenv("TEMP") != nullptr || getenv("TMP") != nullptr) {
-    if (GetTempPathW(MAX_PATH, buffer_1) != 0) {
-      temp_directory_w = buffer_1;
-    }
-  }
-  if (temp_directory_w.empty()) {
-    if (SHGetSpecialFolderPathW(nullptr, buffer_1, CSIDL_INTERNET_CACHE, true)) {
-      temp_directory_w = buffer_1;
-
-      // That just *might* return a non-writable folder, if we're in Protected
-      // Mode.  We'll test this with GetTempFileName().
-      wchar_t temp_buffer[MAX_PATH];
-      if (!GetTempFileNameW(temp_directory_w.c_str(), L"p3d", 0, temp_buffer)) {
-        nout << "GetTempFileName failed on " << temp_directory_w
-             << ", switching to GetTempPath\n";
-        temp_directory_w.clear();
-      } else {
-        DeleteFileW(temp_buffer);
-      }
-    }
-  }
-
-  // If both of the above failed, we'll fall back to GetTempPath() once again
-  // as a last resort, which is supposed to return *something* that works,
-  // even if $TEMP and $TMP are undefined.
-  if (temp_directory_w.empty()) {
-    if (GetTempPathW(MAX_PATH, buffer_1) != 0) {
-      temp_directory_w = buffer_1;
-    }
-  }
-
-  // Also insist that the temp directory is fully specified.
-  size_t needs_size_2 = GetFullPathNameW(temp_directory_w.c_str(), 0, nullptr, nullptr);
-  wchar_t *buffer_2 = new wchar_t[needs_size_2];
-  if (GetFullPathNameW(temp_directory_w.c_str(), needs_size_2, buffer_2, nullptr) != 0) {
-    temp_directory_w = buffer_2;
-  }
-  delete[] buffer_2;
-
-  // And make sure the directory actually exists.
-  mkdir_complete_w(temp_directory_w, nout);
-  wstring_to_string(_temp_directory, temp_directory_w);
-
-#else
-  _temp_directory = "/tmp/";
-#endif  // _WIN32
-
-  // Ensure that the temp directory ends with a slash.
-  if (!_temp_directory.empty() && _temp_directory[_temp_directory.size() - 1] != '/') {
-#ifdef _WIN32
-    if (_temp_directory[_temp_directory.size() - 1] != '\\')
-#endif
-      _temp_directory += "/";
-  }
-
-  nout << "\n_root_dir = " << _root_dir
-       << ", _temp_directory = " << _temp_directory
-       << ", platform = " << _platform
-       << ", host_url = " << _host_url
-       << ", verify_contents = " << _verify_contents
-       << "\n";
-  if (!_host_dir.empty()) {
-    nout << "_host_dir = " << _host_dir << "\n";
-  }
-  nout << "api_version = " << _api_version << "\n";
-
-  // Make the certificate directory.
-  _certs_dir = _root_dir + "/certs";
-  if (!get_trusted_environment()) {
-    if (!mkdir_complete(_certs_dir, nout)) {
-      nout << "Couldn't mkdir " << _certs_dir << "\n";
-    }
-  }
-
-#ifndef _WIN32
-  // If we're running from the console, make sure that terminating the parent
-  // process will cause the child process to terminate as well.
-  if (_console_environment) {
-    struct sigaction ignore;
-    memset(&ignore, 0, sizeof(ignore));
-    ignore.sa_handler = SIG_IGN;
-    sigaction(SIGINT, &ignore, nullptr);
-  }
-#endif
-
-  _created_runtime_environment = true;
-}
-
-/**
- * Appends a single directory component, implementing append_safe_dir(),
- * above.
- */
-void P3DInstanceManager::
-append_safe_dir_component(string &root, const string &component) {
-  if (component.empty()) {
-    return;
-  }
-  root += '/';
-  if (component[0] == '.') {
-    root += 'x';
-  }
-  root += component;
-}
-
-/**
- * The main function for the notify thread.
- */
-void P3DInstanceManager::
-nt_thread_run() {
-/*
- * The notify thread exists because we need to be able to send asynchronous
- * notifications of request events.  These request events were detected in the
- * various read threads associated with each session, but we can't call back
- * into the plugin host space from the read thread, since if the host
- * immediately responds to a callback by calling back into the p3d_plugin
- * space, we will have our read thread doing stuff in here that's not related
- * to the read thread.  Even worse, some of the things it might need to do
- * might require a separate read thread to be running!
- */
-
-  _notify_ready.acquire();
-  while (_notify_thread_continue) {
-    NotifyInstances instances;
-    while (!_notify_instances.empty()) {
-      instances.clear();
-      instances.swap(_notify_instances);
-
-      // Go ahead and drop the lock while we make the callback, to reduce the
-      // risk of deadlock.  We don't want to be holding any locks when we call
-      // into client code.
-      _notify_ready.release();
-      NotifyInstances::iterator ni;
-      for (ni = instances.begin(); ni != instances.end(); ++ni) {
-        // TODO: a race condition here when instances are deleted.
-        P3DInstance *inst = (*ni);
-        assert(inst != nullptr);
-        P3D_request_ready_func *func = inst->get_request_ready_func();
-        if (func != nullptr) {
-          (*func)(inst);
-        }
-      }
-      _notify_ready.acquire();
-    }
-
-    _notify_ready.wait();
-  }
-  _notify_ready.release();
-}
-
-#ifdef _WIN32
-bool P3DInstanceManager::
-supports_win64() {
-  BOOL is_win64 = false;
-
-  typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
-  LPFN_ISWOW64PROCESS _IsWow64Process;
-  _IsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");
-
-  if (_IsWow64Process != nullptr) {
-    if (!_IsWow64Process(GetCurrentProcess(), &is_win64)) {
-      is_win64 = false;
-    }
-  }
-  return (is_win64 != 0);
-}
-#endif  // _WIN32

+ 0 - 244
direct/src/plugin/p3dInstanceManager.h

@@ -1,244 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dInstanceManager.h
- * @author drose
- * @date 2009-05-29
- */
-
-#ifndef P3DINSTANCEMANAGER_H
-#define P3DINSTANCEMANAGER_H
-
-#include "p3d_plugin_common.h"
-#include "p3dConditionVar.h"
-
-#include <set>
-#include <map>
-#include <vector>
-
-#ifndef _WIN32
-#include <signal.h>
-#endif
-
-#define OPENSSL_NO_KRB5
-#include <openssl/x509.h>
-#include <openssl/pem.h>
-#include <openssl/md5.h>
-
-class P3DInstance;
-class P3DSession;
-class P3DAuthSession;
-class P3DHost;
-class P3DPackage;
-class FileSpec;
-class TiXmlElement;
-
-/**
- * This global class manages the set of instances in the universe.
- */
-class P3DInstanceManager {
-private:
-  P3DInstanceManager();
-  ~P3DInstanceManager();
-
-public:
-  bool initialize(int api_version, const std::string &contents_filename,
-                  const std::string &host_url,
-                  P3D_verify_contents verify_contents,
-                  const std::string &platform,
-                  const std::string &log_directory,
-                  const std::string &log_basename,
-                  bool trusted_environment,
-                  bool console_environment,
-                  const std::string &root_dir = "",
-                  const std::string &host_dir = "",
-                  const std::string &start_dir = "");
-
-  inline bool is_initialized() const;
-  inline void reconsider_runtime_environment();
-  inline P3D_verify_contents get_verify_contents() const;
-  inline void reset_verify_contents();
-
-  inline int get_api_version() const;
-  inline const std::string &get_host_url() const;
-  inline const std::string &get_root_dir() const;
-  inline const std::string &get_start_dir() const;
-  inline const std::string &get_platform() const;
-  inline const std::string &get_temp_directory() const;
-  inline const std::string &get_log_directory() const;
-  inline const std::string &get_log_pathname() const;
-  inline bool get_trusted_environment() const;
-  inline bool get_console_environment() const;
-
-  inline int get_num_supported_platforms() const;
-  inline const std::string &get_supported_platform(int n) const;
-
-  void set_plugin_version(int major, int minor, int sequence,
-                          bool official, const std::string &distributor,
-                          const std::string &coreapi_host_url,
-                          time_t coreapi_timestamp,
-                          const std::string &coreapi_set_ver);
-  inline int get_plugin_major_version() const;
-  inline int get_plugin_minor_version() const;
-  inline int get_plugin_sequence_version() const;
-  inline bool get_plugin_official_version() const;
-  inline const std::string &get_plugin_distributor() const;
-  inline const std::string &get_coreapi_host_url() const;
-  inline time_t get_coreapi_timestamp() const;
-  inline const std::string &get_coreapi_set_ver() const;
-
-  void set_super_mirror(const std::string &super_mirror_url);
-  inline const std::string &get_super_mirror() const;
-
-  P3DInstance *
-  create_instance(P3D_request_ready_func *func,
-                  const P3D_token tokens[], size_t num_tokens,
-                  int argc, const char *argv[], void *user_data);
-
-  bool set_p3d_filename(P3DInstance *inst, bool is_local,
-                        const std::string &p3d_filename, const int &p3d_offset);
-  int make_p3d_stream(P3DInstance *inst, const std::string &p3d_url);
-  bool start_instance(P3DInstance *inst);
-  void finish_instance(P3DInstance *inst);
-  P3DAuthSession *authorize_instance(P3DInstance *inst);
-
-  P3DInstance *validate_instance(P3D_instance *instance);
-
-  P3DInstance *check_request();
-  void wait_request(double timeout);
-
-  P3DHost *get_host(const std::string &host_url);
-  void forget_host(P3DHost *host);
-
-  inline int get_num_instances() const;
-
-  int get_unique_id();
-  void signal_request_ready(P3DInstance *inst);
-
-  P3D_class_definition *make_class_definition() const;
-
-  inline P3D_object *new_undefined_object();
-  inline P3D_object *new_none_object();
-  inline P3D_object *new_bool_object(bool value);
-
-  std::string make_temp_filename(const std::string &extension);
-  void release_temp_filename(const std::string &filename);
-
-  bool find_cert(X509 *cert);
-  void read_certlist(P3DPackage *package);
-  std::string get_cert_dir(X509 *cert);
-  static std::string cert_to_der(X509 *cert);
-
-  void uninstall_all();
-
-  static P3DInstanceManager *get_global_ptr();
-  static void delete_global_ptr();
-
-  static inline char encode_hexdigit(int c);
-  static bool scan_directory(const std::string &dirname, std::vector<std::string> &contents);
-  static bool scan_directory_recursively(const std::string &dirname,
-                                         std::vector<std::string> &filename_contents,
-                                         std::vector<std::string> &dirname_contents,
-                                         const std::string &prefix = "");
-  static void delete_directory_recursively(const std::string &root_dir);
-  static bool remove_file_from_list(std::vector<std::string> &contents, const std::string &filename);
-
-  static void append_safe_dir(std::string &root, const std::string &basename);
-
-private:
-  void create_runtime_environment();
-  static void append_safe_dir_component(std::string &root, const std::string &component);
-
-private:
-  // The notify thread.  This thread runs only for the purpose of generating
-  // asynchronous notifications of requests, to callers who ask for it.
-  THREAD_CALLBACK_DECLARATION(P3DInstanceManager, nt_thread_run);
-  void nt_thread_run();
-
-#ifdef _WIN32
-  static bool supports_win64();
-#endif  // _WIN32
-
-private:
-  bool _is_initialized;
-  bool _created_runtime_environment;
-  int _api_version;
-  std::string _host_url;
-  std::string _root_dir;
-  std::string _host_dir;
-  std::string _start_dir;
-  std::string _certs_dir;
-  P3D_verify_contents _verify_contents;
-  std::string _platform;
-  std::string _log_directory;
-  std::string _log_basename;
-  std::string _log_pathname;
-  std::string _temp_directory;
-  bool _trusted_environment;
-  bool _console_environment;
-  int _plugin_major_version;
-  int _plugin_minor_version;
-  int _plugin_sequence_version;
-  bool _plugin_official_version;
-  std::string _plugin_distributor;
-  std::string _coreapi_host_url;
-  time_t _coreapi_timestamp;
-  std::string _coreapi_set_ver;
-  std::string _super_mirror_url;
-
-  typedef std::vector<std::string> SupportedPlatforms;
-  SupportedPlatforms _supported_platforms;
-
-  P3D_object *_undefined_object;
-  P3D_object *_none_object;
-  P3D_object *_true_object;
-  P3D_object *_false_object;
-
-  typedef std::set<std::string> ApprovedCerts;
-  ApprovedCerts _approved_certs;
-
-  typedef std::set<P3DInstance *> Instances;
-  Instances _instances;
-  P3DAuthSession *_auth_session;
-
-  typedef std::map<std::string, P3DSession *> Sessions;
-  Sessions _sessions;
-
-  typedef std::map<std::string, P3DHost *> Hosts;
-  Hosts _hosts;
-
-  typedef std::set<std::string> TempFilenames;
-  TempFilenames _temp_filenames;
-  int _next_temp_filename_counter;
-
-  int _unique_id;
-
-  // This condition var is waited on the main thread and signaled in a sub-
-  // thread when new request notices arrive.
-  P3DConditionVar _request_ready;
-
-  // We may need a thread to send async request notices to callers.
-  bool _notify_thread_continue;
-  bool _started_notify_thread;
-  THREAD _notify_thread;
-  // This queue of instances that need to send notifications is protected by
-  // _notify_ready's mutex.
-  typedef std::vector<P3DInstance *> NotifyInstances;
-  NotifyInstances _notify_instances;
-  P3DConditionVar _notify_ready;
-
-#ifndef _WIN32
-  struct sigaction _old_sigpipe;
-#endif
-
-  static P3DInstanceManager *_global_ptr;
-};
-
-#include "p3dInstanceManager.I"
-
-#endif

+ 0 - 66
direct/src/plugin/p3dIntObject.cxx

@@ -1,66 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dIntObject.cxx
- * @author drose
- * @date 2009-06-30
- */
-
-#include "p3dIntObject.h"
-
-/**
- *
- */
-P3DIntObject::
-P3DIntObject(int value) : _value(value) {
-}
-
-/**
- *
- */
-P3DIntObject::
-P3DIntObject(const P3DIntObject &copy) :
-  P3DObject(copy),
-  _value(copy._value)
-{
-}
-
-/**
- * Returns the fundamental type of this kind of object.
- */
-P3D_object_type P3DIntObject::
-get_type() {
-  return P3D_OT_int;
-}
-
-/**
- * Returns the object value coerced to a boolean, if possible.
- */
-bool P3DIntObject::
-get_bool() {
-  return (_value != 0);
-}
-
-/**
- * Returns the object value coerced to an integer, if possible.
- */
-int P3DIntObject::
-get_int() {
-  return _value;
-}
-
-/**
- * Fills the indicated C++ string object with the value of this object coerced
- * to a string.
- */
-void P3DIntObject::
-make_string(std::string &value) {
-  std::ostringstream strm;
-  strm << _value;
-  value = strm.str();
-}

+ 0 - 38
direct/src/plugin/p3dIntObject.h

@@ -1,38 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dIntObject.h
- * @author drose
- * @date 2009-06-30
- */
-
-#ifndef P3DINTOBJECT_H
-#define P3DINTOBJECT_H
-
-#include "p3d_plugin_common.h"
-#include "p3dObject.h"
-
-/**
- * An object type that contains an integer value.
- */
-class P3DIntObject : public P3DObject {
-public:
-  P3DIntObject(int value);
-  P3DIntObject(const P3DIntObject &copy);
-
-public:
-  virtual P3D_object_type get_type();
-  virtual bool get_bool();
-  virtual int get_int();
-  virtual void make_string(std::string &value);
-
-private:
-  int _value;
-};
-
-#endif

+ 0 - 707
direct/src/plugin/p3dMainObject.cxx

@@ -1,707 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dMainObject.cxx
- * @author drose
- * @date 2009-07-10
- */
-
-#include "p3dMainObject.h"
-#include "p3dPythonObject.h"
-#include "p3dInstance.h"
-#include "p3dSession.h"
-#include "p3dStringObject.h"
-#include "p3dInstanceManager.h"
-
-using std::ios;
-using std::max;
-using std::streamsize;
-using std::string;
-
-/**
- *
- */
-P3DMainObject::
-P3DMainObject() :
-  _pyobj(nullptr),
-  _inst(nullptr),
-  _unauth_play(false)
-{
-}
-
-/**
- *
- */
-P3DMainObject::
-~P3DMainObject() {
-  set_pyobj(nullptr);
-
-  // Clear the local properties.
-  Properties::const_iterator pi;
-  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
-    P3D_object *value = (*pi).second;
-    P3D_OBJECT_DECREF(value);
-  }
-  _properties.clear();
-}
-
-/**
- * Returns the fundamental type of this kind of object.
- */
-P3D_object_type P3DMainObject::
-get_type() {
-  return P3D_OT_object;
-}
-
-/**
- * Returns the object value coerced to a boolean, if possible.
- */
-bool P3DMainObject::
-get_bool() {
-  return true;
-}
-
-/**
- * Returns the object value coerced to an integer, if possible.
- */
-int P3DMainObject::
-get_int() {
-  return 0;
-}
-
-/**
- * Returns the object value coerced to a floating-point value, if possible.
- */
-double P3DMainObject::
-get_float() {
-  return 0.0;
-}
-
-/**
- * Fills the indicated C++ string object with the value of this object coerced
- * to a string.
- */
-void P3DMainObject::
-make_string(string &value) {
-  if (_pyobj == nullptr) {
-    value = "P3DMainObject";
-  } else {
-    int size = P3D_OBJECT_GET_STRING(_pyobj, nullptr, 0);
-    char *buffer = new char[size];
-    P3D_OBJECT_GET_STRING(_pyobj, buffer, size);
-    value = string(buffer, size);
-    delete[] buffer;
-  }
-}
-
-/**
- * Returns the named property element in the object.  The return value is a
- * new-reference P3D_object, or NULL on error.
- */
-P3D_object *P3DMainObject::
-get_property(const string &property) {
-  if (_pyobj == nullptr) {
-    // Without a pyobj, we just report whatever's been stored locally.
-    Properties::const_iterator pi;
-    pi = _properties.find(property);
-    if (pi != _properties.end()) {
-      P3D_object *result = (*pi).second;
-      P3D_OBJECT_INCREF(result);
-      return result;
-    }
-    return nullptr;
-  }
-
-  // With a pyobj, we pass the query down to it.
-  return P3D_OBJECT_GET_PROPERTY(_pyobj, property.c_str());
-}
-
-/**
- * Modifies (or deletes, if value is NULL) the named property element in the
- * object.  Returns true on success, false on failure.
- */
-bool P3DMainObject::
-set_property(const string &property, bool needs_response, P3D_object *value) {
-  // First, we set the property locally.
-  if (value != nullptr) {
-    Properties::iterator pi;
-    pi = _properties.insert(Properties::value_type(property, nullptr)).first;
-    assert(pi != _properties.end());
-    P3D_object *orig_value = (*pi).second;
-    if (orig_value != value) {
-      P3D_OBJECT_XDECREF(orig_value);
-      (*pi).second = value;
-      P3D_OBJECT_INCREF(value);
-    }
-  } else {
-    // (Or delete the property locally.)
-    Properties::iterator pi;
-    pi = _properties.find(property);
-    if (pi != _properties.end()) {
-      P3D_object *orig_value = (*pi).second;
-      P3D_OBJECT_DECREF(orig_value);
-      _properties.erase(pi);
-    }
-  }
-
-  if (_pyobj == nullptr) {
-    // Without a pyobj, that's all we do.
-    return true;
-  }
-
-  // With a pyobj, we also pass this request down.
-  return P3D_OBJECT_SET_PROPERTY(_pyobj, property.c_str(), needs_response, value);
-}
-
-/**
- * Returns true if the named method exists on this object, false otherwise.
- */
-bool P3DMainObject::
-has_method(const string &method_name) {
-  // Some special-case methods implemented in-place.
-  if (method_name == "play") {
-    return true;
-  } else if (method_name == "read_game_log") {
-    return true;
-  } else if (method_name == "read_system_log") {
-    return true;
-  } else if (method_name == "read_log") {
-    return true;
-  } else if (method_name == "uninstall") {
-    return true;
-  }
-
-  if (_pyobj == nullptr) {
-    // No methods until we get our pyobj.
-    return false;
-  }
-
-  return P3D_OBJECT_HAS_METHOD(_pyobj, method_name.c_str());
-}
-
-/**
- * Invokes the named method on the object, passing the indicated parameters.
- * If the method name is empty, invokes the object itself.
- *
- * If needs_response is true, the return value is a new-reference P3D_object
- * on success, or NULL on failure.  If needs_response is false, the return
- * value is always NULL, and there is no way to determine success or failure.
- */
-P3D_object *P3DMainObject::
-call(const string &method_name, bool needs_response,
-     P3D_object *params[], int num_params) {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  nout << "main." << method_name << "(";
-  for (int i = 0; i < num_params; ++i) {
-    if (i != 0) {
-      nout << ", ";
-    }
-    int buffer_size = P3D_OBJECT_GET_REPR(params[i], nullptr, 0);
-    char *buffer = new char[buffer_size];
-    P3D_OBJECT_GET_REPR(params[i], buffer, buffer_size);
-    nout.write(buffer, buffer_size);
-    delete[] buffer;
-  }
-  nout << ")\n";
-
-  if (method_name == "play") {
-    return call_play(params, num_params);
-  } else if (method_name == "read_game_log") {
-    return call_read_game_log(params, num_params);
-  } else if (method_name == "read_system_log") {
-    return call_read_system_log(params, num_params);
-  } else if (method_name == "read_log") {
-    return call_read_log(params, num_params);
-  } else if (method_name == "uninstall") {
-    return call_uninstall(params, num_params);
-  }
-
-  if (_pyobj == nullptr) {
-    // No methods until we get our pyobj.
-    return nullptr;
-  }
-
-  return P3D_OBJECT_CALL(_pyobj, method_name.c_str(), needs_response,
-                         params, num_params);
-}
-
-/**
- * Writes a formatted representation of the value to the indicated string.
- * This is intended for developer assistance.
- */
-void P3DMainObject::
-output(std::ostream &out) {
-  out << "P3DMainObject";
-}
-
-/**
- * Changes the internal pyobj pointer.  This is the P3D_object that references
- * the actual PyObject held within the child process, corresponding to the
- * true main object there.  The new object's reference count is incremented,
- * and the previous object's is decremented.
- */
-void P3DMainObject::
-set_pyobj(P3D_object *pyobj) {
-  if (pyobj == this) {
-    // We are setting a reference directly to ourselves.  This happens when
-    // the application has accepted the main object we gave it in
-    // set_instance_info().  This means the application is directly
-    // manipulating this object as its appRunner.main.  In this case, we don't
-    // actually need to set the reference; instead, we clear anything we had
-    // set.
-    nout << "application shares main object\n";
-    pyobj = nullptr;
-
-  } else if (pyobj != nullptr) {
-    // In the alternate case, the application has its own, separate
-    // appRunner.main object.  Thus, we do need to set the pointer.
-    nout << "application has its own main object\n";
-  }
-
-  if (_pyobj != pyobj) {
-    P3D_OBJECT_XDECREF(_pyobj);
-    _pyobj = pyobj;
-    if (_pyobj != nullptr) {
-      P3D_OBJECT_INCREF(_pyobj);
-
-      // Now that we have a pyobj, we have to transfer down all of the
-      // properties we'd set locally.
-      apply_properties(_pyobj);
-    }
-  }
-}
-
-/**
- * Returns the internal pyobj pointer, or NULL if it has not yet been set.
- */
-P3D_object *P3DMainObject::
-get_pyobj() const {
-  return _pyobj;
-}
-
-/**
- * Applies the locally-set properties onto the indicated Python object, but
- * does not store the object.  This is a one-time copy of the locally-set
- * properties (like "coreapiHostUrl" and the like) onto the indicated Python
- * object.
- */
-void P3DMainObject::
-apply_properties(P3D_object *pyobj) {
-  P3DPythonObject *p3dpyobj = nullptr;
-  if (pyobj->_class == &P3DObject::_object_class) {
-    p3dpyobj = ((P3DObject *)pyobj)->as_python_object();
-  }
-
-  Properties::const_iterator pi;
-  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
-    const string &property_name = (*pi).first;
-    P3D_object *value = (*pi).second;
-    if (p3dpyobj != nullptr && P3D_OBJECT_GET_TYPE(value) != P3D_OT_object) {
-      // If we know we have an actual P3DPythonObject (we really expect this),
-      // then we can call set_property_insecure() directly, because we want to
-      // allow setting the initial properties even if Javascript has no
-      // permissions to write into Python.  But we don't allow setting objects
-      // this way in any event.
-      p3dpyobj->set_property_insecure(property_name, false, value);
-    } else {
-      // Otherwise, we go through the generic interface.
-      P3D_OBJECT_SET_PROPERTY(pyobj, property_name.c_str(), false, value);
-    }
-  }
-}
-
-/**
- * Sets a callback pointer to the instance that owns this object.  When this
- * instance destructs, it clears this pointer to NULL.
- */
-void P3DMainObject::
-set_instance(P3DInstance *inst) {
-  if (_inst != nullptr) {
-    // Save the game log filename of the instance just before it goes away, in
-    // case JavaScript asks for it later.
-    _game_log_pathname = _inst->get_log_pathname();
-  }
-
-  _inst = inst;
-}
-
-/**
- * Starts the process remotely, as if the play button had been clicked.  If
- * the application has not yet been validated, this pops up the validation
- * dialog.
- *
- * Only applicable if the application was in the ready state, or the unauth
- * state.  Returns true if the application is now started, false otherwise.
- *
- * This may be invoked from the unauth state only once.  If the user chooses
- * not to authorize the plugin at that time, it may not be invoked
- * automatically again.
- */
-P3D_object *P3DMainObject::
-call_play(P3D_object *params[], int num_params) {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  if (_inst == nullptr) {
-    return inst_mgr->new_bool_object(false);
-  }
-
-  // I guess there's no harm in allowing JavaScript to call play(), with or
-  // without explicit scripting authorization.
-  nout << "play() called from JavaScript\n";
-
-  if (!_inst->is_trusted()) {
-    // Requires authorization.  We allow this only once; beyond that, and
-    // you're only annoying the user.
-    if (!_unauth_play) {
-      _unauth_play = true;
-      _inst->splash_button_clicked_main_thread();
-    }
-
-  } else if (!_inst->is_started()) {
-    // We allow calling play() from a ready state without limit, but probably
-    // only once will be necessary.
-    _inst->splash_button_clicked_main_thread();
-  }
-
-  return inst_mgr->new_bool_object(_inst->is_started());
-}
-
-/**
- * Reads the entire logfile as a string, and returns it to the calling
- * JavaScript process.
- */
-P3D_object *P3DMainObject::
-call_read_game_log(P3D_object *params[], int num_params) {
-  if (_inst != nullptr) {
-    string log_pathname = _inst->get_log_pathname();
-    return read_log(log_pathname, params, num_params);
-  }
-
-  if (!_game_log_pathname.empty()) {
-    // The instance has already finished, but we saved its log filename.
-    return read_log(_game_log_pathname, params, num_params);
-  }
-
-  // No log available for us.
-  nout << "read_game_log: error: game log name unknown" << "\n";
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  return inst_mgr->new_undefined_object();
-}
-
-/**
- * As above, but reads the system log, the logfile for the installation
- * process.
- */
-P3D_object *P3DMainObject::
-call_read_system_log(P3D_object *params[], int num_params) {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  string log_pathname = inst_mgr->get_log_pathname();
-  return read_log(log_pathname, params, num_params);
-}
-
-/**
- * Reads a named logfile.  The filename must end in ".log" and must not
- * contain any slashes or colons (it must be found within the log directory).
- */
-P3D_object *P3DMainObject::
-call_read_log(P3D_object *params[], int num_params) {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  if (num_params < 1) {
-    nout << "read_log: error: not enough parameters" << "\n";
-    return inst_mgr->new_undefined_object();
-  }
-
-  int size = P3D_OBJECT_GET_STRING(params[0], nullptr, 0);
-  char *buffer = new char[size];
-  P3D_OBJECT_GET_STRING(params[0], buffer, size);
-  string log_filename = string(buffer, size);
-  delete[] buffer;
-
-  if (log_filename.size() < 4 || log_filename.substr(log_filename.size() - 4) != string(".log")) {
-    // Wrong filename extension.
-    nout << "read_log: error: invalid filename" << "\n";
-    return inst_mgr->new_undefined_object();
-  }
-
-  size_t slash = log_filename.find('/');
-  if (slash != string::npos) {
-    // No slashes allowed.
-    nout << "read_log: error: invalid filename" << "\n";
-    return inst_mgr->new_undefined_object();
-  }
-
-  slash = log_filename.find('\\');
-  if (slash != string::npos) {
-    // Nor backslashes.
-    nout << "read_log: error: invalid filename" << "\n";
-    return inst_mgr->new_undefined_object();
-  }
-
-  size_t colon = log_filename.find(':');
-  if (colon != string::npos) {
-    // Nor colons, for that matter.
-    nout << "read_log: error: invalid filename" << "\n";
-    return inst_mgr->new_undefined_object();
-  }
-
-  string log_pathname = inst_mgr->get_log_directory() + log_filename;
-  P3D_object *result = read_log(log_pathname, params + 1, num_params - 1);
-  return result;
-}
-
-/**
- * log-reader meta function that handles reading previous log files in
- * addition to the present one
- */
-P3D_object *P3DMainObject::
-read_log(const string &log_pathname, P3D_object *params[], int num_params) {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  string log_directory = inst_mgr->get_log_directory();
-  std::ostringstream log_data;
-
-  // Check the first parameter, if any--if given, it specifies the last n
-  // bytes to retrieve.
-  size_t tail_bytes = 0;
-  if (num_params > 0) {
-    tail_bytes = (size_t)max(P3D_OBJECT_GET_INT(params[0]), 0);
-  }
-  // Check the second parameter, if any--if given, it specifies the first n
-  // bytes to retrieve.
-  size_t head_bytes = 0;
-  if (num_params > 1) {
-    head_bytes = (size_t)max(P3D_OBJECT_GET_INT(params[1]), 0);
-  }
-  // Check the third parameter, if any--if given, it specifies the last n
-  // bytes to retrieve from previous copies of this file.
-  size_t tail_bytes_prev = 0;
-  if (num_params > 2) {
-    tail_bytes_prev = (size_t)max(P3D_OBJECT_GET_INT(params[2]), 0);
-  }
-  // Check the fourth parameter, if any--if given, it specifies the first n
-  // bytes to retrieve from previous copies of this file.
-  size_t head_bytes_prev = 0;
-  if (num_params > 3) {
-    head_bytes_prev = (size_t)max(P3D_OBJECT_GET_INT(params[3]), 0);
-  }
-
-  // Determine the base of the log file names
-  nout << "log_pathname: " << log_pathname << "\n";
-  string log_basename = log_pathname;
-  size_t slash = log_basename.rfind('/');
-  if (slash != string::npos) {
-    log_basename = log_basename.substr(slash + 1);
-  }
-#ifdef _WIN32
-  slash = log_basename.rfind('\\');
-  if (slash != string::npos) {
-    log_basename = log_basename.substr(slash + 1);
-  }
-#endif  // _WIN32
-  string log_leafname_primary = log_basename;
-  int dash = log_basename.rfind("-");
-  if (dash != string::npos) {
-    log_basename = log_basename.substr(0, dash+1);
-  } else {
-    int dotLog = log_basename.rfind(".log");
-    if (dotLog != string::npos) {
-      log_basename = log_basename.substr(0, dotLog);
-      log_basename += "-";
-    }
-  }
-
-  // Read matching files
-  std::vector<string> all_logs;
-  int log_matches_found = 0;
-  string log_matching_pathname;
-  inst_mgr->scan_directory(log_directory, all_logs);
-  for (int i = (int)all_logs.size() - 1; i >= 0; --i) {
-    if (all_logs[i] == log_leafname_primary ||
-        (all_logs[i].find(log_basename) == 0 &&
-         all_logs[i].size() > 4 &&
-         all_logs[i].substr(all_logs[i].size() - 4) == string(".log"))) {
-      log_matches_found++;
-      log_matching_pathname = (log_directory + all_logs[i]);
-      read_log_file(log_matching_pathname, tail_bytes, head_bytes, log_data);
-      tail_bytes = tail_bytes_prev;
-      head_bytes = head_bytes_prev;
-    }
-  }
-
-  if (log_matches_found == 0) {
-    nout << "read_log: warning: no matching file(s) on disk." << "\n";
-  }
-
-  string log_data_str = log_data.str();
-  P3D_object *result = new P3DStringObject(log_data_str);
-  return result;
-}
-
-/**
- * The generic log-reader function.
- */
-void P3DMainObject::
-read_log_file(const string &log_pathname,
-              size_t tail_bytes, size_t head_bytes,
-              std::ostringstream &log_data) {
-
-  // Get leaf name
-  string log_leafname = log_pathname;
-  size_t slash = log_leafname.rfind('/');
-  if (slash != string::npos) {
-    log_leafname = log_leafname.substr(slash + 1);
-  }
-#ifdef _WIN32
-  slash = log_leafname.rfind('\\');
-  if (slash != string::npos) {
-    log_leafname = log_leafname.substr(slash + 1);
-  }
-#endif  // _WIN32
-
-  // Render log file header to log_data
-  log_data << "=======================================";
-  log_data << "=======================================" << "\n";
-  log_data << "== PandaLog-" << log_pathname << "\n";
-
-  // load file
-  ifstream log(log_pathname.c_str(), ios::in);
-  if (!log) {
-    log_data << "== PandaLog-" << "Error opening file";
-    log_data << " " << "(" << log_leafname << ")" << "\n";
-    return;
-  }
-
-  // Get the size of the file.
-  log.seekg(0, ios::end);
-  size_t file_size = (size_t)log.tellg();
-  nout << "read_log: " << log_pathname << " is " << file_size
-       << " bytes, tail_bytes = " << tail_bytes << ", head_bytes = "
-       << head_bytes << "\n";
-
-  // Early out if the file is empty
-  if (file_size == (size_t)0) {
-    log_data << "== PandaLog-" << "Empty File";
-    log_data << " " << "(" << log_leafname << ")" << "\n";
-    return;
-  }
-
-  // Check if we are getting the full file
-  size_t full_bytes = 0;
-  if (file_size <= head_bytes + tail_bytes) {
-    // We will return the entire log.
-    full_bytes = file_size;
-    head_bytes = 0;
-    tail_bytes = 0;
-  }
-
-  // Allocate a temp buffer to hold file data
-  size_t buffer_bytes = max(max(full_bytes, head_bytes), tail_bytes) + 1;
-  nout << "allocating " << buffer_bytes << " bytes to read at a time from file of size " << file_size << ".\n";
-  char *buffer = new char[buffer_bytes];
-  if (buffer == nullptr) {
-    log_data << "== PandaLog-" << "Error allocating buffer";
-    log_data << " " << "(" << log_leafname << ")" << "\n";
-    return;
-  }
-
-  // Render log data if full file is to be fetched
-  if (full_bytes > 0) {
-    log.seekg(0, ios::beg);
-    log.read(buffer, full_bytes);
-    streamsize read_bytes = log.gcount();
-    assert(read_bytes < (streamsize)buffer_bytes);
-    buffer[read_bytes] = '\0';
-    log_data << "== PandaLog-" << "Full Start";
-    log_data << " " << "(" << log_leafname << ")" << "\n";
-    log_data << buffer;
-    log_data << "== PandaLog-" << "Full End";
-    log_data << " " << "(" << log_leafname << ")" << "\n";
-  }
-
-  // Render log data if head bytes are to be fetched
-  if (head_bytes > 0) {
-    log.seekg(0, ios::beg);
-    log.read(buffer, head_bytes);
-    streamsize read_bytes = log.gcount();
-    assert(read_bytes < (streamsize)buffer_bytes);
-    buffer[read_bytes] = '\0';
-    log_data << "== PandaLog-" << "Head Start";
-    log_data << " " << "(" << log_leafname << ")" << "\n";
-    log_data << buffer << "\n";
-    log_data << "== PandaLog-" << "Head End";
-    log_data << " " << "(" << log_leafname << ")" << "\n";
-  }
-
-  // Render separator if head & tail bytes are to be fetched
-  if ((head_bytes > 0) && (tail_bytes > 0)) {
-    log_data << "== PandaLog-" << "truncated";
-    log_data << " " << "(" << log_leafname << ")" << "\n";
-  }
-
-  // Render log data if tail bytes are to be fetched
-  if (tail_bytes > 0) {
-    log.seekg(file_size - tail_bytes, ios::beg);
-    log.read(buffer, tail_bytes);
-    streamsize read_bytes = log.gcount();
-    assert(read_bytes < (streamsize)buffer_bytes);
-    buffer[read_bytes] = '\0';
-    log_data << "== PandaLog-" << "Tail Start";
-    log_data << " " << "(" << log_leafname << ")" << "\n";
-    log_data << buffer;
-    log_data << "== PandaLog-" << "Tail End";
-    log_data << " " << "(" << log_leafname << ")" << "\n";
-  }
-
-  // Render log file footer to log_data log_data <<
-  // "======================================="; log_data <<
-  // "=======================================" << "\n";
-
-  // cleanup
-  delete[] buffer;
-  buffer = nullptr;
-}
-
-/**
- * Implements the uninstall() plugin method, which removes all Panda installed
- * files for a particular host, or referenced by a particular p3d file.
- */
-P3D_object *P3DMainObject::
-call_uninstall(P3D_object *params[], int num_params) {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-  // Get the first parameter, the uninstall mode.
-  string mode;
-  if (num_params > 0) {
-    int size = P3D_OBJECT_GET_STRING(params[0], nullptr, 0);
-    char *buffer = new char[size];
-    P3D_OBJECT_GET_STRING(params[0], buffer, size);
-    mode = string(buffer, size);
-    delete[] buffer;
-  }
-
-  if (mode == "all") {
-    nout << "uninstall all\n";
-    inst_mgr->uninstall_all();
-    return inst_mgr->new_bool_object(true);
-  }
-
-  if (_inst != nullptr) {
-    nout << "uninstall " << mode << " for " << _inst << "\n";
-    bool success = false;
-    if (mode == "host") {
-      success = _inst->uninstall_host();
-    } else {
-      success = _inst->uninstall_packages();
-    }
-    return inst_mgr->new_bool_object(success);
-  }
-
-  nout << "couldn't uninstall; no instance.\n";
-  return inst_mgr->new_bool_object(false);
-}

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