diff --git a/direct/src/directscripts/DetectPanda3D.js b/direct/src/directscripts/DetectPanda3D.js
deleted file mode 100644
index 52c57cc034..0000000000
--- a/direct/src/directscripts/DetectPanda3D.js
+++ /dev/null
@@ -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('');
-}
diff --git a/direct/src/directscripts/RunPanda3D.js b/direct/src/directscripts/RunPanda3D.js
deleted file mode 100644
index b26d3b99c3..0000000000
--- a/direct/src/directscripts/RunPanda3D.js
+++ /dev/null
@@ -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
-// 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 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 += '';
- for (var i in params)
- {
- str += ' ';
- }
- }
- else
- {
- str += ' ';
- }
- if (imageAttrs["src"]) {
- if (imageAttrs["href"]) {
- str += '';
- }
- str += ' ';
- if (imageAttrs["href"]) {
- str += ' ';
- }
- }
- str += ' ';
-
- 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;
-}
diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py
index f8160ed546..b6a6617fad 100644
--- a/direct/src/dist/commands.py
+++ b/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
diff --git a/direct/src/dist/icon.py b/direct/src/dist/icon.py
new file mode 100644
index 0000000000..b2f162349d
--- /dev/null
+++ b/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('= 256:
+ # Find closest pixel instead.
+ index = closest_indices[index - 256]
+ fp.write(struct.pack('> 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('> 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('= 256:
+ continue
+ ico.write(struct.pack('> 3
+ if andsize % 4 != 0:
+ andsize += 4 - (andsize % 4)
+ datasize = 40 + 256 * 4 + (xorsize + andsize) * size
+
+ ico.write(struct.pack(' 256:
+ continue
+ elif size == 256:
+ ico.write(b'\0\0')
+ else:
+ ico.write(struct.pack('> 3
+ if andsize % 4 != 0:
+ andsize += 4 - (andsize % 4)
+ datasize = 40 + (xorsize + andsize) * size
+
+ ico.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('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
+
+
diff --git a/direct/src/p3d/AppRunner.py b/direct/src/p3d/AppRunner.py
deleted file mode 100644
index aae03754e6..0000000000
--- a/direct/src/p3d/AppRunner.py
+++ /dev/null
@@ -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 entry. """
-
- url = xhost.Attribute('url')
- host = self.getHost(url)
- host.readHostXml(xhost)
-
- # Scan for a matching . 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
-
diff --git a/direct/src/p3d/DWBPackageInstaller.py b/direct/src/p3d/DWBPackageInstaller.py
deleted file mode 100644
index c4daa67cb1..0000000000
--- a/direct/src/p3d/DWBPackageInstaller.py
+++ /dev/null
@@ -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)
diff --git a/direct/src/p3d/DeploymentTools.py b/direct/src/p3d/DeploymentTools.py
deleted file mode 100644
index ebc20bfda9..0000000000
--- a/direct/src/p3d/DeploymentTools.py
+++ /dev/null
@@ -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(' %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('= 256:
- # Find closest pixel instead.
- index = closest_indices[index - 256]
- fp.write(struct.pack('> 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('> 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('= 256:
- continue
- ico.write(struct.pack('> 3
- if andsize % 4 != 0:
- andsize += 4 - (andsize % 4)
- datasize = 40 + 256 * 4 + (xorsize + andsize) * size
-
- ico.write(struct.pack(' 256:
- continue
- elif size == 256:
- ico.write(b'\0\0')
- else:
- ico.write(struct.pack('> 3
- if andsize % 4 != 0:
- andsize += 4 - (andsize % 4)
- datasize = 40 + (xorsize + andsize) * size
-
- ico.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('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"!\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('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\tCFBundleIdentifier \n')
- plist.write('\t%s.pkg.%s \n' % (self.authorid, self.shortname))
- plist.write('\tCFBundleShortVersionString \n')
- plist.write('\t%s \n' % self.version)
- plist.write('\tIFMajorVersion \n')
- plist.write('\t1 \n')
- plist.write('\tIFMinorVersion \n')
- plist.write('\t9 \n')
- plist.write('\tIFPkgFlagAllowBackRev \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagAuthorizationAction \n')
- plist.write('\tRootAuthorization \n')
- plist.write('\tIFPkgFlagDefaultLocation \n')
- plist.write('\t/ \n')
- plist.write('\tIFPkgFlagFollowLinks \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagIsRequired \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagOverwritePermissions \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagRelocatable \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagRestartAction \n')
- plist.write('\tNone \n')
- plist.write('\tIFPkgFlagRootVolumeOnly \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagUpdateInstalledLanguages \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFormatVersion \n')
- plist.write('\t0.10000000149011612 \n')
- plist.write('\tIFPkgPathMappings \n')
- plist.write('\t\n')
- plist.write('\t\t%s \n' % appname)
- plist.write('\t\t{pkmk-token-2} \n')
- plist.write('\t \n')
- plist.write(' \n')
- plist.write(' \n')
- plist.close()
-
- plist = open(Filename(output, "Contents/Resources/TokenDefinitions.plist").toOsSpecific(), "w")
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\tpkmk-token-2 \n')
- plist.write('\t\n')
- plist.write('\t\t\n')
- plist.write('\t\t\tidentifier \n')
- plist.write('\t\t\t%s.%s \n' % (self.authorid, self.shortname))
- plist.write('\t\t\tpath \n')
- plist.write('\t\t\t%s \n' % appname)
- plist.write('\t\t\tsearchPlugin \n')
- plist.write('\t\t\tCommonAppSearch \n')
- plist.write('\t\t \n')
- plist.write('\t \n')
- plist.write(' \n')
- plist.write(' \n')
- plist.close()
-
- plist = open(Filename(output, "Contents/Resources/en.lproj/Description.plist").toOsSpecific(), "w")
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\tIFPkgDescriptionDescription \n')
- plist.write('\t \n')
- plist.write('\tIFPkgDescriptionTitle \n')
- plist.write('\t%s \n' % self.fullname)
- plist.write(' \n')
- plist.write(' \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
diff --git a/direct/src/p3d/FileSpec.py b/direct/src/p3d/FileSpec.py
deleted file mode 100644
index ddd1dafd6e..0000000000
--- a/direct/src/p3d/FileSpec.py
+++ /dev/null
@@ -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
diff --git a/direct/src/p3d/HostInfo.py b/direct/src/p3d/HostInfo.py
deleted file mode 100644
index 4e5efdcd3e..0000000000
--- a/direct/src/p3d/HostInfo.py
+++ /dev/null
@@ -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 or 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 or 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 or 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
diff --git a/direct/src/p3d/InstalledHostData.py b/direct/src/p3d/InstalledHostData.py
deleted file mode 100644
index d98abb6d18..0000000000
--- a/direct/src/p3d/InstalledHostData.py
+++ /dev/null
@@ -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'
diff --git a/direct/src/p3d/InstalledPackageData.py b/direct/src/p3d/InstalledPackageData.py
deleted file mode 100644
index 09ee81cda6..0000000000
--- a/direct/src/p3d/InstalledPackageData.py
+++ /dev/null
@@ -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()
-
diff --git a/direct/src/p3d/JavaScript.py b/direct/src/p3d/JavaScript.py
deleted file mode 100644
index b59deb1aa2..0000000000
--- a/direct/src/p3d/JavaScript.py
+++ /dev/null
@@ -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)
diff --git a/direct/src/p3d/PackageInfo.py b/direct/src/p3d/PackageInfo.py
deleted file mode 100644
index 08a73365b8..0000000000
--- a/direct/src/p3d/PackageInfo.py
+++ /dev/null
@@ -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)
-
diff --git a/direct/src/p3d/PackageInstaller.py b/direct/src/p3d/PackageInstaller.py
deleted file mode 100644
index cec1e07b11..0000000000
--- a/direct/src/p3d/PackageInstaller.py
+++ /dev/null
@@ -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
-
diff --git a/direct/src/p3d/PackageMerger.py b/direct/src/p3d/PackageMerger.py
deleted file mode 100644
index a22d8f5f2d..0000000000
--- a/direct/src/p3d/PackageMerger.py
+++ /dev/null
@@ -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 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()
-
diff --git a/direct/src/p3d/Packager.py b/direct/src/p3d/Packager.py
deleted file mode 100644
index 4e8a303f7f..0000000000
--- a/direct/src/p3d/Packager.py
+++ /dev/null
@@ -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 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))
diff --git a/direct/src/p3d/PatchMaker.py b/direct/src/p3d/PatchMaker.py
deleted file mode 100644
index c1624b3cc1..0000000000
--- a/direct/src/p3d/PatchMaker.py
+++ /dev/null
@@ -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
diff --git a/direct/src/p3d/ScanDirectoryNode.py b/direct/src/p3d/ScanDirectoryNode.py
deleted file mode 100644
index 429b8f0fd2..0000000000
--- a/direct/src/p3d/ScanDirectoryNode.py
+++ /dev/null
@@ -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
-
-
diff --git a/direct/src/p3d/SeqValue.py b/direct/src/p3d/SeqValue.py
deleted file mode 100644
index 958132f74b..0000000000
--- a/direct/src/p3d/SeqValue.py
+++ /dev/null
@@ -1,90 +0,0 @@
-__all__ = ["SeqValue"]
-
-class SeqValue:
-
- """ This represents a sequence value read from a contents.xml
- file, either from the or the 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))
diff --git a/direct/src/p3d/__init__.py b/direct/src/p3d/__init__.py
deleted file mode 100644
index e6ab93b09d..0000000000
--- a/direct/src/p3d/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-"""
-This package provides the Python interface to functionality relating to
-the Panda3D Runtime environment.
-"""
diff --git a/direct/src/p3d/coreapi.pdef b/direct/src/p3d/coreapi.pdef
deleted file mode 100644
index 03612be605..0000000000
--- a/direct/src/p3d/coreapi.pdef
+++ /dev/null
@@ -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)
diff --git a/direct/src/p3d/p3dWrapper.c b/direct/src/p3d/p3dWrapper.c
deleted file mode 100644
index ef96e036f9..0000000000
--- a/direct/src/p3d/p3dWrapper.c
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-#include
-
-#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;
-}
diff --git a/direct/src/p3d/packp3d.py b/direct/src/p3d/packp3d.py
deleted file mode 100755
index dee4fa2656..0000000000
--- a/direct/src/p3d/packp3d.py
+++ /dev/null
@@ -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)
diff --git a/direct/src/p3d/panda3d.pdef b/direct/src/p3d/panda3d.pdef
deleted file mode 100644
index 78e7af2cf0..0000000000
--- a/direct/src/p3d/panda3d.pdef
+++ /dev/null
@@ -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 portal { 1 }
-egg-object-type-polylight polylight { 1 }
-egg-object-type-seq24 { 1 } fps { 24 }
-egg-object-type-seq12 { 1 } fps { 12 }
-egg-object-type-indexed indexed { 1 }
-egg-object-type-seq10 { 1 } fps { 10 }
-egg-object-type-seq8 { 1 } fps { 8 }
-egg-object-type-seq6 { 1 } fps { 6 }
-egg-object-type-seq4 { 1 } fps { 4 }
-egg-object-type-seq2 { 1 } fps { 2 }
-
-egg-object-type-binary alpha { binary }
-egg-object-type-dual alpha { dual }
-egg-object-type-glass alpha { blend_no_occlude }
-
-egg-object-type-model { 1 }
-egg-object-type-dcs { 1 }
-egg-object-type-notouch { no_touch }
-
-egg-object-type-barrier { Polyset descend }
-egg-object-type-sphere { Sphere descend }
-egg-object-type-invsphere { InvSphere descend }
-egg-object-type-tube { Tube descend }
-egg-object-type-trigger { Polyset descend intangible }
-egg-object-type-trigger-sphere { Sphere descend intangible }
-egg-object-type-floor { Polyset descend level }
-egg-object-type-dupefloor { Polyset keep descend level }
-egg-object-type-bubble { Sphere keep descend }
-egg-object-type-ghost collide-mask { 0 }
-egg-object-type-glow blend { add }
-egg-object-type-direct-widget collide-mask { 0x80000000 } { 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')
-
diff --git a/direct/src/p3d/pdeploy.py b/direct/src/p3d/pdeploy.py
deleted file mode 100644
index f98d10b65d..0000000000
--- a/direct/src/p3d/pdeploy.py
+++ /dev/null
@@ -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("\n")
- html.write("\n")
- html.write(" \n")
- html.write(" %s \n" % fullname)
- html.write(" \n")
- if authorname:
- html.write(" \n" % authorname.replace('"', '\\"'))
- html.write(" \n")
- html.write(" \n")
- html.write(" ")
- html.write(" \n" % (w, h))
- for key, value in tokens.items():
- html.write(" \n" % (key, value.replace('"', '\\"')))
- html.write(" \n")
- html.write(" \n")
- html.write(" \n")
- html.write("\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)
-
diff --git a/direct/src/p3d/pmerge.py b/direct/src/p3d/pmerge.py
deleted file mode 100755
index 99492df35b..0000000000
--- a/direct/src/p3d/pmerge.py
+++ /dev/null
@@ -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)
diff --git a/direct/src/p3d/ppackage.py b/direct/src/p3d/ppackage.py
deleted file mode 100755
index cf42ea8403..0000000000
--- a/direct/src/p3d/ppackage.py
+++ /dev/null
@@ -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)
diff --git a/direct/src/p3d/ppatcher.py b/direct/src/p3d/ppatcher.py
deleted file mode 100755
index 69e9689622..0000000000
--- a/direct/src/p3d/ppatcher.py
+++ /dev/null
@@ -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)
diff --git a/direct/src/p3d/runp3d.py b/direct/src/p3d/runp3d.py
deleted file mode 100644
index 2ab457bc3c..0000000000
--- a/direct/src/p3d/runp3d.py
+++ /dev/null
@@ -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()
diff --git a/direct/src/p3d/thirdparty.pdef b/direct/src/p3d/thirdparty.pdef
deleted file mode 100644
index bc001e427d..0000000000
--- a/direct/src/p3d/thirdparty.pdef
+++ /dev/null
@@ -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)
diff --git a/direct/src/plugin/binaryXml.cxx b/direct/src/plugin/binaryXml.cxx
deleted file mode 100644
index 7dc4a60268..0000000000
--- a/direct/src/plugin/binaryXml.cxx
+++ /dev/null
@@ -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
-
-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;
-}
diff --git a/direct/src/plugin/binaryXml.h b/direct/src/plugin/binaryXml.h
deleted file mode 100644
index 477c51e83f..0000000000
--- a/direct/src/plugin/binaryXml.h
+++ /dev/null
@@ -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
-
-// 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
diff --git a/direct/src/plugin/failed.pgm b/direct/src/plugin/failed.pgm
deleted file mode 100644
index c470d8fb6f..0000000000
Binary files a/direct/src/plugin/failed.pgm and /dev/null differ
diff --git a/direct/src/plugin/failed.xcf b/direct/src/plugin/failed.xcf
deleted file mode 100644
index 4f3a406f00..0000000000
Binary files a/direct/src/plugin/failed.xcf and /dev/null differ
diff --git a/direct/src/plugin/fhandle.h b/direct/src/plugin/fhandle.h
deleted file mode 100644
index 90459efbe1..0000000000
--- a/direct/src/plugin/fhandle.h
+++ /dev/null
@@ -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
-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
diff --git a/direct/src/plugin/fileSpec.I b/direct/src/plugin/fileSpec.I
deleted file mode 100644
index 22e6f26f81..0000000000
--- a/direct/src/plugin/fileSpec.I
+++ /dev/null
@@ -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';
-}
diff --git a/direct/src/plugin/fileSpec.cxx b/direct/src/plugin/fileSpec.cxx
deleted file mode 100644
index fd7169c4d1..0000000000
--- a/direct/src/plugin/fileSpec.cxx
+++ /dev/null
@@ -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
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#ifdef _WIN32
-#include
-#include
-#define utimbuf _utimbuf
-
-#else
-#include
-
-#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 ©) :
- _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 ©) {
- _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));
- }
-}
diff --git a/direct/src/plugin/fileSpec.h b/direct/src/plugin/fileSpec.h
deleted file mode 100644
index 3a661ab603..0000000000
--- a/direct/src/plugin/fileSpec.h
+++ /dev/null
@@ -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
-
-/**
- * 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 ©);
- void operator = (const FileSpec ©);
- ~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
diff --git a/direct/src/plugin/find_root_dir.cxx b/direct/src/plugin/find_root_dir.cxx
deleted file mode 100644
index 7d20461aa9..0000000000
--- a/direct/src/plugin/find_root_dir.cxx
+++ /dev/null
@@ -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
-#include
-#else
-#include
-#include
-#include
-#include
-#include
-#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 SettingsLocal 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 SettingsLocal 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., UsersLibraryCachesPanda3D
- string root = find_osx_root_dir();
- if (!root.empty()) {
- return root;
- }
-
-#else // The Linux/*BSD case
- // e.g., home.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 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;
-}
diff --git a/direct/src/plugin/find_root_dir.h b/direct/src/plugin/find_root_dir.h
deleted file mode 100644
index e76be59af3..0000000000
--- a/direct/src/plugin/find_root_dir.h
+++ /dev/null
@@ -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
-#include
-
-std::string find_root_dir();
-
-#ifdef __APPLE__
-std::string find_osx_root_dir();
-#endif // __APPLE__
-
-#endif
diff --git a/direct/src/plugin/find_root_dir_assist.mm b/direct/src/plugin/find_root_dir_assist.mm
deleted file mode 100644
index f27693d147..0000000000
--- a/direct/src/plugin/find_root_dir_assist.mm
+++ /dev/null
@@ -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
-#include
-
-/**
- * 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__
diff --git a/direct/src/plugin/get_tinyxml.h b/direct/src/plugin/get_tinyxml.h
deleted file mode 100644
index 12f12f054d..0000000000
--- a/direct/src/plugin/get_tinyxml.h
+++ /dev/null
@@ -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
-
-#endif
diff --git a/direct/src/plugin/get_twirl_data.cxx b/direct/src/plugin/get_twirl_data.cxx
deleted file mode 100644
index 8d1bbd73fd..0000000000
--- a/direct/src/plugin/get_twirl_data.cxx
+++ /dev/null
@@ -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
-
-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;
-}
diff --git a/direct/src/plugin/get_twirl_data.h b/direct/src/plugin/get_twirl_data.h
deleted file mode 100644
index 3b021a01e4..0000000000
--- a/direct/src/plugin/get_twirl_data.h
+++ /dev/null
@@ -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
-
-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
diff --git a/direct/src/plugin/handleStream.I b/direct/src/plugin/handleStream.I
deleted file mode 100644
index 60021cee96..0000000000
--- a/direct/src/plugin/handleStream.I
+++ /dev/null
@@ -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();
-}
diff --git a/direct/src/plugin/handleStream.cxx b/direct/src/plugin/handleStream.cxx
deleted file mode 100644
index 5d6580f493..0000000000
--- a/direct/src/plugin/handleStream.cxx
+++ /dev/null
@@ -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"
diff --git a/direct/src/plugin/handleStream.h b/direct/src/plugin/handleStream.h
deleted file mode 100644
index b28dec5f6a..0000000000
--- a/direct/src/plugin/handleStream.h
+++ /dev/null
@@ -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
diff --git a/direct/src/plugin/handleStreamBuf.I b/direct/src/plugin/handleStreamBuf.I
deleted file mode 100644
index 24036ee9d3..0000000000
--- a/direct/src/plugin/handleStreamBuf.I
+++ /dev/null
@@ -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());
-}
diff --git a/direct/src/plugin/handleStreamBuf.cxx b/direct/src/plugin/handleStreamBuf.cxx
deleted file mode 100644
index 55e74977b5..0000000000
--- a/direct/src/plugin/handleStreamBuf.cxx
+++ /dev/null
@@ -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
-#include
-
-#ifndef _WIN32
-#include
-#include
-#include
-#include
-#include
-#endif // _WIN32
-
-#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__FreeBSD__)
-#include
-#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;
-}
diff --git a/direct/src/plugin/handleStreamBuf.h b/direct/src/plugin/handleStreamBuf.h
deleted file mode 100644
index da3448d4d3..0000000000
--- a/direct/src/plugin/handleStreamBuf.h
+++ /dev/null
@@ -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
-
-/**
- *
- */
-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
diff --git a/direct/src/plugin/is_pathsep.I b/direct/src/plugin/is_pathsep.I
deleted file mode 100644
index 3dd35068b8..0000000000
--- a/direct/src/plugin/is_pathsep.I
+++ /dev/null
@@ -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;
-}
diff --git a/direct/src/plugin/is_pathsep.h b/direct/src/plugin/is_pathsep.h
deleted file mode 100644
index d1e326ee2d..0000000000
--- a/direct/src/plugin/is_pathsep.h
+++ /dev/null
@@ -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
diff --git a/direct/src/plugin/load_plugin.cxx b/direct/src/plugin/load_plugin.cxx
deleted file mode 100644
index a1575ff087..0000000000
--- a/direct/src/plugin/load_plugin.cxx
+++ /dev/null
@@ -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
-
-#include
-
-#ifndef _WIN32
-#include
-#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;
-}
diff --git a/direct/src/plugin/load_plugin.h b/direct/src/plugin/load_plugin.h
deleted file mode 100644
index 2e462ed9cd..0000000000
--- a/direct/src/plugin/load_plugin.h
+++ /dev/null
@@ -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
-
-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
diff --git a/direct/src/plugin/mkdir_complete.cxx b/direct/src/plugin/mkdir_complete.cxx
deleted file mode 100644
index 6689584198..0000000000
--- a/direct/src/plugin/mkdir_complete.cxx
+++ /dev/null
@@ -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
-#include // chmod()
-#else
-#include
-#include // for mkdir()
-#include
-#include // strerror()
-#include
-#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
diff --git a/direct/src/plugin/mkdir_complete.h b/direct/src/plugin/mkdir_complete.h
deleted file mode 100644
index be79cf7e8e..0000000000
--- a/direct/src/plugin/mkdir_complete.h
+++ /dev/null
@@ -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
-#include
-
-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
diff --git a/direct/src/plugin/p3dAuthSession.I b/direct/src/plugin/p3dAuthSession.I
deleted file mode 100644
index 0e451a8f87..0000000000
--- a/direct/src/plugin/p3dAuthSession.I
+++ /dev/null
@@ -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
- */
diff --git a/direct/src/plugin/p3dAuthSession.cxx b/direct/src/plugin/p3dAuthSession.cxx
deleted file mode 100644
index e76bbe0a07..0000000000
--- a/direct/src/plugin/p3dAuthSession.cxx
+++ /dev/null
@@ -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
-
-#ifndef _WIN32
-#include
-#include
-#include
-#include
-#include
-#include
-#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 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
diff --git a/direct/src/plugin/p3dAuthSession.h b/direct/src/plugin/p3dAuthSession.h
deleted file mode 100644
index f37f24d9e3..0000000000
--- a/direct/src/plugin/p3dAuthSession.h
+++ /dev/null
@@ -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
diff --git a/direct/src/plugin/p3dBoolObject.cxx b/direct/src/plugin/p3dBoolObject.cxx
deleted file mode 100644
index 7740c2f669..0000000000
--- a/direct/src/plugin/p3dBoolObject.cxx
+++ /dev/null
@@ -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 ©) :
- 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";
- }
-}
diff --git a/direct/src/plugin/p3dBoolObject.h b/direct/src/plugin/p3dBoolObject.h
deleted file mode 100644
index 85eb0a127c..0000000000
--- a/direct/src/plugin/p3dBoolObject.h
+++ /dev/null
@@ -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 ©);
-
-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
diff --git a/direct/src/plugin/p3dCInstance.I b/direct/src/plugin/p3dCInstance.I
deleted file mode 100644
index 34edfd6b62..0000000000
--- a/direct/src/plugin/p3dCInstance.I
+++ /dev/null
@@ -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;
-}
diff --git a/direct/src/plugin/p3dCInstance.cxx b/direct/src/plugin/p3dCInstance.cxx
deleted file mode 100644
index 0f77925b22..0000000000
--- a/direct/src/plugin/p3dCInstance.cxx
+++ /dev/null
@@ -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() {
-}
diff --git a/direct/src/plugin/p3dCInstance.h b/direct/src/plugin/p3dCInstance.h
deleted file mode 100644
index 8d0ebc29c2..0000000000
--- a/direct/src/plugin/p3dCInstance.h
+++ /dev/null
@@ -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
diff --git a/direct/src/plugin/p3dCert.cxx b/direct/src/plugin/p3dCert.cxx
deleted file mode 100644
index e6868bdb80..0000000000
--- a/direct/src/plugin/p3dCert.cxx
+++ /dev/null
@@ -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
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#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
-#include
-#include
-
-#define snprintf sprintf_s
-#endif
-
-#ifdef __APPLE__
-#include
-#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();
-}
diff --git a/direct/src/plugin/p3dCert.h b/direct/src/plugin/p3dCert.h
deleted file mode 100644
index c17c8129e8..0000000000
--- a/direct/src/plugin/p3dCert.h
+++ /dev/null
@@ -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
-#include
-
-#define OPENSSL_NO_KRB5
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-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
diff --git a/direct/src/plugin/p3dCert_strings.cxx b/direct/src/plugin/p3dCert_strings.cxx
deleted file mode 100644
index 575dee34b0..0000000000
--- a/direct/src/plugin/p3dCert_strings.cxx
+++ /dev/null
@@ -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
-// spa: Imanol Celaya 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.",
-};
diff --git a/direct/src/plugin/p3dCert_strings.h b/direct/src/plugin/p3dCert_strings.h
deleted file mode 100644
index 7e6d391e2a..0000000000
--- a/direct/src/plugin/p3dCert_strings.h
+++ /dev/null
@@ -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];
diff --git a/direct/src/plugin/p3dCert_wx.cxx b/direct/src/plugin/p3dCert_wx.cxx
deleted file mode 100644
index e7002db46a..0000000000
--- a/direct/src/plugin/p3dCert_wx.cxx
+++ /dev/null
@@ -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
-#include
-
-#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));
-}
diff --git a/direct/src/plugin/p3dCert_wx.h b/direct/src/plugin/p3dCert_wx.h
deleted file mode 100644
index b0b3dda12e..0000000000
--- a/direct/src/plugin/p3dCert_wx.h
+++ /dev/null
@@ -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
-
-#define OPENSSL_NO_KRB5
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-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
diff --git a/direct/src/plugin/p3dConcreteSequence.cxx b/direct/src/plugin/p3dConcreteSequence.cxx
deleted file mode 100644
index ec4291cb7b..0000000000
--- a/direct/src/plugin/p3dConcreteSequence.cxx
+++ /dev/null
@@ -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);
-}
diff --git a/direct/src/plugin/p3dConcreteSequence.h b/direct/src/plugin/p3dConcreteSequence.h
deleted file mode 100644
index 09b489c5ba..0000000000
--- a/direct/src/plugin/p3dConcreteSequence.h
+++ /dev/null
@@ -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 Elements;
- Elements _elements;
-};
-
-#endif
diff --git a/direct/src/plugin/p3dConcreteStruct.cxx b/direct/src/plugin/p3dConcreteStruct.cxx
deleted file mode 100644
index fa9917d955..0000000000
--- a/direct/src/plugin/p3dConcreteStruct.cxx
+++ /dev/null
@@ -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 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;
-}
diff --git a/direct/src/plugin/p3dConcreteStruct.h b/direct/src/plugin/p3dConcreteStruct.h
deleted file mode 100644
index cccd35bbeb..0000000000
--- a/direct/src/plugin/p3dConcreteStruct.h
+++ /dev/null
@@ -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 Elements;
- Elements _elements;
-};
-
-#endif
diff --git a/direct/src/plugin/p3dConditionVar.I b/direct/src/plugin/p3dConditionVar.I
deleted file mode 100644
index 08ae4ffe7e..0000000000
--- a/direct/src/plugin/p3dConditionVar.I
+++ /dev/null
@@ -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
- */
diff --git a/direct/src/plugin/p3dConditionVar.cxx b/direct/src/plugin/p3dConditionVar.cxx
deleted file mode 100644
index 22133c0781..0000000000
--- a/direct/src/plugin/p3dConditionVar.cxx
+++ /dev/null
@@ -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
-#include
-#endif
-#include
-
-/**
- *
- */
-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
-}
diff --git a/direct/src/plugin/p3dConditionVar.h b/direct/src/plugin/p3dConditionVar.h
deleted file mode 100644
index f4de8f549d..0000000000
--- a/direct/src/plugin/p3dConditionVar.h
+++ /dev/null
@@ -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
diff --git a/direct/src/plugin/p3dDownload.I b/direct/src/plugin/p3dDownload.I
deleted file mode 100644
index c248948612..0000000000
--- a/direct/src/plugin/p3dDownload.I
+++ /dev/null
@@ -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;
-}
diff --git a/direct/src/plugin/p3dDownload.cxx b/direct/src/plugin/p3dDownload.cxx
deleted file mode 100644
index 3b6a6cca7a..0000000000
--- a/direct/src/plugin/p3dDownload.cxx
+++ /dev/null
@@ -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 ©) :
- _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";
-}
diff --git a/direct/src/plugin/p3dDownload.h b/direct/src/plugin/p3dDownload.h
deleted file mode 100644
index ad2cd872a5..0000000000
--- a/direct/src/plugin/p3dDownload.h
+++ /dev/null
@@ -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
-
-/**
- * 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 ©);
- 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
diff --git a/direct/src/plugin/p3dFileDownload.I b/direct/src/plugin/p3dFileDownload.I
deleted file mode 100644
index af4e2b1ec8..0000000000
--- a/direct/src/plugin/p3dFileDownload.I
+++ /dev/null
@@ -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;
-}
diff --git a/direct/src/plugin/p3dFileDownload.cxx b/direct/src/plugin/p3dFileDownload.cxx
deleted file mode 100644
index a978c3640e..0000000000
--- a/direct/src/plugin/p3dFileDownload.cxx
+++ /dev/null
@@ -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 ©) :
- 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();
-}
diff --git a/direct/src/plugin/p3dFileDownload.h b/direct/src/plugin/p3dFileDownload.h
deleted file mode 100644
index 923f8ec0ec..0000000000
--- a/direct/src/plugin/p3dFileDownload.h
+++ /dev/null
@@ -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
-
-/**
- * This is a specialization on P3DDownload that specifically writes a disk
- * file.
- */
-class P3DFileDownload : public P3DDownload {
-public:
- P3DFileDownload();
- P3DFileDownload(const P3DFileDownload ©);
-
- 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
diff --git a/direct/src/plugin/p3dFileParams.I b/direct/src/plugin/p3dFileParams.I
deleted file mode 100644
index 8565c47a90..0000000000
--- a/direct/src/plugin/p3dFileParams.I
+++ /dev/null
@@ -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;
-}
diff --git a/direct/src/plugin/p3dFileParams.cxx b/direct/src/plugin/p3dFileParams.cxx
deleted file mode 100644
index a32ede4b95..0000000000
--- a/direct/src/plugin/p3dFileParams.cxx
+++ /dev/null
@@ -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
-
-using std::string;
-
-/**
- *
- */
-P3DFileParams::
-P3DFileParams() : _p3d_offset(0) {
-}
-
-/**
- *
- */
-P3DFileParams::
-P3DFileParams(const P3DFileParams ©) :
- _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;
-}
diff --git a/direct/src/plugin/p3dFileParams.h b/direct/src/plugin/p3dFileParams.h
deleted file mode 100644
index 53b2630680..0000000000
--- a/direct/src/plugin/p3dFileParams.h
+++ /dev/null
@@ -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
-
-/**
- * Encapsulates the file parameters: the p3d_filename, and extra tokens.
- */
-class P3DFileParams {
-public:
- P3DFileParams();
- P3DFileParams(const P3DFileParams ©);
- 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 Tokens;
- typedef std::vector Args;
-
- std::string _p3d_filename;
- int _p3d_offset;
- std::string _p3d_url;
- Tokens _tokens;
- Args _args;
-};
-
-#include "p3dFileParams.I"
-
-#endif
diff --git a/direct/src/plugin/p3dFloatObject.cxx b/direct/src/plugin/p3dFloatObject.cxx
deleted file mode 100644
index 3262d45a54..0000000000
--- a/direct/src/plugin/p3dFloatObject.cxx
+++ /dev/null
@@ -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 ©) :
- 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();
-}
diff --git a/direct/src/plugin/p3dFloatObject.h b/direct/src/plugin/p3dFloatObject.h
deleted file mode 100644
index 13f7ba8358..0000000000
--- a/direct/src/plugin/p3dFloatObject.h
+++ /dev/null
@@ -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 ©);
-
-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
diff --git a/direct/src/plugin/p3dHost.I b/direct/src/plugin/p3dHost.I
deleted file mode 100644
index 9fb3672205..0000000000
--- a/direct/src/plugin/p3dHost.I
+++ /dev/null
@@ -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);
-}
diff --git a/direct/src/plugin/p3dHost.cxx b/direct/src/plugin/p3dHost.cxx
deleted file mode 100644
index db189e14c4..0000000000
--- a/direct/src/plugin/p3dHost.cxx
+++ /dev/null
@@ -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
-
-#include
-
-#ifndef _WIN32
-#include
-#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 (or ) 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 &result, int num_mirrors) {
- std::vector 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;
- }
-}
diff --git a/direct/src/plugin/p3dHost.h b/direct/src/plugin/p3dHost.h
deleted file mode 100644
index fd880ccde0..0000000000
--- a/direct/src/plugin/p3dHost.h
+++ /dev/null
@@ -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
-
-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 &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 Mirrors;
- Mirrors _mirrors;
-
- typedef std::map AltHosts;
- AltHosts _alt_hosts;
-
- typedef std::vector PlatformPackages;
- typedef std::map PackageMap;
- typedef std::map Packages;
- Packages _packages;
- typedef std::vector FailedPackages;
- FailedPackages _failed_packages;
-
- friend class P3DInstanceManager;
-};
-
-#include "p3dHost.I"
-
-#endif
diff --git a/direct/src/plugin/p3dInstance.I b/direct/src/plugin/p3dInstance.I
deleted file mode 100644
index 63765832d3..0000000000
--- a/direct/src/plugin/p3dInstance.I
+++ /dev/null
@@ -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;
- }
-}
diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx
deleted file mode 100644
index 220e28c667..0000000000
--- a/direct/src/plugin/p3dInstance.cxx
+++ /dev/null
@@ -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
-#include
-
-#ifdef __APPLE__
-#include
-#include
-
-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(×tamp);
- 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 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::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 orig_components;
- separate_components(orig_components, orig);
-
- vector 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::const_iterator p = orig_components.begin();
- vector::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 &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 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();
- }
-}
diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h
deleted file mode 100644
index 7972ef7fbe..0000000000
--- a/direct/src/plugin/p3dInstance.h
+++ /dev/null
@@ -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
-#endif
-
-#ifndef _WIN32
-#include
-#endif
-
-#include
-#include
-
-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 &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 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 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 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 RawRequests;
- RawRequests _raw_requests;
- bool _requested_stop;
-
- // The _baked_requests queue is only touched in the main thread; no lock
- // needed.
- typedef std::deque 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
diff --git a/direct/src/plugin/p3dInstanceManager.I b/direct/src/plugin/p3dInstanceManager.I
deleted file mode 100644
index 3241fea0cf..0000000000
--- a/direct/src/plugin/p3dInstanceManager.I
+++ /dev/null
@@ -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';
-}
diff --git a/direct/src/plugin/p3dInstanceManager.cxx b/direct/src/plugin/p3dInstanceManager.cxx
deleted file mode 100644
index 93e2ffcfc2..0000000000
--- a/direct/src/plugin/p3dInstanceManager.cxx
+++ /dev/null
@@ -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
-#include // chmod()
-#include // rmdir()
-#include // GetModuleHandle() etc.
-#else
-#include
-#include
-#include
-#endif
-
-#ifdef __APPLE__
-#include
-#endif
-
-#include
-
-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 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::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 contents;
- scan_directory(package->get_package_dir(), contents);
-
- vector::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 &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 &filename_contents,
- vector &dirname_contents,
- const string &prefix) {
- vector dir_contents;
- if (!scan_directory(dirname, dir_contents)) {
- // Apparently dirname wasn't a directory.
- return false;
- }
-
- // Walk through the contents of dirname.
- vector::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 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::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 &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::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
diff --git a/direct/src/plugin/p3dInstanceManager.h b/direct/src/plugin/p3dInstanceManager.h
deleted file mode 100644
index 08591e14cc..0000000000
--- a/direct/src/plugin/p3dInstanceManager.h
+++ /dev/null
@@ -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
-#include
-#include
-
-#ifndef _WIN32
-#include
-#endif
-
-#define OPENSSL_NO_KRB5
-#include
-#include
-#include
-
-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 &contents);
- static bool scan_directory_recursively(const std::string &dirname,
- std::vector &filename_contents,
- std::vector