Remove p3d/AppRunner/plugin system

Co-authored-by: rdb <git@rdb.name>

Closes #734
This commit is contained in:
Mitchell Stokes
2019-09-10 16:37:01 +02:00
committed by rdb
parent 4d9bfe76f4
commit 660249a5cc
264 changed files with 987 additions and 72222 deletions

View File

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

View File

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

View File

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

269
direct/src/dist/icon.py vendored Normal file
View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -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)

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

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

View File

@@ -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'

View File

@@ -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()

View File

@@ -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)

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

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

View File

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

View File

@@ -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)

View File

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

View File

@@ -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)

View File

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

View File

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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()

View File

@@ -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)

View File

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

View File

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

Binary file not shown.

Binary file not shown.

View File

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

View File

@@ -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';
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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();
}

View File

@@ -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"

View File

@@ -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

View File

@@ -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());
}

View File

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

View File

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

View File

@@ -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;
}

View File

@@ -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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
*/

View File

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

View File

@@ -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

View File

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

View File

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

View File

@@ -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;
}

View File

@@ -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() {
}

View File

@@ -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

View File

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

View File

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

View File

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

View File

@@ -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];

View File

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

View File

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

View File

@@ -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);
}

View File

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

View File

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

View File

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

View File

@@ -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
*/

View File

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

View File

@@ -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

View File

@@ -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;
}

View File

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

View File

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

View File

@@ -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;
}

View File

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

View File

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

View File

@@ -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;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -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;
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -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';
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

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