diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000..f8d79052bf
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+open_collective: panda3d
+
diff --git a/.gitignore b/.gitignore
index 0b40b42f79..de3e59aef4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,11 +4,12 @@
/targetroot/
/dstroot/
-# Core dumps
+# Core dumps and traces
core
core.*
vgcore.*
*.core
+*.trace
# Editor files/directories
*.save
@@ -26,6 +27,7 @@ vgcore.*
/+DESC
/+MANIFEST
/pkg-plist
+/debug.ks
# Produced installer/executables
/*.exe
@@ -36,6 +38,7 @@ vgcore.*
/*.dmg
/*.whl
/*.txz
+/*.apk
# CMake
/build/
diff --git a/.travis.yml b/.travis.yml
index 2c4efe8a65..ee9b09d37e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -44,7 +44,6 @@ install:
- $PYTHONV -m pip install pytest
script:
- $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT $FLAGS --threads 4
- - test -n "$SKIP_TESTS" || LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV makepanda/test_imports.py
- test -n "$SKIP_TESTS" || LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV -m pytest -v tests
notifications:
irc:
@@ -54,3 +53,8 @@ notifications:
on_failure: always
use_notice: true
skip_join: false
+ webhooks:
+ urls:
+ - https://www.panda3d.org/webhooks/travis-ci.php
+ on_success: change
+ on_failure: always
diff --git a/BACKERS.md b/BACKERS.md
new file mode 100644
index 0000000000..d3dcf17084
--- /dev/null
+++ b/BACKERS.md
@@ -0,0 +1,35 @@
+# Panda3D Backers
+
+This is a list of all the people who are contributing financially to Panda3D. If you'd like to join them, visit [our campaign on OpenCollective](https://opencollective.com/panda3d)!
+
+## Gold Sponsors
+
+
+
+* [tcdude](https://opencollective.com/tizilogic)
+
+## Bronze Sponsors
+
+
+
+* [Mitchell Stokes](https://opencollective.com/mitchell-stokes)
+* [Daniel Stokes](https://opencollective.com/daniel-stokes)
+* [David Rose](https://opencollective.com/david-rose)
+* [Carnetsoft](https://cs-driving-simulator.com/)
+
+## Benefactors
+
+
+
+* Sam Edwards
+* Max Voss
+
+## Enthusiasts
+
+
+
+* Eric Thomson
+
+## Backers
+
+
diff --git a/README.md b/README.md
index a9de69e51b..1a5af7deb8 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
[](https://travis-ci.org/panda3d/panda3d)
+[](https://opencollective.com/panda3d)
+[](https://opencollective.com/panda3d)
-
+
Panda3D
=======
@@ -8,12 +10,12 @@ Panda3D
Panda3D is a game engine, a framework for 3D rendering and game development for
Python and C++ programs. Panda3D is open-source and free for any purpose,
including commercial ventures, thanks to its
-[liberal license](https://www.panda3d.org/license.php). To learn more about
-Panda3D's capabilities, visit the [gallery](https://www.panda3d.org/gallery.php)
-and the [feature list](https://www.panda3d.org/features.php). To learn how to
-use Panda3D, check the [documentation](https://www.panda3d.org/documentation.php)
+[liberal license](https://www.panda3d.org/license/). To learn more about
+Panda3D's capabilities, visit the [gallery](https://www.panda3d.org/gallery/)
+and the [feature list](https://www.panda3d.org/features/). To learn how to
+use Panda3D, check the [documentation](https://www.panda3d.org/documentation/)
resources. If you get stuck, ask for help from our active
-[community](https://www.panda3d.org/community.php).
+[community](https://discourse.panda3d.org).
Panda3D is licensed under the Modified BSD License. See the LICENSE file for
more details.
@@ -21,7 +23,16 @@ more details.
Installing Panda3D
==================
-By far, the easiest way to install the latest development build of Panda3D
+The latest Panda3D SDK can be downloaded from
+[this page](https://www.panda3d.org/download/sdk-1-10-4-1/).
+If you are familiar with installing Python packages, you can use
+the following comand:
+
+```bash
+pip install panda3d
+```
+
+The easiest way to install the latest development build of Panda3D
into an existing Python installation is using the following command:
```bash
@@ -31,9 +42,7 @@ pip install --pre --extra-index-url https://archive.panda3d.org/ panda3d
If this command fails, please make sure your version of pip is up-to-date.
If you prefer to install the full SDK with all tools, the latest development
-builds can be obtained from this page:
-
-https://www.panda3d.org/download.php?sdk&version=devel
+builds can be obtained from [this page](https://www.panda3d.org/download.php?version=devel&sdk).
These are automatically kept up-to-date with the latest GitHub version of Panda.
@@ -55,8 +64,8 @@ depending on whether you are on a 32-bit or 64-bit system, or you can
[click here](https://github.com/rdb/panda3d-thirdparty) for instructions on
building them from source.
-https://www.panda3d.org/download/panda3d-1.10.0/panda3d-1.10.0-tools-win64.zip
-https://www.panda3d.org/download/panda3d-1.10.0/panda3d-1.10.0-tools-win32.zip
+https://www.panda3d.org/download/panda3d-1.10.4.1/panda3d-1.10.4.1-tools-win64.zip
+https://www.panda3d.org/download/panda3d-1.10.4.1/panda3d-1.10.4.1-tools-win32.zip
After acquiring these dependencies, you may simply build Panda3D from the
command prompt using the following command. (Change `14.1` to `14` if you are
@@ -96,14 +105,14 @@ python makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-g
You will probably see some warnings saying that it's unable to find several
dependency packages. You should determine which ones you want to include in
your build and install the respective development packages. You may visit
-[this manual page](https://www.panda3d.org/manual/index.php/Dependencies)
+[this manual page](https://www.panda3d.org/manual/?title=Third-party_dependencies_and_license_info)
for an overview of the various dependencies.
If you are on Ubuntu, this command should cover the most frequently
used third-party packages:
```bash
-sudo apt-get install build-essential pkg-config python-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev libassimp-dev libopenexr-dev
+sudo apt-get install build-essential pkg-config fakeroot python-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev libassimp-dev libopenexr-dev
```
Once Panda3D has built, you can either install the .deb or .rpm package that
@@ -126,7 +135,7 @@ macOS
-----
On macOS, you will need to download a set of precompiled thirdparty packages in order to
-compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-mac.tar.gz).
+compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.4.1/panda3d-1.10.4.1-tools-mac.tar.gz).
After placing the thirdparty directory inside the panda3d source directory,
you may build Panda3D using a command like the following:
@@ -135,9 +144,8 @@ you may build Panda3D using a command like the following:
python makepanda/makepanda.py --everything --installer
```
-In order to make a universal build, pass the --universal flag. You may also
-target a specific minimum macOS version using the --osxtarget flag followed
-by the release number, eg. 10.6 or 10.7.
+You may target a specific minimum macOS version using the --osxtarget flag
+followed by the release number, eg. 10.7 or 10.9.
If the build was successful, makepanda will have generated a .dmg file in
the source directory containing the installer. Simply open it and run the
@@ -151,7 +159,7 @@ install the requisite packages using the system package manager. To install
the recommended set of dependencies, you can use this command:
```bash
-pkg install pkgconf png jpeg-turbo tiff freetype2 eigen squish openal opusfile libvorbis libX11 libGL ode bullet assimp openexr
+pkg install pkgconf bison png jpeg-turbo tiff freetype2 harfbuzz eigen squish openal opusfile libvorbis libX11 mesa-libs ode bullet assimp openexr
```
You will also need to choose which version of Python you want to use.
@@ -183,7 +191,7 @@ pkg install python-dev termux-tools ndk-stl ndk-sysroot clang libvorbis-dev libo
Then, you can build and install the .apk right away using these commands:
```bash
-python makepanda/makepanda.py --everything --target android-21 --installer
+python makepanda/makepanda.py --everything --target android-21 --no-tiff --installer
xdg-open panda3d.apk
```
@@ -222,3 +230,25 @@ models that are necessary for the developers to reproduce the issue.
If you're not sure whether you've encountered a bug, feel free to ask about
it in the forums or the IRC channel first.
+
+Supporting the Project
+======================
+
+If you would like to support the project financially, visit
+[our campaign on OpenCollective](https://opencollective.com/panda3d). Your
+contributions help us accelerate the development of Panda3D.
+
+For the list of backers, see the [BACKERS.md](BACKERS.md) file or visit the
+[Sponsors page](https://www.panda3d.org/sponsors) on our web site. Thank you
+to everyone who has donated!
+
+
+
+
+
+### Gold Sponsors
+[](https://opencollective.com/panda3d/tiers/gold-sponsor/0/website)
+[](https://opencollective.com/panda3d/tiers/gold-sponsor/1/website)
+[](https://opencollective.com/panda3d/tiers/gold-sponsor/2/website)
+[](https://opencollective.com/panda3d/tiers/gold-sponsor/3/website)
+[](https://opencollective.com/panda3d/tiers/gold-sponsor/4/website)
diff --git a/contrib/src/ai/aiBehaviors.cxx b/contrib/src/ai/aiBehaviors.cxx
index 1ace436e5b..62f577241b 100644
--- a/contrib/src/ai/aiBehaviors.cxx
+++ b/contrib/src/ai/aiBehaviors.cxx
@@ -13,6 +13,17 @@
#include "aiBehaviors.h"
+#include "arrival.h"
+#include "evade.h"
+#include "flee.h"
+#include "flock.h"
+#include "obstacleAvoidance.h"
+#include "pathFind.h"
+#include "pathFollow.h"
+#include "pursue.h"
+#include "seek.h"
+#include "wander.h"
+
using std::cout;
using std::endl;
using std::string;
diff --git a/contrib/src/ai/aiBehaviors.h b/contrib/src/ai/aiBehaviors.h
index 3524a32b63..eb70906e4a 100644
--- a/contrib/src/ai/aiBehaviors.h
+++ b/contrib/src/ai/aiBehaviors.h
@@ -28,6 +28,9 @@ class PathFollow;
class PathFind;
class ObstacleAvoidance;
+#include "flee.h"
+#include "evade.h"
+
typedef std::list > ListFlee;
typedef std::list > ListEvade;
diff --git a/contrib/src/ai/aiGlobals.h b/contrib/src/ai/aiGlobals.h
index 1d8e8a9c41..f068bd4594 100644
--- a/contrib/src/ai/aiGlobals.h
+++ b/contrib/src/ai/aiGlobals.h
@@ -15,7 +15,6 @@
#define _AI_GLOBALS_H
#include "config_ai.h"
-#include "pandaFramework.h"
#include "textNode.h"
#include "pandaSystem.h"
diff --git a/contrib/src/ai/arrival.cxx b/contrib/src/ai/arrival.cxx
index 966a6b2611..3ce0f706da 100644
--- a/contrib/src/ai/arrival.cxx
+++ b/contrib/src/ai/arrival.cxx
@@ -13,6 +13,9 @@
#include "arrival.h"
+#include "pursue.h"
+#include "seek.h"
+
Arrival::Arrival(AICharacter *ai_ch, double distance) {
_ai_char = ai_ch;
diff --git a/contrib/src/ai/obstacleAvoidance.cxx b/contrib/src/ai/obstacleAvoidance.cxx
index 87ce79026b..42c301e033 100644
--- a/contrib/src/ai/obstacleAvoidance.cxx
+++ b/contrib/src/ai/obstacleAvoidance.cxx
@@ -13,6 +13,8 @@
#include "obstacleAvoidance.h"
+#include "aiWorld.h"
+
ObstacleAvoidance::
ObstacleAvoidance(AICharacter *ai_char, float feeler_length) {
_ai_char = ai_char;
diff --git a/contrib/src/ai/pathFind.cxx b/contrib/src/ai/pathFind.cxx
index ee58f4940d..5813c9d503 100644
--- a/contrib/src/ai/pathFind.cxx
+++ b/contrib/src/ai/pathFind.cxx
@@ -13,6 +13,8 @@
#include "pathFind.h"
+#include "pathFollow.h"
+
using std::cout;
using std::endl;
using std::string;
diff --git a/contrib/src/ai/pathFollow.cxx b/contrib/src/ai/pathFollow.cxx
index dd7720e318..942d7c7daa 100644
--- a/contrib/src/ai/pathFollow.cxx
+++ b/contrib/src/ai/pathFollow.cxx
@@ -1,6 +1,20 @@
+/**
+ * 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 pathFind.cxx
+ * @author Deepak, John, Navin
+ * @date 2009-10-24
+ */
#include "pathFollow.h"
+#include "pathFind.h"
+
PathFollow::PathFollow(AICharacter *ai_ch, float follow_wt) {
_follow_weight = follow_wt;
_curr_path_waypoint = -1;
diff --git a/contrib/src/rplight/shadowAtlas.h b/contrib/src/rplight/shadowAtlas.h
index e3f0958a50..d6296e7658 100644
--- a/contrib/src/rplight/shadowAtlas.h
+++ b/contrib/src/rplight/shadowAtlas.h
@@ -28,7 +28,7 @@
#define SHADOWATLAS_H
#include "pandabase.h"
-#include "lvecBase4.h"
+#include "luse.h"
NotifyCategoryDecl(shadowatlas, EXPORT_CLASS, EXPORT_TEMPL);
diff --git a/direct/src/actor/Actor.py b/direct/src/actor/Actor.py
index b7d48f2d87..902c6830d7 100644
--- a/direct/src/actor/Actor.py
+++ b/direct/src/actor/Actor.py
@@ -67,7 +67,7 @@ class Actor(DirectObject, NodePath):
def __init__(self, filename = None, animBundle = None):
self.filename = filename
- self.animBundle = None
+ self.animBundle = animBundle
self.animControl = None
def makeCopy(self):
@@ -1947,6 +1947,7 @@ class Actor(DirectObject, NodePath):
animName = acc.getAnimName(i)
animDef = Actor.AnimDef()
+ animDef.animBundle = animControl.getAnim()
animDef.animControl = animControl
self.__animControlDict[lodName][partName][animName] = animDef
@@ -2507,7 +2508,7 @@ class Actor(DirectObject, NodePath):
else:
animNames = [animName]
for animName in animNames:
- if animName is 'nothing':
+ if animName == 'nothing':
continue
thisAnim = ''
totalEffect = 0.
diff --git a/direct/src/controls/GravityWalker.py b/direct/src/controls/GravityWalker.py
index 32c18c05f3..bf3ea8a9bd 100755
--- a/direct/src/controls/GravityWalker.py
+++ b/direct/src/controls/GravityWalker.py
@@ -71,90 +71,6 @@ class GravityWalker(DirectObject.DirectObject):
self.isAirborne = 0
self.highMark = 0
- """
- def spawnTest(self):
- assert self.notify.debugStateCall(self)
- if not self.wantDebugIndicator:
- return
- from pandac.PandaModules import *
- from direct.interval.IntervalGlobal import *
- from toontown.coghq import MovingPlatform
-
- if hasattr(self, "platform"):
- # Remove the prior instantiation:
- self.moveIval.pause()
- del self.moveIval
- self.platform.destroy()
- del self.platform
- self.platform2.destroy()
- del self.platform2
-
- model = loader.loadModel('phase_9/models/cogHQ/platform1')
- fakeId = id(self)
- self.platform = MovingPlatform.MovingPlatform()
- self.platform.setupCopyModel(fakeId, model, 'platformcollision')
- self.platformRoot = render.attachNewNode("GravityWalker-spawnTest-%s"%fakeId)
- self.platformRoot.setPos(base.localAvatar, Vec3(0.0, 0.0, 1.0))
- self.platformRoot.setHpr(base.localAvatar, Vec3.zero())
- self.platform.reparentTo(self.platformRoot)
-
- self.platform2 = MovingPlatform.MovingPlatform()
- self.platform2.setupCopyModel(1+fakeId, model, 'platformcollision')
- self.platform2Root = render.attachNewNode("GravityWalker-spawnTest2-%s"%fakeId)
- self.platform2Root.setPos(base.localAvatar, Vec3(-16.0, 30.0, 1.0))
- self.platform2Root.setHpr(base.localAvatar, Vec3.zero())
- self.platform2.reparentTo(self.platform2Root)
-
- duration = 5
- self.moveIval = Parallel(
- Sequence(
- WaitInterval(0.3),
- LerpPosInterval(self.platform, duration,
- Vec3(0.0, 30.0, 0.0),
- name='platformOut%s' % fakeId,
- fluid = 1),
- WaitInterval(0.3),
- LerpPosInterval(self.platform, duration,
- Vec3(0.0, 0.0, 0.0),
- name='platformBack%s' % fakeId,
- fluid = 1),
- WaitInterval(0.3),
- LerpPosInterval(self.platform, duration,
- Vec3(0.0, 0.0, 30.0),
- name='platformUp%s' % fakeId,
- fluid = 1),
- WaitInterval(0.3),
- LerpPosInterval(self.platform, duration,
- Vec3(0.0, 0.0, 0.0),
- name='platformDown%s' % fakeId,
- fluid = 1),
- ),
- Sequence(
- WaitInterval(0.3),
- LerpPosInterval(self.platform2, duration,
- Vec3(0.0, -30.0, 0.0),
- name='platform2Out%s' % fakeId,
- fluid = 1),
- WaitInterval(0.3),
- LerpPosInterval(self.platform2, duration,
- Vec3(0.0, 30.0, 30.0),
- name='platform2Back%s' % fakeId,
- fluid = 1),
- WaitInterval(0.3),
- LerpPosInterval(self.platform2, duration,
- Vec3(0.0, -30.0, 0.0),
- name='platform2Up%s' % fakeId,
- fluid = 1),
- WaitInterval(0.3),
- LerpPosInterval(self.platform2, duration,
- Vec3(0.0, 0.0, 0.0),
- name='platformDown%s' % fakeId,
- fluid = 1),
- ),
- name='platformIval%s' % fakeId,
- )
- self.moveIval.loop()
- """
def setWalkSpeed(self, forward, jump, reverse, rotate):
assert self.notify.debugStateCall(self)
self.avatarControlForwardSpeed=forward
diff --git a/direct/src/controls/PhysicsWalker.py b/direct/src/controls/PhysicsWalker.py
index fc6fe1ca70..de6ebcefa8 100755
--- a/direct/src/controls/PhysicsWalker.py
+++ b/direct/src/controls/PhysicsWalker.py
@@ -67,51 +67,6 @@ class PhysicsWalker(DirectObject.DirectObject):
self.isAirborne = 0
self.highMark = 0
- """
- def spawnTest(self):
- assert self.debugPrint("\n\nspawnTest()\n")
- if not self.wantDebugIndicator:
- return
- from pandac.PandaModules import *
- from direct.interval.IntervalGlobal import *
- from toontown.coghq import MovingPlatform
-
- if hasattr(self, "platform"):
- # Remove the prior instantiation:
- self.moveIval.pause()
- del self.moveIval
- self.platform.destroy()
- del self.platform
-
- model = loader.loadModel('phase_9/models/cogHQ/platform1')
- fakeId = id(self)
- self.platform = MovingPlatform.MovingPlatform()
- self.platform.setupCopyModel(fakeId, model, 'platformcollision')
- self.platformRoot = render.attachNewNode("physicsWalker-spawnTest-%s"%fakeId)
- self.platformRoot.setPos(base.localAvatar, Vec3(0.0, 3.0, 1.0))
- self.platformRoot.setHpr(base.localAvatar, Vec3.zero())
- self.platform.reparentTo(self.platformRoot)
-
- startPos = Vec3(0.0, -15.0, 0.0)
- endPos = Vec3(0.0, 15.0, 0.0)
- distance = Vec3(startPos-endPos).length()
- duration = distance/4
- self.moveIval = Sequence(
- WaitInterval(0.3),
- LerpPosInterval(self.platform, duration,
- endPos, startPos=startPos,
- name='platformOut%s' % fakeId,
- fluid = 1),
- WaitInterval(0.3),
- LerpPosInterval(self.platform, duration,
- startPos, startPos=endPos,
- name='platformBack%s' % fakeId,
- fluid = 1),
- name='platformIval%s' % fakeId,
- )
- self.moveIval.loop()
- """
-
def setWalkSpeed(self, forward, jump, reverse, rotate):
assert self.debugPrint("setWalkSpeed()")
self.avatarControlForwardSpeed=forward
diff --git a/direct/src/dcparser/dcClass.cxx b/direct/src/dcparser/dcClass.cxx
index 9487c5e323..f1d6955981 100644
--- a/direct/src/dcparser/dcClass.cxx
+++ b/direct/src/dcparser/dcClass.cxx
@@ -16,22 +16,13 @@
#include "dcAtomicField.h"
#include "hashGenerator.h"
#include "dcindent.h"
-#include "dcmsgtypes.h"
#include "dcClassParameter.h"
#include
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
-using std::ostream;
-using std::ostringstream;
using std::string;
#ifdef WITHIN_PANDA
-#include "pStatTimer.h"
-
#ifndef CPPPARSER
PStatCollector DCClass::_update_pcollector("App:Show code:readerPollTask:Update");
PStatCollector DCClass::_generate_pcollector("App:Show code:readerPollTask:Generate");
@@ -86,10 +77,7 @@ DCClass(DCFile *dc_file, const string &name, bool is_struct, bool bogus_class) :
_number = -1;
_constructor = nullptr;
-#ifdef HAVE_PYTHON
- _class_def = nullptr;
- _owner_class_def = nullptr;
-#endif
+ _python_class_defs = nullptr;
}
/**
@@ -105,11 +93,6 @@ DCClass::
for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
delete (*fi);
}
-
-#ifdef HAVE_PYTHON
- Py_XDECREF(_class_def);
- Py_XDECREF(_owner_class_def);
-#endif
}
/**
@@ -335,7 +318,7 @@ inherits_from_bogus_class() const {
* Write a string representation of this instance to .
*/
void DCClass::
-output(ostream &out) const {
+output(std::ostream &out) const {
if (_is_struct) {
out << "struct";
} else {
@@ -346,678 +329,11 @@ output(ostream &out) const {
}
}
-#ifdef HAVE_PYTHON
-/**
- * Returns true if the DCClass object has an associated Python class
- * definition, false otherwise.
- */
-bool DCClass::
-has_class_def() const {
- return (_class_def != nullptr);
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Sets the class object associated with this DistributedClass. This object
- * will be used to construct new instances of the class.
- */
-void DCClass::
-set_class_def(PyObject *class_def) {
- Py_XINCREF(class_def);
- Py_XDECREF(_class_def);
- _class_def = class_def;
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns the class object that was previously associated with this
- * DistributedClass. This will return a new reference to the object.
- */
-PyObject *DCClass::
-get_class_def() const {
- if (_class_def == nullptr) {
- Py_INCREF(Py_None);
- return Py_None;
- }
-
- Py_INCREF(_class_def);
- return _class_def;
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns true if the DCClass object has an associated Python owner class
- * definition, false otherwise.
- */
-bool DCClass::
-has_owner_class_def() const {
- return (_owner_class_def != nullptr);
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Sets the owner class object associated with this DistributedClass. This
- * object will be used to construct new owner instances of the class.
- */
-void DCClass::
-set_owner_class_def(PyObject *owner_class_def) {
- Py_XINCREF(owner_class_def);
- Py_XDECREF(_owner_class_def);
- _owner_class_def = owner_class_def;
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns the owner class object that was previously associated with this
- * DistributedClass. This will return a new reference to the object.
- */
-PyObject *DCClass::
-get_owner_class_def() const {
- if (_owner_class_def == nullptr) {
- Py_INCREF(Py_None);
- return Py_None;
- }
-
- Py_INCREF(_owner_class_def);
- return _owner_class_def;
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Extracts the update message out of the packer and applies it to the
- * indicated object by calling the appropriate method.
- */
-void DCClass::
-receive_update(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
- PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
- DCPacker packer;
- const char *data = (const char *)di.get_datagram().get_data();
- packer.set_unpack_data(data + di.get_current_index(),
- di.get_remaining_size(), false);
-
- int field_id = packer.raw_unpack_uint16();
- DCField *field = get_field_by_index(field_id);
- if (field == nullptr) {
- ostringstream strm;
- strm
- << "Received update for field " << field_id << ", not in class "
- << get_name();
- nassert_raise(strm.str());
- return;
- }
-
- packer.begin_unpack(field);
- field->receive_update(packer, distobj);
- packer.end_unpack();
-
- di.skip_bytes(packer.get_num_unpacked_bytes());
-
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a big datagram that includes all of the "required" fields that
- * are sent along with a normal "generate with required" message. This is all
- * of the atomic fields that are marked "broadcast required".
- */
-void DCClass::
-receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
- PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
- DCPacker packer;
- const char *data = (const char *)di.get_datagram().get_data();
- packer.set_unpack_data(data + di.get_current_index(),
- di.get_remaining_size(), false);
-
- int num_fields = get_num_inherited_fields();
- for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
- DCField *field = get_inherited_field(i);
- if (field->as_molecular_field() == nullptr &&
- field->is_required() && field->is_broadcast()) {
- packer.begin_unpack(field);
- field->receive_update(packer, distobj);
- if (!packer.end_unpack()) {
- break;
- }
- }
- }
-
- di.skip_bytes(packer.get_num_unpacked_bytes());
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a big datagram that includes all of the "required" fields that
- * are sent along with a normal "generate with required" message. This is all
- * of the atomic fields that are marked "broadcast ownrecv". Should be used
- * for 'owner-view' objects.
- */
-void DCClass::
-receive_update_broadcast_required_owner(PyObject *distobj,
- DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
- PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
- DCPacker packer;
- const char *data = (const char *)di.get_datagram().get_data();
- packer.set_unpack_data(data + di.get_current_index(),
- di.get_remaining_size(), false);
-
- int num_fields = get_num_inherited_fields();
- for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
- DCField *field = get_inherited_field(i);
- if (field->as_molecular_field() == nullptr &&
- field->is_required() && (field->is_ownrecv() || field->is_broadcast())) {
- packer.begin_unpack(field);
- field->receive_update(packer, distobj);
- if (!packer.end_unpack()) {
- break;
- }
- }
- }
-
- di.skip_bytes(packer.get_num_unpacked_bytes());
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a big datagram that includes all of the "required" fields that
- * are sent when an avatar is created. This is all of the atomic fields that
- * are marked "required", whether they are broadcast or not.
- */
-void DCClass::
-receive_update_all_required(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
- PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
- DCPacker packer;
- const char *data = (const char *)di.get_datagram().get_data();
- packer.set_unpack_data(data + di.get_current_index(),
- di.get_remaining_size(), false);
-
- int num_fields = get_num_inherited_fields();
- for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
- DCField *field = get_inherited_field(i);
- if (field->as_molecular_field() == nullptr &&
- field->is_required()) {
- packer.begin_unpack(field);
- field->receive_update(packer, distobj);
- if (!packer.end_unpack()) {
- break;
- }
- }
- }
-
- di.skip_bytes(packer.get_num_unpacked_bytes());
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a datagram that lists some additional fields that are broadcast
- * in one chunk.
- */
-void DCClass::
-receive_update_other(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
- PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
- int num_fields = di.get_uint16();
- for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
- receive_update(distobj, di);
- }
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes an update for a named field from a packed value blob.
- */
-void DCClass::
-direct_update(PyObject *distobj, const string &field_name,
- const vector_uchar &value_blob) {
- DCField *field = get_field_by_name(field_name);
- nassertv_always(field != nullptr);
-
- DCPacker packer;
- packer.set_unpack_data(value_blob);
- packer.begin_unpack(field);
- field->receive_update(packer, distobj);
- packer.end_unpack();
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes an update for a named field from a packed datagram.
- */
-void DCClass::
-direct_update(PyObject *distobj, const string &field_name,
- const Datagram &datagram) {
- DCField *field = get_field_by_name(field_name);
- nassertv_always(field != nullptr);
-
- DCPacker packer;
- packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false);
- packer.begin_unpack(field);
- field->receive_update(packer, distobj);
- packer.end_unpack();
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Looks up the current value of the indicated field by calling the
- * appropriate get*() function, then packs that value into the datagram. This
- * field is presumably either a required field or a specified optional field,
- * and we are building up a datagram for the generate-with-required message.
- *
- * Returns true on success, false on failure.
- */
-bool DCClass::
-pack_required_field(Datagram &datagram, PyObject *distobj,
- const DCField *field) const {
- DCPacker packer;
- packer.begin_pack(field);
- if (!pack_required_field(packer, distobj, field)) {
- return false;
- }
- if (!packer.end_pack()) {
- return false;
- }
-
- datagram.append_data(packer.get_data(), packer.get_length());
- return true;
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Looks up the current value of the indicated field by calling the
- * appropriate get*() function, then packs that value into the packer. This
- * field is presumably either a required field or a specified optional field,
- * and we are building up a datagram for the generate-with-required message.
- *
- * Returns true on success, false on failure.
- */
-bool DCClass::
-pack_required_field(DCPacker &packer, PyObject *distobj,
- const DCField *field) const {
- const DCParameter *parameter = field->as_parameter();
- if (parameter != nullptr) {
- // This is the easy case: to pack a parameter, we just look on the class
- // object for the data element.
- string field_name = field->get_name();
-
- if (!PyObject_HasAttrString(distobj, (char *)field_name.c_str())) {
- // If the attribute is not defined, but the field has a default value
- // specified, quietly pack the default value.
- if (field->has_default_value()) {
- packer.pack_default_value();
- return true;
- }
-
- // If there is no default value specified, it's an error.
- ostringstream strm;
- strm << "Data element " << field_name
- << ", required by dc file for dclass " << get_name()
- << ", not defined on object";
- nassert_raise(strm.str());
- return false;
- }
- PyObject *result =
- PyObject_GetAttrString(distobj, (char *)field_name.c_str());
- nassertr(result != nullptr, false);
-
- // Now pack the value into the datagram.
- bool pack_ok = parameter->pack_args(packer, result);
- Py_DECREF(result);
-
- return pack_ok;
- }
-
- if (field->as_molecular_field() != nullptr) {
- ostringstream strm;
- strm << "Cannot pack molecular field " << field->get_name()
- << " for generate";
- nassert_raise(strm.str());
- return false;
- }
-
- const DCAtomicField *atom = field->as_atomic_field();
- nassertr(atom != nullptr, false);
-
- // We need to get the initial value of this field. There isn't a good,
- // robust way to get this; presently, we just mangle the "setFoo()" name of
- // the required field into "getFoo()" and call that.
- string setter_name = atom->get_name();
-
- if (setter_name.empty()) {
- ostringstream strm;
- strm << "Required field is unnamed!";
- nassert_raise(strm.str());
- return false;
- }
-
- if (atom->get_num_elements() == 0) {
- // It sure doesn't make sense to have a required field with no parameters.
- // What data, exactly, is required?
- ostringstream strm;
- strm << "Required field " << setter_name << " has no parameters!";
- nassert_raise(strm.str());
- return false;
- }
-
- string getter_name = setter_name;
- if (setter_name.substr(0, 3) == "set") {
- // If the original method started with "set", we mangle this directly to
- // "get".
- getter_name[0] = 'g';
-
- } else {
- // Otherwise, we add a "get" prefix, and capitalize the next letter.
- getter_name = "get" + setter_name;
- getter_name[3] = toupper(getter_name[3]);
- }
-
- // Now we have to look up the getter on the distributed object and call it.
- if (!PyObject_HasAttrString(distobj, (char *)getter_name.c_str())) {
- // As above, if there's no getter but the field has a default value
- // specified, quietly pack the default value.
- if (field->has_default_value()) {
- packer.pack_default_value();
- return true;
- }
-
- // Otherwise, with no default value it's an error.
- ostringstream strm;
- strm << "Distributed class " << get_name()
- << " doesn't have getter named " << getter_name
- << " to match required field " << setter_name;
- nassert_raise(strm.str());
- return false;
- }
- PyObject *func =
- PyObject_GetAttrString(distobj, (char *)getter_name.c_str());
- nassertr(func != nullptr, false);
-
- PyObject *empty_args = PyTuple_New(0);
- PyObject *result = PyObject_CallObject(func, empty_args);
- Py_DECREF(empty_args);
- Py_DECREF(func);
- if (result == nullptr) {
- // We don't set this as an exception, since presumably the Python method
- // itself has already triggered a Python exception.
- std::cerr << "Error when calling " << getter_name << "\n";
- return false;
- }
-
- if (atom->get_num_elements() == 1) {
- // In this case, we expect the getter to return one object, which we wrap
- // up in a tuple.
- PyObject *tuple = PyTuple_New(1);
- PyTuple_SET_ITEM(tuple, 0, result);
- result = tuple;
-
- } else {
- // Otherwise, it had better already be a sequence or tuple of some sort.
- if (!PySequence_Check(result)) {
- ostringstream strm;
- strm << "Since dclass " << get_name() << " method " << setter_name
- << " is declared to have multiple parameters, Python function "
- << getter_name << " must return a list or tuple.\n";
- nassert_raise(strm.str());
- return false;
- }
- }
-
- // Now pack the arguments into the datagram.
- bool pack_ok = atom->pack_args(packer, result);
- Py_DECREF(result);
-
- return pack_ok;
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the client.
- */
-Datagram DCClass::
-client_format_update(const string &field_name, DOID_TYPE do_id,
- PyObject *args) const {
- DCField *field = get_field_by_name(field_name);
- if (field == nullptr) {
- ostringstream strm;
- strm << "No field named " << field_name << " in class " << get_name()
- << "\n";
- nassert_raise(strm.str());
- return Datagram();
- }
-
- return field->client_format_update(do_id, args);
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the AI.
- */
-Datagram DCClass::
-ai_format_update(const string &field_name, DOID_TYPE do_id,
- CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
- DCField *field = get_field_by_name(field_name);
- if (field == nullptr) {
- ostringstream strm;
- strm << "No field named " << field_name << " in class " << get_name()
- << "\n";
- nassert_raise(strm.str());
- return Datagram();
- }
-
- return field->ai_format_update(do_id, to_id, from_id, args);
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update,
- * using the indicated msg type for the indicated distributed object from the
- * AI.
- */
-Datagram DCClass::
-ai_format_update_msg_type(const string &field_name, DOID_TYPE do_id,
- CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
- DCField *field = get_field_by_name(field_name);
- if (field == nullptr) {
- ostringstream strm;
- strm << "No field named " << field_name << " in class " << get_name()
- << "\n";
- nassert_raise(strm.str());
- return Datagram();
- }
-
- return field->ai_format_update_msg_type(do_id, to_id, from_id, msg_type, args);
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to generate a new
- * distributed object from the client. This requires querying the object for
- * the initial value of its required fields.
- *
- * optional_fields is a list of fieldNames to generate in addition to the
- * normal required fields.
- *
- * This method is only called by the CMU implementation.
- */
-Datagram DCClass::
-client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
- ZONEID_TYPE zone_id,
- PyObject *optional_fields) const {
- DCPacker packer;
-
- packer.raw_pack_uint16(CLIENT_OBJECT_GENERATE_CMU);
-
- packer.raw_pack_uint32(zone_id);
- packer.raw_pack_uint16(_number);
- packer.raw_pack_uint32(do_id);
-
- // Specify all of the required fields.
- int num_fields = get_num_inherited_fields();
- for (int i = 0; i < num_fields; ++i) {
- DCField *field = get_inherited_field(i);
- if (field->is_required() && field->as_molecular_field() == nullptr) {
- packer.begin_pack(field);
- if (!pack_required_field(packer, distobj, field)) {
- return Datagram();
- }
- packer.end_pack();
- }
- }
-
- // Also specify the optional fields.
- int num_optional_fields = 0;
- if (PyObject_IsTrue(optional_fields)) {
- num_optional_fields = PySequence_Size(optional_fields);
- }
- packer.raw_pack_uint16(num_optional_fields);
-
- for (int i = 0; i < num_optional_fields; i++) {
- PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
-#if PY_MAJOR_VERSION >= 3
- string field_name = PyUnicode_AsUTF8(py_field_name);
-#else
- string field_name = PyString_AsString(py_field_name);
-#endif
- Py_XDECREF(py_field_name);
-
- DCField *field = get_field_by_name(field_name);
- if (field == nullptr) {
- ostringstream strm;
- strm << "No field named " << field_name << " in class " << get_name()
- << "\n";
- nassert_raise(strm.str());
- return Datagram();
- }
- packer.raw_pack_uint16(field->get_number());
- packer.begin_pack(field);
- if (!pack_required_field(packer, distobj, field)) {
- return Datagram();
- }
- packer.end_pack();
- }
-
- return Datagram(packer.get_data(), packer.get_length());
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to generate a new
- * distributed object from the AI. This requires querying the object for the
- * initial value of its required fields.
- *
- * optional_fields is a list of fieldNames to generate in addition to the
- * normal required fields.
- */
-Datagram DCClass::
-ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
- DOID_TYPE parent_id, ZONEID_TYPE zone_id,
- CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id,
- PyObject *optional_fields) const {
- DCPacker packer;
-
- packer.raw_pack_uint8(1);
- packer.RAW_PACK_CHANNEL(district_channel_id);
- packer.RAW_PACK_CHANNEL(from_channel_id);
- // packer.raw_pack_uint8('A');
-
- bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0);
-
- if (has_optional_fields) {
- packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER);
- } else {
- packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED);
- }
-
- packer.raw_pack_uint32(do_id);
- // Parent is a bit overloaded; this parent is not about inheritance, this
- // one is about the visibility container parent, i.e. the zone parent:
- packer.raw_pack_uint32(parent_id);
- packer.raw_pack_uint32(zone_id);
- packer.raw_pack_uint16(_number);
-
- // Specify all of the required fields.
- int num_fields = get_num_inherited_fields();
- for (int i = 0; i < num_fields; ++i) {
- DCField *field = get_inherited_field(i);
- if (field->is_required() && field->as_molecular_field() == nullptr) {
- packer.begin_pack(field);
- if (!pack_required_field(packer, distobj, field)) {
- return Datagram();
- }
- packer.end_pack();
- }
- }
-
- // Also specify the optional fields.
- if (has_optional_fields) {
- int num_optional_fields = PySequence_Size(optional_fields);
- packer.raw_pack_uint16(num_optional_fields);
-
- for (int i = 0; i < num_optional_fields; ++i) {
- PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
-#if PY_MAJOR_VERSION >= 3
- string field_name = PyUnicode_AsUTF8(py_field_name);
-#else
- string field_name = PyString_AsString(py_field_name);
-#endif
- Py_XDECREF(py_field_name);
-
- DCField *field = get_field_by_name(field_name);
- if (field == nullptr) {
- ostringstream strm;
- strm << "No field named " << field_name << " in class " << get_name()
- << "\n";
- nassert_raise(strm.str());
- return Datagram();
- }
-
- packer.raw_pack_uint16(field->get_number());
-
- packer.begin_pack(field);
- if (!pack_required_field(packer, distobj, field)) {
- return Datagram();
- }
- packer.end_pack();
- }
- }
-
- return Datagram(packer.get_data(), packer.get_length());
-}
-#endif // HAVE_PYTHON
-
/**
* Write a string representation of this instance to .
*/
void DCClass::
-output(ostream &out, bool brief) const {
+output(std::ostream &out, bool brief) const {
output_instance(out, brief, "", "", "");
}
@@ -1026,7 +342,7 @@ output(ostream &out, bool brief) const {
* stream.
*/
void DCClass::
-write(ostream &out, bool brief, int indent_level) const {
+write(std::ostream &out, bool brief, int indent_level) const {
indent(out, indent_level);
if (_is_struct) {
out << "struct";
@@ -1086,7 +402,7 @@ write(ostream &out, bool brief, int indent_level) const {
* stream.
*/
void DCClass::
-output_instance(ostream &out, bool brief, const string &prename,
+output_instance(std::ostream &out, bool brief, const string &prename,
const string &name, const string &postname) const {
if (_is_struct) {
out << "struct";
diff --git a/direct/src/dcparser/dcClass.h b/direct/src/dcparser/dcClass.h
index 94feae1c19..2dcc6bc560 100644
--- a/direct/src/dcparser/dcClass.h
+++ b/direct/src/dcparser/dcClass.h
@@ -17,11 +17,12 @@
#include "dcbase.h"
#include "dcField.h"
#include "dcDeclaration.h"
-#include "dcPython.h"
#ifdef WITHIN_PANDA
#include "pStatCollector.h"
#include "configVariableBool.h"
+#include "extension.h"
+#include "datagramIterator.h"
extern ConfigVariableBool dc_multiple_inheritance;
extern ConfigVariableBool dc_virtual_inheritance;
@@ -80,44 +81,52 @@ PUBLISHED:
virtual void output(std::ostream &out) const;
-#ifdef HAVE_PYTHON
- bool has_class_def() const;
- void set_class_def(PyObject *class_def);
- PyObject *get_class_def() const;
- bool has_owner_class_def() const;
- void set_owner_class_def(PyObject *owner_class_def);
- PyObject *get_owner_class_def() const;
+ EXTENSION(bool has_class_def() const);
+ EXTENSION(void set_class_def(PyObject *class_def));
+ EXTENSION(PyObject *get_class_def() const);
+ EXTENSION(bool has_owner_class_def() const);
+ EXTENSION(void set_owner_class_def(PyObject *owner_class_def));
+ EXTENSION(PyObject *get_owner_class_def() const);
- void receive_update(PyObject *distobj, DatagramIterator &di) const;
- void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const;
- void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const;
- void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const;
- void receive_update_other(PyObject *distobj, DatagramIterator &di) const;
+ EXTENSION(void receive_update(PyObject *distobj, DatagramIterator &di) const);
+ EXTENSION(void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const);
+ EXTENSION(void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const);
+ EXTENSION(void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const);
+ EXTENSION(void receive_update_other(PyObject *distobj, DatagramIterator &di) const);
- void direct_update(PyObject *distobj, const std::string &field_name,
- const vector_uchar &value_blob);
- void direct_update(PyObject *distobj, const std::string &field_name,
- const Datagram &datagram);
- bool pack_required_field(Datagram &datagram, PyObject *distobj,
- const DCField *field) const;
- bool pack_required_field(DCPacker &packer, PyObject *distobj,
- const DCField *field) const;
+ EXTENSION(void direct_update(PyObject *distobj, const std::string &field_name,
+ const vector_uchar &value_blob));
+ EXTENSION(void direct_update(PyObject *distobj, const std::string &field_name,
+ const Datagram &datagram));
+ EXTENSION(bool pack_required_field(Datagram &datagram, PyObject *distobj,
+ const DCField *field) const);
+ EXTENSION(bool pack_required_field(DCPacker &packer, PyObject *distobj,
+ const DCField *field) const);
- Datagram client_format_update(const std::string &field_name,
- DOID_TYPE do_id, PyObject *args) const;
- Datagram ai_format_update(const std::string &field_name, DOID_TYPE do_id,
- CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const;
- Datagram ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
- CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const;
- Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id, ZONEID_TYPE parent_id, ZONEID_TYPE zone_id,
- CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id,
- PyObject *optional_fields) const;
- Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
- ZONEID_TYPE zone_id, PyObject *optional_fields) const;
+ EXTENSION(Datagram client_format_update(const std::string &field_name,
+ DOID_TYPE do_id, PyObject *args) const);
+ EXTENSION(Datagram ai_format_update(const std::string &field_name,
+ DOID_TYPE do_id,
+ CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+ PyObject *args) const);
+ EXTENSION(Datagram ai_format_update_msg_type(const std::string &field_name,
+ DOID_TYPE do_id,
+ CHANNEL_TYPE to_id,
+ CHANNEL_TYPE from_id,
+ int msg_type,
+ PyObject *args) const);
+ EXTENSION(Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
+ ZONEID_TYPE parent_id,
+ ZONEID_TYPE zone_id,
+ CHANNEL_TYPE district_channel_id,
+ CHANNEL_TYPE from_channel_id,
+ PyObject *optional_fields) const);
+ EXTENSION(Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
+ ZONEID_TYPE zone_id,
+ PyObject *optional_fields) const);
-#endif
public:
virtual void output(std::ostream &out, bool brief) const;
@@ -136,8 +145,8 @@ private:
void shadow_inherited_field(const std::string &name);
#ifdef WITHIN_PANDA
- PStatCollector _class_update_pcollector;
- PStatCollector _class_generate_pcollector;
+ mutable PStatCollector _class_update_pcollector;
+ mutable PStatCollector _class_generate_pcollector;
static PStatCollector _update_pcollector;
static PStatCollector _generate_pcollector;
#endif
@@ -163,12 +172,17 @@ private:
typedef pmap FieldsByIndex;
FieldsByIndex _fields_by_index;
-#ifdef HAVE_PYTHON
- PyObject *_class_def;
- PyObject *_owner_class_def;
-#endif
+ // See pandaNode.h for an explanation of this trick
+ class PythonClassDefs : public ReferenceCount {
+ public:
+ virtual ~PythonClassDefs() {};
+ };
+ PT(PythonClassDefs) _python_class_defs;
friend class DCField;
+#ifdef WITHIN_PANDA
+ friend class Extension;
+#endif
};
#include "dcClass.I"
diff --git a/direct/src/dcparser/dcClass_ext.cxx b/direct/src/dcparser/dcClass_ext.cxx
new file mode 100644
index 0000000000..4e9ffb9aa9
--- /dev/null
+++ b/direct/src/dcparser/dcClass_ext.cxx
@@ -0,0 +1,665 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University. All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license. You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcClass_ext.cxx
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#include "dcClass_ext.h"
+#include "dcField_ext.h"
+#include "dcAtomicField.h"
+#include "dcPacker.h"
+#include "dcmsgtypes.h"
+
+#include "datagram.h"
+#include "datagramIterator.h"
+#include "pStatTimer.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Returns true if the DCClass object has an associated Python class
+ * definition, false otherwise.
+ */
+bool Extension::
+has_class_def() const {
+ return _this->_python_class_defs != nullptr
+ && ((PythonClassDefsImpl *)_this->_python_class_defs.p())->_class_def != nullptr;
+}
+
+/**
+ * Sets the class object associated with this DistributedClass. This object
+ * will be used to construct new instances of the class.
+ */
+void Extension::
+set_class_def(PyObject *class_def) {
+ PythonClassDefsImpl *defs = do_get_defs();
+
+ Py_XINCREF(class_def);
+ Py_XDECREF(defs->_class_def);
+ defs->_class_def = class_def;
+}
+
+/**
+ * Returns the class object that was previously associated with this
+ * DistributedClass. This will return a new reference to the object.
+ */
+PyObject *Extension::
+get_class_def() const {
+ if (!has_class_def()) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ PythonClassDefsImpl *defs = do_get_defs();
+ Py_INCREF(defs->_class_def);
+ return defs->_class_def;
+}
+
+/**
+ * Returns true if the DCClass object has an associated Python owner class
+ * definition, false otherwise.
+ */
+bool Extension::
+has_owner_class_def() const {
+ return _this->_python_class_defs != nullptr
+ && ((PythonClassDefsImpl *)_this->_python_class_defs.p())->_owner_class_def != nullptr;
+}
+
+/**
+ * Sets the owner class object associated with this DistributedClass. This
+ * object will be used to construct new owner instances of the class.
+ */
+void Extension::
+set_owner_class_def(PyObject *owner_class_def) {
+ PythonClassDefsImpl *defs = do_get_defs();
+
+ Py_XINCREF(owner_class_def);
+ Py_XDECREF(defs->_owner_class_def);
+ defs->_owner_class_def = owner_class_def;
+}
+
+/**
+ * Returns the owner class object that was previously associated with this
+ * DistributedClass. This will return a new reference to the object.
+ */
+PyObject *Extension::
+get_owner_class_def() const {
+ if (!has_owner_class_def()) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ PythonClassDefsImpl *defs = do_get_defs();
+ Py_INCREF(defs->_owner_class_def);
+ return defs->_owner_class_def;
+}
+
+/**
+ * Extracts the update message out of the packer and applies it to the
+ * indicated object by calling the appropriate method.
+ */
+void Extension::
+receive_update(PyObject *distobj, DatagramIterator &di) const {
+ PStatTimer timer(_this->_class_update_pcollector);
+ DCPacker packer;
+ const char *data = (const char *)di.get_datagram().get_data();
+ packer.set_unpack_data(data + di.get_current_index(),
+ di.get_remaining_size(), false);
+
+ int field_id = packer.raw_unpack_uint16();
+ DCField *field = _this->get_field_by_index(field_id);
+ if (field == nullptr) {
+ ostringstream strm;
+ strm
+ << "Received update for field " << field_id << ", not in class "
+ << _this->get_name();
+ nassert_raise(strm.str());
+ return;
+ }
+
+ packer.begin_unpack(field);
+ invoke_extension(field).receive_update(packer, distobj);
+ packer.end_unpack();
+
+ di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a big datagram that includes all of the "required" fields that
+ * are sent along with a normal "generate with required" message. This is all
+ * of the atomic fields that are marked "broadcast required".
+ */
+void Extension::
+receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const {
+ PStatTimer timer(_this->_class_update_pcollector);
+ DCPacker packer;
+ const char *data = (const char *)di.get_datagram().get_data();
+ packer.set_unpack_data(data + di.get_current_index(),
+ di.get_remaining_size(), false);
+
+ int num_fields = _this->get_num_inherited_fields();
+ for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+ DCField *field = _this->get_inherited_field(i);
+ if (field->as_molecular_field() == nullptr &&
+ field->is_required() && field->is_broadcast()) {
+ packer.begin_unpack(field);
+ invoke_extension(field).receive_update(packer, distobj);
+ if (!packer.end_unpack()) {
+ break;
+ }
+ }
+ }
+
+ di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a big datagram that includes all of the "required" fields that
+ * are sent along with a normal "generate with required" message. This is all
+ * of the atomic fields that are marked "broadcast ownrecv". Should be used
+ * for 'owner-view' objects.
+ */
+void Extension::
+receive_update_broadcast_required_owner(PyObject *distobj,
+ DatagramIterator &di) const {
+ PStatTimer timer(_this->_class_update_pcollector);
+ DCPacker packer;
+ const char *data = (const char *)di.get_datagram().get_data();
+ packer.set_unpack_data(data + di.get_current_index(),
+ di.get_remaining_size(), false);
+
+ int num_fields = _this->get_num_inherited_fields();
+ for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+ DCField *field = _this->get_inherited_field(i);
+ if (field->as_molecular_field() == nullptr &&
+ field->is_required() && (field->is_ownrecv() || field->is_broadcast())) {
+ packer.begin_unpack(field);
+ invoke_extension(field).receive_update(packer, distobj);
+ if (!packer.end_unpack()) {
+ break;
+ }
+ }
+ }
+
+ di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a big datagram that includes all of the "required" fields that
+ * are sent when an avatar is created. This is all of the atomic fields that
+ * are marked "required", whether they are broadcast or not.
+ */
+void Extension::
+receive_update_all_required(PyObject *distobj, DatagramIterator &di) const {
+ PStatTimer timer(_this->_class_update_pcollector);
+ DCPacker packer;
+ const char *data = (const char *)di.get_datagram().get_data();
+ packer.set_unpack_data(data + di.get_current_index(),
+ di.get_remaining_size(), false);
+
+ int num_fields = _this->get_num_inherited_fields();
+ for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+ DCField *field = _this->get_inherited_field(i);
+ if (field->as_molecular_field() == nullptr &&
+ field->is_required()) {
+ packer.begin_unpack(field);
+ invoke_extension(field).receive_update(packer, distobj);
+ if (!packer.end_unpack()) {
+ break;
+ }
+ }
+ }
+
+ di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a datagram that lists some additional fields that are broadcast
+ * in one chunk.
+ */
+void Extension::
+receive_update_other(PyObject *distobj, DatagramIterator &di) const {
+ PStatTimer timer(_this->_class_update_pcollector);
+ int num_fields = di.get_uint16();
+ for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+ receive_update(distobj, di);
+ }
+}
+
+/**
+ * Processes an update for a named field from a packed value blob.
+ */
+void Extension::
+direct_update(PyObject *distobj, const std::string &field_name,
+ const vector_uchar &value_blob) {
+ DCField *field = _this->get_field_by_name(field_name);
+ nassertv_always(field != nullptr);
+
+ DCPacker packer;
+ packer.set_unpack_data(value_blob);
+ packer.begin_unpack(field);
+ invoke_extension(field).receive_update(packer, distobj);
+ packer.end_unpack();
+}
+
+/**
+ * Processes an update for a named field from a packed datagram.
+ */
+void Extension::
+direct_update(PyObject *distobj, const std::string &field_name,
+ const Datagram &datagram) {
+ DCField *field = _this->get_field_by_name(field_name);
+ nassertv_always(field != nullptr);
+
+ DCPacker packer;
+ packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false);
+ packer.begin_unpack(field);
+ invoke_extension(field).receive_update(packer, distobj);
+ packer.end_unpack();
+}
+
+/**
+ * Looks up the current value of the indicated field by calling the
+ * appropriate get*() function, then packs that value into the datagram. This
+ * field is presumably either a required field or a specified optional field,
+ * and we are building up a datagram for the generate-with-required message.
+ *
+ * Returns true on success, false on failure.
+ */
+bool Extension::
+pack_required_field(Datagram &datagram, PyObject *distobj,
+ const DCField *field) const {
+ DCPacker packer;
+ packer.begin_pack(field);
+ if (!pack_required_field(packer, distobj, field)) {
+ return false;
+ }
+ if (!packer.end_pack()) {
+ return false;
+ }
+
+ datagram.append_data(packer.get_data(), packer.get_length());
+ return true;
+}
+
+/**
+ * Looks up the current value of the indicated field by calling the
+ * appropriate get*() function, then packs that value into the packer. This
+ * field is presumably either a required field or a specified optional field,
+ * and we are building up a datagram for the generate-with-required message.
+ *
+ * Returns true on success, false on failure.
+ */
+bool Extension::
+pack_required_field(DCPacker &packer, PyObject *distobj,
+ const DCField *field) const {
+ using std::ostringstream;
+
+ const DCParameter *parameter = field->as_parameter();
+ if (parameter != nullptr) {
+ // This is the easy case: to pack a parameter, we just look on the class
+ // object for the data element.
+ std::string field_name = field->get_name();
+
+ if (!PyObject_HasAttrString(distobj, (char *)field_name.c_str())) {
+ // If the attribute is not defined, but the field has a default value
+ // specified, quietly pack the default value.
+ if (field->has_default_value()) {
+ packer.pack_default_value();
+ return true;
+ }
+
+ // If there is no default value specified, it's an error.
+ ostringstream strm;
+ strm << "Data element " << field_name
+ << ", required by dc file for dclass " << _this->get_name()
+ << ", not defined on object";
+ nassert_raise(strm.str());
+ return false;
+ }
+ PyObject *result =
+ PyObject_GetAttrString(distobj, (char *)field_name.c_str());
+ nassertr(result != nullptr, false);
+
+ // Now pack the value into the datagram.
+ bool pack_ok = invoke_extension((DCField *)parameter).pack_args(packer, result);
+ Py_DECREF(result);
+
+ return pack_ok;
+ }
+
+ if (field->as_molecular_field() != nullptr) {
+ ostringstream strm;
+ strm << "Cannot pack molecular field " << field->get_name()
+ << " for generate";
+ nassert_raise(strm.str());
+ return false;
+ }
+
+ const DCAtomicField *atom = field->as_atomic_field();
+ nassertr(atom != nullptr, false);
+
+ // We need to get the initial value of this field. There isn't a good,
+ // robust way to get this; presently, we just mangle the "setFoo()" name of
+ // the required field into "getFoo()" and call that.
+ std::string setter_name = atom->get_name();
+
+ if (setter_name.empty()) {
+ ostringstream strm;
+ strm << "Required field is unnamed!";
+ nassert_raise(strm.str());
+ return false;
+ }
+
+ if (atom->get_num_elements() == 0) {
+ // It sure doesn't make sense to have a required field with no parameters.
+ // What data, exactly, is required?
+ ostringstream strm;
+ strm << "Required field " << setter_name << " has no parameters!";
+ nassert_raise(strm.str());
+ return false;
+ }
+
+ std::string getter_name = setter_name;
+ if (setter_name.substr(0, 3) == "set") {
+ // If the original method started with "set", we mangle this directly to
+ // "get".
+ getter_name[0] = 'g';
+
+ } else {
+ // Otherwise, we add a "get" prefix, and capitalize the next letter.
+ getter_name = "get" + setter_name;
+ getter_name[3] = toupper(getter_name[3]);
+ }
+
+ // Now we have to look up the getter on the distributed object and call it.
+ if (!PyObject_HasAttrString(distobj, (char *)getter_name.c_str())) {
+ // As above, if there's no getter but the field has a default value
+ // specified, quietly pack the default value.
+ if (field->has_default_value()) {
+ packer.pack_default_value();
+ return true;
+ }
+
+ // Otherwise, with no default value it's an error.
+ ostringstream strm;
+ strm << "Distributed class " << _this->get_name()
+ << " doesn't have getter named " << getter_name
+ << " to match required field " << setter_name;
+ nassert_raise(strm.str());
+ return false;
+ }
+ PyObject *func =
+ PyObject_GetAttrString(distobj, (char *)getter_name.c_str());
+ nassertr(func != nullptr, false);
+
+ PyObject *empty_args = PyTuple_New(0);
+ PyObject *result = PyObject_CallObject(func, empty_args);
+ Py_DECREF(empty_args);
+ Py_DECREF(func);
+ if (result == nullptr) {
+ // We don't set this as an exception, since presumably the Python method
+ // itself has already triggered a Python exception.
+ std::cerr << "Error when calling " << getter_name << "\n";
+ return false;
+ }
+
+ if (atom->get_num_elements() == 1) {
+ // In this case, we expect the getter to return one object, which we wrap
+ // up in a tuple.
+ PyObject *tuple = PyTuple_New(1);
+ PyTuple_SET_ITEM(tuple, 0, result);
+ result = tuple;
+
+ } else {
+ // Otherwise, it had better already be a sequence or tuple of some sort.
+ if (!PySequence_Check(result)) {
+ ostringstream strm;
+ strm << "Since dclass " << _this->get_name() << " method " << setter_name
+ << " is declared to have multiple parameters, Python function "
+ << getter_name << " must return a list or tuple.\n";
+ nassert_raise(strm.str());
+ return false;
+ }
+ }
+
+ // Now pack the arguments into the datagram.
+ bool pack_ok = invoke_extension((DCField *)atom).pack_args(packer, result);
+ Py_DECREF(result);
+
+ return pack_ok;
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the client.
+ */
+Datagram Extension::
+client_format_update(const std::string &field_name, DOID_TYPE do_id,
+ PyObject *args) const {
+ DCField *field = _this->get_field_by_name(field_name);
+ if (field == nullptr) {
+ std::ostringstream strm;
+ strm << "No field named " << field_name << " in class " << _this->get_name()
+ << "\n";
+ nassert_raise(strm.str());
+ return Datagram();
+ }
+
+ return invoke_extension(field).client_format_update(do_id, args);
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the AI.
+ */
+Datagram Extension::
+ai_format_update(const std::string &field_name, DOID_TYPE do_id,
+ CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
+ DCField *field = _this->get_field_by_name(field_name);
+ if (field == nullptr) {
+ std::ostringstream strm;
+ strm << "No field named " << field_name << " in class " << _this->get_name()
+ << "\n";
+ nassert_raise(strm.str());
+ return Datagram();
+ }
+
+ return invoke_extension(field).ai_format_update(do_id, to_id, from_id, args);
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update,
+ * using the indicated msg type for the indicated distributed object from the
+ * AI.
+ */
+Datagram Extension::
+ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
+ CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+ int msg_type, PyObject *args) const {
+ DCField *field = _this->get_field_by_name(field_name);
+ if (field == nullptr) {
+ std::ostringstream strm;
+ strm << "No field named " << field_name << " in class " << _this->get_name()
+ << "\n";
+ nassert_raise(strm.str());
+ return Datagram();
+ }
+
+ return invoke_extension(field).ai_format_update_msg_type(do_id, to_id, from_id, msg_type, args);
+}
+
+/**
+ * Generates a datagram containing the message necessary to generate a new
+ * distributed object from the client. This requires querying the object for
+ * the initial value of its required fields.
+ *
+ * optional_fields is a list of fieldNames to generate in addition to the
+ * normal required fields.
+ *
+ * This method is only called by the CMU implementation.
+ */
+Datagram Extension::
+client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
+ ZONEID_TYPE zone_id,
+ PyObject *optional_fields) const {
+ DCPacker packer;
+
+ packer.raw_pack_uint16(CLIENT_OBJECT_GENERATE_CMU);
+
+ packer.raw_pack_uint32(zone_id);
+ packer.raw_pack_uint16(_this->_number);
+ packer.raw_pack_uint32(do_id);
+
+ // Specify all of the required fields.
+ int num_fields = _this->get_num_inherited_fields();
+ for (int i = 0; i < num_fields; ++i) {
+ DCField *field = _this->get_inherited_field(i);
+ if (field->is_required() && field->as_molecular_field() == nullptr) {
+ packer.begin_pack(field);
+ if (!pack_required_field(packer, distobj, field)) {
+ return Datagram();
+ }
+ packer.end_pack();
+ }
+ }
+
+ // Also specify the optional fields.
+ int num_optional_fields = 0;
+ if (PyObject_IsTrue(optional_fields)) {
+ num_optional_fields = PySequence_Size(optional_fields);
+ }
+ packer.raw_pack_uint16(num_optional_fields);
+
+ for (int i = 0; i < num_optional_fields; i++) {
+ PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
+#if PY_MAJOR_VERSION >= 3
+ std::string field_name = PyUnicode_AsUTF8(py_field_name);
+#else
+ std::string field_name = PyString_AsString(py_field_name);
+#endif
+ Py_XDECREF(py_field_name);
+
+ DCField *field = _this->get_field_by_name(field_name);
+ if (field == nullptr) {
+ std::ostringstream strm;
+ strm << "No field named " << field_name << " in class " << _this->get_name()
+ << "\n";
+ nassert_raise(strm.str());
+ return Datagram();
+ }
+ packer.raw_pack_uint16(field->get_number());
+ packer.begin_pack(field);
+ if (!pack_required_field(packer, distobj, field)) {
+ return Datagram();
+ }
+ packer.end_pack();
+ }
+
+ return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Generates a datagram containing the message necessary to generate a new
+ * distributed object from the AI. This requires querying the object for the
+ * initial value of its required fields.
+ *
+ * optional_fields is a list of fieldNames to generate in addition to the
+ * normal required fields.
+ */
+Datagram Extension::
+ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
+ DOID_TYPE parent_id, ZONEID_TYPE zone_id,
+ CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id,
+ PyObject *optional_fields) const {
+ DCPacker packer;
+
+ packer.raw_pack_uint8(1);
+ packer.RAW_PACK_CHANNEL(district_channel_id);
+ packer.RAW_PACK_CHANNEL(from_channel_id);
+ // packer.raw_pack_uint8('A');
+
+ bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0);
+
+ if (has_optional_fields) {
+ packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER);
+ } else {
+ packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED);
+ }
+
+ packer.raw_pack_uint32(do_id);
+ // Parent is a bit overloaded; this parent is not about inheritance, this
+ // one is about the visibility container parent, i.e. the zone parent:
+ packer.raw_pack_uint32(parent_id);
+ packer.raw_pack_uint32(zone_id);
+ packer.raw_pack_uint16(_this->_number);
+
+ // Specify all of the required fields.
+ int num_fields = _this->get_num_inherited_fields();
+ for (int i = 0; i < num_fields; ++i) {
+ DCField *field = _this->get_inherited_field(i);
+ if (field->is_required() && field->as_molecular_field() == nullptr) {
+ packer.begin_pack(field);
+ if (!pack_required_field(packer, distobj, field)) {
+ return Datagram();
+ }
+ packer.end_pack();
+ }
+ }
+
+ // Also specify the optional fields.
+ if (has_optional_fields) {
+ int num_optional_fields = PySequence_Size(optional_fields);
+ packer.raw_pack_uint16(num_optional_fields);
+
+ for (int i = 0; i < num_optional_fields; ++i) {
+ PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
+#if PY_MAJOR_VERSION >= 3
+ std::string field_name = PyUnicode_AsUTF8(py_field_name);
+#else
+ std::string field_name = PyString_AsString(py_field_name);
+#endif
+ Py_XDECREF(py_field_name);
+
+ DCField *field = _this->get_field_by_name(field_name);
+ if (field == nullptr) {
+ std::ostringstream strm;
+ strm << "No field named " << field_name << " in class "
+ << _this->get_name() << "\n";
+ nassert_raise(strm.str());
+ return Datagram();
+ }
+
+ packer.raw_pack_uint16(field->get_number());
+
+ packer.begin_pack(field);
+ if (!pack_required_field(packer, distobj, field)) {
+ return Datagram();
+ }
+ packer.end_pack();
+ }
+ }
+
+ return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Returns the PythonClassDefsImpl object stored on the DCClass object,
+ * creating it if it didn't yet exist.
+ */
+Extension::PythonClassDefsImpl *Extension::
+do_get_defs() const {
+ if (!_this->_python_class_defs) {
+ _this->_python_class_defs = new PythonClassDefsImpl();
+ }
+ return (PythonClassDefsImpl *)_this->_python_class_defs.p();
+}
+
+#endif // HAVE_PYTHON
diff --git a/direct/src/dcparser/dcClass_ext.h b/direct/src/dcparser/dcClass_ext.h
new file mode 100644
index 0000000000..9f81b7b722
--- /dev/null
+++ b/direct/src/dcparser/dcClass_ext.h
@@ -0,0 +1,93 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University. All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license. You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcClass_ext.h
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#ifndef DCCLASS_EXT_H
+#define DCCLASS_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "dcClass.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for DCClass, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension : public ExtensionBase {
+public:
+ bool has_class_def() const;
+ void set_class_def(PyObject *class_def);
+ PyObject *get_class_def() const;
+ bool has_owner_class_def() const;
+ void set_owner_class_def(PyObject *owner_class_def);
+ PyObject *get_owner_class_def() const;
+
+ void receive_update(PyObject *distobj, DatagramIterator &di) const;
+ void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const;
+ void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const;
+ void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const;
+ void receive_update_other(PyObject *distobj, DatagramIterator &di) const;
+
+ void direct_update(PyObject *distobj, const std::string &field_name,
+ const vector_uchar &value_blob);
+ void direct_update(PyObject *distobj, const std::string &field_name,
+ const Datagram &datagram);
+ bool pack_required_field(Datagram &datagram, PyObject *distobj,
+ const DCField *field) const;
+ bool pack_required_field(DCPacker &packer, PyObject *distobj,
+ const DCField *field) const;
+
+
+
+ Datagram client_format_update(const std::string &field_name,
+ DOID_TYPE do_id, PyObject *args) const;
+ Datagram ai_format_update(const std::string &field_name, DOID_TYPE do_id,
+ CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const;
+ Datagram ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
+ CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const;
+ Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
+ ZONEID_TYPE parent_id, ZONEID_TYPE zone_id,
+ CHANNEL_TYPE district_channel_id,
+ CHANNEL_TYPE from_channel_id,
+ PyObject *optional_fields) const;
+ Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
+ ZONEID_TYPE zone_id,
+ PyObject *optional_fields) const;
+
+private:
+ /**
+ * Implementation of DCClass::PythonClassDefs which actually stores the
+ * Python pointers. This needs to be defined here rather than on DCClass
+ * itself, since DCClass cannot include Python.h or call Python functions.
+ */
+ class PythonClassDefsImpl : public DCClass::PythonClassDefs {
+ public:
+ virtual ~PythonClassDefsImpl() {
+ Py_XDECREF(_class_def);
+ Py_XDECREF(_owner_class_def);
+ }
+
+ PyObject *_class_def = nullptr;
+ PyObject *_owner_class_def = nullptr;
+ };
+
+ PythonClassDefsImpl *do_get_defs() const;
+};
+
+#endif // HAVE_PYTHON
+
+#endif // DCCLASS_EXT_H
diff --git a/direct/src/dcparser/dcField.cxx b/direct/src/dcparser/dcField.cxx
index 5880fd5097..9265160b28 100644
--- a/direct/src/dcparser/dcField.cxx
+++ b/direct/src/dcparser/dcField.cxx
@@ -18,16 +18,6 @@
#include "hashGenerator.h"
#include "dcmsgtypes.h"
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
-#ifdef WITHIN_PANDA
-#include "pStatTimer.h"
-#endif
-
-using std::string;
-
/**
*
*/
@@ -58,7 +48,7 @@ DCField() :
*
*/
DCField::
-DCField(const string &name, DCClass *dclass) :
+DCField(const std::string &name, DCClass *dclass) :
DCPackerInterface(name),
_dclass(dclass)
#ifdef WITHIN_PANDA
@@ -161,14 +151,14 @@ as_parameter() const {
* string formatting it for human consumption. Returns empty string if there
* is an error.
*/
-string DCField::
+std::string DCField::
format_data(const vector_uchar &packed_data, bool show_field_names) {
DCPacker packer;
packer.set_unpack_data(packed_data);
packer.begin_unpack(this);
- string result = packer.unpack_and_format(show_field_names);
+ std::string result = packer.unpack_and_format(show_field_names);
if (!packer.end_unpack()) {
- return string();
+ return std::string();
}
return result;
}
@@ -179,7 +169,7 @@ format_data(const vector_uchar &packed_data, bool show_field_names) {
* the corresponding packed data. Returns empty string if there is an error.
*/
vector_uchar DCField::
-parse_string(const string &formatted_string) {
+parse_string(const std::string &formatted_string) {
DCPacker packer;
packer.begin_pack(this);
if (!packer.parse_and_pack(formatted_string)) {
@@ -212,254 +202,6 @@ validate_ranges(const vector_uchar &packed_data) const {
return (packer.get_num_unpacked_bytes() == packed_data.size());
}
-#ifdef HAVE_PYTHON
-/**
- * Packs the Python arguments from the indicated tuple into the packer.
- * Returns true on success, false on failure.
- *
- * It is assumed that the packer is currently positioned on this field.
- */
-bool DCField::
-pack_args(DCPacker &packer, PyObject *sequence) const {
- nassertr(!packer.had_error(), false);
- nassertr(packer.get_current_field() == this, false);
-
- packer.pack_object(sequence);
- if (!packer.had_error()) {
- /*
- cerr << "pack " << get_name() << get_pystr(sequence) << "\n";
- */
-
- return true;
- }
-
- if (!Notify::ptr()->has_assert_failed()) {
- std::ostringstream strm;
- PyObject *exc_type = PyExc_Exception;
-
- if (as_parameter() != nullptr) {
- // If it's a parameter-type field, the value may or may not be a
- // sequence.
- if (packer.had_pack_error()) {
- strm << "Incorrect arguments to field: " << get_name()
- << " = " << get_pystr(sequence);
- exc_type = PyExc_TypeError;
- } else {
- strm << "Value out of range on field: " << get_name()
- << " = " << get_pystr(sequence);
- exc_type = PyExc_ValueError;
- }
-
- } else {
- // If it's a molecular or atomic field, the value should be a sequence.
- PyObject *tuple = PySequence_Tuple(sequence);
- if (tuple == nullptr) {
- strm << "Value for " << get_name() << " not a sequence: " \
- << get_pystr(sequence);
- exc_type = PyExc_TypeError;
-
- } else {
- if (packer.had_pack_error()) {
- strm << "Incorrect arguments to field: " << get_name()
- << get_pystr(sequence);
- exc_type = PyExc_TypeError;
- } else {
- strm << "Value out of range on field: " << get_name()
- << get_pystr(sequence);
- exc_type = PyExc_ValueError;
- }
-
- Py_DECREF(tuple);
- }
- }
-
- string message = strm.str();
- PyErr_SetString(exc_type, message.c_str());
- }
- return false;
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Unpacks the values from the packer, beginning at the current point in the
- * unpack_buffer, into a Python tuple and returns the tuple.
- *
- * It is assumed that the packer is currently positioned on this field.
- */
-PyObject *DCField::
-unpack_args(DCPacker &packer) const {
- nassertr(!packer.had_error(), nullptr);
- nassertr(packer.get_current_field() == this, nullptr);
-
- size_t start_byte = packer.get_num_unpacked_bytes();
- PyObject *object = packer.unpack_object();
-
- if (!packer.had_error()) {
- // Successfully unpacked.
- /*
- cerr << "recv " << get_name() << get_pystr(object) << "\n";
- */
-
- return object;
- }
-
- if (!Notify::ptr()->has_assert_failed()) {
- std::ostringstream strm;
- PyObject *exc_type = PyExc_Exception;
-
- if (packer.had_pack_error()) {
- strm << "Data error unpacking field ";
- output(strm, true);
- size_t length = packer.get_unpack_length() - start_byte;
- strm << "\nGot data (" << (int)length << " bytes):\n";
- Datagram dg(packer.get_unpack_data() + start_byte, length);
- dg.dump_hex(strm);
- size_t error_byte = packer.get_num_unpacked_bytes() - start_byte;
- strm << "Error detected on byte " << error_byte
- << " (" << std::hex << error_byte << std::dec << " hex)";
-
- exc_type = PyExc_RuntimeError;
- } else {
- strm << "Value outside specified range when unpacking field "
- << get_name() << ": " << get_pystr(object);
- exc_type = PyExc_ValueError;
- }
-
- string message = strm.str();
- PyErr_SetString(exc_type, message.c_str());
- }
-
- Py_XDECREF(object);
- return nullptr;
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Extracts the update message out of the datagram and applies it to the
- * indicated object by calling the appropriate method.
- */
-void DCField::
-receive_update(DCPacker &packer, PyObject *distobj) const {
- if (as_parameter() != nullptr) {
- // If it's a parameter-type field, just store a new value on the object.
- PyObject *value = unpack_args(packer);
- if (value != nullptr) {
- PyObject_SetAttrString(distobj, (char *)_name.c_str(), value);
- }
- Py_DECREF(value);
-
- } else {
- // Otherwise, it must be an atomic or molecular field, so call the
- // corresponding method.
-
- if (!PyObject_HasAttrString(distobj, (char *)_name.c_str())) {
- // If there's no Python method to receive this message, don't bother
- // unpacking it to a Python tuple--just skip past the message.
- packer.unpack_skip();
-
- } else {
- // Otherwise, get a Python tuple from the args and call the Python
- // method.
- PyObject *args = unpack_args(packer);
-
- if (args != nullptr) {
- PyObject *func = PyObject_GetAttrString(distobj, (char *)_name.c_str());
- nassertv(func != nullptr);
-
- PyObject *result;
- {
-#ifdef WITHIN_PANDA
- PStatTimer timer(((DCField *)this)->_field_update_pcollector);
-#endif
- result = PyObject_CallObject(func, args);
- }
- Py_XDECREF(result);
- Py_DECREF(func);
- Py_DECREF(args);
- }
- }
- }
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the client.
- */
-Datagram DCField::
-client_format_update(DOID_TYPE do_id, PyObject *args) const {
- DCPacker packer;
-
- packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
- packer.raw_pack_uint32(do_id);
- packer.raw_pack_uint16(_number);
-
- packer.begin_pack(this);
- pack_args(packer, args);
- if (!packer.end_pack()) {
- return Datagram();
- }
-
- return Datagram(packer.get_data(), packer.get_length());
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the AI.
- */
-Datagram DCField::
-ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
- DCPacker packer;
-
- packer.raw_pack_uint8(1);
- packer.RAW_PACK_CHANNEL(to_id);
- packer.RAW_PACK_CHANNEL(from_id);
- packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
- packer.raw_pack_uint32(do_id);
- packer.raw_pack_uint16(_number);
-
- packer.begin_pack(this);
- pack_args(packer, args);
- if (!packer.end_pack()) {
- return Datagram();
- }
-
- return Datagram(packer.get_data(), packer.get_length());
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update,
- * with the msg type, for the indicated distributed object from the AI.
- */
-Datagram DCField::
-ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
- DCPacker packer;
-
- packer.raw_pack_uint8(1);
- packer.RAW_PACK_CHANNEL(to_id);
- packer.RAW_PACK_CHANNEL(from_id);
- packer.raw_pack_uint16(msg_type);
- packer.raw_pack_uint32(do_id);
- packer.raw_pack_uint16(_number);
-
- packer.begin_pack(this);
- pack_args(packer, args);
- if (!packer.end_pack()) {
- return Datagram();
- }
-
- return Datagram(packer.get_data(), packer.get_length());
-}
-#endif // HAVE_PYTHON
-
-
/**
* Accumulates the properties of this field into the hash.
*/
@@ -499,62 +241,13 @@ pack_default_value(DCPackData &pack_data, bool &) const {
* Sets the name of this field.
*/
void DCField::
-set_name(const string &name) {
+set_name(const std::string &name) {
DCPackerInterface::set_name(name);
if (_dclass != nullptr) {
_dclass->_dc_file->mark_inherited_fields_stale();
}
}
-#ifdef HAVE_PYTHON
-/**
- * Returns the string representation of the indicated Python object.
- */
-string DCField::
-get_pystr(PyObject *value) {
- if (value == nullptr) {
- return "(null)";
- }
-
- PyObject *str = PyObject_Str(value);
- if (str != nullptr) {
-#if PY_MAJOR_VERSION >= 3
- string result = PyUnicode_AsUTF8(str);
-#else
- string result = PyString_AsString(str);
-#endif
- Py_DECREF(str);
- return result;
- }
-
- PyObject *repr = PyObject_Repr(value);
- if (repr != nullptr) {
-#if PY_MAJOR_VERSION >= 3
- string result = PyUnicode_AsUTF8(repr);
-#else
- string result = PyString_AsString(repr);
-#endif
- Py_DECREF(repr);
- return result;
- }
-
- if (value->ob_type != nullptr) {
- PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
- if (typestr != nullptr) {
-#if PY_MAJOR_VERSION >= 3
- string result = PyUnicode_AsUTF8(typestr);
-#else
- string result = PyString_AsString(typestr);
-#endif
- Py_DECREF(typestr);
- return result;
- }
- }
-
- return "(invalid object)";
-}
-#endif // HAVE_PYTHON
-
/**
* Recomputes the default value of the field by repacking it.
*/
diff --git a/direct/src/dcparser/dcField.h b/direct/src/dcparser/dcField.h
index 96b7da4487..1c6fa8ce5e 100644
--- a/direct/src/dcparser/dcField.h
+++ b/direct/src/dcparser/dcField.h
@@ -17,10 +17,11 @@
#include "dcbase.h"
#include "dcPackerInterface.h"
#include "dcKeywordList.h"
-#include "dcPython.h"
#ifdef WITHIN_PANDA
#include "pStatCollector.h"
+#include "extension.h"
+#include "datagram.h"
#endif
class DCPacker;
@@ -76,18 +77,17 @@ PUBLISHED:
INLINE void output(std::ostream &out) const;
INLINE void write(std::ostream &out, int indent_level) const;
-#ifdef HAVE_PYTHON
- bool pack_args(DCPacker &packer, PyObject *sequence) const;
- PyObject *unpack_args(DCPacker &packer) const;
+ EXTENSION(bool pack_args(DCPacker &packer, PyObject *sequence) const);
+ EXTENSION(PyObject *unpack_args(DCPacker &packer) const);
- void receive_update(DCPacker &packer, PyObject *distobj) const;
+ EXTENSION(void receive_update(DCPacker &packer, PyObject *distobj) const);
- Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const;
- Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
- PyObject *args) const;
- Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
- int msg_type, PyObject *args) const;
-#endif
+ EXTENSION(Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const);
+ EXTENSION(Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id,
+ CHANNEL_TYPE from_id, PyObject *args) const);
+ EXTENSION(Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id,
+ CHANNEL_TYPE from_id, int msg_type,
+ PyObject *args) const);
public:
virtual void output(std::ostream &out, bool brief) const=0;
@@ -100,10 +100,6 @@ public:
INLINE void set_class(DCClass *dclass);
INLINE void set_default_value(vector_uchar default_value);
-#ifdef HAVE_PYTHON
- static std::string get_pystr(PyObject *value);
-#endif
-
protected:
void refresh_default_value();
@@ -119,6 +115,8 @@ private:
#ifdef WITHIN_PANDA
PStatCollector _field_update_pcollector;
+
+ friend class Extension;
#endif
};
diff --git a/direct/src/dcparser/dcField_ext.cxx b/direct/src/dcparser/dcField_ext.cxx
new file mode 100644
index 0000000000..3d229b2906
--- /dev/null
+++ b/direct/src/dcparser/dcField_ext.cxx
@@ -0,0 +1,305 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University. All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license. You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcField_ext.cxx
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#include "dcField_ext.h"
+#include "dcPacker_ext.h"
+#include "dcmsgtypes.h"
+
+#include "datagram.h"
+#include "pStatTimer.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Packs the Python arguments from the indicated tuple into the packer.
+ * Returns true on success, false on failure.
+ *
+ * It is assumed that the packer is currently positioned on this field.
+ */
+bool Extension::
+pack_args(DCPacker &packer, PyObject *sequence) const {
+ nassertr(!packer.had_error(), false);
+ nassertr(packer.get_current_field() == _this, false);
+
+ invoke_extension(&packer).pack_object(sequence);
+ if (!packer.had_error()) {
+ /*
+ cerr << "pack " << _this->get_name() << get_pystr(sequence) << "\n";
+ */
+
+ return true;
+ }
+
+ if (!Notify::ptr()->has_assert_failed()) {
+ std::ostringstream strm;
+ PyObject *exc_type = PyExc_Exception;
+
+ if (_this->as_parameter() != nullptr) {
+ // If it's a parameter-type field, the value may or may not be a
+ // sequence.
+ if (packer.had_pack_error()) {
+ strm << "Incorrect arguments to field: " << _this->get_name()
+ << " = " << get_pystr(sequence);
+ exc_type = PyExc_TypeError;
+ } else {
+ strm << "Value out of range on field: " << _this->get_name()
+ << " = " << get_pystr(sequence);
+ exc_type = PyExc_ValueError;
+ }
+
+ } else {
+ // If it's a molecular or atomic field, the value should be a sequence.
+ PyObject *tuple = PySequence_Tuple(sequence);
+ if (tuple == nullptr) {
+ strm << "Value for " << _this->get_name() << " not a sequence: " \
+ << get_pystr(sequence);
+ exc_type = PyExc_TypeError;
+
+ } else {
+ if (packer.had_pack_error()) {
+ strm << "Incorrect arguments to field: " << _this->get_name()
+ << get_pystr(sequence);
+ exc_type = PyExc_TypeError;
+ } else {
+ strm << "Value out of range on field: " << _this->get_name()
+ << get_pystr(sequence);
+ exc_type = PyExc_ValueError;
+ }
+
+ Py_DECREF(tuple);
+ }
+ }
+
+ std::string message = strm.str();
+ PyErr_SetString(exc_type, message.c_str());
+ }
+ return false;
+}
+
+/**
+ * Unpacks the values from the packer, beginning at the current point in the
+ * unpack_buffer, into a Python tuple and returns the tuple.
+ *
+ * It is assumed that the packer is currently positioned on this field.
+ */
+PyObject *Extension::
+unpack_args(DCPacker &packer) const {
+ nassertr(!packer.had_error(), nullptr);
+ nassertr(packer.get_current_field() == _this, nullptr);
+
+ size_t start_byte = packer.get_num_unpacked_bytes();
+ PyObject *object = invoke_extension(&packer).unpack_object();
+
+ if (!packer.had_error()) {
+ // Successfully unpacked.
+ /*
+ cerr << "recv " << _this->get_name() << get_pystr(object) << "\n";
+ */
+
+ return object;
+ }
+
+ if (!Notify::ptr()->has_assert_failed()) {
+ std::ostringstream strm;
+ PyObject *exc_type = PyExc_Exception;
+
+ if (packer.had_pack_error()) {
+ strm << "Data error unpacking field ";
+ _this->output(strm, true);
+ size_t length = packer.get_unpack_length() - start_byte;
+ strm << "\nGot data (" << (int)length << " bytes):\n";
+ Datagram dg(packer.get_unpack_data() + start_byte, length);
+ dg.dump_hex(strm);
+ size_t error_byte = packer.get_num_unpacked_bytes() - start_byte;
+ strm << "Error detected on byte " << error_byte
+ << " (" << std::hex << error_byte << std::dec << " hex)";
+
+ exc_type = PyExc_RuntimeError;
+ } else {
+ strm << "Value outside specified range when unpacking field "
+ << _this->get_name() << ": " << get_pystr(object);
+ exc_type = PyExc_ValueError;
+ }
+
+ std::string message = strm.str();
+ PyErr_SetString(exc_type, message.c_str());
+ }
+
+ Py_XDECREF(object);
+ return nullptr;
+}
+
+/**
+ * Extracts the update message out of the datagram and applies it to the
+ * indicated object by calling the appropriate method.
+ */
+void Extension::
+receive_update(DCPacker &packer, PyObject *distobj) const {
+ if (_this->as_parameter() != nullptr) {
+ // If it's a parameter-type field, just store a new value on the object.
+ PyObject *value = unpack_args(packer);
+ if (value != nullptr) {
+ PyObject_SetAttrString(distobj, (char *)_this->_name.c_str(), value);
+ }
+ Py_DECREF(value);
+
+ } else {
+ // Otherwise, it must be an atomic or molecular field, so call the
+ // corresponding method.
+
+ if (!PyObject_HasAttrString(distobj, (char *)_this->_name.c_str())) {
+ // If there's no Python method to receive this message, don't bother
+ // unpacking it to a Python tuple--just skip past the message.
+ packer.unpack_skip();
+
+ } else {
+ // Otherwise, get a Python tuple from the args and call the Python
+ // method.
+ PyObject *args = unpack_args(packer);
+
+ if (args != nullptr) {
+ PyObject *func = PyObject_GetAttrString(distobj, (char *)_this->_name.c_str());
+ nassertv(func != nullptr);
+
+ PyObject *result;
+ {
+#ifdef WITHIN_PANDA
+ PStatTimer timer(_this->_field_update_pcollector);
+#endif
+ result = PyObject_CallObject(func, args);
+ }
+ Py_XDECREF(result);
+ Py_DECREF(func);
+ Py_DECREF(args);
+ }
+ }
+ }
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the client.
+ */
+Datagram Extension::
+client_format_update(DOID_TYPE do_id, PyObject *args) const {
+ DCPacker packer;
+
+ packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
+ packer.raw_pack_uint32(do_id);
+ packer.raw_pack_uint16(_this->_number);
+
+ packer.begin_pack(_this);
+ pack_args(packer, args);
+ if (!packer.end_pack()) {
+ return Datagram();
+ }
+
+ return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the AI.
+ */
+Datagram Extension::
+ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
+ DCPacker packer;
+
+ packer.raw_pack_uint8(1);
+ packer.RAW_PACK_CHANNEL(to_id);
+ packer.RAW_PACK_CHANNEL(from_id);
+ packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
+ packer.raw_pack_uint32(do_id);
+ packer.raw_pack_uint16(_this->_number);
+
+ packer.begin_pack(_this);
+ pack_args(packer, args);
+ if (!packer.end_pack()) {
+ return Datagram();
+ }
+
+ return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update,
+ * with the msg type, for the indicated distributed object from the AI.
+ */
+Datagram Extension::
+ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
+ DCPacker packer;
+
+ packer.raw_pack_uint8(1);
+ packer.RAW_PACK_CHANNEL(to_id);
+ packer.RAW_PACK_CHANNEL(from_id);
+ packer.raw_pack_uint16(msg_type);
+ packer.raw_pack_uint32(do_id);
+ packer.raw_pack_uint16(_this->_number);
+
+ packer.begin_pack(_this);
+ pack_args(packer, args);
+ if (!packer.end_pack()) {
+ return Datagram();
+ }
+
+ return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Returns the string representation of the indicated Python object.
+ */
+std::string Extension::
+get_pystr(PyObject *value) {
+ if (value == nullptr) {
+ return "(null)";
+ }
+
+ PyObject *str = PyObject_Str(value);
+ if (str != nullptr) {
+#if PY_MAJOR_VERSION >= 3
+ std::string result = PyUnicode_AsUTF8(str);
+#else
+ std::string result = PyString_AsString(str);
+#endif
+ Py_DECREF(str);
+ return result;
+ }
+
+ PyObject *repr = PyObject_Repr(value);
+ if (repr != nullptr) {
+#if PY_MAJOR_VERSION >= 3
+ std::string result = PyUnicode_AsUTF8(repr);
+#else
+ std::string result = PyString_AsString(repr);
+#endif
+ Py_DECREF(repr);
+ return result;
+ }
+
+ if (value->ob_type != nullptr) {
+ PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
+ if (typestr != nullptr) {
+#if PY_MAJOR_VERSION >= 3
+ std::string result = PyUnicode_AsUTF8(typestr);
+#else
+ std::string result = PyString_AsString(typestr);
+#endif
+ Py_DECREF(typestr);
+ return result;
+ }
+ }
+
+ return "(invalid object)";
+}
+
+#endif // HAVE_PYTHON
diff --git a/direct/src/dcparser/dcField_ext.h b/direct/src/dcparser/dcField_ext.h
new file mode 100644
index 0000000000..45350b4596
--- /dev/null
+++ b/direct/src/dcparser/dcField_ext.h
@@ -0,0 +1,48 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University. All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license. You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcField_ext.h
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#ifndef DCFIELD_EXT_H
+#define DCFIELD_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "dcField.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for DCField, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension : public ExtensionBase {
+public:
+ bool pack_args(DCPacker &packer, PyObject *sequence) const;
+ PyObject *unpack_args(DCPacker &packer) const;
+
+ void receive_update(DCPacker &packer, PyObject *distobj) const;
+
+ Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const;
+ Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+ PyObject *args) const;
+ Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+ int msg_type, PyObject *args) const;
+
+ static std::string get_pystr(PyObject *value);
+};
+
+#endif // HAVE_PYTHON
+
+#endif // DCFIELD_EXT_H
diff --git a/direct/src/dcparser/dcPacker.cxx b/direct/src/dcparser/dcPacker.cxx
index b6dbd60855..ce67a570bf 100644
--- a/direct/src/dcparser/dcPacker.cxx
+++ b/direct/src/dcparser/dcPacker.cxx
@@ -19,10 +19,6 @@
#include "dcSwitchParameter.h"
#include "dcClass.h"
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
using std::istream;
using std::istringstream;
using std::ostream;
@@ -622,335 +618,6 @@ unpack_skip() {
}
}
-#ifdef HAVE_PYTHON
-/**
- * Packs the Python object of whatever type into the packer. Each numeric
- * object and string object maps to the corresponding pack_value() call; a
- * tuple or sequence maps to a push() followed by all of the tuple's contents
- * followed by a pop().
- */
-void DCPacker::
-pack_object(PyObject *object) {
- nassertv(_mode == M_pack || _mode == M_repack);
- DCPackType pack_type = get_pack_type();
-
- // had to add this for basic 64 and unsigned data to get packed right .. Not
- // sure if we can just do the rest this way..
-
- switch(pack_type)
- {
- case PT_int64:
- if(PyLong_Check(object))
- {
- pack_int64(PyLong_AsLongLong(object));
- return;
- }
-#if PY_MAJOR_VERSION < 3
- else if (PyInt_Check(object))
- {
- pack_int64(PyInt_AsLong(object));
- return;
- }
-#endif
- break;
- case PT_uint64:
- if(PyLong_Check(object))
- {
- pack_uint64(PyLong_AsUnsignedLongLong(object));
- return;
- }
-#if PY_MAJOR_VERSION < 3
- else if(PyInt_Check(object))
- {
- PyObject *obj1 = PyNumber_Long(object);
- pack_int(PyLong_AsUnsignedLongLong(obj1));
- Py_DECREF(obj1);
- return;
- }
-#endif
- break;
- case PT_int:
- if(PyLong_Check(object))
- {
- pack_int(PyLong_AsLong(object));
- return;
- }
-#if PY_MAJOR_VERSION < 3
- else if (PyInt_Check(object))
- {
- pack_int(PyInt_AsLong(object));
- return;
- }
-#endif
- break;
- case PT_uint:
- if(PyLong_Check(object))
- {
- pack_uint(PyLong_AsUnsignedLong(object));
- return;
- }
-#if PY_MAJOR_VERSION < 3
- else if (PyInt_Check(object))
- {
- PyObject *obj1 = PyNumber_Long(object);
- pack_uint(PyLong_AsUnsignedLong(obj1));
- Py_DECREF(obj1);
- return;
- }
-#endif
- break;
- default:
- break;
- }
- if (PyLong_Check(object)) {
- pack_int(PyLong_AsLong(object));
-#if PY_MAJOR_VERSION < 3
- } else if (PyInt_Check(object)) {
- pack_int(PyInt_AS_LONG(object));
-#endif
- } else if (PyFloat_Check(object)) {
- pack_double(PyFloat_AS_DOUBLE(object));
- } else if (PyLong_Check(object)) {
- pack_int64(PyLong_AsLongLong(object));
-#if PY_MAJOR_VERSION >= 3
- } else if (PyUnicode_Check(object)) {
- const char *buffer;
- Py_ssize_t length;
- buffer = PyUnicode_AsUTF8AndSize(object, &length);
- if (buffer) {
- pack_string(string(buffer, length));
- }
- } else if (PyBytes_Check(object)) {
- const unsigned char *buffer;
- Py_ssize_t length;
- PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
- if (buffer) {
- pack_blob(vector_uchar(buffer, buffer + length));
- }
-#else
- } else if (PyString_Check(object) || PyUnicode_Check(object)) {
- char *buffer;
- Py_ssize_t length;
- PyString_AsStringAndSize(object, &buffer, &length);
- if (buffer) {
- pack_string(string(buffer, length));
- }
-#endif
- } else {
- // For some reason, PySequence_Check() is incorrectly reporting that a
- // class instance is a sequence, even if it doesn't provide __len__, so we
- // double-check by testing for __len__ explicitly.
- bool is_sequence =
- (PySequence_Check(object) != 0) &&
- (PyObject_HasAttrString(object, "__len__") != 0);
- bool is_instance = false;
-
- const DCClass *dclass = nullptr;
- const DCPackerInterface *current_field = get_current_field();
- if (current_field != nullptr) {
- const DCClassParameter *class_param = get_current_field()->as_class_parameter();
- if (class_param != nullptr) {
- dclass = class_param->get_class();
-
- if (dclass->has_class_def()) {
- PyObject *class_def = dclass->get_class_def();
- is_instance = (PyObject_IsInstance(object, dclass->get_class_def()) != 0);
- Py_DECREF(class_def);
- }
- }
- }
-
- // If dclass is not NULL, the packer is expecting a class object. There
- // are then two cases: (1) the user has supplied a matching class object,
- // or (2) the user has supplied a sequence object. Unfortunately, it may
- // be difficult to differentiate these two cases, since a class object may
- // also be a sequence object.
-
- // The rule to differentiate them is:
-
- // (1) If the supplied class object is an instance of the expected class
- // object, it is considered to be a class object.
-
- // (2) Otherwise, if the supplied class object has a __len__() method
- // (i.e. PySequence_Check() returns true), then it is considered to be a
- // sequence.
-
- // (3) Otherwise, it is considered to be a class object.
-
- if (dclass != nullptr && (is_instance || !is_sequence)) {
- // The supplied object is either an instance of the expected class
- // object, or it is not a sequence--this is case (1) or (3).
- pack_class_object(dclass, object);
- } else if (is_sequence) {
- // The supplied object is not an instance of the expected class object,
- // but it is a sequence. This is case (2).
- push();
- int size = PySequence_Size(object);
- for (int i = 0; i < size; ++i) {
- PyObject *element = PySequence_GetItem(object, i);
- if (element != nullptr) {
- pack_object(element);
- Py_DECREF(element);
- } else {
- std::cerr << "Unable to extract item " << i << " from sequence.\n";
- }
- }
- pop();
- } else {
- // The supplied object is not a sequence, and we weren't expecting a
- // class parameter. This is none of the above, an error.
- ostringstream strm;
- strm << "Don't know how to pack object: "
- << DCField::get_pystr(object);
- nassert_raise(strm.str());
- _pack_error = true;
- }
- }
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Unpacks a Python object of the appropriate type from the stream for the
- * current field. This may be an integer or a string for a simple field
- * object; if the current field represents a list of fields it will be a
- * tuple.
- */
-PyObject *DCPacker::
-unpack_object() {
- PyObject *object = nullptr;
-
- DCPackType pack_type = get_pack_type();
-
- switch (pack_type) {
- case PT_invalid:
- object = Py_None;
- Py_INCREF(object);
- unpack_skip();
- break;
-
- case PT_double:
- {
- double value = unpack_double();
- object = PyFloat_FromDouble(value);
- }
- break;
-
- case PT_int:
- {
- int value = unpack_int();
-#if PY_MAJOR_VERSION >= 3
- object = PyLong_FromLong(value);
-#else
- object = PyInt_FromLong(value);
-#endif
- }
- break;
-
- case PT_uint:
- {
- unsigned int value = unpack_uint();
-#if PY_MAJOR_VERSION >= 3
- object = PyLong_FromLong(value);
-#else
- if (value & 0x80000000) {
- object = PyLong_FromUnsignedLong(value);
- } else {
- object = PyInt_FromLong(value);
- }
-#endif
- }
- break;
-
- case PT_int64:
- {
- int64_t value = unpack_int64();
- object = PyLong_FromLongLong(value);
- }
- break;
-
- case PT_uint64:
- {
- uint64_t value = unpack_uint64();
- object = PyLong_FromUnsignedLongLong(value);
- }
- break;
-
- case PT_blob:
-#if PY_MAJOR_VERSION >= 3
- {
- string str;
- unpack_string(str);
- object = PyBytes_FromStringAndSize(str.data(), str.size());
- }
- break;
-#endif
- // On Python 2, fall through to below.
-
- case PT_string:
- {
- string str;
- unpack_string(str);
-#if PY_MAJOR_VERSION >= 3
- object = PyUnicode_FromStringAndSize(str.data(), str.size());
-#else
- object = PyString_FromStringAndSize(str.data(), str.size());
-#endif
- }
- break;
-
- case PT_class:
- {
- const DCClassParameter *class_param = get_current_field()->as_class_parameter();
- if (class_param != nullptr) {
- const DCClass *dclass = class_param->get_class();
- if (dclass->has_class_def()) {
- // If we know what kind of class object this is and it has a valid
- // constructor, create the class object instead of just a tuple.
- object = unpack_class_object(dclass);
- if (object == nullptr) {
- std::cerr << "Unable to construct object of class "
- << dclass->get_name() << "\n";
- } else {
- break;
- }
- }
- }
- }
- // Fall through (if no constructor)
-
- // If we don't know what kind of class object it is, or it doesn't have a
- // constructor, fall through and make a tuple.
- default:
- {
- // First, build up a list from the nested objects.
- object = PyList_New(0);
-
- push();
- while (more_nested_fields()) {
- PyObject *element = unpack_object();
- PyList_Append(object, element);
- Py_DECREF(element);
- }
- pop();
-
- if (pack_type != PT_array) {
- // For these other kinds of objects, we'll convert the list into a
- // tuple.
- PyObject *tuple = PyList_AsTuple(object);
- Py_DECREF(object);
- object = tuple;
- }
- }
- break;
- }
-
- nassertr(object != nullptr, nullptr);
- return object;
-}
-#endif // HAVE_PYTHON
-
-
/**
* Parses an object's value according to the DC file syntax (e.g. as a
* default value string) and packs it. Returns true on success, false on a
@@ -1206,178 +873,3 @@ clear_stack() {
_stack = next;
}
}
-
-#ifdef HAVE_PYTHON
-/**
- * Given that the current element is a ClassParameter for a Python class
- * object, try to extract the appropriate values from the class object and
- * pack in.
- */
-void DCPacker::
-pack_class_object(const DCClass *dclass, PyObject *object) {
- push();
- while (more_nested_fields() && !_pack_error) {
- const DCField *field = get_current_field()->as_field();
- nassertv(field != nullptr);
- get_class_element(dclass, object, field);
- }
- pop();
-}
-#endif // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Given that the current element is a ClassParameter for a Python class for
- * which we have a valid constructor, unpack it and fill in its values.
- */
-PyObject *DCPacker::
-unpack_class_object(const DCClass *dclass) {
- PyObject *class_def = dclass->get_class_def();
- nassertr(class_def != nullptr, nullptr);
-
- PyObject *object = nullptr;
-
- if (!dclass->has_constructor()) {
- // If the class uses a default constructor, go ahead and create the Python
- // object for it now.
- object = PyObject_CallObject(class_def, nullptr);
- if (object == nullptr) {
- return nullptr;
- }
- }
-
- push();
- if (object == nullptr && more_nested_fields()) {
- // The first nested field will be the constructor.
- const DCField *field = get_current_field()->as_field();
- nassertr(field != nullptr, object);
- nassertr(field == dclass->get_constructor(), object);
-
- set_class_element(class_def, object, field);
-
- // By now, the object should have been constructed.
- if (object == nullptr) {
- return nullptr;
- }
- }
- while (more_nested_fields()) {
- const DCField *field = get_current_field()->as_field();
- nassertr(field != nullptr, object);
-
- set_class_element(class_def, object, field);
- }
- pop();
-
- return object;
-}
-#endif // HAVE_PYTHON
-
-
-#ifdef HAVE_PYTHON
-/**
- * Unpacks the current element and stuffs it on the Python class object in
- * whatever way is appropriate.
- */
-void DCPacker::
-set_class_element(PyObject *class_def, PyObject *&object,
- const DCField *field) {
- string field_name = field->get_name();
- DCPackType pack_type = get_pack_type();
-
- if (field_name.empty()) {
- switch (pack_type) {
- case PT_class:
- case PT_switch:
- // If the field has no name, but it is one of these container objects,
- // we want to unpack its nested objects directly into the class.
- push();
- while (more_nested_fields()) {
- const DCField *field = get_current_field()->as_field();
- nassertv(field != nullptr);
- nassertv(object != nullptr);
- set_class_element(class_def, object, field);
- }
- pop();
- break;
-
- default:
- // Otherwise, we just skip over the field.
- unpack_skip();
- }
-
- } else {
- // If the field does have a name, we will want to store it on the class,
- // either by calling a method (for a PT_field pack_type) or by setting a
- // value (for any other kind of pack_type).
-
- PyObject *element = unpack_object();
-
- if (pack_type == PT_field) {
- if (object == nullptr) {
- // If the object hasn't been constructed yet, assume this is the
- // constructor.
- object = PyObject_CallObject(class_def, element);
-
- } else {
- if (PyObject_HasAttrString(object, (char *)field_name.c_str())) {
- PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
- if (func != nullptr) {
- PyObject *result = PyObject_CallObject(func, element);
- Py_XDECREF(result);
- Py_DECREF(func);
- }
- }
- }
-
- } else {
- nassertv(object != nullptr);
- PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
- }
-
- Py_DECREF(element);
- }
-}
-#endif // HAVE_PYTHON
-
-
-#ifdef HAVE_PYTHON
-/**
- * Gets the current element from the Python object and packs it.
- */
-void DCPacker::
-get_class_element(const DCClass *dclass, PyObject *object,
- const DCField *field) {
- string field_name = field->get_name();
- DCPackType pack_type = get_pack_type();
-
- if (field_name.empty()) {
- switch (pack_type) {
- case PT_class:
- case PT_switch:
- // If the field has no name, but it is one of these container objects,
- // we want to get its nested objects directly from the class.
- push();
- while (more_nested_fields() && !_pack_error) {
- const DCField *field = get_current_field()->as_field();
- nassertv(field != nullptr);
- get_class_element(dclass, object, field);
- }
- pop();
- break;
-
- default:
- // Otherwise, we just pack the default value.
- pack_default_value();
- }
-
- } else {
- // If the field does have a name, we will want to get it from the class
- // and pack it. It just so happens that there's already a method that
- // does this on DCClass.
-
- if (!dclass->pack_required_field(*this, object, field)) {
- _pack_error = true;
- }
- }
-}
-#endif // HAVE_PYTHON
diff --git a/direct/src/dcparser/dcPacker.h b/direct/src/dcparser/dcPacker.h
index dce28dbc30..32f7e57de4 100644
--- a/direct/src/dcparser/dcPacker.h
+++ b/direct/src/dcparser/dcPacker.h
@@ -19,7 +19,10 @@
#include "dcSubatomicType.h"
#include "dcPackData.h"
#include "dcPackerCatalog.h"
-#include "dcPython.h"
+
+#ifdef WITHIN_PANDA
+#include "extension.h"
+#endif
class DCClass;
class DCSwitchParameter;
@@ -104,10 +107,8 @@ public:
PUBLISHED:
-#ifdef HAVE_PYTHON
- void pack_object(PyObject *object);
- PyObject *unpack_object();
-#endif
+ EXTENSION(void pack_object(PyObject *object));
+ EXTENSION(PyObject *unpack_object());
bool parse_and_pack(const std::string &formatted_object);
bool parse_and_pack(std::istream &in);
@@ -195,14 +196,12 @@ private:
void clear();
void clear_stack();
-#ifdef HAVE_PYTHON
- void pack_class_object(const DCClass *dclass, PyObject *object);
- PyObject *unpack_class_object(const DCClass *dclass);
- void set_class_element(PyObject *class_def, PyObject *&object,
- const DCField *field);
- void get_class_element(const DCClass *dclass, PyObject *object,
- const DCField *field);
-#endif
+ EXTENSION(void pack_class_object(const DCClass *dclass, PyObject *object));
+ EXTENSION(PyObject *unpack_class_object(const DCClass *dclass));
+ EXTENSION(void set_class_element(PyObject *class_def, PyObject *&object,
+ const DCField *field));
+ EXTENSION(void get_class_element(const DCClass *dclass, PyObject *object,
+ const DCField *field));
private:
enum Mode {
@@ -223,7 +222,7 @@ private:
const DCPackerCatalog *_catalog;
const DCPackerCatalog::LiveCatalog *_live_catalog;
- class StackElement {
+ class EXPCL_DIRECT_DCPARSER StackElement {
public:
// As an optimization, we implement operator new and delete here to
// minimize allocation overhead during push() and pop().
@@ -258,6 +257,10 @@ private:
bool _parse_error;
bool _pack_error;
bool _range_error;
+
+#ifdef WITHIN_PANDA
+ friend class Extension;
+#endif
};
#include "dcPacker.I"
diff --git a/direct/src/dcparser/dcPacker_ext.cxx b/direct/src/dcparser/dcPacker_ext.cxx
new file mode 100644
index 0000000000..8e7eb1ec5f
--- /dev/null
+++ b/direct/src/dcparser/dcPacker_ext.cxx
@@ -0,0 +1,508 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University. All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license. You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcPacker_ext.cxx
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#include "dcPacker_ext.h"
+#include "dcClass_ext.h"
+#include "dcField_ext.h"
+
+#include "dcClassParameter.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Packs the Python object of whatever type into the packer. Each numeric
+ * object and string object maps to the corresponding pack_value() call; a
+ * tuple or sequence maps to a push() followed by all of the tuple's contents
+ * followed by a pop().
+ */
+void Extension::
+pack_object(PyObject *object) {
+ nassertv(_this->_mode == DCPacker::Mode::M_pack ||
+ _this->_mode == DCPacker::Mode::M_repack);
+ DCPackType pack_type = _this->get_pack_type();
+
+ // had to add this for basic 64 and unsigned data to get packed right .. Not
+ // sure if we can just do the rest this way..
+
+ switch(pack_type) {
+ case PT_int64:
+ if (PyLong_Check(object)) {
+ _this->pack_int64(PyLong_AsLongLong(object));
+ return;
+ }
+#if PY_MAJOR_VERSION < 3
+ else if (PyInt_Check(object)) {
+ _this->pack_int64(PyInt_AsLong(object));
+ return;
+ }
+#endif
+ break;
+
+ case PT_uint64:
+ if (PyLong_Check(object)) {
+ _this->pack_uint64(PyLong_AsUnsignedLongLong(object));
+ return;
+ }
+#if PY_MAJOR_VERSION < 3
+ else if (PyInt_Check(object)) {
+ PyObject *obj1 = PyNumber_Long(object);
+ _this->pack_int(PyLong_AsUnsignedLongLong(obj1));
+ Py_DECREF(obj1);
+ return;
+ }
+#endif
+ break;
+
+ case PT_int:
+ if (PyLong_Check(object)) {
+ _this->pack_int(PyLong_AsLong(object));
+ return;
+ }
+#if PY_MAJOR_VERSION < 3
+ else if (PyInt_Check(object)) {
+ _this->pack_int(PyInt_AsLong(object));
+ return;
+ }
+#endif
+ break;
+
+ case PT_uint:
+ if (PyLong_Check(object)) {
+ _this->pack_uint(PyLong_AsUnsignedLong(object));
+ return;
+ }
+#if PY_MAJOR_VERSION < 3
+ else if (PyInt_Check(object)) {
+ PyObject *obj1 = PyNumber_Long(object);
+ _this->pack_uint(PyLong_AsUnsignedLong(obj1));
+ Py_DECREF(obj1);
+ return;
+ }
+#endif
+ break;
+
+ default:
+ break;
+ }
+
+ if (PyLong_Check(object)) {
+ _this->pack_int(PyLong_AsLong(object));
+#if PY_MAJOR_VERSION < 3
+ } else if (PyInt_Check(object)) {
+ _this->pack_int(PyInt_AS_LONG(object));
+#endif
+ } else if (PyFloat_Check(object)) {
+ _this->pack_double(PyFloat_AS_DOUBLE(object));
+ } else if (PyLong_Check(object)) {
+ _this->pack_int64(PyLong_AsLongLong(object));
+#if PY_MAJOR_VERSION >= 3
+ } else if (PyUnicode_Check(object)) {
+ const char *buffer;
+ Py_ssize_t length;
+ buffer = PyUnicode_AsUTF8AndSize(object, &length);
+ if (buffer) {
+ _this->pack_string(std::string(buffer, length));
+ }
+ } else if (PyBytes_Check(object)) {
+ const unsigned char *buffer;
+ Py_ssize_t length;
+ PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
+ if (buffer) {
+ _this->pack_blob(vector_uchar(buffer, buffer + length));
+ }
+#else
+ } else if (PyString_Check(object) || PyUnicode_Check(object)) {
+ char *buffer;
+ Py_ssize_t length;
+ PyString_AsStringAndSize(object, &buffer, &length);
+ if (buffer) {
+ _this->pack_string(std::string(buffer, length));
+ }
+#endif
+ } else {
+ // For some reason, PySequence_Check() is incorrectly reporting that a
+ // class instance is a sequence, even if it doesn't provide __len__, so we
+ // double-check by testing for __len__ explicitly.
+ bool is_sequence =
+ (PySequence_Check(object) != 0) &&
+ (PyObject_HasAttrString(object, "__len__") != 0);
+ bool is_instance = false;
+
+ const DCClass *dclass = nullptr;
+ const DCPackerInterface *current_field = _this->get_current_field();
+ if (current_field != nullptr) {
+ const DCClassParameter *class_param = _this->get_current_field()->as_class_parameter();
+ if (class_param != nullptr) {
+ dclass = class_param->get_class();
+
+ if (invoke_extension(dclass).has_class_def()) {
+ PyObject *class_def = invoke_extension(dclass).get_class_def();
+ is_instance = (PyObject_IsInstance(object, invoke_extension(dclass).get_class_def()) != 0);
+ Py_DECREF(class_def);
+ }
+ }
+ }
+
+ // If dclass is not NULL, the packer is expecting a class object. There
+ // are then two cases: (1) the user has supplied a matching class object,
+ // or (2) the user has supplied a sequence object. Unfortunately, it may
+ // be difficult to differentiate these two cases, since a class object may
+ // also be a sequence object.
+
+ // The rule to differentiate them is:
+
+ // (1) If the supplied class object is an instance of the expected class
+ // object, it is considered to be a class object.
+
+ // (2) Otherwise, if the supplied class object has a __len__() method
+ // (i.e. PySequence_Check() returns true), then it is considered to be a
+ // sequence.
+
+ // (3) Otherwise, it is considered to be a class object.
+
+ if (dclass != nullptr && (is_instance || !is_sequence)) {
+ // The supplied object is either an instance of the expected class
+ // object, or it is not a sequence--this is case (1) or (3).
+ pack_class_object(dclass, object);
+ } else if (is_sequence) {
+ // The supplied object is not an instance of the expected class object,
+ // but it is a sequence. This is case (2).
+ _this->push();
+ int size = PySequence_Size(object);
+ for (int i = 0; i < size; ++i) {
+ PyObject *element = PySequence_GetItem(object, i);
+ if (element != nullptr) {
+ pack_object(element);
+ Py_DECREF(element);
+ } else {
+ std::cerr << "Unable to extract item " << i << " from sequence.\n";
+ }
+ }
+ _this->pop();
+ } else {
+ // The supplied object is not a sequence, and we weren't expecting a
+ // class parameter. This is none of the above, an error.
+ std::ostringstream strm;
+ strm << "Don't know how to pack object: "
+ << Extension::get_pystr(object);
+ nassert_raise(strm.str());
+ _this->_pack_error = true;
+ }
+ }
+}
+
+/**
+ * Unpacks a Python object of the appropriate type from the stream for the
+ * current field. This may be an integer or a string for a simple field
+ * object; if the current field represents a list of fields it will be a
+ * tuple.
+ */
+PyObject *Extension::
+unpack_object() {
+ PyObject *object = nullptr;
+
+ DCPackType pack_type = _this->get_pack_type();
+
+ switch (pack_type) {
+ case PT_invalid:
+ object = Py_None;
+ Py_INCREF(object);
+ _this->unpack_skip();
+ break;
+
+ case PT_double:
+ {
+ double value = _this->unpack_double();
+ object = PyFloat_FromDouble(value);
+ }
+ break;
+
+ case PT_int:
+ {
+ int value = _this->unpack_int();
+#if PY_MAJOR_VERSION >= 3
+ object = PyLong_FromLong(value);
+#else
+ object = PyInt_FromLong(value);
+#endif
+ }
+ break;
+
+ case PT_uint:
+ {
+ unsigned int value = _this->unpack_uint();
+#if PY_MAJOR_VERSION >= 3
+ object = PyLong_FromLong(value);
+#else
+ if (value & 0x80000000) {
+ object = PyLong_FromUnsignedLong(value);
+ } else {
+ object = PyInt_FromLong(value);
+ }
+#endif
+ }
+ break;
+
+ case PT_int64:
+ {
+ int64_t value = _this->unpack_int64();
+ object = PyLong_FromLongLong(value);
+ }
+ break;
+
+ case PT_uint64:
+ {
+ uint64_t value = _this->unpack_uint64();
+ object = PyLong_FromUnsignedLongLong(value);
+ }
+ break;
+
+ case PT_blob:
+#if PY_MAJOR_VERSION >= 3
+ {
+ std::string str;
+ _this->unpack_string(str);
+ object = PyBytes_FromStringAndSize(str.data(), str.size());
+ }
+ break;
+#endif
+ // On Python 2, fall through to below.
+
+ case PT_string:
+ {
+ std::string str;
+ _this->unpack_string(str);
+#if PY_MAJOR_VERSION >= 3
+ object = PyUnicode_FromStringAndSize(str.data(), str.size());
+#else
+ object = PyString_FromStringAndSize(str.data(), str.size());
+#endif
+ }
+ break;
+
+ case PT_class:
+ {
+ const DCClassParameter *class_param = _this->get_current_field()->as_class_parameter();
+ if (class_param != nullptr) {
+ const DCClass *dclass = class_param->get_class();
+ if (invoke_extension(dclass).has_class_def()) {
+ // If we know what kind of class object this is and it has a valid
+ // constructor, create the class object instead of just a tuple.
+ object = unpack_class_object(dclass);
+ if (object == nullptr) {
+ std::cerr << "Unable to construct object of class "
+ << dclass->get_name() << "\n";
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ // Fall through (if no constructor)
+
+ // If we don't know what kind of class object it is, or it doesn't have a
+ // constructor, fall through and make a tuple.
+ default:
+ {
+ // First, build up a list from the nested objects.
+ object = PyList_New(0);
+
+ _this->push();
+ while (_this->more_nested_fields()) {
+ PyObject *element = unpack_object();
+ PyList_Append(object, element);
+ Py_DECREF(element);
+ }
+ _this->pop();
+
+ if (pack_type != PT_array) {
+ // For these other kinds of objects, we'll convert the list into a
+ // tuple.
+ PyObject *tuple = PyList_AsTuple(object);
+ Py_DECREF(object);
+ object = tuple;
+ }
+ }
+ break;
+ }
+
+ nassertr(object != nullptr, nullptr);
+ return object;
+}
+
+/**
+ * Given that the current element is a ClassParameter for a Python class
+ * object, try to extract the appropriate values from the class object and
+ * pack in.
+ */
+void Extension::
+pack_class_object(const DCClass *dclass, PyObject *object) {
+ _this->push();
+ while (_this->more_nested_fields() && !_this->_pack_error) {
+ const DCField *field = _this->get_current_field()->as_field();
+ nassertv(field != nullptr);
+ get_class_element(dclass, object, field);
+ }
+ _this->pop();
+}
+
+/**
+ * Given that the current element is a ClassParameter for a Python class for
+ * which we have a valid constructor, unpack it and fill in its values.
+ */
+PyObject *Extension::
+unpack_class_object(const DCClass *dclass) {
+ PyObject *class_def = invoke_extension(dclass).get_class_def();
+ nassertr(class_def != nullptr, nullptr);
+
+ PyObject *object = nullptr;
+
+ if (!dclass->has_constructor()) {
+ // If the class uses a default constructor, go ahead and create the Python
+ // object for it now.
+ object = PyObject_CallObject(class_def, nullptr);
+ if (object == nullptr) {
+ return nullptr;
+ }
+ }
+
+ _this->push();
+ if (object == nullptr && _this->more_nested_fields()) {
+ // The first nested field will be the constructor.
+ const DCField *field = _this->get_current_field()->as_field();
+ nassertr(field != nullptr, object);
+ nassertr(field == dclass->get_constructor(), object);
+
+ set_class_element(class_def, object, field);
+
+ // By now, the object should have been constructed.
+ if (object == nullptr) {
+ return nullptr;
+ }
+ }
+ while (_this->more_nested_fields()) {
+ const DCField *field = _this->get_current_field()->as_field();
+ nassertr(field != nullptr, object);
+
+ set_class_element(class_def, object, field);
+ }
+ _this->pop();
+
+ return object;
+}
+
+/**
+ * Unpacks the current element and stuffs it on the Python class object in
+ * whatever way is appropriate.
+ */
+void Extension::
+set_class_element(PyObject *class_def, PyObject *&object,
+ const DCField *field) {
+ std::string field_name = field->get_name();
+ DCPackType pack_type = _this->get_pack_type();
+
+ if (field_name.empty()) {
+ switch (pack_type) {
+ case PT_class:
+ case PT_switch:
+ // If the field has no name, but it is one of these container objects,
+ // we want to unpack its nested objects directly into the class.
+ _this->push();
+ while (_this->more_nested_fields()) {
+ const DCField *field = _this->get_current_field()->as_field();
+ nassertv(field != nullptr);
+ nassertv(object != nullptr);
+ set_class_element(class_def, object, field);
+ }
+ _this->pop();
+ break;
+
+ default:
+ // Otherwise, we just skip over the field.
+ _this->unpack_skip();
+ }
+
+ } else {
+ // If the field does have a name, we will want to store it on the class,
+ // either by calling a method (for a PT_field pack_type) or by setting a
+ // value (for any other kind of pack_type).
+
+ PyObject *element = unpack_object();
+
+ if (pack_type == PT_field) {
+ if (object == nullptr) {
+ // If the object hasn't been constructed yet, assume this is the
+ // constructor.
+ object = PyObject_CallObject(class_def, element);
+
+ } else {
+ if (PyObject_HasAttrString(object, (char *)field_name.c_str())) {
+ PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
+ if (func != nullptr) {
+ PyObject *result = PyObject_CallObject(func, element);
+ Py_XDECREF(result);
+ Py_DECREF(func);
+ }
+ }
+ }
+
+ } else {
+ nassertv(object != nullptr);
+ PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
+ }
+
+ Py_DECREF(element);
+ }
+}
+
+/**
+ * Gets the current element from the Python object and packs it.
+ */
+void Extension::
+get_class_element(const DCClass *dclass, PyObject *object,
+ const DCField *field) {
+ std::string field_name = field->get_name();
+ DCPackType pack_type = _this->get_pack_type();
+
+ if (field_name.empty()) {
+ switch (pack_type) {
+ case PT_class:
+ case PT_switch:
+ // If the field has no name, but it is one of these container objects,
+ // we want to get its nested objects directly from the class.
+ _this->push();
+ while (_this->more_nested_fields() && !_this->_pack_error) {
+ const DCField *field = _this->get_current_field()->as_field();
+ nassertv(field != nullptr);
+ get_class_element(dclass, object, field);
+ }
+ _this->pop();
+ break;
+
+ default:
+ // Otherwise, we just pack the default value.
+ _this->pack_default_value();
+ }
+
+ } else {
+ // If the field does have a name, we will want to get it from the class
+ // and pack it. It just so happens that there's already a method that
+ // does this on DCClass.
+
+ if (!invoke_extension(dclass).pack_required_field(*_this, object, field)) {
+ _this->_pack_error = true;
+ }
+ }
+}
+
+#endif // HAVE_PYTHON
diff --git a/direct/src/dcparser/dcPacker_ext.h b/direct/src/dcparser/dcPacker_ext.h
new file mode 100644
index 0000000000..c7e8333a5f
--- /dev/null
+++ b/direct/src/dcparser/dcPacker_ext.h
@@ -0,0 +1,45 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University. All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license. You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcPacker_ext.h
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#ifndef DCPACKER_EXT_H
+#define DCPACKER_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "dcPacker.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for DCPacker, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension : public ExtensionBase {
+public:
+ void pack_object(PyObject *object);
+ PyObject *unpack_object();
+
+ void pack_class_object(const DCClass *dclass, PyObject *object);
+ PyObject *unpack_class_object(const DCClass *dclass);
+ void set_class_element(PyObject *class_def, PyObject *&object,
+ const DCField *field);
+ void get_class_element(const DCClass *dclass, PyObject *object,
+ const DCField *field);
+};
+
+#endif // HAVE_PYTHON
+
+#endif // DCPACKER_EXT_H
diff --git a/direct/src/dcparser/dcPython.h b/direct/src/dcparser/dcPython.h
deleted file mode 100644
index 513f1b008e..0000000000
--- a/direct/src/dcparser/dcPython.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file dcPython.h
- * @author drose
- * @date 2004-06-22
- */
-
-#ifndef DCPYTHON_H
-#define DCPYTHON_H
-
-// The only purpose of this file is to serve as a common place to put the
-// nonsense associated with #including .
-
-#ifdef HAVE_PYTHON
-
-#define PY_SSIZE_T_CLEAN 1
-
-#undef _POSIX_C_SOURCE
-#undef _XOPEN_SOURCE
-#include
-
-// Python 2.5 adds Py_ssize_t; earlier versions don't have it.
-#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
-typedef int Py_ssize_t;
-#define PY_SSIZE_T_MAX INT_MAX
-#define PY_SSIZE_T_MIN INT_MIN
-#endif
-
-// Several interfaces in this module that use Python also require these header
-// files, so we might as well pick them up too.
-#include "datagram.h"
-#include "datagramIterator.h"
-
-#endif // HAVE_PYTHON
-
-#endif
diff --git a/direct/src/dcparser/p3dcparser_ext_composite.cxx b/direct/src/dcparser/p3dcparser_ext_composite.cxx
new file mode 100644
index 0000000000..3d05bfd964
--- /dev/null
+++ b/direct/src/dcparser/p3dcparser_ext_composite.cxx
@@ -0,0 +1,3 @@
+#include "dcClass_ext.cxx"
+#include "dcField_ext.cxx"
+#include "dcPacker_ext.cxx"
diff --git a/direct/src/directscripts/DetectPanda3D.js b/direct/src/directscripts/DetectPanda3D.js
deleted file mode 100644
index 52c57cc034..0000000000
--- a/direct/src/directscripts/DetectPanda3D.js
+++ /dev/null
@@ -1,99 +0,0 @@
-// Based on Apple sample code at
-// http://developer.apple.com/internet/webcontent/examples/detectplugins_source.html
-
-
-// initialize global variables
-var detectableWithVB = false;
-var pluginFound = false;
-
-function goURL(daURL) {
- // Assume we have Javascript 1.1 functionality.
- window.location.replace(daURL);
- return;
-}
-
-function redirectCheck(pluginFound, redirectURL, redirectIfFound) {
- // check for redirection
- if( redirectURL && ((pluginFound && redirectIfFound) ||
- (!pluginFound && !redirectIfFound)) ) {
- // go away
- goURL(redirectURL);
- return pluginFound;
- } else {
- // stay here and return result of plugin detection
- return pluginFound;
- }
-}
-
-function canDetectPlugins() {
- if( detectableWithVB || (navigator.plugins && navigator.plugins.length > 0) ) {
- return true;
- } else {
- return false;
- }
-}
-
-function detectPanda3D(redirectURL, redirectIfFound) {
- pluginFound = detectPlugin('Panda3D');
- // if not found, try to detect with VisualBasic
- if(!pluginFound && detectableWithVB) {
- pluginFound = detectActiveXControl('P3DACTIVEX.P3DActiveXCtrl.1');
- }
- // check for redirection
- return redirectCheck(pluginFound, redirectURL, redirectIfFound);
-}
-
-function detectPlugin() {
- // allow for multiple checks in a single pass
- var daPlugins = detectPlugin.arguments;
- // consider pluginFound to be false until proven true
- var pluginFound = false;
- // if plugins array is there and not fake
- if (navigator.plugins && navigator.plugins.length > 0) {
- var pluginsArrayLength = navigator.plugins.length;
- // for each plugin...
- for (pluginsArrayCounter=0; pluginsArrayCounter < pluginsArrayLength; pluginsArrayCounter++ ) {
- // loop through all desired names and check each against the current plugin name
- var numFound = 0;
- for(namesCounter=0; namesCounter < daPlugins.length; namesCounter++) {
- // if desired plugin name is found in either plugin name or description
- if( (navigator.plugins[pluginsArrayCounter].name.indexOf(daPlugins[namesCounter]) >= 0) ||
- (navigator.plugins[pluginsArrayCounter].description.indexOf(daPlugins[namesCounter]) >= 0) ) {
- // this name was found
- numFound++;
- }
- }
- // now that we have checked all the required names against this one plugin,
- // if the number we found matches the total number provided then we were successful
- if(numFound == daPlugins.length) {
- pluginFound = true;
- // if we've found the plugin, we can stop looking through at the rest of the plugins
- break;
- }
- }
- }
- return pluginFound;
-} // detectPlugin
-
-
-// Here we write out the VBScript block for MSIE Windows
-if ((navigator.userAgent.indexOf('MSIE') != -1) && (navigator.userAgent.indexOf('Win') != -1)) {
- document.writeln('');
-}
diff --git a/direct/src/directscripts/Doxyfile.cxx b/direct/src/directscripts/Doxyfile.cxx
index 6c81025960..db71c81b3b 100644
--- a/direct/src/directscripts/Doxyfile.cxx
+++ b/direct/src/directscripts/Doxyfile.cxx
@@ -432,7 +432,7 @@ EXTRACT_STATIC = NO
# for Java sources.
# The default value is: YES.
-EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_CLASSES = NO
# This flag is only useful for Objective-C code. When set to YES local methods,
# which are defined in the implementation section but not in the interface are
@@ -802,6 +802,11 @@ EXCLUDE = dtool/src/parser-inc \
panda/src/iphone \
panda/src/tinydisplay \
panda/src/movies/dr_flac.h \
+ panda/src/windisplay/winDetectDx.h \
+ panda/src/wgldisplay/wglext.h \
+ panda/src/glxdisplay/panda_glxext.h \
+ pandatool/src/gtk-stats \
+ dtool/src/dtoolbase/fakestringstream.h \
dtool/src/dtoolbase/pdtoa.cxx \
dtool/src/dtoolutil/panda_getopt_long.h \
dtool/src/dtoolutil/panda_getopt_impl.h \
@@ -840,7 +845,10 @@ EXCLUDE_PATTERNS = */Opt*-*/* \
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
-EXCLUDE_SYMBOLS =
+EXCLUDE_SYMBOLS = InterrogateFunctionWrapper::Parameter \
+ CollisionFloorMesh::TriangleIndices \
+ tagTOUCHINPUT \
+ WINDOW_METRICS
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
@@ -1018,7 +1026,7 @@ ALPHABETICAL_INDEX = YES
# Minimum value: 1, maximum value: 20, default value: 5.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-COLS_IN_ALPHA_INDEX = 5
+COLS_IN_ALPHA_INDEX = 3
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
@@ -1972,6 +1980,7 @@ INCLUDE_FILE_PATTERNS =
PREDEFINED = TVOLATILE= \
INLINE=inline \
+ ALWAYS_INLINE=inline \
PUBLISHED=public \
protected=private \
INLINE_LINMATH=inline \
@@ -1986,7 +1995,8 @@ PREDEFINED = TVOLATILE= \
MAKE_SEQ(x)= \
MAKE_SEQ_PROPERTY(x)= \
MAKE_MAP_PROPERTY(x)= \
- MAKE_MAP_KEYS_SEQ(x)=
+ MAKE_MAP_KEYS_SEQ(x)= \
+ RETURNS_ALIGNED(x)=
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
diff --git a/direct/src/directscripts/Doxyfile.python b/direct/src/directscripts/Doxyfile.python
index 2119f7b321..170c3917c1 100644
--- a/direct/src/directscripts/Doxyfile.python
+++ b/direct/src/directscripts/Doxyfile.python
@@ -805,7 +805,7 @@ ALPHABETICAL_INDEX = YES
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
-COLS_IN_ALPHA_INDEX = 5
+COLS_IN_ALPHA_INDEX = 2
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
diff --git a/direct/src/directscripts/RunPanda3D.js b/direct/src/directscripts/RunPanda3D.js
deleted file mode 100644
index b26d3b99c3..0000000000
--- a/direct/src/directscripts/RunPanda3D.js
+++ /dev/null
@@ -1,132 +0,0 @@
-// This script injects the appropriate syntax into the document to
-// embed Panda3D, either for the ActiveX or Mozilla-based plugin.
-
-// It is also possible to write browser-independent code by nesting
-// tags, but this causes problems when you need to reference
-// the particular object that is actually running (which object is
-// it?) for scripting purposes.
-
-// This script writes only a single tag, and it can be
-// assigned the id you specify, avoiding this ambiguity.
-
-var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
-var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
-var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;
-
-
-function P3D_Generateobj(objAttrs, params, embedAttrs, imageAttrs)
-{
- var str = '';
-
- if (isIE && isWin && !isOpera)
- {
- str += '';
- for (var i in params)
- {
- str += ' ';
- }
- }
- else
- {
- str += ' ';
- }
- if (imageAttrs["src"]) {
- if (imageAttrs["href"]) {
- str += '';
- }
- str += ' ';
- if (imageAttrs["href"]) {
- str += ' ';
- }
- }
- str += ' ';
-
- document.write(str);
-}
-
-function P3D_RunContent() {
- var ret =
- P3D_GetArgs
- (arguments, "clsid:924b4927-d3ba-41ea-9f7e-8a89194ab3ac",
- "application/x-panda3d");
- P3D_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs, ret.imageAttrs);
-}
-
-function P3D_GetArgs(args, classid, mimeType){
- var ret = new Object();
- ret.embedAttrs = new Object();
- ret.params = new Object();
- ret.objAttrs = new Object();
- ret.imageAttrs = new Object();
-
- for (var i = 0; i < args.length; i = i + 2){
- var currArg = args[i].toLowerCase();
-
- switch (currArg){
- case "src":
- case "data":
- ret.embedAttrs['data'] = args[i+1];
- ret.params['data'] = args[i+1];
- break;
-
- case "codebase":
- ret.objAttrs['codebase'] = args[i+1];
- break;
-
- case "noplugin_img":
- ret.imageAttrs["src"] = args[i+1];
- ret.imageAttrs["border"] = '0';
- break;
-
- case "noplugin_href":
- ret.imageAttrs["href"] = args[i+1];
- break;
-
- case "splash_img":
- ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
- if (!ret.imageAttrs["src"]) {
- ret.imageAttrs["src"] = args[i+1];
- }
- break;
-
- case "width":
- case "height":
- ret.imageAttrs[args[i]] = ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
- break;
-
- case "id":
- case "align":
- case "vspace":
- case "hspace":
- case "class":
- case "title":
- case "accesskey":
- case "name":
- case "tabindex":
- ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
- break;
-
- default:
- ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
- }
- }
- ret.objAttrs["classid"] = classid;
- if (mimeType) ret.embedAttrs["type"] = mimeType;
- return ret;
-}
diff --git a/direct/src/directscripts/extract_docs.py b/direct/src/directscripts/extract_docs.py
index caead7e4dc..50a775d108 100644
--- a/direct/src/directscripts/extract_docs.py
+++ b/direct/src/directscripts/extract_docs.py
@@ -273,6 +273,7 @@ def processModule(handle, package):
if "panda3d." + package == module_name:
processType(handle, type)
else:
+ typename = interrogate_type_name(type)
print("Type %s has no module name" % typename)
for i_func in range(interrogate_number_of_global_functions()):
@@ -283,7 +284,8 @@ def processModule(handle, package):
if "panda3d." + package == module_name:
processFunction(handle, func)
else:
- print("Type %s has no module name" % typename)
+ funcname = interrogate_function_name(func)
+ print("Function %s has no module name" % funcname)
print("}", file=handle)
diff --git a/direct/src/directscripts/gendocs.py b/direct/src/directscripts/gendocs.py
index 6bb83c9567..a8f8bc62ff 100644
--- a/direct/src/directscripts/gendocs.py
+++ b/direct/src/directscripts/gendocs.py
@@ -56,9 +56,9 @@ import os, sys, parser, symbol, token, re
#
########################################################################
-SECHEADER = re.compile("^[A-Z][a-z]+\s*:")
-JUNKHEADER = re.compile("^((Function)|(Access))\s*:")
-IMPORTSTAR = re.compile("^from\s+([a-zA-Z0-9_.]+)\s+import\s+[*]\s*$")
+SECHEADER = re.compile("^[A-Z][a-z]+\\s*:")
+JUNKHEADER = re.compile("^((Function)|(Access))\\s*:")
+IMPORTSTAR = re.compile("^from\\s+([a-zA-Z0-9_.]+)\\s+import\\s+[*]\\s*$")
IDENTIFIER = re.compile("[a-zA-Z0-9_]+")
FILEHEADER = re.compile(
r"""^// Filename: [a-zA-Z.]+
diff --git a/direct/src/directtools/DirectUtil.py b/direct/src/directtools/DirectUtil.py
index 9919f841b2..2a244e325c 100644
--- a/direct/src/directtools/DirectUtil.py
+++ b/direct/src/directtools/DirectUtil.py
@@ -1,5 +1,7 @@
from .DirectGlobals import *
+from panda3d.core import VBase4
+from direct.task.Task import Task
# Routines to adjust values
def ROUND_TO(value, divisor):
diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py
index 5b2cd99043..3b32fcf885 100644
--- a/direct/src/dist/FreezeTool.py
+++ b/direct/src/dist/FreezeTool.py
@@ -210,15 +210,15 @@ class CompilationEnvironment:
if ('MAKEPANDA' in os.environ):
self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" %(filename)s'
self.compileObjDll = self.compileObjExe
- self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(python)s\libs" /out:%(basename)s.exe %(basename)s.obj'
- self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(python)s\libs" /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
+ self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(python)s\\libs" /out:%(basename)s.exe %(basename)s.obj'
+ self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(python)s\\libs" /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
else:
os.environ['PATH'] += ';' + self.MSVC + '\\bin' + self.suffix64 + ';' + self.MSVC + '\\Common7\\IDE;' + self.PSDK + '\\bin'
- self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" /I"%(PSDK)s\include" /I"%(MSVC)s\include" %(filename)s'
+ self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" /I"%(PSDK)s\\include" /I"%(MSVC)s\\include" %(filename)s'
self.compileObjDll = self.compileObjExe
- self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\libs" /out:%(basename)s.exe %(basename)s.obj'
- self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\libs" /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
+ self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\\libs" /out:%(basename)s.exe %(basename)s.obj'
+ self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\\libs" /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
elif self.platform.startswith('osx_'):
# OSX
@@ -800,29 +800,36 @@ class Freezer:
self.moduleSuffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE)
else:
self.moduleSuffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)]
+
+ abi_version = '{0}{1}'.format(*sys.version_info)
+ abi_flags = ''
+ if sys.version_info < (3, 8):
+ abi_flags += 'm'
+
if 'linux' in self.platform:
self.moduleSuffixes += [
- ('.cpython-{0}{1}m-x86_64-linux-gnu.so'.format(*sys.version_info), 'rb', 3),
- ('.cpython-{0}{1}m-i686-linux-gnu.so'.format(*sys.version_info), 'rb', 3),
+ ('.cpython-{0}{1}-x86_64-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
+ ('.cpython-{0}{1}-i686-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
('.so', 'rb', 3),
]
elif 'win' in self.platform:
+ # ABI flags are not appended on Windows.
self.moduleSuffixes += [
- ('.cp{0}{1}-win_amd64.pyd'.format(*sys.version_info), 'rb', 3),
- ('.cp{0}{1}-win32.pyd'.format(*sys.version_info), 'rb', 3),
+ ('.cp{0}-win_amd64.pyd'.format(abi_version), 'rb', 3),
+ ('.cp{0}-win32.pyd'.format(abi_version), 'rb', 3),
('.pyd', 'rb', 3),
]
elif 'mac' in self.platform:
self.moduleSuffixes += [
- ('.cpython-{0}{1}m-darwin.so'.format(*sys.version_info), 'rb', 3),
+ ('.cpython-{0}{1}-darwin.so'.format(abi_version, abi_flags), 'rb', 3),
('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
('.so', 'rb', 3),
]
else: # FreeBSD et al.
self.moduleSuffixes += [
- ('.cpython-{0}{1}m.so'.format(*sys.version_info), 'rb', 3),
- ('.abi{0}.so'.format(*sys.version_info), 'rb', 3),
+ ('.cpython-{0}{1}.so'.format(abi_version, abi_flags), 'rb', 3),
+ ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
('.so', 'rb', 3),
]
@@ -845,7 +852,7 @@ class Freezer:
allowChildren is true, the children of the indicated module
may still be included."""
- assert self.mf == None
+ assert self.mf is None
self.modules[moduleName] = self.ModuleDef(
moduleName, exclude = True,
@@ -879,7 +886,7 @@ class Freezer:
print("couldn't import %s" % (moduleName))
module = None
- if module != None:
+ if module is not None:
for symbol in moduleName.split('.')[1:]:
module = getattr(module, symbol)
if hasattr(module, '__path__'):
@@ -894,7 +901,7 @@ class Freezer:
if '.' in baseName:
parentName, baseName = moduleName.rsplit('.', 1)
path = self.getModulePath(parentName)
- if path == None:
+ if path is None:
return None
try:
@@ -918,7 +925,7 @@ class Freezer:
print("couldn't import %s" % (moduleName))
module = None
- if module != None:
+ if module is not None:
for symbol in moduleName.split('.')[1:]:
module = getattr(module, symbol)
if hasattr(module, '__all__'):
@@ -931,7 +938,7 @@ class Freezer:
if '.' in baseName:
parentName, baseName = moduleName.rsplit('.', 1)
path = self.getModulePath(parentName)
- if path == None:
+ if path is None:
return None
try:
@@ -988,9 +995,9 @@ class Freezer:
for parentName, newParentName in parentNames:
modules = self.getModuleStar(parentName)
- if modules == None:
+ if modules is None:
# It's actually a regular module.
- mdef[newParentName] = self.ModuleDef(
+ mdefs[newParentName] = self.ModuleDef(
parentName, implicit = implicit, guess = guess,
fromSource = fromSource, text = text)
@@ -1024,7 +1031,7 @@ class Freezer:
directories within a particular directory.
"""
- assert self.mf == None
+ assert self.mf is None
if not newName:
newName = moduleName
@@ -1046,7 +1053,7 @@ class Freezer:
to done(), you may not add any more modules until you call
reset(). """
- assert self.mf == None
+ assert self.mf is None
# If we are building an exe, we also need to implicitly
# bring in Python's startup modules.
diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py
index 2254515b2f..b6a6617fad 100644
--- a/direct/src/dist/commands.py
+++ b/direct/src/dist/commands.py
@@ -13,12 +13,15 @@ import stat
import struct
import imp
import string
+import time
+import tempfile
import setuptools
import distutils.log
from . import FreezeTool
from . import pefile
+from .icon import Icon
import panda3d.core as p3d
@@ -30,6 +33,15 @@ if sys.version_info < (3, 0):
# Python 3 defines these subtypes of IOError, but Python 2 doesn't.
FileNotFoundError = IOError
+ # Warn the user. They might be using Python 2 by accident.
+ print("=================================================================")
+ print("WARNING: You are using Python 2, which will soon be discontinued.")
+ print("WARNING: Please use Python 3 for best results and continued")
+ print("WARNING: support after the EOL date of December 31st, 2019.")
+ print("=================================================================")
+ sys.stdout.flush()
+ time.sleep(4.0)
+
def _parse_list(input):
if isinstance(input, basestring):
@@ -54,6 +66,8 @@ def _parse_dict(input):
def egg2bam(_build_cmd, srcpath, dstpath):
+ if dstpath.endswith('.gz') or dstpath.endswith('.pz'):
+ dstpath = dstpath[:-3]
dstpath = dstpath + '.bam'
try:
subprocess.check_call([
@@ -214,6 +228,7 @@ class build_apps(setuptools.Command):
self.exclude_patterns = []
self.include_modules = {}
self.exclude_modules = {}
+ self.icons = {}
self.platforms = [
'manylinux1_x86_64',
'macosx_10_6_x86_64',
@@ -229,7 +244,9 @@ class build_apps(setuptools.Command):
self.requirements_path = os.path.join(os.getcwd(), 'requirements.txt')
self.use_optimized_wheels = True
self.optimized_wheel_index = ''
- self.pypi_extra_indexes = []
+ self.pypi_extra_indexes = [
+ 'https://archive.panda3d.org/thirdparty',
+ ]
self.file_handlers = {}
self.exclude_dependencies = [
# Windows
@@ -286,6 +303,7 @@ class build_apps(setuptools.Command):
key: _parse_list(value)
for key, value in _parse_dict(self.exclude_modules).items()
}
+ self.icons = _parse_dict(self.icons)
self.platforms = _parse_list(self.platforms)
self.plugins = _parse_list(self.plugins)
self.extra_prc_files = _parse_list(self.extra_prc_files)
@@ -345,6 +363,18 @@ class build_apps(setuptools.Command):
tmp.update(self.package_data_dirs)
self.package_data_dirs = tmp
+ self.icon_objects = {}
+ for app, iconpaths in self.icons.items():
+ if not isinstance(iconpaths, list) and not isinstance(iconpaths, tuple):
+ iconpaths = (iconpaths,)
+
+ iconobj = Icon()
+ for iconpath in iconpaths:
+ iconobj.addImage(iconpath)
+
+ iconobj.generateMissingImages()
+ self.icon_objects[app] = iconobj
+
def run(self):
self.announce('Building platforms: {0}'.format(','.join(self.platforms)), distutils.log.INFO)
@@ -421,6 +451,22 @@ class build_apps(setuptools.Command):
return wheelpaths
+ def update_pe_resources(self, appname, runtime):
+ """Update resources (e.g., icons) in windows PE file"""
+
+ icon = self.icon_objects.get(
+ appname,
+ self.icon_objects.get('*', None),
+ )
+
+ if icon is not None:
+ pef = pefile.PEFile()
+ pef.open(runtime, 'r+')
+ pef.add_icon(icon)
+ pef.add_resource_section()
+ pef.write_changes()
+ pef.close()
+
def bundle_macos_app(self, builddir):
"""Bundle built runtime into a .app for macOS"""
@@ -462,6 +508,15 @@ class build_apps(setuptools.Command):
'CFBundleSignature': '', #TODO
'CFBundleExecutable': self.macos_main_app,
}
+
+ icon = self.icon_objects.get(
+ self.macos_main_app,
+ self.icon_objects.get('*', None)
+ )
+ if icon is not None:
+ plist['CFBundleIconFile'] = 'iconfile'
+ icon.makeICNS(os.path.join(resdir, 'iconfile.icns'))
+
with open(os.path.join(contentsdir, 'Info.plist'), 'wb') as f:
if hasattr(plistlib, 'dump'):
plistlib.dump(plist, f)
@@ -606,6 +661,18 @@ class build_apps(setuptools.Command):
stub_path = os.path.join(os.path.dirname(dtool_path), '..', 'bin', stub_name)
stub_file = open(stub_path, 'rb')
+ # Do we need an icon? On Windows, we need to add this to the stub
+ # before we add the blob.
+ if 'win' in platform:
+ temp_file = tempfile.NamedTemporaryFile(suffix='-icon.exe', delete=False)
+ temp_file.write(stub_file.read())
+ stub_file.close()
+ temp_file.close()
+ self.update_pe_resources(appname, temp_file.name)
+ stub_file = open(temp_file.name, 'rb')
+ else:
+ temp_file = None
+
freezer.generateRuntimeFromStub(target_path, stub_file, use_console, {
'prc_data': prcexport if self.embed_prc_data else None,
'default_prc_dir': self.default_prc_dir,
@@ -621,6 +688,9 @@ class build_apps(setuptools.Command):
}, self.log_append)
stub_file.close()
+ if temp_file:
+ os.unlink(temp_file.name)
+
# Copy the dependencies.
search_path = [builddir]
if use_wheels:
@@ -827,6 +897,9 @@ class build_apps(setuptools.Command):
os.makedirs(dst_dir)
ext = os.path.splitext(src)[1]
+ # If the file ends with .gz/.pz, we strip this off.
+ if ext in ('.gz', '.pz'):
+ ext = os.path.splitext(src[:-3])[1]
if not ext:
ext = os.path.basename(src)
@@ -1346,8 +1419,9 @@ class bdist_apps(setuptools.Command):
self.run_command('build_apps')
platforms = build_cmd.platforms
- build_base = build_cmd.build_base
- os.makedirs(self.dist_dir, exist_ok=True)
+ build_base = os.path.abspath(build_cmd.build_base)
+ if not os.path.exists(self.dist_dir):
+ os.makedirs(self.dist_dir)
os.chdir(self.dist_dir)
for platform in platforms:
diff --git a/direct/src/dist/icon.py b/direct/src/dist/icon.py
new file mode 100644
index 0000000000..b2f162349d
--- /dev/null
+++ b/direct/src/dist/icon.py
@@ -0,0 +1,269 @@
+from direct.directnotify.DirectNotifyGlobal import *
+from panda3d.core import PNMImage, Filename
+
+class Icon:
+ """ This class is used to create an icon for various platforms. """
+ notify = directNotify.newCategory("Icon")
+
+ def __init__(self):
+ self.images = {}
+
+ def addImage(self, image):
+ """ Adds an image to the icon. Returns False on failure, True on success.
+ Only one image per size can be loaded, and the image size must be square. """
+
+ if not isinstance(image, PNMImage):
+ fn = image
+ if not isinstance(fn, Filename):
+ fn = Filename.fromOsSpecific(fn)
+
+ image = PNMImage()
+ if not image.read(fn):
+ Icon.notify.warning("Image '%s' could not be read" % fn.getBasename())
+ return False
+
+ if image.getXSize() != image.getYSize():
+ Icon.notify.warning("Ignoring image without square size")
+ return False
+
+ self.images[image.getXSize()] = image
+
+ return True
+
+ def generateMissingImages(self):
+ """ Generates image sizes that should be present but aren't by scaling
+ from the next higher size. """
+
+ for required_size in (256, 128, 48, 32, 16):
+ if required_size in self.images:
+ continue
+
+ sizes = sorted(self.images.keys())
+ if required_size * 2 in sizes:
+ from_size = required_size * 2
+ else:
+ from_size = 0
+ for from_size in sizes:
+ if from_size > required_size:
+ break
+
+ if from_size > required_size:
+ Icon.notify.warning("Generating %dx%d icon by scaling down %dx%d image" % (required_size, required_size, from_size, from_size))
+
+ image = PNMImage(required_size, required_size)
+ if self.images[from_size].hasAlpha():
+ image.addAlpha()
+ image.quickFilterFrom(self.images[from_size])
+ self.images[required_size] = image
+ else:
+ Icon.notify.warning("Cannot generate %dx%d icon; no higher resolution image available" % (required_size, required_size))
+
+ def _write_bitmap(self, fp, image, size, bpp):
+ """ Writes the bitmap header and data of an .ico file. """
+
+ fp.write(struct.pack('= 256:
+ # Find closest pixel instead.
+ index = closest_indices[index - 256]
+ fp.write(struct.pack('> 3) & 3)
+ for y in xrange(size):
+ mask = 0
+ num_bits = 7
+ for x in xrange(size):
+ a = image.get_alpha_val(x, size - y - 1)
+ if a <= 1:
+ mask |= (1 << num_bits)
+ num_bits -= 1
+ if num_bits < 0:
+ fp.write(struct.pack('> 3
+ if andsize % 4 != 0:
+ andsize += 4 - (andsize % 4)
+ fp.write(b'\x00' * (andsize * size))
+
+ def makeICO(self, fn):
+ """ Writes the images to a Windows ICO file. Returns True on success. """
+
+ if not isinstance(fn, Filename):
+ fn = Filename.fromOsSpecific(fn)
+ fn.setBinary()
+
+ # ICO files only support resolutions up to 256x256.
+ count = 0
+ for size in self.images.keys():
+ if size < 256:
+ count += 1
+ if size <= 256:
+ count += 1
+ dataoffs = 6 + count * 16
+
+ ico = open(fn, 'wb')
+ ico.write(struct.pack('= 256:
+ continue
+ ico.write(struct.pack('> 3
+ if andsize % 4 != 0:
+ andsize += 4 - (andsize % 4)
+ datasize = 40 + 256 * 4 + (xorsize + andsize) * size
+
+ ico.write(struct.pack(' 256:
+ continue
+ elif size == 256:
+ ico.write(b'\0\0')
+ else:
+ ico.write(struct.pack('> 3
+ if andsize % 4 != 0:
+ andsize += 4 - (andsize % 4)
+ datasize = 40 + (xorsize + andsize) * size
+
+ ico.write(struct.pack('I', len(pngdata)))
+ icns.write(pngdata)
+
+ elif size in icon_types:
+ # If it has an alpha channel, we write out a mask too.
+ if image.hasAlpha():
+ icns.write(mask_types[size])
+ icns.write(struct.pack('>I', size * size + 8))
+
+ for y in xrange(size):
+ for x in xrange(size):
+ icns.write(struct.pack('I', size * size * 4 + 8))
+
+ for y in xrange(size):
+ for x in xrange(size):
+ r, g, b = image.getXel(x, y)
+ icns.write(struct.pack('>BBBB', 0, int(r * 255), int(g * 255), int(b * 255)))
+
+ length = icns.tell()
+ icns.seek(4)
+ icns.write(struct.pack('>I', length))
+ icns.close()
+
+ return True
+
+
diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx
index 16aea6e8dd..59617330f5 100644
--- a/direct/src/distributed/cConnectionRepository.cxx
+++ b/direct/src/distributed/cConnectionRepository.cxx
@@ -26,6 +26,7 @@
#ifdef HAVE_PYTHON
#include "py_panda.h"
+#include "dcClass_ext.h"
#endif
using std::endl;
@@ -736,7 +737,7 @@ handle_update_field() {
// get into trouble if it tried to delete the object from the doId2do
// map.
Py_INCREF(distobj);
- dclass->receive_update(distobj, _di);
+ invoke_extension(dclass).receive_update(distobj, _di);
Py_DECREF(distobj);
if (PyErr_Occurred()) {
@@ -820,7 +821,7 @@ handle_update_field_owner() {
// make a copy of the datagram iterator so that we can use the main
// iterator for the non-owner update
DatagramIterator _odi(_di);
- dclass->receive_update(distobjOV, _odi);
+ invoke_extension(dclass).receive_update(distobjOV, _odi);
Py_DECREF(distobjOV);
if (PyErr_Occurred()) {
@@ -861,7 +862,7 @@ handle_update_field_owner() {
// get into trouble if it tried to delete the object from the doId2do
// map.
Py_INCREF(distobj);
- dclass->receive_update(distobj, _di);
+ invoke_extension(dclass).receive_update(distobj, _di);
Py_DECREF(distobj);
if (PyErr_Occurred()) {
diff --git a/direct/src/distributed/cConnectionRepository.h b/direct/src/distributed/cConnectionRepository.h
index c67d4aad23..2db877215b 100644
--- a/direct/src/distributed/cConnectionRepository.h
+++ b/direct/src/distributed/cConnectionRepository.h
@@ -53,7 +53,7 @@ class SocketStream;
* the C++ layer, while server messages that are not understood by the C++
* layer are returned up to the Python layer for processing.
*/
-class EXPCL_DIRECT_DISTRIBUTED CConnectionRepository {
+class CConnectionRepository {
PUBLISHED:
explicit CConnectionRepository(bool has_owner_view = false,
bool threaded_net = false);
diff --git a/direct/src/distributed/cDistributedSmoothNodeBase.cxx b/direct/src/distributed/cDistributedSmoothNodeBase.cxx
index 67e273644f..564ac6ac9b 100644
--- a/direct/src/distributed/cDistributedSmoothNodeBase.cxx
+++ b/direct/src/distributed/cDistributedSmoothNodeBase.cxx
@@ -18,6 +18,10 @@
#include "dcmsgtypes.h"
#include "config_distributed.h"
+#ifdef HAVE_PYTHON
+#include "py_panda.h"
+#endif
+
static const PN_stdfloat smooth_node_epsilon = 0.01;
static const double network_time_precision = 100.0; // Matches ClockDelta.py
diff --git a/direct/src/distributed/cDistributedSmoothNodeBase.h b/direct/src/distributed/cDistributedSmoothNodeBase.h
index 6c57f6a2bf..bebe55fcdb 100644
--- a/direct/src/distributed/cDistributedSmoothNodeBase.h
+++ b/direct/src/distributed/cDistributedSmoothNodeBase.h
@@ -18,7 +18,6 @@
#include "nodePath.h"
#include "dcbase.h"
#include "dcPacker.h"
-#include "dcPython.h" // to pick up Python.h
#include "clockObject.h"
class DCClass;
@@ -28,7 +27,7 @@ class CConnectionRepository;
* This class defines some basic methods of DistributedSmoothNodeBase which
* have been moved into C++ as a performance optimization.
*/
-class EXPCL_DIRECT_DISTRIBUTED CDistributedSmoothNodeBase {
+class CDistributedSmoothNodeBase {
PUBLISHED:
CDistributedSmoothNodeBase();
~CDistributedSmoothNodeBase();
diff --git a/direct/src/distributed/config_distributed.h b/direct/src/distributed/config_distributed.h
index 22814bfa13..1690295e5a 100644
--- a/direct/src/distributed/config_distributed.h
+++ b/direct/src/distributed/config_distributed.h
@@ -23,10 +23,10 @@
NotifyCategoryDecl(distributed, EXPCL_DIRECT_DISTRIBUTED, EXPTP_DIRECT_DISTRIBUTED);
-extern ConfigVariableInt game_server_timeout_ms;
-extern ConfigVariableDouble min_lag;
-extern ConfigVariableDouble max_lag;
-extern ConfigVariableBool handle_datagrams_internally;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableInt game_server_timeout_ms;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableDouble min_lag;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableDouble max_lag;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableBool handle_datagrams_internally;
extern EXPCL_DIRECT_DISTRIBUTED void init_libdistributed();
diff --git a/direct/src/filter/FilterManager.py b/direct/src/filter/FilterManager.py
index 4150cba568..c20cbed139 100644
--- a/direct/src/filter/FilterManager.py
+++ b/direct/src/filter/FilterManager.py
@@ -124,7 +124,7 @@ class FilterManager(DirectObject):
return winx,winy
- def renderSceneInto(self, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None):
+ def renderSceneInto(self, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None, fbprops=None):
""" Causes the scene to be rendered into the supplied textures
instead of into the original window. Puts a fullscreen quad
@@ -185,7 +185,10 @@ class FilterManager(DirectObject):
# Choose the size of the offscreen buffer.
(winx, winy) = self.getScaledSize(1,1,1)
- buffer = self.createBuffer("filter-base", winx, winy, texgroup)
+ if fbprops is not None:
+ buffer = self.createBuffer("filter-base", winx, winy, texgroup, fbprops=fbprops)
+ else:
+ buffer = self.createBuffer("filter-base", winx, winy, texgroup)
if (buffer == None):
return None
@@ -236,7 +239,7 @@ class FilterManager(DirectObject):
return quad
- def renderQuadInto(self, name="filter-stage", mul=1, div=1, align=1, depthtex=None, colortex=None, auxtex0=None, auxtex1=None):
+ def renderQuadInto(self, name="filter-stage", mul=1, div=1, align=1, depthtex=None, colortex=None, auxtex0=None, auxtex1=None, fbprops=None):
""" Creates an offscreen buffer for an intermediate
computation. Installs a quad into the buffer. Returns
@@ -250,7 +253,10 @@ class FilterManager(DirectObject):
depthbits = bool(depthtex != None)
- buffer = self.createBuffer(name, winx, winy, texgroup, depthbits)
+ if fbprops is not None:
+ buffer = self.createBuffer(name, winx, winy, texgroup, depthbits, fbprops=fbprops)
+ else:
+ buffer = self.createBuffer(name, winx, winy, texgroup, depthbits)
if (buffer == None):
return None
@@ -287,7 +293,7 @@ class FilterManager(DirectObject):
return quad
- def createBuffer(self, name, xsize, ysize, texgroup, depthbits=1):
+ def createBuffer(self, name, xsize, ysize, texgroup, depthbits=1, fbprops=None):
""" Low-level buffer creation. Not intended for public use. """
winprops = WindowProperties()
@@ -297,6 +303,9 @@ class FilterManager(DirectObject):
props.setRgbColor(1)
props.setDepthBits(depthbits)
props.setStereo(self.win.isStereo())
+ if fbprops is not None:
+ props.addProperties(fbprops)
+
depthtex, colortex, auxtex0, auxtex1 = texgroup
if (auxtex0 != None):
props.setAuxRgba(1)
diff --git a/direct/src/gui/DirectDialog.py b/direct/src/gui/DirectDialog.py
index 33852dc504..ba56fdbfa0 100644
--- a/direct/src/gui/DirectDialog.py
+++ b/direct/src/gui/DirectDialog.py
@@ -3,6 +3,7 @@
__all__ = ['findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog', 'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog']
from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
from . import DirectGuiGlobals as DGG
from .DirectFrame import *
from .DirectButton import *
@@ -207,7 +208,7 @@ class DirectDialog(DirectFrame):
image = None
# Get size of text/geom without image (for state 0)
if image:
- image.reparentTo(hidden)
+ image.reparentTo(ShowBaseGlobal.hidden)
bounds = self.stateNodePath[0].getTightBounds()
if image:
image.reparentTo(self.stateNodePath[0])
diff --git a/direct/src/gui/DirectEntry.py b/direct/src/gui/DirectEntry.py
index e345c526e9..d614a3825a 100644
--- a/direct/src/gui/DirectEntry.py
+++ b/direct/src/gui/DirectEntry.py
@@ -4,6 +4,7 @@ text entered using the keyboard."""
__all__ = ['DirectEntry']
from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
from . import DirectGuiGlobals as DGG
from .DirectFrame import *
from .OnscreenText import OnscreenText
@@ -94,7 +95,7 @@ class DirectEntry(DirectFrame):
self.onscreenText = self.createcomponent(
'text', (), None,
OnscreenText,
- (), parent = hidden,
+ (), parent = ShowBaseGlobal.hidden,
# Pass in empty text to avoid extra work, since its really
# The PGEntry which will use the TextNode to generate geometry
text = '',
@@ -215,11 +216,11 @@ class DirectEntry(DirectFrame):
self._autoCapitalize()
def _autoCapitalize(self):
- name = self.get().decode('utf-8')
+ name = self.guiItem.getWtext()
# capitalize each word, allowing for things like McMutton
- capName = ''
+ capName = u''
# track each individual word to detect prefixes like Mc
- wordSoFar = ''
+ wordSoFar = u''
# track whether the previous character was part of a word or not
wasNonWordChar = True
for i, character in enumerate(name):
@@ -228,9 +229,9 @@ class DirectEntry(DirectFrame):
# This assumes that string.lower and string.upper will return different
# values for all unicode letters.
# - Don't count apostrophes as a break between words
- if character.lower() == character.upper() and character != "'":
+ if character.lower() == character.upper() and character != u"'":
# we are between words
- wordSoFar = ''
+ wordSoFar = u''
wasNonWordChar = True
else:
capitalize = False
@@ -254,7 +255,8 @@ class DirectEntry(DirectFrame):
wordSoFar += character
wasNonWordChar = False
capName += character
- self.enterText(capName.encode('utf-8'))
+ self.guiItem.setWtext(capName)
+ self.guiItem.setCursorPosition(self.guiItem.getNumCharacters())
def focusOutCommandFunc(self):
if self['focusOutCommand']:
diff --git a/direct/src/gui/DirectEntryScroll.py b/direct/src/gui/DirectEntryScroll.py
index 0cd7c3ea21..fd369b2716 100644
--- a/direct/src/gui/DirectEntryScroll.py
+++ b/direct/src/gui/DirectEntryScroll.py
@@ -29,19 +29,41 @@ class DirectEntryScroll(DirectFrame):
# frameSize = (-0.006, 3.2, -0.015, 0.036),
# if you need to scale the entry scale it's parent instead
- self.entry = entry
self.canvas = NodePath(self.guiItem.getCanvasNode())
- self.entry.reparentTo(self.canvas)
self.canvas.setPos(0,0,0)
- self.entry.bind(DGG.CURSORMOVE,self.cursorMove)
+ self.entry = None
+ if entry is not None:
+ self.entry = entry
+ self.entry.reparentTo(self.canvas)
+ self.entry.bind(DGG.CURSORMOVE, self.cursorMove)
self.canvas.node().setBounds(OmniBoundingVolume())
self.canvas.node().setFinal(1)
self.resetCanvas()
+ def setEntry(self, entry):
+ """
+ Sets a DirectEntry element for this scroll frame. A DirectEntryScroll
+ can only hold one entry at a time, so make sure to not call this
+ function twice or call clearEntry before to make sure no entry
+ is already set.
+ """
+ assert self.entry is None, "An entry was already set for this DirectEntryScroll element"
+ self.entry = entry
+ self.entry.reparentTo(self.canvas)
+ self.entry.bind(DGG.CURSORMOVE, self.cursorMove)
+ def clearEntry(self):
+ """
+ detaches and unbinds the entry from the scroll frame and its
+ events. You'll be responsible for destroying it.
+ """
+ if self.entry is None: return
+ self.entry.unbind(DGG.CURSORMOVE)
+ self.entry.detachNode()
+ self.entry = None
def cursorMove(self, cursorX, cursorY):
cursorX = self.entry.guiItem.getCursorX() * self.entry['text_scale'][0]
diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py
index 3180d0dfce..3b47ac1973 100644
--- a/direct/src/gui/DirectGuiBase.py
+++ b/direct/src/gui/DirectGuiBase.py
@@ -634,7 +634,7 @@ class DirectGuiBase(DirectObject.DirectObject):
"""
# Need to tack on gui item specific id
gEvent = event + self.guiId
- if ShowBase.config.GetBool('debug-directgui-msgs', False):
+ if ShowBaseGlobal.config.GetBool('debug-directgui-msgs', False):
from direct.showbase.PythonUtil import StackTrace
print(gEvent)
print(StackTrace())
@@ -663,7 +663,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
# Determine the default initial state for inactive (or
# unclickable) components. If we are in edit mode, these are
# actually clickable by default.
- guiEdit = ShowBase.config.GetBool('direct-gui-edit', False)
+ guiEdit = ShowBaseGlobal.config.GetBool('direct-gui-edit', False)
if guiEdit:
inactiveInitState = DGG.NORMAL
else:
@@ -729,7 +729,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
guiObjectCollector.addLevel(1)
guiObjectCollector.flushLevel()
# track gui items by guiId for tracking down leaks
- if ShowBase.config.GetBool('track-gui-items', False):
+ if ShowBaseGlobal.config.GetBool('track-gui-items', False):
if not hasattr(ShowBase, 'guiItems'):
ShowBase.guiItems = {}
if self.guiId in ShowBase.guiItems:
diff --git a/direct/src/gui/DirectOptionMenu.py b/direct/src/gui/DirectOptionMenu.py
index 40d48f7672..cbf3a2974e 100644
--- a/direct/src/gui/DirectOptionMenu.py
+++ b/direct/src/gui/DirectOptionMenu.py
@@ -22,10 +22,12 @@ class DirectOptionMenu(DirectButton):
# List of items to display on the popup menu
('items', [], self.setItems),
# Initial item to display on menu button
- # Can be an interger index or the same string as the button
+ # Can be an integer index or the same string as the button
('initialitem', None, DGG.INITOPT),
# Amount of padding to place around popup button indicator
('popupMarkerBorder', (.1, .1), None),
+ # The initial position of the popup marker
+ ('popupMarker_pos', None, None),
# Background color to use to highlight popup menu items
('highlightColor', (.5, .5, .5, 1), None),
# Extra scale to use on highlight popup menu items
@@ -50,6 +52,8 @@ class DirectOptionMenu(DirectButton):
frameSize = (-0.5, 0.5, -0.2, 0.2),
scale = 0.4,
relief = DGG.RAISED)
+ # Record any user specified popup marker position
+ self.initPopupMarkerPos = self['popupMarker_pos']
# This needs to popup the menu too
self.popupMarker.bind(DGG.B1PRESS, self.showPopupMenu)
# Check if item is highlighted on release and select it if it is
@@ -73,6 +77,7 @@ class DirectOptionMenu(DirectButton):
state = 'normal')
# Make sure this is on top of all the other widgets
self.cancelFrame.setBin('gui-popup', 0)
+ self.cancelFrame.node().setBounds(OmniBoundingVolume())
self.cancelFrame.bind(DGG.B1PRESS, self.hidePopupMenu)
# Default action on press is to show popup menu
self.bind(DGG.B1PRESS, self.showPopupMenu)
@@ -168,8 +173,13 @@ class DirectOptionMenu(DirectButton):
else:
# Or base it upon largest item
bounds = [self.minX, self.maxX, self.minZ, self.maxZ]
- pm.setPos(bounds[1] + pmw/2.0, 0,
- bounds[2] + (bounds[3] - bounds[2])/2.0)
+ if self.initPopupMarkerPos:
+ # Use specified position
+ pmPos = list(self.initPopupMarkerPos)
+ else:
+ # Or base the position on the frame size.
+ pmPos = [bounds[1] + pmw/2.0, 0, bounds[2] + (bounds[3] - bounds[2])/2.0]
+ pm.setPos(pmPos[0], pmPos[1], pmPos[2])
# Adjust popup menu button to fit all items (or use user specified
# frame size
bounds[1] += pmw
@@ -184,6 +194,12 @@ class DirectOptionMenu(DirectButton):
Adjust popup position if default position puts it outside of
visible screen region
"""
+
+ # Needed attributes (such as minZ) won't be set unless the user has specified
+ # items to display. Let's assert that we've given items to work with.
+ items = self['items']
+ assert items and len(items) > 0, 'Cannot show an empty popup menu! You must add items!'
+
# Show the menu
self.popupMenu.show()
# Make sure its at the right scale
diff --git a/direct/src/gui/DirectScrolledFrame.py b/direct/src/gui/DirectScrolledFrame.py
index c44bc7a0fb..2c60ed950f 100644
--- a/direct/src/gui/DirectScrolledFrame.py
+++ b/direct/src/gui/DirectScrolledFrame.py
@@ -33,7 +33,7 @@ class DirectScrolledFrame(DirectFrame):
('canvasSize', (-1, 1, -1, 1), self.setCanvasSize),
('manageScrollBars', 1, self.setManageScrollBars),
('autoHideScrollBars', 1, self.setAutoHideScrollBars),
- ('scrollBarWidth', 0.08, None),
+ ('scrollBarWidth', 0.08, self.setScrollBarWidth),
('borderWidth', (0.01, 0.01), self.setBorderWidth),
)
@@ -72,6 +72,11 @@ class DirectScrolledFrame(DirectFrame):
# Call option initialization functions
self.initialiseoptions(DirectScrolledFrame)
+ def setScrollBarWidth(self):
+ w = self['scrollBarWidth']
+ self.verticalScroll["frameSize"] = (-w / 2.0, w / 2.0, -1, 1)
+ self.horizontalScroll["frameSize"] = (-1, 1, -w / 2.0, w / 2.0)
+
def setCanvasSize(self):
f = self['canvasSize']
self.guiItem.setVirtualFrame(f[0], f[1], f[2], f[3])
@@ -100,8 +105,10 @@ class DirectScrolledFrame(DirectFrame):
simpleChildGui = self.guiDict.get(parts[-1])
if simpleChildGui:
simpleChildGui.destroy()
- self.verticalScroll.destroy()
- self.horizontalScroll.destroy()
- del self.verticalScroll
- del self.horizontalScroll
+ if self.verticalScroll:
+ self.verticalScroll.destroy()
+ if self.horizontalScroll:
+ self.horizontalScroll.destroy()
+ self.verticalScroll = None
+ self.horizontalScroll = None
DirectFrame.destroy(self)
diff --git a/direct/src/gui/DirectScrolledList.py b/direct/src/gui/DirectScrolledList.py
index a89b691576..661b09f13d 100644
--- a/direct/src/gui/DirectScrolledList.py
+++ b/direct/src/gui/DirectScrolledList.py
@@ -3,6 +3,7 @@
__all__ = ['DirectScrolledListItem', 'DirectScrolledList']
from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
from . import DirectGuiGlobals as DGG
from direct.directnotify import DirectNotifyGlobal
from direct.task.Task import Task
@@ -369,7 +370,7 @@ class DirectScrolledList(DirectFrame):
del self.currentSelected
self["items"].remove(item)
if type(item) != type(''):
- item.reparentTo(hidden)
+ item.reparentTo(ShowBaseGlobal.hidden)
self.refresh()
return 1
else:
@@ -387,7 +388,7 @@ class DirectScrolledList(DirectFrame):
item.destroy()
self["items"].remove(item)
if type(item) != type(''):
- item.reparentTo(hidden)
+ item.reparentTo(ShowBaseGlobal.hidden)
self.refresh()
return 1
else:
@@ -410,7 +411,7 @@ class DirectScrolledList(DirectFrame):
self["items"].remove(item)
if type(item) != type(''):
#RAU possible leak here, let's try to do the right thing
- #item.reparentTo(hidden)
+ #item.reparentTo(ShowBaseGlobal.hidden)
item.removeNode()
retval = 1
@@ -435,7 +436,7 @@ class DirectScrolledList(DirectFrame):
self["items"].remove(item)
if type(item) != type(''):
#RAU possible leak here, let's try to do the right thing
- #item.reparentTo(hidden)
+ #item.reparentTo(ShowBaseGlobal.hidden)
item.removeNode()
retval = 1
if (refresh):
diff --git a/direct/src/interval/SoundInterval.py b/direct/src/interval/SoundInterval.py
index 5927d14703..e5c5a04467 100644
--- a/direct/src/interval/SoundInterval.py
+++ b/direct/src/interval/SoundInterval.py
@@ -27,8 +27,7 @@ class SoundInterval(Interval.Interval):
# than explicitly restarting the sound every time around. This
# prevents a skip in the sound at every repetition (the gap in
# the sound is caused by the delay between the end of the sound
- # and the next taskMgr cycle). There still seems to be a skip
- # in Miles when looping MP3s. =(
+ # and the next taskMgr cycle).
# RAU 03/01/07 add listenerNode in case we don't want to
# use base.camera as the listener, node must not be None
def __init__(self, sound, loop = 0, duration = 0.0, name = None,
@@ -62,23 +61,6 @@ class SoundInterval(Interval.Interval):
#if (duration == 0):
# self.notify.warning('zero length duration!')
- # MPG - hack for Miles bug
- #duration += 1.5
-
- # DCR - hack for Miles bug - adding 1.5 seconds caused
- # problems for MG_neg_buzzer.wav
-
- # DCR - what this is all about: Miles is under-reporting the
- # length of MP3 files, and they're getting cut off too early.
- # This is a temporary hack. We could:
- # - hack Miles to fix its MP3 length calculation
- # - complain louder about this to RAD
- # - precompute MP3 durations and store them in a table
-
- # drose - ok, I've put in a lower-level workaround in the
- # MilesAudioManager. This is no longer necessary up here,
- # where it pollutes SoundInterval for everyone.
- #duration += min(duration * 2.4, 1.5)
# Generate unique name if necessary
if (name == None):
diff --git a/direct/src/interval/cLerpNodePathInterval.I b/direct/src/interval/cLerpNodePathInterval.I
index 03c90753cb..5d4337f9df 100644
--- a/direct/src/interval/cLerpNodePathInterval.I
+++ b/direct/src/interval/cLerpNodePathInterval.I
@@ -106,6 +106,8 @@ set_end_hpr(const LQuaternion &quat) {
* if either set_end_quat() or set_end_hpr() is also called. This parameter
* is optional; if unspecified, the value will be taken from the node's actual
* rotation at the time the lerp is performed.
+ *
+ * The given quaternion needs to be normalized.
*/
INLINE void CLerpNodePathInterval::
set_start_quat(const LQuaternion &quat) {
@@ -143,6 +145,8 @@ set_end_quat(const LVecBase3 &hpr) {
* This replaces a previous call to set_end_hpr(). If neither set_end_quat()
* nor set_end_hpr() is called, the node's rotation will not be affected by
* the lerp.
+ *
+ * The given quaternion needs to be normalized.
*/
INLINE void CLerpNodePathInterval::
set_end_quat(const LQuaternion &quat) {
diff --git a/direct/src/interval/cLerpNodePathInterval.cxx b/direct/src/interval/cLerpNodePathInterval.cxx
index 2fbc8a7e75..97babae49e 100644
--- a/direct/src/interval/cLerpNodePathInterval.cxx
+++ b/direct/src/interval/cLerpNodePathInterval.cxx
@@ -174,14 +174,14 @@ priv_step(double t) {
setup_slerp();
} else if ((_flags & F_bake_in_start) != 0) {
- set_start_quat(transform->get_quat());
+ set_start_quat(transform->get_norm_quat());
setup_slerp();
} else {
if (_prev_d == 1.0) {
_start_quat = _end_quat;
} else {
- LQuaternion prev_value = transform->get_quat();
+ LQuaternion prev_value = transform->get_norm_quat();
_start_quat = (prev_value - _prev_d * _end_quat) / (1.0 - _prev_d);
}
setup_slerp();
diff --git a/direct/src/leveleditor/ActionMgr.py b/direct/src/leveleditor/ActionMgr.py
index 29d9ba3742..dcc5b7ff91 100755
--- a/direct/src/leveleditor/ActionMgr.py
+++ b/direct/src/leveleditor/ActionMgr.py
@@ -1,4 +1,4 @@
-from pandac.PandaModules import *
+from panda3d.core import *
from . import ObjectGlobals as OG
class ActionMgr:
diff --git a/direct/src/leveleditor/CurveEditor.py b/direct/src/leveleditor/CurveEditor.py
index b1acc04525..57cc119378 100755
--- a/direct/src/leveleditor/CurveEditor.py
+++ b/direct/src/leveleditor/CurveEditor.py
@@ -2,7 +2,7 @@
This is the module for curve edit
"""
-from pandac.PandaModules import *
+from panda3d.core import *
from direct.wxwidgets.WxPandaShell import *
from direct.showbase.DirectObject import *
from direct.directtools.DirectSelection import SelectionRay
diff --git a/direct/src/leveleditor/FileMgr.py b/direct/src/leveleditor/FileMgr.py
index a0b00d1a04..1f11dc7133 100755
--- a/direct/src/leveleditor/FileMgr.py
+++ b/direct/src/leveleditor/FileMgr.py
@@ -11,7 +11,7 @@ class FileMgr:
def saveToFile(self, fileName):
try:
f = open(fileName, 'w')
- f.write("from pandac.PandaModules import *\n")
+ f.write("from panda3d.core import *\n")
f.write("\nif hasattr(base, 'le'):\n")
f.write(" objectMgr = base.le.objectMgr\n")
f.write(" animMgr = base.le.animMgr\n")
diff --git a/direct/src/leveleditor/LayerEditorUI.py b/direct/src/leveleditor/LayerEditorUI.py
index d77f8779bb..e30dc343b4 100644
--- a/direct/src/leveleditor/LayerEditorUI.py
+++ b/direct/src/leveleditor/LayerEditorUI.py
@@ -2,7 +2,7 @@
Defines Layer UI
"""
import wx
-from pandac.PandaModules import *
+from panda3d.core import *
from . import ObjectGlobals as OG
diff --git a/direct/src/leveleditor/LevelEditor.py b/direct/src/leveleditor/LevelEditor.py
index c1322c455d..36b1dd86e0 100644
--- a/direct/src/leveleditor/LevelEditor.py
+++ b/direct/src/leveleditor/LevelEditor.py
@@ -31,7 +31,7 @@ class LevelEditor(LevelEditorBase):
# Populating uderlined data-structures
self.ui = LevelEditorUI(self)
- self.ui.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+ self.ui.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
self.objectPalette.populate()
self.protoPalette.populate()
@@ -42,4 +42,4 @@ class LevelEditor(LevelEditorBase):
# When you define your own LevelEditor class inheriting LevelEditorBase
# you should call self.initialize() at the end of __init__() function
self.initialize()
- self.ui.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
+ self.ui.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
diff --git a/direct/src/leveleditor/LevelEditorBase.py b/direct/src/leveleditor/LevelEditorBase.py
index ad351a7e39..16ff696744 100755
--- a/direct/src/leveleditor/LevelEditorBase.py
+++ b/direct/src/leveleditor/LevelEditorBase.py
@@ -305,7 +305,7 @@ class LevelEditorBase(DirectObject):
if self.settingsFile is None:
return
- self.ui.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+ self.ui.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
try:
f = open(self.settingsFile, 'r')
configLines = f.readlines()
@@ -342,7 +342,7 @@ class LevelEditorBase(DirectObject):
self.ui.updateMenu()
except:
pass
- self.ui.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
+ self.ui.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
def convertMaya(self, modelname, callBack, obj=None, isAnim=False):
if obj and isAnim:
diff --git a/direct/src/leveleditor/LevelEditorStart.py b/direct/src/leveleditor/LevelEditorStart.py
index 4ec577106c..bcb8800c94 100644
--- a/direct/src/leveleditor/LevelEditorStart.py
+++ b/direct/src/leveleditor/LevelEditorStart.py
@@ -1,4 +1,4 @@
-from . import LevelEditor
+from direct.leveleditor import LevelEditor
if __name__ == '__main__':
base.le = LevelEditor.LevelEditor()
diff --git a/direct/src/leveleditor/LevelEditorUIBase.py b/direct/src/leveleditor/LevelEditorUIBase.py
index f457f838d6..33101dff22 100755
--- a/direct/src/leveleditor/LevelEditorUIBase.py
+++ b/direct/src/leveleditor/LevelEditorUIBase.py
@@ -2,7 +2,7 @@
## import os
## from wx.lib.agw import fourwaysplitter as FWS
-from pandac.PandaModules import *
+from panda3d.core import *
from direct.wxwidgets.WxPandaShell import *
from direct.directtools.DirectSelection import SelectionRay
@@ -478,7 +478,7 @@ class LevelEditorUIBase(WxPandaShell):
self.editor.reset()
def onOpen(self, evt=None):
- dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", wx.OPEN)
+ dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if dialog.ShowModal() == wx.ID_OK:
self.editor.load(dialog.GetPath())
self.editor.setTitleWithFilename(dialog.GetPath())
@@ -492,7 +492,7 @@ class LevelEditorUIBase(WxPandaShell):
self.editor.save()
def onSaveAs(self, evt):
- dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", wx.SAVE)
+ dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
result = True
if dialog.ShowModal() == wx.ID_OK:
self.editor.saveAs(dialog.GetPath())
@@ -503,7 +503,7 @@ class LevelEditorUIBase(WxPandaShell):
return result
def onExportToMaya(self, evt):
- dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.mb", wx.SAVE)
+ dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.mb", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
if dialog.ShowModal() == wx.ID_OK:
self.editor.exportToMaya(dialog.GetPath())
dialog.Destroy()
diff --git a/direct/src/leveleditor/ObjectMgrBase.py b/direct/src/leveleditor/ObjectMgrBase.py
index 3a65d6a998..2e9cc2a17c 100755
--- a/direct/src/leveleditor/ObjectMgrBase.py
+++ b/direct/src/leveleditor/ObjectMgrBase.py
@@ -6,7 +6,7 @@ import os, time, copy
from direct.task import Task
from direct.actor.Actor import Actor
-from pandac.PandaModules import *
+from panda3d.core import *
from .ActionMgr import *
from . import ObjectGlobals as OG
diff --git a/direct/src/leveleditor/ObjectPaletteUI.py b/direct/src/leveleditor/ObjectPaletteUI.py
index bf84e3d492..ffd168a258 100755
--- a/direct/src/leveleditor/ObjectPaletteUI.py
+++ b/direct/src/leveleditor/ObjectPaletteUI.py
@@ -69,4 +69,4 @@ class ObjectPaletteUI(wx.Panel):
return cmp(index1, index2)
def getSelected(self):
- return self.tree.GetItemPyData(self.tree.GetSelection())
+ return self.tree.GetItemData(self.tree.GetSelection())
diff --git a/direct/src/leveleditor/ObjectPropertyUI.py b/direct/src/leveleditor/ObjectPropertyUI.py
index 08adb8b784..4b76cb8879 100755
--- a/direct/src/leveleditor/ObjectPropertyUI.py
+++ b/direct/src/leveleditor/ObjectPropertyUI.py
@@ -9,7 +9,7 @@ from wx.lib.embeddedimage import PyEmbeddedImage
from wx.lib.scrolledpanel import ScrolledPanel
from wx.lib.agw.cubecolourdialog import *
from direct.wxwidgets.WxSlider import *
-from pandac.PandaModules import *
+from panda3d.core import *
from . import ObjectGlobals as OG
from . import AnimGlobals as AG
diff --git a/direct/src/leveleditor/PaletteTreeCtrl.py b/direct/src/leveleditor/PaletteTreeCtrl.py
index c4a5d46fce..75c752f0fc 100644
--- a/direct/src/leveleditor/PaletteTreeCtrl.py
+++ b/direct/src/leveleditor/PaletteTreeCtrl.py
@@ -41,7 +41,7 @@ class PaletteTreeCtrl(wx.TreeCtrl):
roots.append(key)
for root in roots:
newItem = self.AppendItem(parentItem, root)
- self.SetItemPyData(newItem, root)
+ self.SetItemData(newItem, root)
rootItems.append(newItem)
itemKeys.remove(root)
for rootItem in rootItems:
@@ -108,7 +108,7 @@ class PaletteTreeCtrl(wx.TreeCtrl):
item, cookie = self.GetFirstChild(parent)
while item:
itemName = self.GetItemText(item)
- itemData = self.GetItemPyData(item)
+ itemData = self.GetItemData(item)
newItem = self.AppendItem(newParent, itemName)
self.SetItemPyData(newItem, itemData)
diff --git a/direct/src/leveleditor/ProtoObjsUI.py b/direct/src/leveleditor/ProtoObjsUI.py
index 63222e3a2d..a4e09af9f3 100755
--- a/direct/src/leveleditor/ProtoObjsUI.py
+++ b/direct/src/leveleditor/ProtoObjsUI.py
@@ -4,7 +4,7 @@ Defines ProtoObjs List UI
import wx
import os
-from pandac.PandaModules import *
+from panda3d.core import *
from .ProtoObjs import *
class ProtoDropTarget(wx.PyDropTarget):
diff --git a/direct/src/leveleditor/ProtoPaletteUI.py b/direct/src/leveleditor/ProtoPaletteUI.py
index d9f047b55f..6736311821 100755
--- a/direct/src/leveleditor/ProtoPaletteUI.py
+++ b/direct/src/leveleditor/ProtoPaletteUI.py
@@ -3,13 +3,13 @@ Defines ProtoPalette tree UI
"""
import wx
import os
-from pandac.PandaModules import *
+from panda3d.core import *
from .PaletteTreeCtrl import *
-class UniversalDropTarget(wx.PyDropTarget):
+class UniversalDropTarget(wx.DropTarget):
"""Implements drop target functionality to receive files, bitmaps and text"""
def __init__(self, editor):
- wx.PyDropTarget.__init__(self)
+ wx.DropTarget.__init__(self)
self.editor = editor
self.do = wx.DataObjectComposite() # the dataobject that gets filled with the appropriate data
self.filedo = wx.FileDataObject()
diff --git a/direct/src/leveleditor/SceneGraphUIBase.py b/direct/src/leveleditor/SceneGraphUIBase.py
index c98cf8505c..66537fbbdf 100755
--- a/direct/src/leveleditor/SceneGraphUIBase.py
+++ b/direct/src/leveleditor/SceneGraphUIBase.py
@@ -2,7 +2,7 @@
Defines Scene Graph tree UI Base
"""
import wx
-from pandac.PandaModules import *
+from panda3d.core import *
from .ActionMgr import *
from . import ObjectGlobals as OG
@@ -26,7 +26,7 @@ class SceneGraphUIBase(wx.Panel):
size=wx.DefaultSize, style=wx.TR_MULTIPLE|wx.TR_DEFAULT_STYLE,
validator=wx.DefaultValidator, name="treeCtrl")
self.root = self.tree.AddRoot('render')
- self.tree.SetItemPyData(self.root, "render")
+ self.tree.SetItemData(self.root, "render")
self.shouldShowPandaObjChildren = False
@@ -61,7 +61,7 @@ class SceneGraphUIBase(wx.Panel):
self.tree.Delete(item)
def traversePandaObjects(self, parent, objNodePath):
- itemId = self.tree.GetItemPyData(parent)
+ itemId = self.tree.GetItemData(parent)
i = 0
for child in objNodePath.getChildren():
if child.hasTag('OBJRoot'):
@@ -78,7 +78,7 @@ class SceneGraphUIBase(wx.Panel):
def addPandaObjectChildren(self, parent):
# first, find Panda Object's NodePath of the item
- itemId = self.tree.GetItemPyData(parent)
+ itemId = self.tree.GetItemData(parent)
if itemId == "render":
return
obj = self.editor.objectMgr.findObjectById(itemId)
@@ -96,7 +96,7 @@ class SceneGraphUIBase(wx.Panel):
def removePandaObjectChildren(self, parent):
# first, find Panda Object's NodePath of the item
- itemId = self.tree.GetItemPyData(parent)
+ itemId = self.tree.GetItemData(parent)
if itemId == "render":
return
obj = self.editor.objectMgr.findObjectById(itemId)
@@ -142,14 +142,14 @@ class SceneGraphUIBase(wx.Panel):
def traverse(self, parent, itemId):
# prevent from traversing into self
- if itemId == self.tree.GetItemPyData(parent):
+ if itemId == self.tree.GetItemData(parent):
return None
# main loop - serching for an item with an itemId
item, cookie = self.tree.GetFirstChild(parent)
while item:
# if the item was found - return it
- if itemId == self.tree.GetItemPyData(item):
+ if itemId == self.tree.GetItemData(item):
return item
# the tem was not found - checking if it has children
@@ -168,7 +168,7 @@ class SceneGraphUIBase(wx.Panel):
item, cookie = self.tree.GetFirstChild(parent)
while item:
data = self.tree.GetItemText(item)
- itemId = self.tree.GetItemPyData(item)
+ itemId = self.tree.GetItemData(item)
newItem = self.tree.AppendItem(newParent, data)
self.tree.SetItemPyData(newItem, itemId)
@@ -186,13 +186,13 @@ class SceneGraphUIBase(wx.Panel):
def reParent(self, oldParent, newParent, child):
if newParent is None:
newParent = self.root
- itemId = self.tree.GetItemPyData(oldParent)
+ itemId = self.tree.GetItemData(oldParent)
newItem = self.tree.AppendItem(newParent, child)
self.tree.SetItemPyData(newItem, itemId)
self.reParentTree(oldParent, newItem)
obj = self.editor.objectMgr.findObjectById(itemId)
- itemId = self.tree.GetItemPyData(newParent)
+ itemId = self.tree.GetItemData(newParent)
if itemId != "render":
newParentObj = self.editor.objectMgr.findObjectById(itemId)
self.reParentData(newParentObj[OG.OBJ_NP], obj[OG.OBJ_NP])
@@ -207,7 +207,7 @@ class SceneGraphUIBase(wx.Panel):
self.addPandaObjectChildren(newpParent)
def isChildOrGrandChild(self, parent, child):
- childId = self.tree.GetItemPyData(child)
+ childId = self.tree.GetItemData(child)
return self.traverse(parent, childId)
def changeHierarchy(self, data, x, y):
@@ -226,7 +226,7 @@ class SceneGraphUIBase(wx.Panel):
return
# undo function setup...
- action = ActionChangeHierarchy(self.editor, self.tree.GetItemPyData(self.tree.GetItemParent(item)), self.tree.GetItemPyData(item), self.tree.GetItemPyData(dragToItem), data)
+ action = ActionChangeHierarchy(self.editor, self.tree.GetItemData(self.tree.GetItemParent(item)), self.tree.GetItemData(item), self.tree.GetItemData(dragToItem), data)
self.editor.actionMgr.push(action)
action()
@@ -283,7 +283,7 @@ class SceneGraphUIBase(wx.Panel):
def onSelected(self, event):
item = event.GetItem();
if item:
- itemId = self.tree.GetItemPyData(item)
+ itemId = self.tree.GetItemData(item)
if itemId:
obj = self.editor.objectMgr.findObjectById(itemId);
if obj:
@@ -313,7 +313,7 @@ class SceneGraphUIBase(wx.Panel):
if not item.IsOk():
return
self.currItem = item
- itemId = self.tree.GetItemPyData(item)
+ itemId = self.tree.GetItemData(item)
if not itemId:
return
self.currObj = self.editor.objectMgr.findObjectById(itemId);
diff --git a/direct/src/leveleditor/testData.py b/direct/src/leveleditor/testData.py
index 28d4a04d1c..d35c01b103 100755
--- a/direct/src/leveleditor/testData.py
+++ b/direct/src/leveleditor/testData.py
@@ -1,4 +1,4 @@
-from pandac.PandaModules import *
+from panda3d.core import *
if hasattr(base, 'le'):
objectMgr = base.le.objectMgr
diff --git a/direct/src/p3d/AppRunner.py b/direct/src/p3d/AppRunner.py
deleted file mode 100644
index aae03754e6..0000000000
--- a/direct/src/p3d/AppRunner.py
+++ /dev/null
@@ -1,1245 +0,0 @@
-"""
-This module is intended to be compiled into the Panda3D runtime
-distributable, to execute a packaged p3d application, but it can also
-be run directly via the Python interpreter (if the current Panda3D and
-Python versions match the version expected by the application). See
-runp3d.py for a command-line tool to invoke this module.
-
-The global AppRunner instance may be imported as follows::
-
- from direct.showbase.AppRunnerGlobal import appRunner
-
-This will be None if Panda was not run from the runtime environment.
-"""
-
-__all__ = ["AppRunner", "dummyAppRunner", "ArgumentError"]
-
-import sys
-import os
-
-if sys.version_info >= (3, 0):
- import builtins
-else:
- import __builtin__ as builtins
-
-from direct.showbase import VFSImporter
-from direct.showbase.DirectObject import DirectObject
-from panda3d.core import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, WindowProperties, ExecutionEnvironment, PandaSystem, Notify, StreamWriter, ConfigVariableString, ConfigPageManager
-from panda3d.direct import init_app_for_gui
-from panda3d import core
-from direct.stdpy import file, glob
-from direct.task.TaskManagerGlobal import taskMgr
-from direct.showbase.MessengerGlobal import messenger
-from direct.showbase import AppRunnerGlobal
-from direct.directnotify.DirectNotifyGlobal import directNotify
-from direct.p3d.HostInfo import HostInfo
-from direct.p3d.ScanDirectoryNode import ScanDirectoryNode
-from direct.p3d.InstalledHostData import InstalledHostData
-from direct.p3d.InstalledPackageData import InstalledPackageData
-
-# These imports are read by the C++ wrapper in p3dPythonRun.cxx.
-from direct.p3d.JavaScript import Undefined, ConcreteStruct
-
-class ArgumentError(AttributeError):
- pass
-
-class ScriptAttributes:
- """ This dummy class serves as the root object for the scripting
- interface. The Python code can store objects and functions here
- for direct inspection by the browser's JavaScript code. """
- pass
-
-class AppRunner(DirectObject):
-
- """ This class is intended to be compiled into the Panda3D runtime
- distributable, to execute a packaged p3d application. It also
- provides some useful runtime services while running in that
- packaged environment.
-
- It does not usually exist while running Python directly, but you
- can use dummyAppRunner() to create one at startup for testing or
- development purposes. """
-
- notify = directNotify.newCategory("AppRunner")
-
- ConfigBasename = 'config.xml'
-
- # Default values for parameters that are absent from the config file:
- maxDiskUsage = 2048 * 1048576 # 2 GB
-
- # Values for verifyContents, from p3d_plugin.h
- P3DVCNone = 0
- P3DVCNormal = 1
- P3DVCForce = 2
- P3DVCNever = 3
-
- # Also from p3d_plugin.h
- P3D_CONTENTS_DEFAULT_MAX_AGE = 5
-
- def __init__(self):
- DirectObject.__init__(self)
-
- # We direct both our stdout and stderr objects onto Panda's
- # Notify stream. This ensures that unadorned print statements
- # made within Python will get routed into the log properly.
- stream = StreamWriter(Notify.out(), False)
- sys.stdout = stream
- sys.stderr = stream
-
- # This is set true by dummyAppRunner(), below.
- self.dummy = False
-
- # These will be set from the application flags when
- # setP3DFilename() is called.
- self.allowPythonDev = False
- self.guiApp = False
- self.interactiveConsole = False
- self.initialAppImport = False
- self.trueFileIO = False
- self.respectPerPlatform = None
-
- self.verifyContents = self.P3DVCNone
-
- self.sessionId = 0
- self.packedAppEnvironmentInitialized = False
- self.gotWindow = False
- self.gotP3DFilename = False
- self.p3dFilename = None
- self.p3dUrl = None
- self.started = False
- self.windowOpened = False
- self.windowPrc = None
-
- self.http = None
- if hasattr(core, 'HTTPClient'):
- self.http = core.HTTPClient.getGlobalPtr()
-
- self.Undefined = Undefined
- self.ConcreteStruct = ConcreteStruct
-
- # This is per session.
- self.nextScriptId = 0
-
- # TODO: we need one of these per instance, not per session.
- self.instanceId = None
-
- # The root Panda3D install directory. This is filled in when
- # the instance starts up.
- self.rootDir = None
-
- # The log directory. Also filled in when the instance starts.
- self.logDirectory = None
-
- # self.superMirrorUrl, if nonempty, is the "super mirror" URL
- # that should be contacted first before trying the actual
- # host. This is primarily used for "downloading" from a
- # locally-stored Panda3D installation. This is also filled in
- # when the instance starts up.
- self.superMirrorUrl = None
-
- # A list of the Panda3D packages that have been loaded.
- self.installedPackages = []
-
- # A list of the Panda3D packages that in the queue to be
- # downloaded.
- self.downloadingPackages = []
-
- # A dictionary of HostInfo objects for the various download
- # hosts we have imported packages from.
- self.hosts = {}
-
- # The altHost string that is in effect from the HTML tokens,
- # if any, and the dictionary of URL remapping: orig host url
- # -> alt host url.
- self.altHost = None
- self.altHostMap = {}
-
- # The URL from which Panda itself should be downloaded.
- self.pandaHostUrl = PandaSystem.getPackageHostUrl()
-
- # Application code can assign a callable object here; if so,
- # it will be invoked when an uncaught exception propagates to
- # the top of the TaskMgr.run() loop.
- self.exceptionHandler = None
-
- # Managing packages for runtime download.
- self.downloadingPackages = []
- self.downloadTask = None
-
- # The mount point for the multifile. For now, this is always
- # the current working directory, for convenience; but when we
- # move to multiple-instance sessions, it may have to be
- # different for each instance.
- self.multifileRoot = str(ExecutionEnvironment.getCwd())
-
- # The "main" object will be exposed to the DOM as a property
- # of the plugin object; that is, document.pluginobject.main in
- # JavaScript will be appRunner.main here. This may be
- # replaced with a direct reference to the JavaScript object
- # later, in setInstanceInfo().
- self.main = ScriptAttributes()
-
- # By default, we publish a stop() method so the browser can
- # easy stop the plugin. A particular application can remove
- # this if it chooses.
- self.main.stop = self.stop
-
- # This will be the browser's toplevel window DOM object;
- # e.g. self.dom.document will be the document.
- self.dom = None
-
- # This is the list of expressions we will evaluate when
- # self.dom gets assigned.
- self.deferredEvals = []
-
- # This is the default requestFunc that is installed if we
- # never call setRequestFunc().
- def defaultRequestFunc(*args):
- if args[1] == 'notify':
- # Quietly ignore notifies.
- return
- self.notify.info("Ignoring request: %s" % (args,))
- self.requestFunc = defaultRequestFunc
-
- # This will be filled in with the default WindowProperties for
- # this instance, e.g. the WindowProperties necessary to
- # re-embed a window in the browser frame.
- self.windowProperties = None
-
- # Store our pointer so DirectStart-based apps can find us.
- if AppRunnerGlobal.appRunner is None:
- AppRunnerGlobal.appRunner = self
-
- # We use this messenger hook to dispatch this __startIfReady()
- # call back to the main thread.
- self.accept('AppRunner_startIfReady', self.__startIfReady)
-
- def getToken(self, tokenName):
- """ Returns the value of the indicated web token as a string,
- if it was set, or None if it was not. """
-
- return self.tokenDict.get(tokenName.lower(), None)
-
- def getTokenInt(self, tokenName):
- """ Returns the value of the indicated web token as an integer
- value, if it was set, or None if it was not, or not an
- integer. """
-
- value = self.getToken(tokenName)
- if value is not None:
- try:
- value = int(value)
- except ValueError:
- value = None
- return value
-
- def getTokenFloat(self, tokenName):
- """ Returns the value of the indicated web token as a
- floating-point value value, if it was set, or None if it was
- not, or not a number. """
-
- value = self.getToken(tokenName)
- if value is not None:
- try:
- value = float(value)
- except ValueError:
- value = None
- return value
-
- def getTokenBool(self, tokenName):
- """ Returns the value of the indicated web token as a boolean
- value, if it was set, or None if it was not. """
-
- value = self.getTokenInt(tokenName)
- if value is not None:
- value = bool(value)
- return value
-
-
-
- def installPackage(self, packageName, version = None, hostUrl = None):
-
- """ Installs the named package, downloading it first if
- necessary. Returns true on success, false on failure. This
- method runs synchronously, and will block until it is
- finished; see the PackageInstaller class if you want this to
- happen asynchronously instead. """
-
- host = self.getHostWithAlt(hostUrl)
- if not host.downloadContentsFile(self.http):
- return False
-
- # All right, get the package info now.
- package = host.getPackage(packageName, version)
- if not package:
- self.notify.warning("Package %s %s not known on %s" % (
- packageName, version, hostUrl))
- return False
-
- return self.__rInstallPackage(package, [])
-
- def __rInstallPackage(self, package, nested):
- """ The recursive implementation of installPackage(). The new
- parameter, nested, is a list of packages that we are
- recursively calling this from, to avoid recursive loops. """
-
- package.checkStatus()
- if not package.downloadDescFile(self.http):
- return False
-
- # Now that we've downloaded and read the desc file, we can
- # install all of the required packages first.
- nested = nested[:] + [self]
- for packageName, version, host in package.requires:
- if host.downloadContentsFile(self.http):
- p2 = host.getPackage(packageName, version)
- if not p2:
- self.notify.warning("Couldn't find %s %s on %s" % (packageName, version, host.hostUrl))
- else:
- if p2 not in nested:
- self.__rInstallPackage(p2, nested)
-
- # Now that all of the required packages are installed, carry
- # on to download and install this package.
- if not package.downloadPackage(self.http):
- return False
-
- if not package.installPackage(self):
- return False
-
- self.notify.info("Package %s %s installed." % (
- package.packageName, package.packageVersion))
- return True
-
- def getHostWithAlt(self, hostUrl):
- """ Returns a suitable HostInfo object for downloading
- contents from the indicated URL. This is almost always the
- same thing as getHost(), except in the rare case when we have
- an alt_host specified in the HTML tokens; in this case, we may
- actually want to download the contents from a different URL
- than the one given, for instance to download a version in
- testing. """
-
- if hostUrl is None:
- hostUrl = self.pandaHostUrl
-
- altUrl = self.altHostMap.get(hostUrl, None)
- if altUrl:
- # We got an alternate host. Use it.
- return self.getHost(altUrl)
-
- # We didn't get an aternate host, use the original.
- host = self.getHost(hostUrl)
-
- # But we might need to consult the host itself to see if *it*
- # recommends an altHost.
- if self.altHost:
- # This means forcing the host to download its contents
- # file on the spot, a blocking operation. This is a
- # little unfortunate, but since alt_host is so rarely
- # used, probably not really a problem.
- host.downloadContentsFile(self.http)
- altUrl = host.altHosts.get(self.altHost, None)
- if altUrl:
- return self.getHost(altUrl)
-
- # No shenanigans, just return the requested host.
- return host
-
- def getHost(self, hostUrl, hostDir = None):
- """ Returns a new HostInfo object corresponding to the
- indicated host URL. If we have already seen this URL
- previously, returns the same object.
-
- This returns the literal referenced host. To return the
- mapped host, which is the one we should actually download
- from, see getHostWithAlt(). """
-
- if not hostUrl:
- hostUrl = self.pandaHostUrl
-
- host = self.hosts.get(hostUrl, None)
- if not host:
- host = HostInfo(hostUrl, appRunner = self, hostDir = hostDir)
- self.hosts[hostUrl] = host
- return host
-
- def getHostWithDir(self, hostDir):
- """ Returns the HostInfo object that corresponds to the
- indicated on-disk host directory. This would be used when
- reading a host directory from disk, instead of downloading it
- from a server. Supply the full path to the host directory, as
- a Filename. Returns None if the contents.xml in the indicated
- host directory cannot be read or doesn't seem consistent. """
-
- host = HostInfo(None, hostDir = hostDir, appRunner = self)
- if not host.hasContentsFile:
- if not host.readContentsFile():
- # Couldn't read the contents.xml file
- return None
-
- if not host.hostUrl:
- # The contents.xml file there didn't seem to indicate the
- # same host directory.
- return None
-
- host2 = self.hosts.get(host.hostUrl)
- if host2 is None:
- # No such host already; store this one.
- self.hosts[host.hostUrl] = host
- return host
-
- if host2.hostDir != host.hostDir:
- # Hmm, we already have that host somewhere else.
- return None
-
- # We already have that host, and it's consistent.
- return host2
-
- def deletePackages(self, packages):
- """ Removes all of the indicated packages from the disk,
- uninstalling them and deleting all of their files. The
- packages parameter must be a list of one or more PackageInfo
- objects, for instance as returned by getHost().getPackage().
- Returns the list of packages that were NOT found. """
-
- for hostUrl, host in self.hosts.items():
- packages = host.deletePackages(packages)
-
- if not host.packages:
- # If that's all of the packages for this host, delete
- # the host directory too.
- del self.hosts[hostUrl]
- self.__deleteHostFiles(host)
-
- return packages
-
- def __deleteHostFiles(self, host):
- """ Called by deletePackages(), this removes all the files for
- the indicated host (for which we have presumably already
- removed all of the packages). """
-
- self.notify.info("Deleting host %s: %s" % (host.hostUrl, host.hostDir))
- self.rmtree(host.hostDir)
-
- self.sendRequest('forget_package', host.hostUrl, '', '')
-
-
- def freshenFile(self, host, fileSpec, localPathname):
- """ Ensures that the localPathname is the most current version
- of the file defined by fileSpec, as offered by host. If not,
- it downloads a new version on-the-spot. Returns true on
- success, false on failure. """
-
- assert self.http
- return host.freshenFile(self.http, fileSpec, localPathname)
-
- def scanInstalledPackages(self):
- """ Scans the hosts and packages already installed locally on
- the system. Returns a list of InstalledHostData objects, each
- of which contains a list of InstalledPackageData objects. """
-
- result = []
- hostsFilename = Filename(self.rootDir, 'hosts')
- hostsDir = ScanDirectoryNode(hostsFilename)
- for dirnode in hostsDir.nested:
- host = self.getHostWithDir(dirnode.pathname)
- hostData = InstalledHostData(host, dirnode)
-
- if host:
- for package in host.getAllPackages(includeAllPlatforms = True):
- packageDir = package.getPackageDir()
- if not packageDir.exists():
- continue
-
- subdir = dirnode.extractSubdir(packageDir)
- if not subdir:
- # This package, while defined by the host, isn't installed
- # locally; ignore it.
- continue
-
- packageData = InstalledPackageData(package, subdir)
- hostData.packages.append(packageData)
-
- # Now that we've examined all of the packages for the host,
- # anything left over is junk.
- for subdir in dirnode.nested:
- packageData = InstalledPackageData(None, subdir)
- hostData.packages.append(packageData)
-
- result.append(hostData)
-
- return result
-
- def readConfigXml(self):
- """ Reads the config.xml file that may be present in the root
- directory. """
-
- if not hasattr(core, 'TiXmlDocument'):
- return
-
- filename = Filename(self.rootDir, self.ConfigBasename)
- doc = core.TiXmlDocument(filename.toOsSpecific())
- if not doc.LoadFile():
- return
-
- xconfig = doc.FirstChildElement('config')
- if xconfig:
- maxDiskUsage = xconfig.Attribute('max_disk_usage')
- try:
- self.maxDiskUsage = int(maxDiskUsage or '')
- except ValueError:
- pass
-
- def writeConfigXml(self):
- """ Rewrites the config.xml to the root directory. This isn't
- called automatically; an application may call this after
- adjusting some parameters (such as self.maxDiskUsage). """
-
- from panda3d.core import TiXmlDocument, TiXmlDeclaration, TiXmlElement
-
- filename = Filename(self.rootDir, self.ConfigBasename)
- doc = TiXmlDocument(filename.toOsSpecific())
- decl = TiXmlDeclaration("1.0", "utf-8", "")
- doc.InsertEndChild(decl)
-
- xconfig = TiXmlElement('config')
- xconfig.SetAttribute('max_disk_usage', str(self.maxDiskUsage))
- doc.InsertEndChild(xconfig)
-
- # Write the file to a temporary filename, then atomically move
- # it to its actual filename, to avoid race conditions when
- # updating this file.
- tfile = Filename.temporary(str(self.rootDir), '.xml')
- if doc.SaveFile(tfile.toOsSpecific()):
- tfile.renameTo(filename)
-
-
- def checkDiskUsage(self):
- """ Checks the total disk space used by all packages, and
- removes old packages if necessary. """
-
- totalSize = 0
- hosts = self.scanInstalledPackages()
- for hostData in hosts:
- for packageData in hostData.packages:
- totalSize += packageData.totalSize
- self.notify.info("Total Panda3D disk space used: %s MB" % (
- (totalSize + 524288) // 1048576))
-
- if self.verifyContents == self.P3DVCNever:
- # We're not allowed to delete anything anyway.
- return
-
- self.notify.info("Configured max usage is: %s MB" % (
- (self.maxDiskUsage + 524288) // 1048576))
- if totalSize <= self.maxDiskUsage:
- # Still within budget; no need to clean up anything.
- return
-
- # OK, we're over budget. Now we have to remove old packages.
- usedPackages = []
- for hostData in hosts:
- for packageData in hostData.packages:
- if packageData.package and packageData.package.installed:
- # Don't uninstall any packages we're currently using.
- continue
-
- usedPackages.append((packageData.lastUse, packageData))
-
- # Sort the packages into oldest-first order.
- usedPackages.sort()
-
- # Delete packages until we free up enough space.
- packages = []
- for lastUse, packageData in usedPackages:
- if totalSize <= self.maxDiskUsage:
- break
- totalSize -= packageData.totalSize
-
- if packageData.package:
- packages.append(packageData.package)
- else:
- # If it's an unknown package, just delete it directly.
- print("Deleting unknown package %s" % (packageData.pathname))
- self.rmtree(packageData.pathname)
-
- packages = self.deletePackages(packages)
- if packages:
- print("Unable to delete %s packages" % (len(packages)))
-
- return
-
- def stop(self):
- """ This method can be called by JavaScript to stop the
- application. """
-
- # We defer the actual exit for a few frames, so we don't raise
- # an exception and invalidate the JavaScript call; and also to
- # help protect against race conditions as the application
- # shuts down.
- taskMgr.doMethodLater(0.5, sys.exit, 'exit')
-
- def run(self):
- """ This method calls taskMgr.run(), with an optional
- exception handler. This is generally the program's main loop
- when running in a p3d environment (except on unusual platforms
- like the iPhone, which have to hand the main loop off to the
- OS, and don't use this interface). """
-
- try:
- taskMgr.run()
-
- except SystemExit as err:
- # Presumably the window has already been shut down here, but shut
- # it down again for good measure.
- if hasattr(builtins, "base"):
- base.destroy()
-
- self.notify.info("Normal exit with status %s." % repr(err.code))
- raise
-
- except:
- # Some unexpected Python exception; pass it to the
- # optional handler, if it is defined.
- if self.exceptionHandler and not self.interactiveConsole:
- self.exceptionHandler()
- else:
- raise
-
- def rmtree(self, filename):
- """ This is like shutil.rmtree(), but it can remove read-only
- files on Windows. It receives a Filename, the root directory
- to delete. """
- if filename.isDirectory():
- for child in filename.scanDirectory():
- self.rmtree(Filename(filename, child))
- if not filename.rmdir():
- print("could not remove directory %s" % (filename))
- else:
- if not filename.unlink():
- print("could not delete %s" % (filename))
-
- def setSessionId(self, sessionId):
- """ This message should come in at startup. """
- self.sessionId = sessionId
- self.nextScriptId = self.sessionId * 1000 + 10000
-
- def initPackedAppEnvironment(self):
- """ This function sets up the Python environment suitably for
- running a packed app. It should only run once in any given
- session (and it includes logic to ensure this). """
-
- if self.packedAppEnvironmentInitialized:
- return
-
- self.packedAppEnvironmentInitialized = True
-
- vfs = VirtualFileSystem.getGlobalPtr()
-
- # Now set up Python to import this stuff.
- VFSImporter.register()
- sys.path.append(self.multifileRoot)
-
- # Make sure that $MAIN_DIR is set to the p3d root before we
- # start executing the code in this file.
- ExecutionEnvironment.setEnvironmentVariable("MAIN_DIR", Filename(self.multifileRoot).toOsSpecific())
-
- # Put our root directory on the model-path, too.
- getModelPath().appendDirectory(self.multifileRoot)
-
- if not self.trueFileIO:
- # Replace the builtin open and file symbols so user code will get
- # our versions by default, which can open and read files out of
- # the multifile.
- builtins.open = file.open
- if sys.version_info < (3, 0):
- builtins.file = file.open
- builtins.execfile = file.execfile
- os.listdir = file.listdir
- os.walk = file.walk
- os.path.join = file.join
- os.path.isfile = file.isfile
- os.path.isdir = file.isdir
- os.path.exists = file.exists
- os.path.lexists = file.lexists
- os.path.getmtime = file.getmtime
- os.path.getsize = file.getsize
- sys.modules['glob'] = glob
-
- self.checkDiskUsage()
-
- def __startIfReady(self):
- """ Called internally to start the application. """
- if self.started:
- return
-
- if self.gotWindow and self.gotP3DFilename:
- self.started = True
-
- # Now we can ignore future calls to startIfReady().
- self.ignore('AppRunner_startIfReady')
-
- # Hang a hook so we know when the window is actually opened.
- self.acceptOnce('window-event', self.__windowEvent)
-
- # Look for the startup Python file. This might be a magic
- # filename (like "__main__", or any filename that contains
- # invalid module characters), so we can't just import it
- # directly; instead, we go through the low-level importer.
-
- # If there's no p3d_info.xml file, we look for "main".
- moduleName = 'main'
- if self.p3dPackage:
- mainName = self.p3dPackage.Attribute('main_module')
- if mainName:
- moduleName = mainName
-
- # Temporarily set this flag while we import the app, so
- # that if the app calls run() within its own main.py, it
- # will properly get ignored by ShowBase.
- self.initialAppImport = True
-
- # Python won't let us import a module named __main__. So,
- # we have to do that manually, via the VFSImporter.
- if moduleName == '__main__':
- dirName = Filename(self.multifileRoot).toOsSpecific()
- importer = VFSImporter.VFSImporter(dirName)
- loader = importer.find_module('__main__')
- if loader is None:
- raise ImportError('No module named __main__')
-
- mainModule = loader.load_module('__main__')
- else:
- __import__(moduleName)
- mainModule = sys.modules[moduleName]
-
- # Check if it has a main() function. If so, call it.
- if hasattr(mainModule, 'main') and hasattr(mainModule.main, '__call__'):
- mainModule.main(self)
-
- # Now clear this flag.
- self.initialAppImport = False
-
- if self.interactiveConsole:
- # At this point, we have successfully loaded the app.
- # If the interactive_console flag is enabled, stop the
- # main loop now and give the user a Python prompt.
- taskMgr.stop()
-
- def getPandaScriptObject(self):
- """ Called by the browser to query the Panda instance's
- toplevel scripting object, for querying properties in the
- Panda instance. The attributes on this object are mapped to
- document.pluginobject.main within the DOM. """
-
- return self.main
-
- def setBrowserScriptObject(self, dom):
- """ Called by the browser to supply the browser's toplevel DOM
- object, for controlling the JavaScript and the document in the
- same page with the Panda3D plugin. """
-
- self.dom = dom
-
- # Now evaluate any deferred expressions.
- for expression in self.deferredEvals:
- self.scriptRequest('eval', self.dom, value = expression,
- needsResponse = False)
- self.deferredEvals = []
-
- def setInstanceInfo(self, rootDir, logDirectory, superMirrorUrl,
- verifyContents, main, respectPerPlatform):
- """ Called by the browser to set some global information about
- the instance. """
-
- # rootDir is the root Panda3D install directory on the local
- # machine.
- self.rootDir = Filename.fromOsSpecific(rootDir)
-
- # logDirectory is the directory name where all log files end
- # up.
- if logDirectory:
- self.logDirectory = Filename.fromOsSpecific(logDirectory)
- else:
- self.logDirectory = Filename(rootDir, 'log')
-
- # The "super mirror" URL, generally used only by panda3d.exe.
- self.superMirrorUrl = superMirrorUrl
-
- # How anxious should we be about contacting the server for
- # the latest code?
- self.verifyContents = verifyContents
-
- # The initial "main" object, if specified.
- if main is not None:
- self.main = main
-
- self.respectPerPlatform = respectPerPlatform
- #self.notify.info("respectPerPlatform = %s" % (self.respectPerPlatform))
-
- # Now that we have rootDir, we can read the config file.
- self.readConfigXml()
-
-
- def addPackageInfo(self, name, platform, version, hostUrl, hostDir = None,
- recurse = False):
- """ Called by the browser for each one of the "required"
- packages that were preloaded before starting the application.
- If for some reason the package isn't already downloaded, this
- will download it on the spot. Raises OSError on failure. """
-
- host = self.getHost(hostUrl, hostDir = hostDir)
-
- if not host.hasContentsFile:
- # Always pre-read these hosts' contents.xml files, even if
- # we have P3DVCForce in effect, since presumably we've
- # already forced them on the plugin side.
- host.readContentsFile()
-
- if not host.downloadContentsFile(self.http):
- # Couldn't download? Must have failed to download in the
- # plugin as well. But since we launched, we probably have
- # a copy already local; let's use it.
- message = "Host %s cannot be downloaded, cannot preload %s." % (hostUrl, name)
- if not host.hasContentsFile:
- # This is weird. How did we launch without having
- # this file at all?
- raise OSError(message)
-
- # Just make it a warning and continue.
- self.notify.warning(message)
-
- if name == 'panda3d' and not self.pandaHostUrl:
- # A special case: in case we don't have the PackageHostUrl
- # compiled in, infer it from the first package we
- # installed named "panda3d".
- self.pandaHostUrl = hostUrl
-
- if not platform:
- platform = None
- package = host.getPackage(name, version, platform = platform)
- if not package:
- if not recurse:
- # Maybe the contents.xml file isn't current. Re-fetch it.
- if host.redownloadContentsFile(self.http):
- return self.addPackageInfo(name, platform, version, hostUrl, hostDir = hostDir, recurse = True)
-
- message = "Couldn't find %s %s on %s" % (name, version, hostUrl)
- raise OSError(message)
-
- package.checkStatus()
- if not package.downloadDescFile(self.http):
- message = "Couldn't get desc file for %s" % (name)
- raise OSError(message)
-
- if not package.downloadPackage(self.http):
- message = "Couldn't download %s" % (name)
- raise OSError(message)
-
- if not package.installPackage(self):
- message = "Couldn't install %s" % (name)
- raise OSError(message)
-
- if package.guiApp:
- self.guiApp = True
- init_app_for_gui()
-
- def setP3DFilename(self, p3dFilename, tokens, argv, instanceId,
- interactiveConsole, p3dOffset = 0, p3dUrl = None):
- """ Called by the browser to specify the p3d file that
- contains the application itself, along with the web tokens
- and/or command-line arguments. Once this method has been
- called, the application is effectively started. """
-
- # One day we will have support for multiple instances within a
- # Python session. Against that day, we save the instance ID
- # for this instance.
- self.instanceId = instanceId
-
- self.tokens = tokens
- self.argv = argv
-
- # We build up a token dictionary with care, so that if a given
- # token appears twice in the token list, we record only the
- # first value, not the second or later. This is consistent
- # with the internal behavior of the core API.
- self.tokenDict = {}
- for token, keyword in tokens:
- self.tokenDict.setdefault(token, keyword)
-
- # Also store the arguments on sys, for applications that
- # aren't instance-ready.
- sys.argv = argv
-
- # That means we now know the altHost in effect.
- self.altHost = self.tokenDict.get('alt_host', None)
-
- # Tell the browser that Python is up and running, and ready to
- # respond to queries.
- self.notifyRequest('onpythonload')
-
- # Now go load the applet.
- fname = Filename.fromOsSpecific(p3dFilename)
- vfs = VirtualFileSystem.getGlobalPtr()
-
- if not vfs.exists(fname):
- raise ArgumentError("No such file: %s" % (p3dFilename))
-
- fname.makeAbsolute()
- fname.setBinary()
- mf = Multifile()
- if p3dOffset == 0:
- if not mf.openRead(fname):
- raise ArgumentError("Not a Panda3D application: %s" % (p3dFilename))
- else:
- if not mf.openRead(fname, p3dOffset):
- raise ArgumentError("Not a Panda3D application: %s at offset: %s" % (p3dFilename, p3dOffset))
-
- # Now load the p3dInfo file.
- self.p3dInfo = None
- self.p3dPackage = None
- self.p3dConfig = None
- self.allowPythonDev = False
-
- i = mf.findSubfile('p3d_info.xml')
- if i >= 0 and hasattr(core, 'readXmlStream'):
- stream = mf.openReadSubfile(i)
- self.p3dInfo = core.readXmlStream(stream)
- mf.closeReadSubfile(stream)
- if self.p3dInfo:
- self.p3dPackage = self.p3dInfo.FirstChildElement('package')
- if self.p3dPackage:
- self.p3dConfig = self.p3dPackage.FirstChildElement('config')
-
- xhost = self.p3dPackage.FirstChildElement('host')
- while xhost:
- self.__readHostXml(xhost)
- xhost = xhost.NextSiblingElement('host')
-
- if self.p3dConfig:
- allowPythonDev = self.p3dConfig.Attribute('allow_python_dev')
- if allowPythonDev:
- self.allowPythonDev = int(allowPythonDev)
- guiApp = self.p3dConfig.Attribute('gui_app')
- if guiApp:
- self.guiApp = int(guiApp)
-
- trueFileIO = self.p3dConfig.Attribute('true_file_io')
- if trueFileIO:
- self.trueFileIO = int(trueFileIO)
-
- # The interactiveConsole flag can only be set true if the
- # application has allow_python_dev set.
- if not self.allowPythonDev and interactiveConsole:
- raise Exception("Impossible, interactive_console set without allow_python_dev.")
- self.interactiveConsole = interactiveConsole
-
- if self.allowPythonDev:
- # Set the fps text to remind the user that
- # allow_python_dev is enabled.
- ConfigVariableString('frame-rate-meter-text-pattern').setValue('allow_python_dev %0.1f fps')
-
- if self.guiApp:
- init_app_for_gui()
-
- self.initPackedAppEnvironment()
-
- # Mount the Multifile under self.multifileRoot.
- vfs.mount(mf, self.multifileRoot, vfs.MFReadOnly)
- self.p3dMultifile = mf
- VFSImporter.reloadSharedPackages()
-
- self.loadMultifilePrcFiles(mf, self.multifileRoot)
- self.gotP3DFilename = True
- self.p3dFilename = fname
- if p3dUrl:
- # The url from which the p3d file was downloaded is
- # provided if available. It is only for documentation
- # purposes; the actual p3d file has already been
- # downloaded to p3dFilename.
- self.p3dUrl = core.URLSpec(p3dUrl)
-
- # Send this call to the main thread; don't call it directly.
- messenger.send('AppRunner_startIfReady', taskChain = 'default')
-
- def __readHostXml(self, xhost):
- """ Reads the data in the indicated entry. """
-
- url = xhost.Attribute('url')
- host = self.getHost(url)
- host.readHostXml(xhost)
-
- # Scan for a matching . If found, it means we
- # should use the alternate URL instead of the original URL.
- if self.altHost:
- xalthost = xhost.FirstChildElement('alt_host')
- while xalthost:
- keyword = xalthost.Attribute('keyword')
- if keyword == self.altHost:
- origUrl = xhost.Attribute('url')
- newUrl = xalthost.Attribute('url')
- self.altHostMap[origUrl] = newUrl
- break
-
- xalthost = xalthost.NextSiblingElement('alt_host')
-
- def loadMultifilePrcFiles(self, mf, root):
- """ Loads any prc files in the root of the indicated
- Multifile, which is presumed to have been mounted already
- under root. """
-
- # We have to load these prc files explicitly, since the
- # ConfigPageManager can't directly look inside the vfs. Use
- # the Multifile interface to find the prc files, rather than
- # vfs.scanDirectory(), so we only pick up the files in this
- # particular multifile.
- cpMgr = ConfigPageManager.getGlobalPtr()
- for f in mf.getSubfileNames():
- fn = Filename(f)
- if fn.getDirname() == '' and fn.getExtension() == 'prc':
- pathname = '%s/%s' % (root, f)
-
- alreadyLoaded = False
- for cpi in range(cpMgr.getNumImplicitPages()):
- if cpMgr.getImplicitPage(cpi).getName() == pathname:
- # No need to load this file twice.
- alreadyLoaded = True
- break
-
- if not alreadyLoaded:
- data = file.open(Filename(pathname), 'r').read()
- cp = loadPrcFileData(pathname, data)
- # Set it to sort value 20, behind the implicit pages.
- cp.setSort(20)
-
-
- def __clearWindowProperties(self):
- """ Clears the windowPrc file that was created in a previous
- call to setupWindow(), if any. """
-
- if self.windowPrc:
- unloadPrcFile(self.windowPrc)
- self.windowPrc = None
- WindowProperties.clearDefault()
-
- # However, we keep the self.windowProperties object around, in
- # case an application wants to return the window to the
- # browser frame.
-
- def setupWindow(self, windowType, x, y, width, height,
- parent):
- """ Applies the indicated window parameters to the prc
- settings, for future windows; or applies them directly to the
- main window if the window has already been opened. This is
- called by the browser. """
-
- if self.started and base.win:
- # If we've already got a window, this must be a
- # resize/reposition request.
- wp = WindowProperties()
- if x or y or windowType == 'embedded':
- wp.setOrigin(x, y)
- if width or height:
- wp.setSize(width, height)
- if windowType == 'embedded':
- wp.setParentWindow(parent)
- wp.setFullscreen(False)
- base.win.requestProperties(wp)
- self.windowProperties = wp
- return
-
- # If we haven't got a window already, start 'er up. Apply the
- # requested setting to the prc file, and to the default
- # WindowProperties structure.
-
- self.__clearWindowProperties()
-
- if windowType == 'hidden':
- data = 'window-type none\n'
- else:
- data = 'window-type onscreen\n'
-
- wp = WindowProperties.getDefault()
-
- wp.clearParentWindow()
- wp.clearOrigin()
- wp.clearSize()
-
- wp.setFullscreen(False)
- if windowType == 'fullscreen':
- wp.setFullscreen(True)
-
- if windowType == 'embedded':
- wp.setParentWindow(parent)
-
- if x or y or windowType == 'embedded':
- wp.setOrigin(x, y)
-
- if width or height:
- wp.setSize(width, height)
-
- self.windowProperties = wp
- self.windowPrc = loadPrcFileData("setupWindow", data)
- WindowProperties.setDefault(wp)
-
- self.gotWindow = True
-
- # Send this call to the main thread; don't call it directly.
- messenger.send('AppRunner_startIfReady', taskChain = 'default')
-
- def setRequestFunc(self, func):
- """ This method is called by the browser at startup to supply a
- function that can be used to deliver requests upstream, to the
- core API, and thereby to the browser. """
- self.requestFunc = func
-
- def sendRequest(self, request, *args):
- """ Delivers a request to the browser via self.requestFunc.
- This low-level function is not intended to be called directly
- by user code. """
-
- assert self.requestFunc
- return self.requestFunc(self.instanceId, request, args)
-
- def __windowEvent(self, win):
- """ This method is called when we get a window event. We
- listen for this to detect when the window has been
- successfully opened. """
-
- if not self.windowOpened:
- self.windowOpened = True
-
- # Now that the window is open, we don't need to keep those
- # prc settings around any more.
- self.__clearWindowProperties()
-
- # Inform the plugin and browser.
- self.notifyRequest('onwindowopen')
-
- def notifyRequest(self, message):
- """ Delivers a notify request to the browser. This is a "this
- happened" type notification; it also triggers some JavaScript
- code execution, if indicated in the HTML tags, and may also
- trigger some internal automatic actions. (For instance, the
- plugin takes down the splash window when it sees the
- onwindowopen notification. """
-
- self.sendRequest('notify', message.lower())
-
- def evalScript(self, expression, needsResponse = False):
- """ Evaluates an arbitrary JavaScript expression in the global
- DOM space. This may be deferred if necessary if needsResponse
- is False and self.dom has not yet been assigned. If
- needsResponse is true, this waits for the value and returns
- it, which means it cannot be deferred. """
-
- if not self.dom:
- # Defer the expression.
- assert not needsResponse
- self.deferredEvals.append(expression)
- else:
- # Evaluate it now.
- return self.scriptRequest('eval', self.dom, value = expression,
- needsResponse = needsResponse)
-
- def scriptRequest(self, operation, object, propertyName = '',
- value = None, needsResponse = True):
- """ Issues a new script request to the browser. This queries
- or modifies one of the browser's DOM properties. This is a
- low-level method that user code should not call directly;
- instead, just operate on the Python wrapper objects that
- shadow the DOM objects, beginning with appRunner.dom.
-
- operation may be one of [ 'get_property', 'set_property',
- 'call', 'evaluate' ].
-
- object is the browser object to manipulate, or the scope in
- which to evaluate the expression.
-
- propertyName is the name of the property to manipulate, if
- relevant (set to None for the default method name).
-
- value is the new value to assign to the property for
- set_property, or the parameter list for call, or the string
- expression for evaluate.
-
- If needsResponse is true, this method will block until the
- return value is received from the browser, and then it returns
- that value. Otherwise, it returns None immediately, without
- waiting for the browser to process the request.
- """
- uniqueId = self.nextScriptId
- self.nextScriptId = (self.nextScriptId + 1) % 0xffffffff
- self.sendRequest('script', operation, object,
- propertyName, value, needsResponse, uniqueId)
-
- if needsResponse:
- # Now wait for the response to come in.
- result = self.sendRequest('wait_script_response', uniqueId)
- return result
-
- def dropObject(self, objectId):
- """ Inform the parent process that we no longer have an
- interest in the P3D_object corresponding to the indicated
- objectId. Not intended to be called by user code. """
-
- self.sendRequest('drop_p3dobj', objectId)
-
-def dummyAppRunner(tokens = [], argv = None):
- """ This function creates a dummy global AppRunner object, which
- is useful for testing running in a packaged environment without
- actually bothering to package up the application. Call this at
- the start of your application to enable it.
-
- It places the current working directory under /mf, as if it were
- mounted from a packed multifile. It doesn't convert egg files to
- bam files, of course; and there are other minor differences from
- running in an actual packaged environment. But it can be a useful
- first-look sanity check. """
-
- if AppRunnerGlobal.appRunner:
- print("Already have AppRunner, not creating a new one.")
- return AppRunnerGlobal.appRunner
-
- appRunner = AppRunner()
- appRunner.dummy = True
- AppRunnerGlobal.appRunner = appRunner
-
- platform = PandaSystem.getPlatform()
- version = PandaSystem.getPackageVersionString()
- hostUrl = PandaSystem.getPackageHostUrl()
-
- if platform.startswith('win'):
- rootDir = Filename(Filename.getUserAppdataDirectory(), 'Panda3D')
- elif platform.startswith('osx'):
- rootDir = Filename(Filename.getHomeDirectory(), 'Library/Caches/Panda3D')
- else:
- rootDir = Filename(Filename.getHomeDirectory(), '.panda3d')
-
- appRunner.rootDir = rootDir
- appRunner.logDirectory = Filename(rootDir, 'log')
-
- # Of course we will have the panda3d application loaded.
- appRunner.addPackageInfo('panda3d', platform, version, hostUrl)
-
- appRunner.tokens = tokens
- appRunner.tokenDict = dict(tokens)
- if argv is None:
- argv = sys.argv
- appRunner.argv = argv
- appRunner.altHost = appRunner.tokenDict.get('alt_host', None)
-
- appRunner.p3dInfo = None
- appRunner.p3dPackage = None
-
- # Mount the current directory under the multifileRoot, as if it
- # were coming from a multifile.
- cwd = ExecutionEnvironment.getCwd()
- vfs = VirtualFileSystem.getGlobalPtr()
- vfs.mount(cwd, appRunner.multifileRoot, vfs.MFReadOnly)
-
- appRunner.initPackedAppEnvironment()
-
- return appRunner
-
diff --git a/direct/src/p3d/DWBPackageInstaller.py b/direct/src/p3d/DWBPackageInstaller.py
deleted file mode 100644
index c4daa67cb1..0000000000
--- a/direct/src/p3d/DWBPackageInstaller.py
+++ /dev/null
@@ -1,96 +0,0 @@
-__all__ = ["DWBPackageInstaller"]
-
-from direct.p3d.PackageInstaller import PackageInstaller
-from direct.gui.DirectWaitBar import DirectWaitBar
-from direct.gui import DirectGuiGlobals as DGG
-
-class DWBPackageInstaller(DirectWaitBar, PackageInstaller):
- """ This class presents a PackageInstaller that also inherits from
- DirectWaitBar, so it updates its own GUI as it downloads.
-
- Specify perPackage = True to make the progress bar reset for each
- package, or False (the default) to show one continuous progress
- bar for all packages.
-
- Specify updateText = True (the default) to update the text label
- with the name of the package or False to leave it up to you to set
- it.
-
- You can specify a callback function with finished = func; this
- function will be called, with one boolean parameter, when the
- download has completed. The parameter will be true on success, or
- false on failure.
- """
-
- def __init__(self, appRunner, parent = None, **kw):
- PackageInstaller.__init__(self, appRunner)
-
- optiondefs = (
- ('borderWidth', (0.01, 0.01), None),
- ('relief', DGG.SUNKEN, self.setRelief),
- ('range', 1, self.setRange),
- ('barBorderWidth', (0.01, 0.01), self.setBarBorderWidth),
- ('barColor', (0.424, 0.647, 0.878, 1), self.setBarColor),
- ('barRelief', DGG.RAISED, self.setBarRelief),
- ('text', 'Starting', self.setText),
- ('text_pos', (0, -0.025), None),
- ('text_scale', 0.1, None),
- ('perPackage', False, None),
- ('updateText', True, None),
- ('finished', None, None),
- )
- self.defineoptions(kw, optiondefs)
- DirectWaitBar.__init__(self, parent, **kw)
- self.initialiseoptions(DWBPackageInstaller)
- self.updateBarStyle()
-
- # Hidden by default until the download begins.
- self.hide()
-
- def cleanup(self):
- PackageInstaller.cleanup(self)
- DirectWaitBar.destroy(self)
-
- def destroy(self):
- PackageInstaller.cleanup(self)
- DirectWaitBar.destroy(self)
-
- def packageStarted(self, package):
- """ This callback is made for each package between
- downloadStarted() and downloadFinished() to indicate the start
- of a new package. """
- if self['updateText']:
- self['text'] = 'Installing %s' % (package.getFormattedName())
- self.show()
-
- def packageProgress(self, package, progress):
- """ This callback is made repeatedly between packageStarted()
- and packageFinished() to update the current progress on the
- indicated package only. The progress value ranges from 0
- (beginning) to 1 (complete). """
-
- if self['perPackage']:
- self['value'] = progress * self['range']
-
- def downloadProgress(self, overallProgress):
- """ This callback is made repeatedly between downloadStarted()
- and downloadFinished() to update the current progress through
- all packages. The progress value ranges from 0 (beginning) to
- 1 (complete). """
-
- if not self['perPackage']:
- self['value'] = overallProgress * self['range']
-
- def downloadFinished(self, success):
- """ This callback is made when all of the packages have been
- downloaded and installed (or there has been some failure). If
- all packages where successfully installed, success is True.
-
- If there were no packages that required downloading, this
- callback will be made immediately, *without* a corresponding
- call to downloadStarted(). """
-
- self.hide()
-
- if self['finished']:
- self['finished'](success)
diff --git a/direct/src/p3d/DeploymentTools.py b/direct/src/p3d/DeploymentTools.py
deleted file mode 100644
index 9c390fbf07..0000000000
--- a/direct/src/p3d/DeploymentTools.py
+++ /dev/null
@@ -1,1379 +0,0 @@
-""" This module is used to build a graphical installer
-or a standalone executable from a p3d file. It will try
-to build for as many platforms as possible. """
-
-__all__ = ["Standalone", "Installer"]
-
-import os, sys, subprocess, tarfile, shutil, time, zipfile, socket, getpass, struct
-import gzip, plistlib
-from direct.directnotify.DirectNotifyGlobal import *
-from direct.showbase.AppRunnerGlobal import appRunner
-from panda3d.core import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile
-from panda3d.core import TiXmlDocument, TiXmlDeclaration, TiXmlElement, readXmlStream
-from panda3d.core import PNMImage, PNMFileTypeRegistry
-from direct.stdpy.file import *
-from direct.p3d.HostInfo import HostInfo
-# This is important for some reason
-import encodings
-
-try:
- import pwd
-except ImportError:
- pwd = None
-
-if sys.version_info >= (3, 0):
- xrange = range
- from io import BytesIO, TextIOWrapper
-else:
- from io import BytesIO
- from StringIO import StringIO
-
-# Make sure this matches with the magic in p3dEmbedMain.cxx.
-P3DEMBED_MAGIC = 0xFF3D3D00
-
-# This filter function is used when creating
-# an archive that should be owned by root.
-def archiveFilter(info):
- basename = os.path.basename(info.name)
- if basename in [".DS_Store", "Thumbs.db"]:
- return None
-
- info.uid = info.gid = 0
- info.uname = info.gname = "root"
-
- # All files without an extension get mode 0755,
- # all other files are chmodded to 0644.
- # Somewhat hacky, but it's the only way
- # permissions can work on a Windows box.
- if info.type != tarfile.DIRTYPE and '.' in info.name.rsplit('/', 1)[-1]:
- info.mode = 0o644
- else:
- info.mode = 0o755
-
- return info
-
-# Hack to make all files in a tar file owned by root.
-# The tarfile module doesn't have filter functionality until Python 2.7.
-# Yay duck typing!
-class TarInfoRoot(tarfile.TarInfo):
- uid = property(lambda self: 0, lambda self, x: None)
- gid = property(lambda self: 0, lambda self, x: None)
- uname = property(lambda self: "root", lambda self, x: None)
- gname = property(lambda self: "root", lambda self, x: None)
- mode = property(lambda self: 0o644 if self.type != tarfile.DIRTYPE and \
- '.' in self.name.rsplit('/', 1)[-1] else 0o755,
- lambda self, x: None)
-
-# On OSX, the root group is named "wheel".
-class TarInfoRootOSX(TarInfoRoot):
- gname = property(lambda self: "wheel", lambda self, x: None)
-
-
-class Standalone:
- """ This class creates a standalone executable from a given .p3d file. """
- notify = directNotify.newCategory("Standalone")
-
- def __init__(self, p3dfile, tokens = {}):
- self.p3dfile = Filename(p3dfile)
- self.basename = self.p3dfile.getBasenameWoExtension()
- self.tokens = tokens
-
- self.tempDir = Filename.temporary("", self.basename, "") + "/"
- self.tempDir.makeDir()
- self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = appRunner, hostDir = self.tempDir, asMirror = False, perPlatform = True)
-
- self.http = HTTPClient.getGlobalPtr()
- if not self.host.hasContentsFile:
- if not self.host.readContentsFile():
- if not self.host.downloadContentsFile(self.http):
- Standalone.notify.error("couldn't read host")
- return
-
- def __del__(self):
- try:
- appRunner.rmtree(self.tempDir)
- except:
- try: shutil.rmtree(self.tempDir.toOsSpecific())
- except: pass
-
- def buildAll(self, outputDir = "."):
- """ Builds standalone executables for every known platform,
- into the specified output directory. """
-
- platforms = set()
- for package in self.host.getPackages(name = "p3dembed"):
- platforms.add(package.platform)
- if len(platforms) == 0:
- Standalone.notify.warning("No platforms found to build for!")
-
- if 'win32' in platforms and 'win_i386' in platforms:
- platforms.remove('win32')
-
- outputDir = Filename(outputDir + "/")
- outputDir.makeDir()
- for platform in platforms:
- if platform.startswith("win"):
- self.build(Filename(outputDir, platform + "/" + self.basename + ".exe"), platform)
- else:
- self.build(Filename(outputDir, platform + "/" + self.basename), platform)
-
- def build(self, output, platform = None, extraTokens = {}):
- """ Builds a standalone executable and stores it into the path
- indicated by the 'output' argument. You can specify to build for
- a different platform by altering the 'platform' argument. """
-
- if platform == None:
- platform = PandaSystem.getPlatform()
-
- vfs = VirtualFileSystem.getGlobalPtr()
-
- for package in self.host.getPackages(name = "p3dembed", platform = platform):
- if not package.downloadDescFile(self.http):
- Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
- continue
- if not package.downloadPackage(self.http):
- Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
- continue
-
- # Figure out where p3dembed might be now.
- if package.platform.startswith("win"):
- # Use p3dembedw unless console_environment was set.
- cEnv = extraTokens.get("console_environment", self.tokens.get("console_environment", 0))
- if cEnv != "" and int(cEnv) != 0:
- p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed.exe" % package.platform)
- else:
- p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembedw.exe" % package.platform)
- # Fallback for older p3dembed versions
- if not vfs.exists(p3dembed):
- Filename(self.host.hostDir, "p3dembed/%s/p3dembed.exe" % package.platform)
- else:
- p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed" % package.platform)
-
- if not vfs.exists(p3dembed):
- Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
- continue
-
- return self.embed(output, p3dembed, extraTokens)
-
- Standalone.notify.error("Failed to build standalone for platform %s" % platform)
-
- def embed(self, output, p3dembed, extraTokens = {}):
- """ Embeds the p3d file into the provided p3dembed executable.
- This function is not really useful - use build() or buildAll() instead. """
-
- # Load the p3dembed data into memory
- size = p3dembed.getFileSize()
- p3dembed_data = VirtualFileSystem.getGlobalPtr().readFile(p3dembed, True)
- assert len(p3dembed_data) == size
-
- # Find the magic size string and replace it with the real size,
- # regardless of the endianness of the p3dembed executable.
- p3dembed_data = p3dembed_data.replace(struct.pack('>I', P3DEMBED_MAGIC),
- struct.pack('>I', size))
- p3dembed_data = p3dembed_data.replace(struct.pack(' %s failed for platform %s" % (package.packageName, package.platform))
- return []
- if not package.downloadPackage(self.http):
- Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
- return []
-
- filenames = []
- vfs = VirtualFileSystem.getGlobalPtr()
- for e in package.extracts:
- if e.basename not in ["p3dembed", "p3dembed.exe", "p3dembed.exe.manifest", "p3dembedw.exe", "p3dembedw.exe.manifest"]:
- filename = Filename(package.getPackageDir(), e.filename)
- filename.makeAbsolute()
- if vfs.exists(filename):
- filenames.append(filename)
- else:
- Standalone.notify.error("%s mentioned in xml, but does not exist" % e.filename)
-
- return filenames
-
-
-class PackageTree:
- """ A class used internally to build a temporary package
- tree for inclusion into an installer. """
-
- def __init__(self, platform, hostDir, hostUrl):
- self.platform = platform
- self.hosts = {}
- self.packages = {}
- self.hostUrl = hostUrl
- self.hostDir = Filename(hostDir)
- self.hostDir.makeDir()
- self.http = HTTPClient.getGlobalPtr()
-
- def getHost(self, hostUrl):
- if hostUrl in self.hosts:
- return self.hosts[hostUrl]
-
- host = HostInfo(hostUrl, appRunner = appRunner, hostDir = self.hostDir, asMirror = False, perPlatform = True)
- if not host.hasContentsFile:
- if not host.readContentsFile():
- if not host.downloadContentsFile(self.http):
- Installer.notify.error("couldn't read host %s" % host.hostUrl)
- return None
- self.hosts[hostUrl] = host
- return host
-
- def installPackage(self, name, version, hostUrl = None):
- """ Installs the named package into the tree. """
-
- if hostUrl is None:
- hostUrl = self.hostUrl
-
- pkgIdent = (name, version)
- if pkgIdent in self.packages:
- return self.packages[pkgIdent]
-
- package = None
- # Always try the super host first, if any.
- if appRunner and appRunner.superMirrorUrl:
- superHost = self.getHost(appRunner.superMirrorUrl)
- if self.platform:
- package = superHost.getPackage(name, version, self.platform)
- if not package:
- package = superHost.getPackage(name, version)
-
- if not package:
- host = self.getHost(hostUrl)
- if self.platform:
- package = host.getPackage(name, version, self.platform)
- if not package:
- package = host.getPackage(name, version)
-
- if not package:
- Installer.notify.error("Package %s %s for %s not known on %s" % (
- name, version, self.platform, hostUrl))
- return
-
- package.installed = True # Hack not to let it unnecessarily install itself
- if not package.downloadDescFile(self.http):
- Installer.notify.error(" -> %s failed for platform %s" % (package.packageName, package.platform))
- return
- if not package.downloadPackage(self.http):
- Installer.notify.error(" -> %s failed for platform %s" % (package.packageName, package.platform))
- return
-
- self.packages[pkgIdent] = package
-
- # Check for any dependencies.
- for rname, rversion, rhost in package.requires:
- self.installPackage(rname, rversion, rhost.hostUrl)
-
- return package
-
-
-class Icon:
- """ This class is used to create an icon for various platforms. """
- notify = directNotify.newCategory("Icon")
-
- def __init__(self):
- self.images = {}
-
- def addImage(self, image):
- """ Adds an image to the icon. Returns False on failure, True on success.
- Only one image per size can be loaded, and the image size must be square. """
-
- if not isinstance(image, PNMImage):
- fn = image
- if not isinstance(fn, Filename):
- fn = Filename.fromOsSpecific(fn)
-
- image = PNMImage()
- if not image.read(fn):
- Icon.notify.warning("Image '%s' could not be read" % fn.getBasename())
- return False
-
- if image.getXSize() != image.getYSize():
- Icon.notify.warning("Ignoring image without square size")
- return False
-
- self.images[image.getXSize()] = image
-
- return True
-
- def 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()
-
- count = 0
- for size in self.images.keys():
- if size <= 256:
- count += 1
-
- ico = open(fn, 'wb')
- ico.write(struct.pack('I', pngsize + 8))
- icns.seek(start + pngsize)
-
- elif size in icon_types:
- 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)))
-
- if not image.hasAlpha():
- continue
- icns.write(mask_types[size])
- icns.write(struct.pack('>I', size * size + 8))
-
- for y in xrange(size):
- for x in xrange(size):
- icns.write(struct.pack('I', length))
- icns.close()
-
- return True
-
-
-class Installer:
- """ This class creates a (graphical) installer from a given .p3d file. """
- notify = directNotify.newCategory("Installer")
-
- def __init__(self, p3dfile, shortname, fullname, version, tokens = {}):
- self.p3dFilename = p3dfile
- if not shortname:
- shortname = p3dfile.getBasenameWoExtension()
- self.shortname = shortname
- self.fullname = fullname
- self.version = str(version)
- self.includeRequires = False
- self.offerRun = True
- self.offerDesktopShortcut = True
- self.licensename = ""
- self.licensefile = Filename()
- self.authorid = "org.panda3d"
- self.authorname = os.environ.get("DEBFULLNAME", "")
- self.authoremail = os.environ.get("DEBEMAIL", "")
- self.icon = None
-
- # Try to determine a default author name ourselves.
- uname = None
- if pwd is not None and hasattr(os, 'getuid'):
- uinfo = pwd.getpwuid(os.getuid())
- if uinfo:
- uname = uinfo.pw_name
- if not self.authorname:
- self.authorname = \
- uinfo.pw_gecos.split(',', 1)[0]
-
- # Fallbacks in case that didn't work or wasn't supported.
- if not uname:
- uname = getpass.getuser()
- if not self.authorname:
- self.authorname = uname
- if not self.authoremail and ' ' not in uname:
- self.authoremail = "%s@%s" % (uname, socket.gethostname())
-
- # Load the p3d file to read out the required packages
- mf = Multifile()
- if not mf.openRead(self.p3dFilename):
- Installer.notify.error("Not a Panda3D application: %s" % (p3dfile))
- return
-
- # Now load the p3dInfo file.
- self.hostUrl = None
- self.requires = []
- self.extracts = []
- i = mf.findSubfile('p3d_info.xml')
- if i >= 0:
- stream = mf.openReadSubfile(i)
- p3dInfo = readXmlStream(stream)
- mf.closeReadSubfile(stream)
- if p3dInfo:
- p3dPackage = p3dInfo.FirstChildElement('package')
- p3dHost = p3dPackage.FirstChildElement('host')
- if p3dHost.Attribute('url'):
- self.hostUrl = p3dHost.Attribute('url')
- p3dRequires = p3dPackage.FirstChildElement('requires')
- while p3dRequires:
- self.requires.append((
- p3dRequires.Attribute('name'),
- p3dRequires.Attribute('version'),
- p3dRequires.Attribute('host')))
- p3dRequires = p3dRequires.NextSiblingElement('requires')
-
- p3dExtract = p3dPackage.FirstChildElement('extract')
- while p3dExtract:
- filename = p3dExtract.Attribute('filename')
- self.extracts.append(filename)
- p3dExtract = p3dExtract.NextSiblingElement('extract')
-
- if not self.fullname:
- p3dConfig = p3dPackage.FirstChildElement('config')
- if p3dConfig:
- self.fullname = p3dConfig.Attribute('display_name')
- else:
- Installer.notify.warning("No p3d_info.xml was found in .p3d archive.")
-
- mf.close()
-
- if not self.hostUrl:
- self.hostUrl = PandaSystem.getPackageHostUrl()
- if not self.hostUrl:
- self.hostUrl = self.standalone.host.hostUrl
- Installer.notify.warning("No host URL was specified by .p3d archive. Falling back to %s" % (self.hostUrl))
-
- if not self.fullname:
- self.fullname = self.shortname
-
- self.tempDir = Filename.temporary("", self.shortname, "") + "/"
- self.tempDir.makeDir()
- self.__tempRoots = {}
-
- if self.extracts:
- # Copy .p3d to a temporary file so we can remove the extracts.
- p3dfile = Filename(self.tempDir, self.p3dFilename.getBasename())
- shutil.copyfile(self.p3dFilename.toOsSpecific(), p3dfile.toOsSpecific())
- mf = Multifile()
- if not mf.openReadWrite(p3dfile):
- Installer.notify.error("Failure to open %s for writing." % (p3dfile))
-
- # We don't really need this silly thing when embedding, anyway.
- mf.setHeaderPrefix("")
-
- for fn in self.extracts:
- if not mf.removeSubfile(fn):
- Installer.notify.error("Failure to remove %s from multifile." % (p3dfile))
-
- mf.repack()
- mf.close()
-
- self.standalone = Standalone(p3dfile, tokens)
-
- def __del__(self):
- try:
- appRunner.rmtree(self.tempDir)
- except:
- try: shutil.rmtree(self.tempDir.toOsSpecific())
- except: pass
-
- def installPackagesInto(self, hostDir, platform):
- """ Installs the packages required by the .p3d file into
- the specified directory, for the given platform. """
-
- if not self.includeRequires:
- return
-
- # Write out the extracts from the original .p3d.
- if self.extracts:
- mf = Multifile()
- if not mf.openRead(self.p3dFilename):
- Installer.notify.error("Failed to open .p3d archive: %s" % (filename))
-
- for filename in self.extracts:
- i = mf.findSubfile(filename)
- if i < 0:
- Installer.notify.error("Cannot find extract in .p3d archive: %s" % (filename))
- continue
-
- if not mf.extractSubfile(i, Filename(hostDir, filename)):
- Installer.notify.error("Failed to extract file from .p3d archive: %s" % (filename))
- mf.close()
-
- pkgTree = PackageTree(platform, hostDir, self.hostUrl)
- pkgTree.installPackage("images", None, self.standalone.host.hostUrl)
-
- for name, version, hostUrl in self.requires:
- pkgTree.installPackage(name, version, hostUrl)
-
- # Remove the extracted files from the compressed archive, to save space.
- vfs = VirtualFileSystem.getGlobalPtr()
- for package in pkgTree.packages.values():
- if package.uncompressedArchive:
- archive = Filename(package.getPackageDir(), package.uncompressedArchive.filename)
- if not archive.exists():
- continue
-
- mf = Multifile()
- # Make sure that it isn't mounted before altering it, just to be safe
- vfs.unmount(archive)
- os.chmod(archive.toOsSpecific(), 0o644)
- if not mf.openReadWrite(archive):
- Installer.notify.warning("Failed to open archive %s" % (archive))
- continue
-
- # We don't iterate over getNumSubfiles because we're
- # removing subfiles while we're iterating over them.
- subfiles = mf.getSubfileNames()
- for subfile in subfiles:
- # We do *NOT* call vfs.exists here in case the package is mounted.
- if Filename(package.getPackageDir(), subfile).exists():
- Installer.notify.debug("Removing already-extracted %s from multifile" % (subfile))
- mf.removeSubfile(subfile)
-
- # This seems essential for mf.close() not to crash later.
- mf.repack()
-
- # If we have no subfiles left, we can just remove the multifile.
- #XXX rdb: it seems that removing it causes trouble, so let's not.
- #if mf.getNumSubfiles() == 0:
- # Installer.notify.info("Removing empty archive %s" % (package.uncompressedArchive.filename))
- # mf.close()
- # archive.unlink()
- #else:
- mf.close()
- try: os.chmod(archive.toOsSpecific(), 0o444)
- except: pass
-
- # Write out our own contents.xml file.
- doc = TiXmlDocument()
- decl = TiXmlDeclaration("1.0", "utf-8", "")
- doc.InsertEndChild(decl)
-
- xcontents = TiXmlElement("contents")
- for package in pkgTree.packages.values():
- xpackage = TiXmlElement('package')
- xpackage.SetAttribute('name', package.packageName)
-
- filename = package.packageName + "/"
-
- if package.packageVersion:
- xpackage.SetAttribute('version', package.packageVersion)
- filename += package.packageVersion + "/"
-
- if package.platform:
- xpackage.SetAttribute('platform', package.platform)
- filename += package.platform + "/"
- assert package.platform == platform
-
- xpackage.SetAttribute('per_platform', '1')
-
- filename += package.descFileBasename
- xpackage.SetAttribute('filename', filename)
- xcontents.InsertEndChild(xpackage)
-
- doc.InsertEndChild(xcontents)
- doc.SaveFile(Filename(hostDir, "contents.xml").toOsSpecific())
-
- def buildAll(self, outputDir = "."):
- """ Creates a (graphical) installer for every known platform.
- Call this after you have set the desired parameters. """
-
- platforms = set()
- for package in self.standalone.host.getPackages(name = "p3dembed"):
- platforms.add(package.platform)
- if len(platforms) == 0:
- Installer.notify.warning("No platforms found to build for!")
-
- if 'win32' in platforms and 'win_i386' in platforms:
- platforms.remove('win32')
-
- outputDir = Filename(outputDir + "/")
- outputDir.makeDir()
- for platform in platforms:
- output = Filename(outputDir, platform + "/")
- output.makeDir()
- self.build(output, platform)
-
- def build(self, output, platform = None):
- """ Builds (graphical) installers and stores it into the path
- indicated by the 'output' argument. You can specify to build for
- a different platform by altering the 'platform' argument.
- If 'output' is a directory, the installer will be stored in it. """
-
- if platform == None:
- platform = PandaSystem.getPlatform()
-
- if platform.startswith("win"):
- self.buildNSIS(output, platform)
- return
- elif "_" in platform:
- osname, arch = platform.split("_", 1)
- if osname == "linux":
- self.buildDEB(output, platform)
- self.buildArch(output, platform)
- return
- elif osname == "osx":
- self.buildPKG(output, platform)
- return
- Installer.notify.info("Ignoring unknown platform " + platform)
-
- def __buildTempLinux(self, platform):
- """ Builds a filesystem for Linux. Used so that buildDEB,
- buildRPM and buildArch can share the same temp directory. """
-
- if platform in self.__tempRoots:
- return self.__tempRoots[platform]
-
- tempdir = Filename(self.tempDir, platform)
- tempdir.makeDir()
-
- Filename(tempdir, "usr/bin/").makeDir()
- if self.includeRequires:
- extraTokens = {"host_dir" : "/usr/lib/" + self.shortname.lower(),
- "start_dir" : "/usr/lib/" + self.shortname.lower()}
- else:
- extraTokens = {}
- self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform, extraTokens)
- if not self.licensefile.empty():
- Filename(tempdir, "usr/share/doc/%s/" % self.shortname.lower()).makeDir()
- shutil.copyfile(self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/copyright" % self.shortname.lower()).toOsSpecific())
- shutil.copyfile(self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/LICENSE" % self.shortname.lower()).toOsSpecific())
-
- # Add an image file to /usr/share/pixmaps/
- iconFile = None
- if self.icon is not None:
- iconImage = None
- if 48 in self.icon.images:
- iconImage = self.icon.images[48]
- elif 64 in self.icon.images:
- iconImage = self.icon.images[64]
- elif 32 in self.icon.images:
- iconImage = self.icon.images[32]
- else:
- Installer.notify.warning("No suitable icon image for Linux provided, should preferably be 48x48 in size")
-
- if iconImage is not None:
- iconFile = Filename(tempdir, "usr/share/pixmaps/%s.png" % self.shortname)
- iconFile.setBinary()
- iconFile.makeDir()
- if not iconImage.write(iconFile):
- Installer.notify.warning("Failed to write icon file for Linux")
- iconFile.unlink()
- iconFile = None
-
- # Write a .desktop file to /usr/share/applications/
- desktopFile = Filename(tempdir, "usr/share/applications/%s.desktop" % self.shortname.lower())
- desktopFile.setText()
- desktopFile.makeDir()
- desktop = open(desktopFile.toOsSpecific(), 'w')
- desktop.write("[Desktop Entry]\n")
- desktop.write("Name=%s\n" % self.fullname)
- desktop.write("Exec=%s\n" % self.shortname.lower())
- if iconFile is not None:
- desktop.write("Icon=%s\n" % iconFile.getBasename())
-
- # Set the "Terminal" option based on whether or not a console env is requested
- cEnv = self.standalone.tokens.get("console_environment", "")
- if cEnv == "" or int(cEnv) == 0:
- desktop.write("Terminal=false\n")
- else:
- desktop.write("Terminal=true\n")
-
- desktop.write("Type=Application\n")
- desktop.close()
-
- if self.includeRequires or self.extracts:
- hostDir = Filename(tempdir, "usr/lib/%s/" % self.shortname.lower())
- hostDir.makeDir()
- self.installPackagesInto(hostDir, platform)
-
- totsize = 0
- for root, dirs, files in self.os_walk(tempdir.toOsSpecific()):
- for name in files:
- totsize += os.path.getsize(os.path.join(root, name))
-
- self.__tempRoots[platform] = (tempdir, totsize)
- return self.__tempRoots[platform]
-
- def buildDEB(self, output, platform):
- """ Builds a .deb archive and stores it in the path indicated
- by the 'output' argument. It will be built for the architecture
- specified by the 'arch' argument.
- If 'output' is a directory, the deb file will be stored in it. """
-
- arch = platform.rsplit("_", 1)[-1]
- output = Filename(output)
- if output.isDirectory():
- output = Filename(output, "%s_%s_%s.deb" % (self.shortname.lower(), self.version, arch))
- Installer.notify.info("Creating %s..." % output)
- modtime = int(time.time())
-
- # Create a temporary directory and write the launcher and dependencies to it.
- tempdir, totsize = self.__buildTempLinux(platform)
-
- # Create a control file in memory.
- controlfile = BytesIO()
- if sys.version_info >= (3, 0):
- cout = TextIOWrapper(controlfile, encoding='utf-8', newline='')
- else:
- cout = StringIO()
-
- cout.write("Package: %s\n" % self.shortname.lower())
- cout.write("Version: %s\n" % self.version)
- cout.write("Maintainer: %s <%s>\n" % (self.authorname, self.authoremail))
- cout.write("Section: games\n")
- cout.write("Priority: optional\n")
- cout.write("Architecture: %s\n" % arch)
- cout.write("Installed-Size: %d\n" % -(-totsize // 1024))
- cout.write("Description: %s\n" % self.fullname)
- cout.write("Depends: libc6, libgcc1, libstdc++6, libx11-6\n")
- cout.flush()
- if sys.version_info < (3, 0):
- controlfile.write(cout.getvalue().encode('utf-8'))
-
- controlinfo = TarInfoRoot("control")
- controlinfo.mtime = modtime
- controlinfo.size = controlfile.tell()
- controlfile.seek(0)
-
- # Open the deb file and write to it. It's actually
- # just an AR file, which is very easy to make.
- if output.exists():
- output.unlink()
- debfile = open(output.toOsSpecific(), "wb")
- debfile.write(b"!\x0A")
- pad_mtime = str(modtime).encode().ljust(12, b' ')
-
- # The first entry is a special file that marks it a .deb.
- debfile.write(b"debian-binary ")
- debfile.write(pad_mtime)
- debfile.write(b"0 0 100644 4 \x60\x0A")
- debfile.write(b"2.0\x0A")
-
- # Write the control.tar.gz to the archive. We'll leave the
- # size 0 for now, and go back and fill it in later.
- debfile.write(b"control.tar.gz ")
- debfile.write(pad_mtime)
- debfile.write(b"0 0 100644 0 \x60\x0A")
- ctaroffs = debfile.tell()
- ctarfile = tarfile.open("control.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
- ctarfile.addfile(controlinfo, controlfile)
- ctarfile.close()
- ctarsize = debfile.tell() - ctaroffs
- if (ctarsize & 1): debfile.write(b"\x0A")
-
- # Write the data.tar.gz to the archive. Again, leave size 0.
- debfile.write(b"data.tar.gz ")
- debfile.write(pad_mtime)
- debfile.write(b"0 0 100644 0 \x60\x0A")
- dtaroffs = debfile.tell()
- dtarfile = tarfile.open("data.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
- dtarfile.add(Filename(tempdir, "usr").toOsSpecific(), "/usr")
- dtarfile.close()
- dtarsize = debfile.tell() - dtaroffs
- if (dtarsize & 1): debfile.write(b"\x0A")
-
- # Write the correct sizes of the archives.
- debfile.seek(ctaroffs - 12)
- debfile.write(str(ctarsize).encode().ljust(10, b' '))
- debfile.seek(dtaroffs - 12)
- debfile.write(str(dtarsize).encode().ljust(10, b' '))
-
- debfile.close()
-
- return output
-
- def buildArch(self, output, platform):
- """ Builds an ArchLinux package and stores it in the path
- indicated by the 'output' argument. It will be built for the
- architecture specified by the 'arch' argument.
- If 'output' is a directory, the deb file will be stored in it. """
-
- arch = platform.rsplit("_", 1)[-1]
- assert arch in ("i386", "amd64")
- arch = {"i386" : "i686", "amd64" : "x86_64"}[arch]
- pkgver = self.version + "-1"
-
- output = Filename(output)
- if output.isDirectory():
- output = Filename(output, "%s-%s-%s.pkg.tar.gz" % (self.shortname.lower(), pkgver, arch))
- Installer.notify.info("Creating %s..." % output)
- modtime = int(time.time())
-
- # Create a temporary directory and write the launcher and dependencies to it.
- tempdir, totsize = self.__buildTempLinux(platform)
-
- # Create a pkginfo file in memory.
- pkginfo = BytesIO()
- if sys.version_info >= (3, 0):
- pout = TextIOWrapper(pkginfo, encoding='utf-8', newline='')
- else:
- pout = StringIO()
-
- pout.write("# Generated using pdeploy\n")
- pout.write("# %s\n" % time.ctime(modtime))
- pout.write("pkgname = %s\n" % self.shortname.lower())
- pout.write("pkgver = %s\n" % pkgver)
- pout.write("pkgdesc = %s\n" % self.fullname)
- pout.write("builddate = %s\n" % modtime)
- pout.write("packager = %s <%s>\n" % (self.authorname, self.authoremail))
- pout.write("size = %d\n" % totsize)
- pout.write("arch = %s\n" % arch)
- if self.licensename != "":
- pout.write("license = %s\n" % self.licensename)
- pout.flush()
- if sys.version_info < (3, 0):
- pkginfo.write(pout.getvalue().encode('utf-8'))
-
- pkginfoinfo = TarInfoRoot(".PKGINFO")
- pkginfoinfo.mtime = modtime
- pkginfoinfo.size = pkginfo.tell()
- pkginfo.seek(0)
-
- # Create the actual package now.
- pkgfile = tarfile.open(output.toOsSpecific(), "w:gz", tarinfo = TarInfoRoot)
- pkgfile.addfile(pkginfoinfo, pkginfo)
- pkgfile.add(tempdir.toOsSpecific(), "/")
- if not self.licensefile.empty():
- pkgfile.add(self.licensefile.toOsSpecific(), "/usr/share/licenses/%s/LICENSE" % self.shortname.lower())
- pkgfile.close()
-
- return output
-
- def buildAPP(self, output, platform):
-
- output = Filename(output)
- if output.isDirectory() and output.getExtension() != 'app':
- output = Filename(output, "%s.app" % self.fullname)
- Installer.notify.info("Creating %s..." % output)
-
- # Create the executable for the application bundle
- exefile = Filename(output, "Contents/MacOS/" + self.shortname)
- exefile.makeDir()
- if self.includeRequires:
- extraTokens = {"host_dir": "../Resources", "start_dir": "../Resources"}
- else:
- extraTokens = {}
- self.standalone.build(exefile, platform, extraTokens)
- hostDir = Filename(output, "Contents/Resources/")
- hostDir.makeDir()
- self.installPackagesInto(hostDir, platform)
-
- hasIcon = False
- if self.icon is not None:
- Installer.notify.info("Generating %s.icns..." % self.shortname)
- hasIcon = self.icon.makeICNS(Filename(hostDir, "%s.icns" % self.shortname))
-
- # Create the application plist file using Python's plistlib module.
- plist = {
- 'CFBundleDevelopmentRegion': 'English',
- 'CFBundleDisplayName': self.fullname,
- 'CFBundleExecutable': exefile.getBasename(),
- 'CFBundleIdentifier': '%s.%s' % (self.author, self.shortname),
- 'CFBundleInfoDictionaryVersion': '6.0',
- 'CFBundleName': self.shortname,
- 'CFBundlePackageType': 'APPL',
- 'CFBundleShortVersionString': self.version,
- 'CFBundleVersion': self.version,
- 'LSHasLocalizedDisplayName': False,
- 'NSAppleScriptEnabled': False,
- 'NSPrincipalClass': 'NSApplication',
- }
- if hasIcon:
- plist['CFBundleIconFile'] = self.shortname + '.icns'
-
- plistlib.writePlist(plist, Filename(output, "Contents/Info.plist").toOsSpecific())
- return output
-
- def buildPKG(self, output, platform):
- appfn = self.buildAPP(output, platform)
- appname = "/Applications/" + appfn.getBasename()
- output = Filename(output)
- if output.isDirectory():
- output = Filename(output, "%s %s.pkg" % (self.fullname, self.version))
- Installer.notify.info("Creating %s..." % output)
-
- Filename(output, "Contents/Resources/en.lproj/").makeDir()
- if self.licensefile:
- shutil.copyfile(self.licensefile.toOsSpecific(), Filename(output, "Contents/Resources/License.txt").toOsSpecific())
- pkginfo = open(Filename(output, "Contents/PkgInfo").toOsSpecific(), "w")
- pkginfo.write("pmkrpkg1")
- pkginfo.close()
- pkginfo = open(Filename(output, "Contents/Resources/package_version").toOsSpecific(), "w")
- pkginfo.write("major: 1\nminor: 9")
- pkginfo.close()
-
- # Although it might make more sense to use Python's plistlib here,
- # it is not available on non-OSX systems before Python 2.6.
- plist = open(Filename(output, "Contents/Info.plist").toOsSpecific(), "w")
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\tCFBundleIdentifier \n')
- plist.write('\t%s.pkg.%s \n' % (self.authorid, self.shortname))
- plist.write('\tCFBundleShortVersionString \n')
- plist.write('\t%s \n' % self.version)
- plist.write('\tIFMajorVersion \n')
- plist.write('\t1 \n')
- plist.write('\tIFMinorVersion \n')
- plist.write('\t9 \n')
- plist.write('\tIFPkgFlagAllowBackRev \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagAuthorizationAction \n')
- plist.write('\tRootAuthorization \n')
- plist.write('\tIFPkgFlagDefaultLocation \n')
- plist.write('\t/ \n')
- plist.write('\tIFPkgFlagFollowLinks \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagIsRequired \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagOverwritePermissions \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagRelocatable \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagRestartAction \n')
- plist.write('\tNone \n')
- plist.write('\tIFPkgFlagRootVolumeOnly \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFlagUpdateInstalledLanguages \n')
- plist.write('\t \n')
- plist.write('\tIFPkgFormatVersion \n')
- plist.write('\t0.10000000149011612 \n')
- plist.write('\tIFPkgPathMappings \n')
- plist.write('\t\n')
- plist.write('\t\t%s \n' % appname)
- plist.write('\t\t{pkmk-token-2} \n')
- plist.write('\t \n')
- plist.write(' \n')
- plist.write(' \n')
- plist.close()
-
- plist = open(Filename(output, "Contents/Resources/TokenDefinitions.plist").toOsSpecific(), "w")
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\tpkmk-token-2 \n')
- plist.write('\t\n')
- plist.write('\t\t\n')
- plist.write('\t\t\tidentifier \n')
- plist.write('\t\t\t%s.%s \n' % (self.authorid, self.shortname))
- plist.write('\t\t\tpath \n')
- plist.write('\t\t\t%s \n' % appname)
- plist.write('\t\t\tsearchPlugin \n')
- plist.write('\t\t\tCommonAppSearch \n')
- plist.write('\t\t \n')
- plist.write('\t \n')
- plist.write(' \n')
- plist.write(' \n')
- plist.close()
-
- plist = open(Filename(output, "Contents/Resources/en.lproj/Description.plist").toOsSpecific(), "w")
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\n')
- plist.write('\tIFPkgDescriptionDescription \n')
- plist.write('\t \n')
- plist.write('\tIFPkgDescriptionTitle \n')
- plist.write('\t%s \n' % self.fullname)
- plist.write(' \n')
- plist.write(' \n')
- plist.close()
-
- # OS X El Capitan no longer accepts .pax archives - it must be a CPIO archive named .pax.
- archive = gzip.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), 'wb')
- self.__ino = 0
- self.__writeCPIO(archive, appfn, appname)
- archive.write(b"0707070000000000000000000000000000000000010000000000000000000001300000000000TRAILER!!!\0")
- archive.close()
-
- # Put the .pkg into a zipfile
- zip_fn = Filename(output.getDirname(), "%s %s.pkg.zip" % (self.fullname, self.version))
- dir = Filename(output.getDirname())
- dir.makeAbsolute()
- zip = zipfile.ZipFile(zip_fn.toOsSpecific(), 'w')
- for root, dirs, files in self.os_walk(output.toOsSpecific()):
- for name in files:
- file = Filename.fromOsSpecific(os.path.join(root, name))
- file.makeAbsolute()
- file.makeRelativeTo(dir)
- zip.write(os.path.join(root, name), str(file))
- zip.close()
-
- return output
-
- def __writeCPIO(self, archive, fn, name):
- """ Adds the given fn under the given name to the CPIO archive. """
-
- st = os.lstat(fn.toOsSpecific())
-
- archive.write(b"070707") # magic
- archive.write(b"000000") # dev
-
- # Synthesize an inode number, different for each entry.
- self.__ino += 1
- archive.write("%06o" % (self.__ino))
-
- # Determine based on the type which mode to write.
- if os.path.islink(fn.toOsSpecific()):
- archive.write("%06o" % (st.st_mode))
- target = os.path.readlink(fn.toOsSpecific()).encode('utf-8')
- size = len(target)
- elif os.path.isdir(fn.toOsSpecific()):
- archive.write(b"040755")
- size = 0
- elif not fn.getExtension(): # Binary file?
- archive.write(b"100755")
- size = st.st_size
- else:
- archive.write(b"100644")
- size = st.st_size
-
- archive.write("000000") # uid (root)
- archive.write("000000") # gid (wheel)
- archive.write("%06o" % (st.st_nlink))
- archive.write("000000") # rdev
- archive.write("%011o" % (st.st_mtime))
- archive.write("%06o" % (len(name) + 1))
- archive.write("%011o" % (size))
-
- # Write the filename, plus terminating NUL byte.
- archive.write(name.encode('utf-8'))
- archive.write(b"\0")
-
- # Copy the file data to the archive.
- if os.path.islink(fn.toOsSpecific()):
- archive.write(target)
- elif size:
- handle = open(fn.toOsSpecific(), 'rb')
- data = handle.read(1024 * 1024)
- while data:
- archive.write(data)
- data = handle.read(1024 * 1024)
- handle.close()
-
- # If this is a directory, recurse.
- if os.path.isdir(fn.toOsSpecific()):
- for child in os.listdir(fn.toOsSpecific()):
- self.__writeCPIO(archive, Filename(fn, child), name + "/" + child)
-
- def buildNSIS(self, output, platform):
- # Check if we have makensis first
- makensis = None
- if (sys.platform.startswith("win")):
- syspath = os.defpath.split(";") + os.environ["PATH"].split(";")
- for p in set(syspath):
- p1 = os.path.join(p, "makensis.exe")
- p2 = os.path.join(os.path.dirname(p), "nsis", "makensis.exe")
- if os.path.isfile(p1):
- makensis = p1
- break
- elif os.path.isfile(p2):
- makensis = p2
- break
- if not makensis:
- import pandac
- makensis = os.path.dirname(os.path.dirname(pandac.__file__))
- makensis = os.path.join(makensis, "nsis", "makensis.exe")
- if not os.path.isfile(makensis): makensis = None
- else:
- for p in os.defpath.split(":") + os.environ["PATH"].split(":"):
- if os.path.isfile(os.path.join(p, "makensis")):
- makensis = os.path.join(p, "makensis")
-
- if makensis == None:
- Installer.notify.warning("Makensis utility not found, no Windows installer will be built!")
- return None
-
- output = Filename(output)
- if output.isDirectory():
- output = Filename(output, "%s %s.exe" % (self.fullname, self.version))
- Installer.notify.info("Creating %s..." % output)
- output.makeAbsolute()
- extrafiles = self.standalone.getExtraFiles(platform)
-
- exefile = Filename(Filename.getTempDirectory(), self.shortname + ".exe")
- exefile.unlink()
- if self.includeRequires:
- extraTokens = {"host_dir": ".", "start_dir": "."}
- else:
- extraTokens = {}
- self.standalone.build(exefile, platform, extraTokens)
-
- # Temporary directory to store the hostdir in
- hostDir = Filename(self.tempDir, platform + "/")
- if not hostDir.exists():
- hostDir.makeDir()
- self.installPackagesInto(hostDir, platform)
-
- # See if we can generate an icon
- icofile = None
- if self.icon is not None:
- icofile = Filename(Filename.getTempDirectory(), self.shortname + ".ico")
- icofile.unlink()
- Installer.notify.info("Generating %s.ico..." % self.shortname)
- if not self.icon.makeICO(icofile):
- icofile = None
-
- # Create the .nsi installer script
- nsifile = Filename(Filename.getTempDirectory(), self.shortname + ".nsi")
- nsifile.unlink()
- nsi = open(nsifile.toOsSpecific(), "w")
-
- # Some global info
- nsi.write('Name "%s"\n' % self.fullname)
- nsi.write('OutFile "%s"\n' % output.toOsSpecific())
- if platform == 'win_amd64':
- nsi.write('InstallDir "$PROGRAMFILES64\\%s"\n' % self.fullname)
- else:
- nsi.write('InstallDir "$PROGRAMFILES\\%s"\n' % self.fullname)
- nsi.write('SetCompress auto\n')
- nsi.write('SetCompressor lzma\n')
- nsi.write('ShowInstDetails nevershow\n')
- nsi.write('ShowUninstDetails nevershow\n')
- nsi.write('InstType "Typical"\n')
-
- # Tell Vista that we require admin rights
- nsi.write('RequestExecutionLevel admin\n')
- nsi.write('\n')
- if self.offerRun:
- nsi.write('Function launch\n')
- nsi.write(' ExecShell "open" "$INSTDIR\\%s.exe"\n' % self.shortname)
- nsi.write('FunctionEnd\n')
- nsi.write('\n')
-
- if self.offerDesktopShortcut:
- nsi.write('Function desktopshortcut\n')
- if icofile is None:
- nsi.write(' CreateShortcut "$DESKTOP\\%s.lnk" "$INSTDIR\\%s.exe"\n' % (self.fullname, self.shortname))
- else:
- nsi.write(' CreateShortcut "$DESKTOP\\%s.lnk" "$INSTDIR\\%s.exe" "" "$INSTDIR\\%s.ico"\n' % (self.fullname, self.shortname, self.shortname))
- nsi.write('FunctionEnd\n')
- nsi.write('\n')
-
- nsi.write('!include "MUI2.nsh"\n')
- nsi.write('!define MUI_ABORTWARNING\n')
- if self.offerRun:
- nsi.write('!define MUI_FINISHPAGE_RUN\n')
- nsi.write('!define MUI_FINISHPAGE_RUN_NOTCHECKED\n')
- nsi.write('!define MUI_FINISHPAGE_RUN_FUNCTION launch\n')
- nsi.write('!define MUI_FINISHPAGE_RUN_TEXT "Run %s"\n' % self.fullname)
- if self.offerDesktopShortcut:
- nsi.write('!define MUI_FINISHPAGE_SHOWREADME ""\n')
- nsi.write('!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED\n')
- nsi.write('!define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut"\n')
- nsi.write('!define MUI_FINISHPAGE_SHOWREADME_FUNCTION desktopshortcut\n')
- nsi.write('\n')
- nsi.write('Var StartMenuFolder\n')
- nsi.write('!insertmacro MUI_PAGE_WELCOME\n')
- if not self.licensefile.empty():
- abs = Filename(self.licensefile)
- abs.makeAbsolute()
- nsi.write('!insertmacro MUI_PAGE_LICENSE "%s"\n' % abs.toOsSpecific())
- nsi.write('!insertmacro MUI_PAGE_DIRECTORY\n')
- nsi.write('!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder\n')
- nsi.write('!insertmacro MUI_PAGE_INSTFILES\n')
- nsi.write('!insertmacro MUI_PAGE_FINISH\n')
- nsi.write('!insertmacro MUI_UNPAGE_WELCOME\n')
- nsi.write('!insertmacro MUI_UNPAGE_CONFIRM\n')
- nsi.write('!insertmacro MUI_UNPAGE_INSTFILES\n')
- nsi.write('!insertmacro MUI_UNPAGE_FINISH\n')
- nsi.write('!insertmacro MUI_LANGUAGE "English"\n')
-
- # This section defines the installer.
- nsi.write('Section "" SecCore\n')
- nsi.write(' SetOutPath "$INSTDIR"\n')
- nsi.write(' File "%s"\n' % exefile.toOsSpecific())
- if icofile is not None:
- nsi.write(' File "%s"\n' % icofile.toOsSpecific())
- for f in extrafiles:
- nsi.write(' File "%s"\n' % f.toOsSpecific())
- curdir = ""
- for root, dirs, files in self.os_walk(hostDir.toOsSpecific()):
- for name in files:
- basefile = Filename.fromOsSpecific(os.path.join(root, name))
- file = Filename(basefile)
- file.makeAbsolute()
- file.makeRelativeTo(hostDir)
- outdir = file.getDirname().replace('/', '\\')
- if curdir != outdir:
- nsi.write(' SetOutPath "$INSTDIR\\%s"\n' % outdir)
- curdir = outdir
- nsi.write(' File "%s"\n' % (basefile.toOsSpecific()))
- nsi.write(' SetOutPath "$INSTDIR"\n')
- nsi.write(' WriteUninstaller "$INSTDIR\\Uninstall.exe"\n')
- nsi.write(' ; Start menu items\n')
- nsi.write(' !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n')
- nsi.write(' CreateDirectory "$SMPROGRAMS\\$StartMenuFolder"\n')
- if icofile is None:
- nsi.write(' CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk" "$INSTDIR\\%s.exe"\n' % (self.fullname, self.shortname))
- else:
- nsi.write(' CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk" "$INSTDIR\\%s.exe" "" "$INSTDIR\\%s.ico"\n' % (self.fullname, self.shortname, self.shortname))
- nsi.write(' CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\Uninstall.lnk" "$INSTDIR\\Uninstall.exe"\n')
- nsi.write(' !insertmacro MUI_STARTMENU_WRITE_END\n')
- nsi.write('SectionEnd\n')
-
- # This section defines the uninstaller.
- nsi.write('Section Uninstall\n')
- nsi.write(' Delete "$INSTDIR\\%s.exe"\n' % self.shortname)
- if icofile is not None:
- nsi.write(' Delete "$INSTDIR\\%s.ico"\n' % self.shortname)
- for f in extrafiles:
- nsi.write(' Delete "%s"\n' % f.getBasename())
- nsi.write(' Delete "$INSTDIR\\Uninstall.exe"\n')
- nsi.write(' RMDir /r "$INSTDIR"\n')
- nsi.write(' ; Desktop icon\n')
- nsi.write(' Delete "$DESKTOP\\%s.lnk"\n' % self.fullname)
- nsi.write(' ; Start menu items\n')
- nsi.write(' !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder\n')
- nsi.write(' Delete "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk"\n' % self.fullname)
- nsi.write(' Delete "$SMPROGRAMS\\$StartMenuFolder\\Uninstall.lnk"\n')
- nsi.write(' RMDir "$SMPROGRAMS\\$StartMenuFolder"\n')
- nsi.write('SectionEnd\n')
- nsi.close()
-
- cmd = [makensis]
- for o in ["V2"]:
- if sys.platform.startswith("win"):
- cmd.append("/" + o)
- else:
- cmd.append("-" + o)
- cmd.append(nsifile.toOsSpecific())
- print(cmd)
- try:
- retcode = subprocess.call(cmd, shell = False)
- if retcode != 0:
- self.notify.warning("Failure invoking NSIS command.")
- else:
- nsifile.unlink()
- except OSError:
- self.notify.warning("Unable to invoke NSIS command.")
-
- if icofile is not None:
- icofile.unlink()
-
- return output
-
- def os_walk(self, top):
- """ Re-implements os.walk(). For some reason the built-in
- definition is failing on Windows when this is run within a p3d
- environment!? """
-
- dirnames = []
- filenames = []
-
- dirlist = os.listdir(top)
- if dirlist:
- for file in dirlist:
- path = os.path.join(top, file)
- if os.path.isdir(path):
- dirnames.append(file)
- else:
- filenames.append(file)
-
- yield (top, dirnames, filenames)
-
- for dir in dirnames:
- next = os.path.join(top, dir)
- for tuple in self.os_walk(next):
- yield tuple
diff --git a/direct/src/p3d/FileSpec.py b/direct/src/p3d/FileSpec.py
deleted file mode 100644
index ddd1dafd6e..0000000000
--- a/direct/src/p3d/FileSpec.py
+++ /dev/null
@@ -1,246 +0,0 @@
-__all__ = ["FileSpec"]
-
-import os
-import time
-from panda3d.core import Filename, HashVal, VirtualFileSystem
-
-class FileSpec:
- """ This class represents a disk file whose hash and size
- etc. were read from an xml file. This class provides methods to
- verify whether the file on disk matches the version demanded by
- the xml. """
-
- def __init__(self):
- self.actualFile = None
- self.filename = None
- self.size = 0
- self.timestamp = 0
- self.hash = None
-
- def fromFile(self, packageDir, filename, pathname = None, st = None):
- """ Reads the file information from the indicated file. If st
- is supplied, it is the result of os.stat on the filename. """
-
- vfs = VirtualFileSystem.getGlobalPtr()
-
- filename = Filename(filename)
- if pathname is None:
- pathname = Filename(packageDir, filename)
-
- self.filename = str(filename)
- self.basename = filename.getBasename()
-
- if st is None:
- st = os.stat(pathname.toOsSpecific())
- self.size = st.st_size
- self.timestamp = int(st.st_mtime)
-
- self.readHash(pathname)
-
- def readHash(self, pathname):
- """ Reads the hash only from the indicated pathname. """
-
- hv = HashVal()
- hv.hashFile(pathname)
- self.hash = hv.asHex()
-
-
- def loadXml(self, xelement):
- """ Reads the file information from the indicated XML
- element. """
-
- self.filename = xelement.Attribute('filename')
- self.basename = None
- if self.filename:
- self.basename = Filename(self.filename).getBasename()
-
- size = xelement.Attribute('size')
- try:
- self.size = int(size)
- except:
- self.size = 0
-
- timestamp = xelement.Attribute('timestamp')
- try:
- self.timestamp = int(timestamp)
- except:
- self.timestamp = 0
-
- self.hash = xelement.Attribute('hash')
-
- def storeXml(self, xelement):
- """ Adds the file information to the indicated XML
- element. """
-
- if self.filename:
- xelement.SetAttribute('filename', self.filename)
- if self.size:
- xelement.SetAttribute('size', str(self.size))
- if self.timestamp:
- xelement.SetAttribute('timestamp', str(int(self.timestamp)))
- if self.hash:
- xelement.SetAttribute('hash', self.hash)
-
- def storeMiniXml(self, xelement):
- """ Adds the just the "mini" file information--size and
- hash--to the indicated XML element. """
-
- if self.size:
- xelement.SetAttribute('size', str(self.size))
- if self.hash:
- xelement.SetAttribute('hash', self.hash)
-
- def quickVerify(self, packageDir = None, pathname = None,
- notify = None, correctSelf = False):
- """ Performs a quick test to ensure the file has not been
- modified. This test is vulnerable to people maliciously
- attempting to fool the program (by setting datestamps etc.).
-
- if correctSelf is True, then any discrepency is corrected by
- updating the appropriate fields internally, making the
- assumption that the file on disk is the authoritative version.
-
- Returns true if it is intact, false if it is incorrect. If
- correctSelf is true, raises OSError if the self-update is
- impossible (for instance, because the file does not exist)."""
-
- if not pathname:
- pathname = Filename(packageDir, self.filename)
- try:
- st = os.stat(pathname.toOsSpecific())
- except OSError:
- # If the file is missing, the file fails.
- if notify:
- notify.debug("file not found: %s" % (pathname))
- if correctSelf:
- raise
- return False
-
- if st.st_size != self.size:
- # If the size is wrong, the file fails.
- if notify:
- notify.debug("size wrong: %s" % (pathname))
- if correctSelf:
- self.__correctHash(packageDir, pathname, st, notify)
- return False
-
- if int(st.st_mtime) == self.timestamp:
- # If the size is right and the timestamp is right, the
- # file passes.
- if notify:
- notify.debug("file ok: %s" % (pathname))
- return True
-
- if notify:
- notify.debug("modification time wrong: %s" % (pathname))
-
- # If the size is right but the timestamp is wrong, the file
- # soft-fails. We follow this up with a hash check.
- if not self.checkHash(packageDir, pathname, st):
- # Hard fail, the hash is wrong.
- if notify:
- notify.debug("hash check wrong: %s" % (pathname))
- notify.debug(" found %s, expected %s" % (self.actualFile.hash, self.hash))
- if correctSelf:
- self.__correctHash(packageDir, pathname, st, notify)
- return False
-
- if notify:
- notify.debug("hash check ok: %s" % (pathname))
-
- # The hash is OK after all. Change the file's timestamp back
- # to what we expect it to be, so we can quick-verify it
- # successfully next time.
- if correctSelf:
- # Or update our own timestamp.
- self.__correctTimestamp(pathname, st, notify)
- return False
- else:
- self.__updateTimestamp(pathname, st)
-
- return True
-
-
- def fullVerify(self, packageDir = None, pathname = None, notify = None):
- """ Performs a more thorough test to ensure the file has not
- been modified. This test is less vulnerable to malicious
- attacks, since it reads and verifies the entire file.
-
- Returns true if it is intact, false if it needs to be
- redownloaded. """
-
- if not pathname:
- pathname = Filename(packageDir, self.filename)
- try:
- st = os.stat(pathname.toOsSpecific())
- except OSError:
- # If the file is missing, the file fails.
- if notify:
- notify.debug("file not found: %s" % (pathname))
- return False
-
- if st.st_size != self.size:
- # If the size is wrong, the file fails;
- if notify:
- notify.debug("size wrong: %s" % (pathname))
- return False
-
- if not self.checkHash(packageDir, pathname, st):
- # Hard fail, the hash is wrong.
- if notify:
- notify.debug("hash check wrong: %s" % (pathname))
- notify.debug(" found %s, expected %s" % (self.actualFile.hash, self.hash))
- return False
-
- if notify:
- notify.debug("hash check ok: %s" % (pathname))
-
- # The hash is OK. If the timestamp is wrong, change it back
- # to what we expect it to be, so we can quick-verify it
- # successfully next time.
- if int(st.st_mtime) != self.timestamp:
- self.__updateTimestamp(pathname, st)
-
- return True
-
- def __updateTimestamp(self, pathname, st):
- # On Windows, we have to change the file to read-write before
- # we can successfully update its timestamp.
- try:
- os.chmod(pathname.toOsSpecific(), 0o755)
- os.utime(pathname.toOsSpecific(), (st.st_atime, self.timestamp))
- os.chmod(pathname.toOsSpecific(), 0o555)
- except OSError:
- pass
-
- def __correctTimestamp(self, pathname, st, notify):
- """ Corrects the internal timestamp to match the one on
- disk. """
- if notify:
- notify.info("Correcting timestamp of %s to %d (%s)" % (
- self.filename, st.st_mtime, time.asctime(time.localtime(st.st_mtime))))
- self.timestamp = int(st.st_mtime)
-
- def checkHash(self, packageDir, pathname, st):
- """ Returns true if the file has the expected md5 hash, false
- otherwise. As a side effect, stores a FileSpec corresponding
- to the on-disk file in self.actualFile. """
-
- fileSpec = FileSpec()
- fileSpec.fromFile(packageDir, self.filename,
- pathname = pathname, st = st)
- self.actualFile = fileSpec
-
- return (fileSpec.hash == self.hash)
-
- def __correctHash(self, packageDir, pathname, st, notify):
- """ Corrects the internal hash to match the one on disk. """
- if not self.actualFile:
- self.checkHash(packageDir, pathname, st)
-
- if notify:
- notify.info("Correcting hash %s to %s" % (
- self.filename, self.actualFile.hash))
- self.hash = self.actualFile.hash
- self.size = self.actualFile.size
- self.timestamp = self.actualFile.timestamp
diff --git a/direct/src/p3d/HostInfo.py b/direct/src/p3d/HostInfo.py
deleted file mode 100644
index 4e5efdcd3e..0000000000
--- a/direct/src/p3d/HostInfo.py
+++ /dev/null
@@ -1,751 +0,0 @@
-__all__ = ["HostInfo"]
-
-from panda3d.core import HashVal, Filename, PandaSystem, DocumentSpec, Ramfile
-from panda3d.core import HTTPChannel, ConfigVariableInt
-from panda3d import core
-from direct.p3d.PackageInfo import PackageInfo
-from direct.p3d.FileSpec import FileSpec
-from direct.directnotify.DirectNotifyGlobal import directNotify
-import time
-
-class HostInfo:
- """ This class represents a particular download host serving up
- Panda3D packages. It is the Python equivalent of the P3DHost
- class in the core API. """
-
- notify = directNotify.newCategory("HostInfo")
-
- def __init__(self, hostUrl, appRunner = None, hostDir = None,
- rootDir = None, asMirror = False, perPlatform = None):
-
- """ You must specify either an appRunner or a hostDir to the
- HostInfo constructor.
-
- If you pass asMirror = True, it means that this HostInfo
- object is to be used to populate a "mirror" folder, a
- duplicate (or subset) of the contents hosted by a server.
- This means when you use this HostInfo to download packages, it
- will only download the compressed archive file and leave it
- there. At the moment, mirror folders do not download old
- patch files from the server.
-
- If you pass perPlatform = True, then files are unpacked into a
- platform-specific directory, which is appropriate when you
- might be downloading multiple platforms. The default is
- perPlatform = False, which means all files are unpacked into
- the host directory directly, without an intervening
- platform-specific directory name. If asMirror is True, then
- the default is perPlatform = True.
-
- Note that perPlatform is also restricted by the individual
- package's specification. """
-
- self.__setHostUrl(hostUrl)
- self.appRunner = appRunner
- self.rootDir = rootDir
- if rootDir is None and appRunner:
- self.rootDir = appRunner.rootDir
-
- if hostDir and not isinstance(hostDir, Filename):
- hostDir = Filename.fromOsSpecific(hostDir)
-
- self.hostDir = hostDir
- self.asMirror = asMirror
- self.perPlatform = perPlatform
- if perPlatform is None:
- self.perPlatform = asMirror
-
- # Initially false, this is set true when the contents file is
- # successfully read.
- self.hasContentsFile = False
-
- # This is the time value at which the current contents file is
- # no longer valid.
- self.contentsExpiration = 0
-
- # Contains the md5 hash of the original contents.xml file.
- self.contentsSpec = FileSpec()
-
- # descriptiveName will be filled in later, when the
- # contents file is read.
- self.descriptiveName = None
-
- # A list of known mirrors for this host, all URL's guaranteed
- # to end with a slash.
- self.mirrors = []
-
- # A map of keyword -> altHost URL's. An altHost is different
- # than a mirror; an altHost is an alternate URL to download a
- # different (e.g. testing) version of this host's contents.
- # It is rarely used.
- self.altHosts = {}
-
- # This is a dictionary of packages by (name, version). It
- # will be filled in when the contents file is read.
- self.packages = {}
-
- if self.appRunner and self.appRunner.verifyContents != self.appRunner.P3DVCForce:
- # Attempt to pre-read the existing contents.xml; maybe it
- # will be current enough for our purposes.
- self.readContentsFile()
-
- def __setHostUrl(self, hostUrl):
- """ Assigns self.hostUrl, and related values. """
- self.hostUrl = hostUrl
-
- if not self.hostUrl:
- # A special case: the URL will be set later.
- self.hostUrlPrefix = None
- self.downloadUrlPrefix = None
- else:
- # hostUrlPrefix is the host URL, but it is guaranteed to end
- # with a slash.
- self.hostUrlPrefix = hostUrl
- if self.hostUrlPrefix[-1] != '/':
- self.hostUrlPrefix += '/'
-
- # downloadUrlPrefix is the URL prefix that should be used for
- # everything other than the contents.xml file. It might be
- # the same as hostUrlPrefix, but in the case of an
- # https-protected hostUrl, it will be the cleartext channel.
- self.downloadUrlPrefix = self.hostUrlPrefix
-
- def freshenFile(self, http, fileSpec, localPathname):
- """ Ensures that the localPathname is the most current version
- of the file defined by fileSpec, as offered by host. If not,
- it downloads a new version on-the-spot. Returns true on
- success, false on failure. """
-
- if fileSpec.quickVerify(pathname = localPathname):
- # It's good, keep it.
- return True
-
- # It's stale, get a new one.
- doc = None
- if self.appRunner and self.appRunner.superMirrorUrl:
- # Use the "super mirror" first.
- url = core.URLSpec(self.appRunner.superMirrorUrl + fileSpec.filename)
- self.notify.info("Freshening %s" % (url))
- doc = http.getDocument(url)
-
- if not doc or not doc.isValid():
- # Failing the super mirror, contact the actual host.
- url = core.URLSpec(self.hostUrlPrefix + fileSpec.filename)
- self.notify.info("Freshening %s" % (url))
- doc = http.getDocument(url)
- if not doc.isValid():
- return False
-
- file = Filename.temporary('', 'p3d_')
- if not doc.downloadToFile(file):
- # Failed to download.
- file.unlink()
- return False
-
- # Successfully downloaded!
- localPathname.makeDir()
- if not file.renameTo(localPathname):
- # Couldn't move it into place.
- file.unlink()
- return False
-
- if not fileSpec.fullVerify(pathname = localPathname, notify = self.notify):
- # No good after download.
- self.notify.info("%s is still no good after downloading." % (url))
- return False
-
- return True
-
- def downloadContentsFile(self, http, redownload = False,
- hashVal = None):
- """ Downloads the contents.xml file for this particular host,
- synchronously, and then reads it. Returns true on success,
- false on failure. If hashVal is not None, it should be a
- HashVal object, which will be filled with the hash from the
- new contents.xml file."""
-
- if self.hasCurrentContentsFile():
- # We've already got one.
- return True
-
- if self.appRunner and self.appRunner.verifyContents == self.appRunner.P3DVCNever:
- # Not allowed to.
- return False
-
- rf = None
- if http:
- if not redownload and self.appRunner and self.appRunner.superMirrorUrl:
- # We start with the "super mirror", if it's defined.
- url = self.appRunner.superMirrorUrl + 'contents.xml'
- request = DocumentSpec(url)
- self.notify.info("Downloading contents file %s" % (request))
-
- rf = Ramfile()
- channel = http.makeChannel(False)
- channel.getDocument(request)
- if not channel.downloadToRam(rf):
- self.notify.warning("Unable to download %s" % (url))
- rf = None
-
- if not rf:
- # Then go to the main host, if our super mirror let us
- # down.
-
- url = self.hostUrlPrefix + 'contents.xml'
- # Append a uniquifying query string to the URL to force the
- # download to go all the way through any caches. We use the
- # time in seconds; that's unique enough.
- url += '?' + str(int(time.time()))
-
- # We might as well explicitly request the cache to be disabled
- # too, since we have an interface for that via HTTPChannel.
- request = DocumentSpec(url)
- request.setCacheControl(DocumentSpec.CCNoCache)
-
- self.notify.info("Downloading contents file %s" % (request))
- statusCode = None
- statusString = ''
- for attempt in range(int(ConfigVariableInt('contents-xml-dl-attempts', 3))):
- if attempt > 0:
- self.notify.info("Retrying (%s)..."%(attempt,))
- rf = Ramfile()
- channel = http.makeChannel(False)
- channel.getDocument(request)
- if channel.downloadToRam(rf):
- self.notify.info("Successfully downloaded %s" % (url,))
- break
- else:
- rf = None
- statusCode = channel.getStatusCode()
- statusString = channel.getStatusString()
- self.notify.warning("Could not contact download server at %s" % (url,))
- self.notify.warning("Status code = %s %s" % (statusCode, statusString))
-
- if not rf:
- self.notify.warning("Unable to download %s" % (url,))
- try:
- # Something screwed up.
- if statusCode == HTTPChannel.SCDownloadOpenError or \
- statusCode == HTTPChannel.SCDownloadWriteError:
- launcher.setPandaErrorCode(2)
- elif statusCode == 404:
- # 404 not found
- launcher.setPandaErrorCode(5)
- elif statusCode < 100:
- # statusCode < 100 implies the connection attempt itself
- # failed. This is usually due to firewall software
- # interfering. Apparently some firewall software might
- # allow the first connection and disallow subsequent
- # connections; how strange.
- launcher.setPandaErrorCode(4)
- else:
- # There are other kinds of failures, but these will
- # generally have been caught already by the first test; so
- # if we get here there may be some bigger problem. Just
- # give the generic "big problem" message.
- launcher.setPandaErrorCode(6)
- except NameError as e:
- # no launcher
- pass
- except AttributeError as e:
- self.notify.warning("%s" % (str(e),))
- pass
- return False
-
- tempFilename = Filename.temporary('', 'p3d_', '.xml')
- if rf:
- f = open(tempFilename.toOsSpecific(), 'wb')
- f.write(rf.getData())
- f.close()
- if hashVal:
- hashVal.hashString(rf.getData())
-
- if not self.readContentsFile(tempFilename, freshDownload = True):
- self.notify.warning("Failure reading %s" % (url))
- tempFilename.unlink()
- return False
-
- tempFilename.unlink()
- return True
-
- # Couldn't download the file. Maybe we should look for a
- # previously-downloaded copy already on disk?
- return False
-
- def redownloadContentsFile(self, http):
- """ Downloads a new contents.xml file in case it has changed.
- Returns true if the file has indeed changed, false if it has
- not. """
- assert self.hasContentsFile
-
- if self.appRunner and self.appRunner.verifyContents == self.appRunner.P3DVCNever:
- # Not allowed to.
- return False
-
- url = self.hostUrlPrefix + 'contents.xml'
- self.notify.info("Redownloading %s" % (url))
-
- # Get the hash of the original file.
- assert self.hostDir
- hv1 = HashVal()
- if self.contentsSpec.hash:
- hv1.setFromHex(self.contentsSpec.hash)
- else:
- filename = Filename(self.hostDir, 'contents.xml')
- hv1.hashFile(filename)
-
- # Now download it again.
- self.hasContentsFile = False
- hv2 = HashVal()
- if not self.downloadContentsFile(http, redownload = True,
- hashVal = hv2):
- return False
-
- if hv2 == HashVal():
- self.notify.info("%s didn't actually redownload." % (url))
- return False
- elif hv1 != hv2:
- self.notify.info("%s has changed." % (url))
- return True
- else:
- self.notify.info("%s has not changed." % (url))
- return False
-
- def hasCurrentContentsFile(self):
- """ Returns true if a contents.xml file has been successfully
- read for this host and is still current, false otherwise. """
- if not self.appRunner \
- or self.appRunner.verifyContents == self.appRunner.P3DVCNone \
- or self.appRunner.verifyContents == self.appRunner.P3DVCNever:
- # If we're not asking to verify contents, then
- # contents.xml files never expires.
- return self.hasContentsFile
-
- now = int(time.time())
- return now < self.contentsExpiration and self.hasContentsFile
-
- def readContentsFile(self, tempFilename = None, freshDownload = False):
- """ Reads the contents.xml file for this particular host, once
- it has been downloaded into the indicated temporary file.
- Returns true on success, false if the contents file is not
- already on disk or is unreadable.
-
- If tempFilename is specified, it is the filename read, and it
- is copied the file into the standard location if it's not
- there already. If tempFilename is not specified, the standard
- filename is read if it is known. """
-
- if not hasattr(core, 'TiXmlDocument'):
- return False
-
- if not tempFilename:
- if self.hostDir:
- # If the filename is not specified, we can infer it
- # if we already know our hostDir
- hostDir = self.hostDir
- else:
- # Otherwise, we have to guess the hostDir.
- hostDir = self.__determineHostDir(None, self.hostUrl)
-
- tempFilename = Filename(hostDir, 'contents.xml')
-
- doc = core.TiXmlDocument(tempFilename.toOsSpecific())
- if not doc.LoadFile():
- return False
-
- xcontents = doc.FirstChildElement('contents')
- if not xcontents:
- return False
-
- maxAge = xcontents.Attribute('max_age')
- if maxAge:
- try:
- maxAge = int(maxAge)
- except:
- maxAge = None
- if maxAge is None:
- # Default max_age if unspecified (see p3d_plugin.h).
- from direct.p3d.AppRunner import AppRunner
- maxAge = AppRunner.P3D_CONTENTS_DEFAULT_MAX_AGE
-
- # Get the latest possible expiration time, based on the max_age
- # indication. Any expiration time later than this is in error.
- now = int(time.time())
- self.contentsExpiration = now + maxAge
-
- if freshDownload:
- self.contentsSpec.readHash(tempFilename)
-
- # Update the XML with the new download information.
- xorig = xcontents.FirstChildElement('orig')
- while xorig:
- xcontents.RemoveChild(xorig)
- xorig = xcontents.FirstChildElement('orig')
-
- xorig = core.TiXmlElement('orig')
- self.contentsSpec.storeXml(xorig)
- xorig.SetAttribute('expiration', str(self.contentsExpiration))
-
- xcontents.InsertEndChild(xorig)
-
- else:
- # Read the download hash and expiration time from the XML.
- expiration = None
- xorig = xcontents.FirstChildElement('orig')
- if xorig:
- self.contentsSpec.loadXml(xorig)
- expiration = xorig.Attribute('expiration')
- if expiration:
- try:
- expiration = int(expiration)
- except:
- expiration = None
- if not self.contentsSpec.hash:
- self.contentsSpec.readHash(tempFilename)
-
- if expiration is not None:
- self.contentsExpiration = min(self.contentsExpiration, expiration)
-
- # Look for our own entry in the hosts table.
- if self.hostUrl:
- self.__findHostXml(xcontents)
- else:
- assert self.hostDir
- self.__findHostXmlForHostDir(xcontents)
-
- if self.rootDir and not self.hostDir:
- self.hostDir = self.__determineHostDir(None, self.hostUrl)
-
- # Get the list of packages available for download and/or import.
- xpackage = xcontents.FirstChildElement('package')
- while xpackage:
- name = xpackage.Attribute('name')
- platform = xpackage.Attribute('platform')
- version = xpackage.Attribute('version')
- try:
- solo = int(xpackage.Attribute('solo') or '')
- except ValueError:
- solo = False
- try:
- perPlatform = int(xpackage.Attribute('per_platform') or '')
- except ValueError:
- perPlatform = False
-
- package = self.__makePackage(name, platform, version, solo, perPlatform)
- package.descFile = FileSpec()
- package.descFile.loadXml(xpackage)
- package.setupFilenames()
-
- package.importDescFile = None
- ximport = xpackage.FirstChildElement('import')
- if ximport:
- package.importDescFile = FileSpec()
- package.importDescFile.loadXml(ximport)
-
- xpackage = xpackage.NextSiblingElement('package')
-
- self.hasContentsFile = True
-
- # Now save the contents.xml file into the standard location.
- if self.appRunner and self.appRunner.verifyContents != self.appRunner.P3DVCNever:
- assert self.hostDir
- filename = Filename(self.hostDir, 'contents.xml')
- filename.makeDir()
- if freshDownload:
- doc.SaveFile(filename.toOsSpecific())
- else:
- if filename != tempFilename:
- tempFilename.copyTo(filename)
-
- return True
-
- def __findHostXml(self, xcontents):
- """ Looks for the or entry in the
- contents.xml that corresponds to the URL that we actually
- downloaded from. """
-
- xhost = xcontents.FirstChildElement('host')
- while xhost:
- url = xhost.Attribute('url')
- if url == self.hostUrl:
- self.readHostXml(xhost)
- return
-
- xalthost = xhost.FirstChildElement('alt_host')
- while xalthost:
- url = xalthost.Attribute('url')
- if url == self.hostUrl:
- self.readHostXml(xalthost)
- return
- xalthost = xalthost.NextSiblingElement('alt_host')
-
- xhost = xhost.NextSiblingElement('host')
-
- def __findHostXmlForHostDir(self, xcontents):
- """ Looks for the or entry in the
- contents.xml that corresponds to the host dir that we read the
- contents.xml from. This is used when reading a contents.xml
- file found on disk, as opposed to downloading it from a
- site. """
-
- xhost = xcontents.FirstChildElement('host')
- while xhost:
- url = xhost.Attribute('url')
- hostDirBasename = xhost.Attribute('host_dir')
- hostDir = self.__determineHostDir(hostDirBasename, url)
- if hostDir == self.hostDir:
- self.__setHostUrl(url)
- self.readHostXml(xhost)
- return
-
- xalthost = xhost.FirstChildElement('alt_host')
- while xalthost:
- url = xalthost.Attribute('url')
- hostDirBasename = xalthost.Attribute('host_dir')
- hostDir = self.__determineHostDir(hostDirBasename, url)
- if hostDir == self.hostDir:
- self.__setHostUrl(url)
- self.readHostXml(xalthost)
- return
- xalthost = xalthost.NextSiblingElement('alt_host')
-
- xhost = xhost.NextSiblingElement('host')
-
- def readHostXml(self, xhost):
- """ Reads a or entry and applies the data to
- this object. """
-
- descriptiveName = xhost.Attribute('descriptive_name')
- if descriptiveName and not self.descriptiveName:
- self.descriptiveName = descriptiveName
-
- hostDirBasename = xhost.Attribute('host_dir')
- if self.rootDir and not self.hostDir:
- self.hostDir = self.__determineHostDir(hostDirBasename, self.hostUrl)
-
- # Get the "download" URL, which is the source from which we
- # download everything other than the contents.xml file.
- downloadUrl = xhost.Attribute('download_url')
- if downloadUrl:
- self.downloadUrlPrefix = downloadUrl
- if self.downloadUrlPrefix[-1] != '/':
- self.downloadUrlPrefix += '/'
- else:
- self.downloadUrlPrefix = self.hostUrlPrefix
-
- xmirror = xhost.FirstChildElement('mirror')
- while xmirror:
- url = xmirror.Attribute('url')
- if url:
- if url[-1] != '/':
- url += '/'
- if url not in self.mirrors:
- self.mirrors.append(url)
- xmirror = xmirror.NextSiblingElement('mirror')
-
- xalthost = xhost.FirstChildElement('alt_host')
- while xalthost:
- keyword = xalthost.Attribute('keyword')
- url = xalthost.Attribute('url')
- if url and keyword:
- self.altHosts[keyword] = url
- xalthost = xalthost.NextSiblingElement('alt_host')
-
- def __makePackage(self, name, platform, version, solo, perPlatform):
- """ Creates a new PackageInfo entry for the given name,
- version, and platform. If there is already a matching
- PackageInfo, returns it. """
-
- if not platform:
- platform = None
-
- platforms = self.packages.setdefault((name, version or ""), {})
- package = platforms.get("", None)
- if not package:
- package = PackageInfo(self, name, version, platform = platform,
- solo = solo, asMirror = self.asMirror,
- perPlatform = perPlatform)
- platforms[platform or ""] = package
-
- return package
-
- def getPackage(self, name, version, platform = None):
- """ Returns a PackageInfo that matches the indicated name and
- version and the indicated platform or the current runtime
- platform, if one is provided by this host, or None if not. """
-
- assert self.hasContentsFile
- platforms = self.packages.get((name, version or ""), {})
-
- if platform:
- # In this case, we are looking for a specific platform
- # only.
- return platforms.get(platform, None)
-
- # We are looking for one matching the current runtime
- # platform. First, look for a package matching the current
- # platform exactly.
- package = platforms.get(PandaSystem.getPlatform(), None)
-
- # If not found, look for one matching no particular platform.
- if not package:
- package = platforms.get("", None)
-
- return package
-
- def getPackages(self, name = None, platform = None):
- """ Returns a list of PackageInfo objects that match the
- indicated name and/or platform, with no particular regards to
- version. If name is None, all packages are returned. """
-
- assert self.hasContentsFile
-
- packages = []
- for (pn, version), platforms in self.packages.items():
- if name and pn != name:
- continue
-
- if not platform:
- for p2 in platforms:
- package = self.getPackage(pn, version, platform = p2)
- if package:
- packages.append(package)
- else:
- package = self.getPackage(pn, version, platform = platform)
- if package:
- packages.append(package)
-
- return packages
-
- def getAllPackages(self, includeAllPlatforms = False):
- """ Returns a list of all available packages provided by this
- host. """
-
- result = []
-
- items = sorted(self.packages.items())
- for key, platforms in items:
- if self.perPlatform or includeAllPlatforms:
- # If we maintain a different answer per platform,
- # return all of them.
- pitems = sorted(platforms.items())
- for pkey, package in pitems:
- result.append(package)
- else:
- # If we maintain a host for the current platform
- # only (e.g. a client copy), then return only the
- # current platform, or no particular platform.
- package = platforms.get(PandaSystem.getPlatform(), None)
- if not package:
- package = platforms.get("", None)
-
- if package:
- result.append(package)
-
- return result
-
- def deletePackages(self, packages):
- """ Removes all of the indicated packages from the disk,
- uninstalling them and deleting all of their files. The
- packages parameter must be a list of one or more PackageInfo
- objects, for instance as returned by getPackage(). Returns
- the list of packages that were NOT found. """
-
- packages = packages[:]
-
- for key, platforms in list(self.packages.items()):
- for platform, package in list(platforms.items()):
- if package in packages:
- self.__deletePackageFiles(package)
- del platforms[platform]
- packages.remove(package)
-
- if not platforms:
- # If we've removed all the platforms for a given
- # package, remove the key from the toplevel map.
- del self.packages[key]
-
- return packages
-
- def __deletePackageFiles(self, package):
- """ Called by deletePackage(), this actually removes the files
- for the indicated package. """
-
- if self.appRunner:
- self.notify.info("Deleting package %s: %s" % (package.packageName, package.getPackageDir()))
- self.appRunner.rmtree(package.getPackageDir())
-
- self.appRunner.sendRequest('forget_package', self.hostUrl, package.packageName, package.packageVersion or '')
-
- def __determineHostDir(self, hostDirBasename, hostUrl):
- """ Hashes the host URL into a (mostly) unique directory
- string, which will be the root of the host's install tree.
- Returns the resulting path, as a Filename.
-
- This code is duplicated in C++, in
- P3DHost::determine_host_dir(). """
-
- if hostDirBasename:
- # If the contents.xml specified a host_dir parameter, use
- # it.
- hostDir = str(self.rootDir) + '/hosts'
- for component in hostDirBasename.split('/'):
- if component:
- if component[0] == '.':
- # Forbid ".foo" or "..".
- component = 'x' + component
- hostDir += '/'
- hostDir += component
- return Filename(hostDir)
-
- hostDir = 'hosts/'
-
- # Look for a server name in the URL. Including this string in the
- # directory name makes it friendlier for people browsing the
- # directory.
-
- # We could use URLSpec, but we do it by hand instead, to make
- # it more likely that our hash code will exactly match the
- # similar logic in P3DHost.
- p = hostUrl.find('://')
- hostname = ''
- if p != -1:
- start = p + 3
- end = hostUrl.find('/', start)
- # Now start .. end is something like "username@host:port".
-
- at = hostUrl.find('@', start)
- if at != -1 and at < end:
- start = at + 1
-
- colon = hostUrl.find(':', start)
- if colon != -1 and colon < end:
- end = colon
-
- # Now start .. end is just the hostname.
- hostname = hostUrl[start : end]
-
- # Now build a hash string of the whole URL. We'll use MD5 to
- # get a pretty good hash, with a minimum chance of collision.
- # Even if there is a hash collision, though, it's not the end
- # of the world; it just means that both hosts will dump their
- # packages into the same directory, and they'll fight over the
- # toplevel contents.xml file. Assuming they use different
- # version numbers (which should be safe since they have the
- # same hostname), there will be minimal redownloading.
-
- hashSize = 16
- keepHash = hashSize
- if hostname:
- hostDir += hostname + '_'
-
- # If we successfully got a hostname, we don't really need the
- # full hash. We'll keep half of it.
- keepHash = keepHash // 2
-
- md = HashVal()
- md.hashString(hostUrl)
- hostDir += md.asHex()[:keepHash * 2]
-
- hostDir = Filename(self.rootDir, hostDir)
- return hostDir
diff --git a/direct/src/p3d/InstalledHostData.py b/direct/src/p3d/InstalledHostData.py
deleted file mode 100644
index d98abb6d18..0000000000
--- a/direct/src/p3d/InstalledHostData.py
+++ /dev/null
@@ -1,24 +0,0 @@
-__all__ = ["InstalledHostData"]
-
-from panda3d.core import URLSpec
-
-class InstalledHostData:
- """ A list of instances of this class is returned by
- AppRunner.scanInstalledPackages(). Each of these corresponds to a
- particular host that has provided packages that have been
- installed on the local client. """
-
- def __init__(self, host, dirnode):
- self.host = host
- self.pathname = dirnode.pathname
- self.totalSize = dirnode.getTotalSize()
- self.packages = []
-
- if self.host:
- self.hostUrl = self.host.hostUrl
- self.descriptiveName = self.host.descriptiveName
- if not self.descriptiveName:
- self.descriptiveName = URLSpec(self.hostUrl).getServer()
- else:
- self.hostUrl = 'unknown'
- self.descriptiveName = 'unknown'
diff --git a/direct/src/p3d/InstalledPackageData.py b/direct/src/p3d/InstalledPackageData.py
deleted file mode 100644
index 09ee81cda6..0000000000
--- a/direct/src/p3d/InstalledPackageData.py
+++ /dev/null
@@ -1,30 +0,0 @@
-__all__ = ["InstalledPackageData"]
-
-class InstalledPackageData:
- """ A list of instances of this class is maintained by
- InstalledHostData (which is in turn returned by
- AppRunner.scanInstalledPackages()). Each of these corresponds to
- a particular package that has been installed on the local
- client. """
-
- def __init__(self, package, dirnode):
- self.package = package
- self.pathname = dirnode.pathname
- self.totalSize = dirnode.getTotalSize()
- self.lastUse = None
-
- if self.package:
- self.displayName = self.package.getFormattedName()
- xusage = self.package.getUsage()
-
- if xusage:
- lastUse = xusage.Attribute('last_use')
- try:
- lastUse = int(lastUse or '')
- except ValueError:
- lastUse = None
- self.lastUse = lastUse
-
- else:
- self.displayName = dirnode.pathname.getBasename()
-
diff --git a/direct/src/p3d/JavaScript.py b/direct/src/p3d/JavaScript.py
deleted file mode 100644
index b59deb1aa2..0000000000
--- a/direct/src/p3d/JavaScript.py
+++ /dev/null
@@ -1,298 +0,0 @@
-""" This module defines some simple classes and instances which are
-useful when writing code that integrates with JavaScript, especially
-code that runs in a browser via the web plugin. """
-
-__all__ = ["UndefinedObject", "Undefined", "ConcreteStruct", "BrowserObject", "MethodWrapper"]
-
-class UndefinedObject:
- """ This is a special object that is returned by the browser to
- represent an "undefined" or "void" value, typically the value for
- an uninitialized variable or undefined property. It has no
- attributes, similar to None, but it is a slightly different
- concept in JavaScript. """
-
- def __bool__(self):
- return False
-
- __nonzero__ = __bool__ # Python 2
-
- def __str__(self):
- return "Undefined"
-
-# In fact, we normally always return this precise instance of the
-# UndefinedObject.
-Undefined = UndefinedObject()
-
-class ConcreteStruct:
- """ Python objects that inherit from this class are passed to
- JavaScript as a concrete struct: a mapping from string -> value,
- with no methods, passed by value. This can be more optimal than
- traditional Python objects which are passed by reference,
- especially for small objects which might be repeatedly referenced
- on the JavaScript side. """
-
- def __init__(self):
- pass
-
- def getConcreteProperties(self):
- """ Returns a list of 2-tuples of the (key, value) pairs that
- are to be passed to the concrete instance. By default, this
- returns all properties of the object. You can override this
- to restrict the set of properties that are uploaded. """
-
- return list(self.__dict__.items())
-
-class BrowserObject:
- """ This class provides the Python wrapper around some object that
- actually exists in the plugin host's namespace, e.g. a JavaScript
- or DOM object. """
-
- def __init__(self, runner, objectId):
- self.__dict__['_BrowserObject__runner'] = runner
- self.__dict__['_BrowserObject__objectId'] = objectId
-
- # This element is filled in by __getattr__; it connects
- # the object to its parent.
- self.__dict__['_BrowserObject__childObject'] = (None, None)
-
- # This is a cache of method names to MethodWrapper objects in
- # the parent object.
- self.__dict__['_BrowserObject__methods'] = {}
-
- def __del__(self):
- # When the BrowserObject destructs, tell the parent process it
- # doesn't need to keep around its corresponding P3D_object any
- # more.
- self.__runner.dropObject(self.__objectId)
-
- def __cacheMethod(self, methodName):
- """ Stores a pointer to the named method on this object, so
- that the next time __getattr__ is called, it can retrieve the
- method wrapper without having to query the browser. This
- cache assumes that callable methods don't generally come and
- go on and object.
-
- The return value is the MethodWrapper object. """
-
- method = self.__methods.get(methodName, None)
- if method is None:
- method = MethodWrapper(self.__runner, self, methodName)
- self.__methods[methodName] = method
- return method
-
- def __str__(self):
- return self.toString()
-
- def __bool__(self):
- return True
-
- __nonzero__ = __bool__ # Python 2
-
- def __call__(self, *args, **kw):
- needsResponse = True
- if 'needsResponse' in kw:
- needsResponse = kw['needsResponse']
- del kw['needsResponse']
- if kw:
- raise ArgumentError('Keyword arguments not supported')
-
- try:
- parentObj, attribName = self.__childObject
- if parentObj:
- # Call it as a method.
- if parentObj is self.__runner.dom and attribName == 'alert':
- # As a special hack, we don't wait for the return
- # value from the alert() call, since this is a
- # blocking call, and waiting for this could cause
- # problems.
- needsResponse = False
-
- if parentObj is self.__runner.dom and attribName == 'eval' and len(args) == 1 and isinstance(args[0], str):
- # As another special hack, we make dom.eval() a
- # special case, and map it directly into an eval()
- # call. If the string begins with 'void ', we further
- # assume we're not waiting for a response.
- if args[0].startswith('void '):
- needsResponse = False
- result = self.__runner.scriptRequest('eval', parentObj, value = args[0], needsResponse = needsResponse)
- else:
- # This is a normal method call.
- try:
- result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse)
- except EnvironmentError:
- # Problem on the call. Maybe no such method?
- raise AttributeError
-
- # Hey, the method call appears to have succeeded.
- # Cache the method object on the parent so we won't
- # have to look up the method wrapper again next time.
- parentObj.__cacheMethod(attribName)
-
- else:
- # Call it as a plain function.
- result = self.__runner.scriptRequest('call', self, value = args, needsResponse = needsResponse)
- except EnvironmentError:
- # Some odd problem on the call.
- raise TypeError
-
- return result
-
- def __getattr__(self, name):
- """ Remaps attempts to query an attribute, as in obj.attr,
- into the appropriate calls to query the actual browser object
- under the hood. """
-
- # First check to see if there's a cached method wrapper from a
- # previous call.
- method = self.__methods.get(name, None)
- if method:
- return method
-
- # No cache. Go query the browser for the desired value.
- try:
- value = self.__runner.scriptRequest('get_property', self,
- propertyName = name)
- except EnvironmentError:
- # Failed to retrieve the attribute. But maybe there's a
- # method instead?
- if self.__runner.scriptRequest('has_method', self, propertyName = name):
- # Yes, so create a method wrapper for it.
- return self.__cacheMethod(name)
-
- raise AttributeError(name)
-
- if isinstance(value, BrowserObject):
- # Fill in the parent object association, so __call__ can
- # properly call a method. (Javascript needs to know the
- # method container at the time of the call, and doesn't
- # store it on the function object.)
- value.__dict__['_BrowserObject__childObject'] = (self, name)
-
- return value
-
- def __setattr__(self, name, value):
- if name in self.__dict__:
- self.__dict__[name] = value
- return
-
- result = self.__runner.scriptRequest('set_property', self,
- propertyName = name,
- value = value)
- if not result:
- raise AttributeError(name)
-
- def __delattr__(self, name):
- if name in self.__dict__:
- del self.__dict__[name]
- return
-
- result = self.__runner.scriptRequest('del_property', self,
- propertyName = name)
- if not result:
- raise AttributeError(name)
-
- def __getitem__(self, key):
- """ Remaps attempts to query an attribute, as in obj['attr'],
- into the appropriate calls to query the actual browser object
- under the hood. Following the JavaScript convention, we treat
- obj['attr'] almost the same as obj.attr. """
-
- try:
- value = self.__runner.scriptRequest('get_property', self,
- propertyName = str(key))
- except EnvironmentError:
- # Failed to retrieve the property. We return IndexError
- # for numeric keys so we can properly support Python's
- # iterators, but we return KeyError for string keys to
- # emulate mapping objects.
- if isinstance(key, str):
- raise KeyError(key)
- else:
- raise IndexError(key)
-
- return value
-
- def __setitem__(self, key, value):
- result = self.__runner.scriptRequest('set_property', self,
- propertyName = str(key),
- value = value)
- if not result:
- if isinstance(key, str):
- raise KeyError(key)
- else:
- raise IndexError(key)
-
- def __delitem__(self, key):
- result = self.__runner.scriptRequest('del_property', self,
- propertyName = str(key))
- if not result:
- if isinstance(key, str):
- raise KeyError(key)
- else:
- raise IndexError(key)
-
-class MethodWrapper:
- """ This is a Python wrapper around a property of a BrowserObject
- that doesn't appear to be a first-class object in the Python
- sense, but is nonetheless a callable method. """
-
- def __init__(self, runner, parentObj, objectId):
- self.__dict__['_MethodWrapper__runner'] = runner
- self.__dict__['_MethodWrapper__childObject'] = (parentObj, objectId)
-
- def __str__(self):
- parentObj, attribName = self.__childObject
- return "%s.%s" % (parentObj, attribName)
-
- def __bool__(self):
- return True
-
- __nonzero__ = __bool__ # Python 2
-
- def __call__(self, *args, **kw):
- needsResponse = True
- if 'needsResponse' in kw:
- needsResponse = kw['needsResponse']
- del kw['needsResponse']
- if kw:
- raise ArgumentError('Keyword arguments not supported')
-
- try:
- parentObj, attribName = self.__childObject
- # Call it as a method.
- if parentObj is self.__runner.dom and attribName == 'alert':
- # As a special hack, we don't wait for the return
- # value from the alert() call, since this is a
- # blocking call, and waiting for this could cause
- # problems.
- needsResponse = False
-
- if parentObj is self.__runner.dom and attribName == 'eval' and len(args) == 1 and isinstance(args[0], str):
- # As another special hack, we make dom.eval() a
- # special case, and map it directly into an eval()
- # call. If the string begins with 'void ', we further
- # assume we're not waiting for a response.
- if args[0].startswith('void '):
- needsResponse = False
- result = self.__runner.scriptRequest('eval', parentObj, value = args[0], needsResponse = needsResponse)
- else:
- # This is a normal method call.
- try:
- result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse)
- except EnvironmentError:
- # Problem on the call. Maybe no such method?
- raise AttributeError
-
- except EnvironmentError:
- # Some odd problem on the call.
- raise TypeError
-
- return result
-
- def __setattr__(self, name, value):
- """ setattr will generally fail on method objects. """
- raise AttributeError(name)
-
- def __delattr__(self, name):
- """ delattr will generally fail on method objects. """
- raise AttributeError(name)
diff --git a/direct/src/p3d/PackageInfo.py b/direct/src/p3d/PackageInfo.py
deleted file mode 100644
index 08a73365b8..0000000000
--- a/direct/src/p3d/PackageInfo.py
+++ /dev/null
@@ -1,1237 +0,0 @@
-__all__ = ["PackageInfo"]
-
-from panda3d.core import Filename, DocumentSpec, Multifile, Decompressor, EUOk, EUSuccess, VirtualFileSystem, Thread, getModelPath, ExecutionEnvironment, PStatCollector, TiXmlDocument, TiXmlDeclaration, TiXmlElement
-import panda3d.core as core
-from direct.p3d.FileSpec import FileSpec
-from direct.p3d.ScanDirectoryNode import ScanDirectoryNode
-from direct.showbase import VFSImporter
-from direct.directnotify.DirectNotifyGlobal import directNotify
-from direct.task.TaskManagerGlobal import taskMgr
-import os
-import sys
-import random
-import time
-import copy
-
-class PackageInfo:
-
- """ This class represents a downloadable Panda3D package file that
- can be (or has been) installed into the current runtime. It is
- the Python equivalent of the P3DPackage class in the core API. """
-
- notify = directNotify.newCategory("PackageInfo")
-
- # Weight factors for computing download progress. This
- # attempts to reflect the relative time-per-byte of each of
- # these operations.
- downloadFactor = 1
- uncompressFactor = 0.01
- unpackFactor = 0.01
- patchFactor = 0.01
-
- # These tokens are yielded (not returned) by __downloadFile() and
- # other InstallStep functions.
- stepComplete = 1
- stepFailed = 2
- restartDownload = 3
- stepContinue = 4
-
- UsageBasename = 'usage.xml'
-
- class InstallStep:
- """ This class is one step of the installPlan list; it
- represents a single atomic piece of the installation step, and
- the relative effort of that piece. When the plan is executed,
- it will call the saved function pointer here. """
- def __init__(self, func, bytes, factor, stepType):
- self.__funcPtr = func
- self.bytesNeeded = bytes
- self.bytesDone = 0
- self.bytesFactor = factor
- self.stepType = stepType
- self.pStatCol = PStatCollector(':App:PackageInstaller:%s' % (stepType))
-
- def func(self):
- """ self.__funcPtr(self) will return a generator of
- tokens. This function defines a new generator that yields
- each of those tokens, but wraps each call into the nested
- generator within a pair of start/stop collector calls. """
-
- self.pStatCol.start()
- for token in self.__funcPtr(self):
- self.pStatCol.stop()
- yield token
- self.pStatCol.start()
-
- # Shouldn't ever get here.
- self.pStatCol.stop()
- raise StopIteration
-
- def getEffort(self):
- """ Returns the relative amount of effort of this step. """
- return self.bytesNeeded * self.bytesFactor
-
- def getProgress(self):
- """ Returns the progress of this step, in the range
- 0..1. """
- if self.bytesNeeded == 0:
- return 1
- return min(float(self.bytesDone) / float(self.bytesNeeded), 1)
-
- def __init__(self, host, packageName, packageVersion, platform = None,
- solo = False, asMirror = False, perPlatform = False):
- self.host = host
- self.packageName = packageName
- self.packageVersion = packageVersion
- self.platform = platform
- self.solo = solo
- self.asMirror = asMirror
- self.perPlatform = perPlatform
-
- # This will be active while we are in the middle of a download
- # cycle.
- self.http = None
-
- # This will be filled in when the host's contents.xml file is
- # read.
- self.packageDir = None
-
- # These will be filled in by HostInfo when the package is read
- # from contents.xml.
- self.descFile = None
- self.importDescFile = None
-
- # These are filled in when the desc file is successfully read.
- self.hasDescFile = False
- self.patchVersion = None
- self.displayName = None
- self.guiApp = False
- self.uncompressedArchive = None
- self.compressedArchive = None
- self.extracts = []
- self.requires = []
- self.installPlans = None
-
- # This is updated during downloadPackage(). It is in the
- # range 0..1.
- self.downloadProgress = 0
-
- # This is set true when the package file has been fully
- # downloaded and unpacked.
- self.hasPackage = False
-
- # This is set true when the package has been "installed",
- # meaning it's been added to the paths and all.
- self.installed = False
-
- # This is set true when the package has been updated in this
- # session, but not yet written to usage.xml.
- self.updated = False
- self.diskSpace = None
-
- def getPackageDir(self):
- """ Returns the directory in which this package is installed.
- This may not be known until the host's contents.xml file has
- been downloaded, which informs us of the host's own install
- directory. """
-
- if not self.packageDir:
- if not self.host.hasContentsFile:
- if not self.host.readContentsFile():
- self.host.downloadContentsFile(self.http)
-
- # Derive the packageDir from the hostDir.
- self.packageDir = Filename(self.host.hostDir, self.packageName)
- if self.packageVersion:
- self.packageDir = Filename(self.packageDir, self.packageVersion)
-
- if self.host.perPlatform:
- # If we're running on a special host that wants us to
- # include the platform, we include it.
- includePlatform = True
- elif self.perPlatform and self.host.appRunner.respectPerPlatform:
- # Otherwise, if our package spec wants us to include
- # the platform (and our plugin knows about this), then
- # we also include it.
- includePlatform = True
- else:
- # Otherwise, we must be running legacy code
- # somewhere--either an old package or an old
- # plugin--and we therefore shouldn't include the
- # platform in the directory hierarchy.
- includePlatform = False
-
- if includePlatform and self.platform:
- self.packageDir = Filename(self.packageDir, self.platform)
-
- return self.packageDir
-
- def getDownloadEffort(self):
- """ Returns the relative amount of effort it will take to
- download this package. The units are meaningless, except
- relative to other packges."""
-
- if not self.installPlans:
- return 0
-
- # Return the size of plan A, assuming it will work.
- plan = self.installPlans[0]
- size = sum([step.getEffort() for step in plan])
-
- return size
-
- def getPrevDownloadedEffort(self):
- """ Returns a rough estimate of this package's total download
- effort, even if it is already downloaded. """
-
- effort = 0
- if self.compressedArchive:
- effort += self.compressedArchive.size * self.downloadFactor
- if self.uncompressedArchive:
- effort += self.uncompressedArchive.size * self.uncompressFactor
- # Don't bother counting unpacking.
-
- return effort
-
- def getFormattedName(self):
- """ Returns the name of this package, for output to the user.
- This will be the "public" name of the package, as formatted
- for user consumption; it will include capital letters and
- spaces where appropriate. """
-
- if self.displayName:
- name = self.displayName
- else:
- name = self.packageName
- if self.packageVersion:
- name += ' %s' % (self.packageVersion)
-
- if self.patchVersion:
- name += ' rev %s' % (self.patchVersion)
-
- return name
-
-
- def setupFilenames(self):
- """ This is called by the HostInfo when the package is read
- from contents.xml, to set up the internal filenames and such
- that rely on some of the information from contents.xml. """
-
- dirname, basename = self.descFile.filename.rsplit('/', 1)
- self.descFileDirname = dirname
- self.descFileBasename = basename
-
- def checkStatus(self):
- """ Checks the current status of the desc file and the package
- contents on disk. """
-
- if self.hasPackage:
- return True
-
- if not self.hasDescFile:
- filename = Filename(self.getPackageDir(), self.descFileBasename)
- if self.descFile.quickVerify(self.getPackageDir(), pathname = filename, notify = self.notify):
- if self.__readDescFile():
- # Successfully read. We don't need to call
- # checkArchiveStatus again, since readDescFile()
- # has just done it.
- return self.hasPackage
-
- if self.hasDescFile:
- if self.__checkArchiveStatus():
- # It's all good.
- self.hasPackage = True
-
- return self.hasPackage
-
- def hasCurrentDescFile(self):
- """ Returns true if a desc file file has been successfully
- read for this package and is still current, false
- otherwise. """
-
- if not self.host.hasCurrentContentsFile():
- return False
-
- return self.hasDescFile
-
- def downloadDescFile(self, http):
- """ Downloads the desc file for this particular package,
- synchronously, and then reads it. Returns true on success,
- false on failure. """
-
- for token in self.downloadDescFileGenerator(http):
- if token != self.stepContinue:
- break
- Thread.considerYield()
-
- return (token == self.stepComplete)
-
- def downloadDescFileGenerator(self, http):
- """ A generator function that implements downloadDescFile()
- one piece at a time. It yields one of stepComplete,
- stepFailed, or stepContinue. """
-
- assert self.descFile
-
- if self.hasDescFile:
- # We've already got one.
- yield self.stepComplete; return
-
- if not self.host.appRunner or self.host.appRunner.verifyContents != self.host.appRunner.P3DVCNever:
- # We're allowed to download it.
- self.http = http
-
- func = lambda step, self = self: self.__downloadFile(
- None, self.descFile,
- urlbase = self.descFile.filename,
- filename = self.descFileBasename)
- step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc')
-
- for token in step.func():
- if token == self.stepContinue:
- yield token
- else:
- break
-
- while token == self.restartDownload:
- # Try again.
- func = lambda step, self = self: self.__downloadFile(
- None, self.descFile,
- urlbase = self.descFile.filename,
- filename = self.descFileBasename)
- step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc')
- for token in step.func():
- if token == self.stepContinue:
- yield token
- else:
- break
-
- if token == self.stepFailed:
- # Couldn't download the desc file.
- yield self.stepFailed; return
-
- assert token == self.stepComplete
-
- filename = Filename(self.getPackageDir(), self.descFileBasename)
- # Now that we've written the desc file, make it read-only.
- os.chmod(filename.toOsSpecific(), 0o444)
-
- if not self.__readDescFile():
- # Weird, it passed the hash check, but we still can't read
- # it.
- filename = Filename(self.getPackageDir(), self.descFileBasename)
- self.notify.warning("Failure reading %s" % (filename))
- yield self.stepFailed; return
-
- yield self.stepComplete; return
-
- def __readDescFile(self):
- """ Reads the desc xml file for this particular package,
- assuming it's been already downloaded and verified. Returns
- true on success, false on failure. """
-
- if self.hasDescFile:
- # No need to read it again.
- return True
-
- if self.solo:
- # If this is a "solo" package, we don't actually "read"
- # the desc file; that's the entire contents of the
- # package.
- self.hasDescFile = True
- self.hasPackage = True
- return True
-
- filename = Filename(self.getPackageDir(), self.descFileBasename)
-
- if not hasattr(core, 'TiXmlDocument'):
- return False
- doc = core.TiXmlDocument(filename.toOsSpecific())
- if not doc.LoadFile():
- return False
-
- xpackage = doc.FirstChildElement('package')
- if not xpackage:
- return False
-
- try:
- self.patchVersion = int(xpackage.Attribute('patch_version') or '')
- except ValueError:
- self.patchVersion = None
-
- try:
- perPlatform = int(xpackage.Attribute('per_platform') or '')
- except ValueError:
- perPlatform = False
- if perPlatform != self.perPlatform:
- self.notify.warning("per_platform disagreement on package %s" % (self.packageName))
-
- self.displayName = None
- xconfig = xpackage.FirstChildElement('config')
- if xconfig:
- # The name for display to an English-speaking user.
- self.displayName = xconfig.Attribute('display_name')
-
- # True if any apps that use this package must be GUI apps.
- guiApp = xconfig.Attribute('gui_app')
- if guiApp:
- self.guiApp = int(guiApp)
-
- # The uncompressed archive, which will be mounted directly,
- # and also used for patching.
- xuncompressedArchive = xpackage.FirstChildElement('uncompressed_archive')
- if xuncompressedArchive:
- self.uncompressedArchive = FileSpec()
- self.uncompressedArchive.loadXml(xuncompressedArchive)
-
- # The compressed archive, which is what is downloaded.
- xcompressedArchive = xpackage.FirstChildElement('compressed_archive')
- if xcompressedArchive:
- self.compressedArchive = FileSpec()
- self.compressedArchive.loadXml(xcompressedArchive)
-
- # The list of files that should be extracted to disk.
- self.extracts = []
- xextract = xpackage.FirstChildElement('extract')
- while xextract:
- file = FileSpec()
- file.loadXml(xextract)
- self.extracts.append(file)
- xextract = xextract.NextSiblingElement('extract')
-
- # The list of additional packages that must be installed for
- # this package to function properly.
- self.requires = []
- xrequires = xpackage.FirstChildElement('requires')
- while xrequires:
- packageName = xrequires.Attribute('name')
- version = xrequires.Attribute('version')
- hostUrl = xrequires.Attribute('host')
- if packageName and hostUrl:
- host = self.host.appRunner.getHostWithAlt(hostUrl)
- self.requires.append((packageName, version, host))
- xrequires = xrequires.NextSiblingElement('requires')
-
- self.hasDescFile = True
-
- # Now that we've read the desc file, go ahead and use it to
- # verify the download status.
- if self.__checkArchiveStatus():
- # It's all fully downloaded, unpacked, and ready.
- self.hasPackage = True
- return True
-
- # Still have to download it.
- self.__buildInstallPlans()
- return True
-
- def __buildInstallPlans(self):
- """ Sets up self.installPlans, a list of one or more "plans"
- to download and install the package. """
-
- pc = PStatCollector(':App:PackageInstaller:buildInstallPlans')
- pc.start()
-
- self.hasPackage = False
-
- if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
- # We're not allowed to download anything.
- self.installPlans = []
- pc.stop()
- return
-
- if self.asMirror:
- # If we're just downloading a mirror archive, we only need
- # to get the compressed archive file.
-
- # Build a one-item install plan to download the compressed
- # archive.
- downloadSize = self.compressedArchive.size
- func = lambda step, fileSpec = self.compressedArchive: self.__downloadFile(step, fileSpec, allowPartial = True)
-
- step = self.InstallStep(func, downloadSize, self.downloadFactor, 'download')
- installPlan = [step]
- self.installPlans = [installPlan]
- pc.stop()
- return
-
- # The normal download process. Determine what we will need to
- # download, and build a plan (or two) to download it all.
- self.installPlans = None
-
- # We know we will at least need to unpack the archive contents
- # at the end.
- unpackSize = 0
- for file in self.extracts:
- unpackSize += file.size
- step = self.InstallStep(self.__unpackArchive, unpackSize, self.unpackFactor, 'unpack')
- planA = [step]
-
- # If the uncompressed archive file is good, that's all we'll
- # need to do.
- self.uncompressedArchive.actualFile = None
- if self.uncompressedArchive.quickVerify(self.getPackageDir(), notify = self.notify):
- self.installPlans = [planA]
- pc.stop()
- return
-
- # Maybe the compressed archive file is good.
- if self.compressedArchive.quickVerify(self.getPackageDir(), notify = self.notify):
- uncompressSize = self.uncompressedArchive.size
- step = self.InstallStep(self.__uncompressArchive, uncompressSize, self.uncompressFactor, 'uncompress')
- planA = [step] + planA
- self.installPlans = [planA]
- pc.stop()
- return
-
- # Maybe we can download one or more patches. We'll come back
- # to that in a minute as plan A. For now, construct plan B,
- # which will be to download the whole archive.
- planB = planA[:]
-
- uncompressSize = self.uncompressedArchive.size
- step = self.InstallStep(self.__uncompressArchive, uncompressSize, self.uncompressFactor, 'uncompress')
- planB = [step] + planB
-
- downloadSize = self.compressedArchive.size
- func = lambda step, fileSpec = self.compressedArchive: self.__downloadFile(step, fileSpec, allowPartial = True)
-
- step = self.InstallStep(func, downloadSize, self.downloadFactor, 'download')
- planB = [step] + planB
-
- # Now look for patches. Start with the md5 hash from the
- # uncompressedArchive file we have on disk, and see if we can
- # find a patch chain from this file to our target.
- pathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
- fileSpec = self.uncompressedArchive.actualFile
- if fileSpec is None and pathname.exists():
- fileSpec = FileSpec()
- fileSpec.fromFile(self.getPackageDir(), self.uncompressedArchive.filename)
- plan = None
- if fileSpec:
- plan = self.__findPatchChain(fileSpec)
- if plan:
- # We can download patches. Great! That means this is
- # plan A, and the full download is plan B (in case
- # something goes wrong with the patching).
- planA = plan + planA
- self.installPlans = [planA, planB]
- else:
- # There are no patches to download, oh well. Stick with
- # plan B as the only plan.
- self.installPlans = [planB]
-
- # In case of unexpected failures on the internet, we will retry
- # the full download instead of just giving up.
- retries = core.ConfigVariableInt('package-full-dl-retries', 1).getValue()
- for retry in range(retries):
- self.installPlans.append(planB[:])
-
- pc.stop()
-
- def __scanDirectoryRecursively(self, dirname):
- """ Generates a list of Filename objects: all of the files
- (not directories) within and below the indicated dirname. """
-
- contents = []
- for dirpath, dirnames, filenames in os.walk(dirname.toOsSpecific()):
- dirpath = Filename.fromOsSpecific(dirpath)
- if dirpath == dirname:
- dirpath = Filename('')
- else:
- dirpath.makeRelativeTo(dirname)
- for filename in filenames:
- contents.append(Filename(dirpath, filename))
- return contents
-
- def __removeFileFromList(self, contents, filename):
- """ Removes the indicated filename from the given list, if it is
- present. """
- try:
- contents.remove(Filename(filename))
- except ValueError:
- pass
-
- def __checkArchiveStatus(self):
- """ Returns true if the archive and all extractable files are
- already correct on disk, false otherwise. """
-
- if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
- # Assume that everything is just fine.
- return True
-
- # Get a list of all of the files in the directory, so we can
- # remove files that don't belong.
- contents = self.__scanDirectoryRecursively(self.getPackageDir())
- self.__removeFileFromList(contents, self.descFileBasename)
- self.__removeFileFromList(contents, self.compressedArchive.filename)
- self.__removeFileFromList(contents, self.UsageBasename)
- if not self.asMirror:
- self.__removeFileFromList(contents, self.uncompressedArchive.filename)
- for file in self.extracts:
- self.__removeFileFromList(contents, file.filename)
-
- # Now, any files that are still in the contents list don't
- # belong. It's important to remove these files before we
- # start verifying the files that we expect to find here, in
- # case there is a problem with ambiguous filenames or
- # something (e.g. case insensitivity).
- for filename in contents:
- self.notify.info("Removing %s" % (filename))
- pathname = Filename(self.getPackageDir(), filename)
- pathname.unlink()
- self.updated = True
-
- if self.asMirror:
- return self.compressedArchive.quickVerify(self.getPackageDir(), notify = self.notify)
-
- allExtractsOk = True
- if not self.uncompressedArchive.quickVerify(self.getPackageDir(), notify = self.notify):
- self.notify.debug("File is incorrect: %s" % (self.uncompressedArchive.filename))
- allExtractsOk = False
-
- if allExtractsOk:
- # OK, the uncompressed archive is good; that means there
- # shouldn't be a compressed archive file here.
- pathname = Filename(self.getPackageDir(), self.compressedArchive.filename)
- pathname.unlink()
-
- for file in self.extracts:
- if not file.quickVerify(self.getPackageDir(), notify = self.notify):
- self.notify.debug("File is incorrect: %s" % (file.filename))
- allExtractsOk = False
- break
-
- if allExtractsOk:
- self.notify.debug("All %s extracts of %s seem good." % (
- len(self.extracts), self.packageName))
-
- return allExtractsOk
-
- def __updateStepProgress(self, step):
- """ This callback is made from within the several step
- functions as the download step proceeds. It updates
- self.downloadProgress with the current progress, so the caller
- can asynchronously query this value. """
-
- size = self.totalPlanCompleted + self.currentStepEffort * step.getProgress()
- self.downloadProgress = min(float(size) / float(self.totalPlanSize), 1)
-
- def downloadPackage(self, http):
- """ Downloads the package file, synchronously, then
- uncompresses and unpacks it. Returns true on success, false
- on failure.
-
- This assumes that self.installPlans has already been filled
- in, which will have been done by self.__readDescFile().
- """
-
- for token in self.downloadPackageGenerator(http):
- if token != self.stepContinue:
- break
- Thread.considerYield()
-
- return (token == self.stepComplete)
-
- def downloadPackageGenerator(self, http):
- """ A generator function that implements downloadPackage() one
- piece at a time. It yields one of stepComplete, stepFailed,
- or stepContinue. """
-
- assert self.hasDescFile
-
- if self.hasPackage:
- # We've already got one.
- yield self.stepComplete; return
-
- if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
- # We're not allowed to download anything. Assume it's already downloaded.
- yield self.stepComplete; return
-
- # We should have an install plan by the time we get here.
- assert self.installPlans
-
- self.http = http
- for token in self.__followInstallPlans():
- if token == self.stepContinue:
- yield token
- else:
- break
-
- while token == self.restartDownload:
- # Try again.
- for token in self.downloadDescFileGenerator(http):
- if token == self.stepContinue:
- yield token
- else:
- break
- if token == self.stepComplete:
- for token in self.__followInstallPlans():
- if token == self.stepContinue:
- yield token
- else:
- break
-
- if token == self.stepFailed:
- yield self.stepFailed; return
-
- assert token == self.stepComplete
- yield self.stepComplete; return
-
-
- def __followInstallPlans(self):
- """ Performs all of the steps in self.installPlans. Yields
- one of stepComplete, stepFailed, restartDownload, or
- stepContinue. """
-
- if not self.installPlans:
- self.__buildInstallPlans()
-
- installPlans = self.installPlans
- self.installPlans = None
- for plan in installPlans:
- self.totalPlanSize = sum([step.getEffort() for step in plan])
- self.totalPlanCompleted = 0
- self.downloadProgress = 0
-
- planFailed = False
- for step in plan:
- self.currentStepEffort = step.getEffort()
-
- for token in step.func():
- if token == self.stepContinue:
- yield token
- else:
- break
-
- if token == self.restartDownload:
- yield token
- if token == self.stepFailed:
- planFailed = True
- break
- assert token == self.stepComplete
-
- self.totalPlanCompleted += self.currentStepEffort
-
- if not planFailed:
- # Successfully downloaded!
- yield self.stepComplete; return
-
- if taskMgr.destroyed:
- yield self.stepFailed; return
-
- # All plans failed.
- yield self.stepFailed; return
-
- def __findPatchChain(self, fileSpec):
- """ Finds the chain of patches that leads from the indicated
- patch version to the current patch version. If found,
- constructs an installPlan that represents the steps of the
- patch installation; otherwise, returns None. """
-
- from direct.p3d.PatchMaker import PatchMaker
-
- patchMaker = PatchMaker(self.getPackageDir())
- patchChain = patchMaker.getPatchChainToCurrent(self.descFileBasename, fileSpec)
- if patchChain is None:
- # No path.
- patchMaker.cleanup()
- return None
-
- plan = []
- for patchfile in patchChain:
- downloadSize = patchfile.file.size
- func = lambda step, fileSpec = patchfile.file: self.__downloadFile(step, fileSpec, allowPartial = True)
- step = self.InstallStep(func, downloadSize, self.downloadFactor, 'download')
- plan.append(step)
-
- patchSize = patchfile.targetFile.size
- func = lambda step, patchfile = patchfile: self.__applyPatch(step, patchfile)
- step = self.InstallStep(func, patchSize, self.patchFactor, 'patch')
- plan.append(step)
-
- patchMaker.cleanup()
- return plan
-
- def __downloadFile(self, step, fileSpec, urlbase = None, filename = None,
- allowPartial = False):
- """ Downloads the indicated file from the host into
- packageDir. Yields one of stepComplete, stepFailed,
- restartDownload, or stepContinue. """
-
- if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
- # We're not allowed to download anything.
- yield self.stepFailed; return
-
- self.updated = True
-
- if not urlbase:
- urlbase = self.descFileDirname + '/' + fileSpec.filename
-
- # Build up a list of URL's to try downloading from. Unlike
- # the C++ implementation in P3DPackage.cxx, here we build the
- # URL's in forward order.
- tryUrls = []
-
- if self.host.appRunner and self.host.appRunner.superMirrorUrl:
- # We start with the "super mirror", if it's defined.
- url = self.host.appRunner.superMirrorUrl + urlbase
- tryUrls.append((url, False))
-
- if self.host.mirrors:
- # Choose two mirrors at random.
- mirrors = self.host.mirrors[:]
- for i in range(2):
- mirror = random.choice(mirrors)
- mirrors.remove(mirror)
- url = mirror + urlbase
- tryUrls.append((url, False))
- if not mirrors:
- break
-
- # After trying two mirrors and failing (or if there are no
- # mirrors), go get it from the original host.
- url = self.host.downloadUrlPrefix + urlbase
- tryUrls.append((url, False))
-
- # And finally, if the original host also fails, try again with
- # a cache-buster.
- tryUrls.append((url, True))
-
- for url, cacheBust in tryUrls:
- request = DocumentSpec(url)
-
- if cacheBust:
- # On the last attempt to download a particular file,
- # we bust through the cache: append a query string to
- # do this.
- url += '?' + str(int(time.time()))
- request = DocumentSpec(url)
- request.setCacheControl(DocumentSpec.CCNoCache)
-
- self.notify.info("%s downloading %s" % (self.packageName, url))
-
- if not filename:
- filename = fileSpec.filename
- targetPathname = Filename(self.getPackageDir(), filename)
- targetPathname.setBinary()
-
- channel = self.http.makeChannel(False)
-
- # If there's a previous partial download, attempt to resume it.
- bytesStarted = 0
- if allowPartial and not cacheBust and targetPathname.exists():
- bytesStarted = targetPathname.getFileSize()
-
- if bytesStarted < 1024*1024:
- # Not enough bytes downloaded to be worth the risk of
- # a partial download.
- bytesStarted = 0
- elif bytesStarted >= fileSpec.size:
- # Couldn't possibly be our file.
- bytesStarted = 0
-
- if bytesStarted:
- self.notify.info("Resuming %s after %s bytes already downloaded" % (url, bytesStarted))
- # Make sure the file is writable.
- os.chmod(targetPathname.toOsSpecific(), 0o644)
- channel.beginGetSubdocument(request, bytesStarted, 0)
- else:
- # No partial download possible; get the whole file.
- targetPathname.makeDir()
- targetPathname.unlink()
- channel.beginGetDocument(request)
-
- channel.downloadToFile(targetPathname)
- while channel.run():
- if step:
- step.bytesDone = channel.getBytesDownloaded() + channel.getFirstByteDelivered()
- if step.bytesDone > step.bytesNeeded:
- # Oops, too much data. Might as well abort;
- # it's the wrong file.
- self.notify.warning("Got more data than expected for download %s" % (url))
- break
-
- self.__updateStepProgress(step)
-
- if taskMgr.destroyed:
- # If the task manager has been destroyed, we must
- # be shutting down. Get out of here.
- self.notify.warning("Task Manager destroyed, aborting %s" % (url))
- yield self.stepFailed; return
-
- yield self.stepContinue
-
- if step:
- step.bytesDone = channel.getBytesDownloaded() + channel.getFirstByteDelivered()
- self.__updateStepProgress(step)
-
- if not channel.isValid():
- self.notify.warning("Failed to download %s" % (url))
-
- elif not fileSpec.fullVerify(self.getPackageDir(), pathname = targetPathname, notify = self.notify):
- self.notify.warning("After downloading, %s incorrect" % (Filename(fileSpec.filename).getBasename()))
-
- # This attempt failed. Maybe the original contents.xml
- # file is stale. Try re-downloading it now, just to be
- # sure.
- if self.host.redownloadContentsFile(self.http):
- # Yes! Go back and start over from the beginning.
- yield self.restartDownload; return
-
- else:
- # Success!
- yield self.stepComplete; return
-
- # Maybe the mirror is bad. Go back and try the next
- # mirror.
-
- # All attempts failed. Maybe the original contents.xml file
- # is stale. Try re-downloading it now, just to be sure.
- if self.host.redownloadContentsFile(self.http):
- # Yes! Go back and start over from the beginning.
- yield self.restartDownload; return
-
- # All mirrors failed; the server (or the internet connection)
- # must be just fubar.
- yield self.stepFailed; return
-
- def __applyPatch(self, step, patchfile):
- """ Applies the indicated patching in-place to the current
- uncompressed archive. The patchfile is removed after the
- operation. Yields one of stepComplete, stepFailed,
- restartDownload, or stepContinue. """
-
- self.updated = True
-
- origPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
- patchPathname = Filename(self.getPackageDir(), patchfile.file.filename)
- result = Filename.temporary('', 'patch_')
- self.notify.info("Patching %s with %s" % (origPathname, patchPathname))
-
- p = core.Patchfile() # The C++ class
-
- ret = p.initiate(patchPathname, origPathname, result)
- if ret == EUSuccess:
- ret = p.run()
- while ret == EUOk:
- step.bytesDone = step.bytesNeeded * p.getProgress()
- self.__updateStepProgress(step)
- if taskMgr.destroyed:
- # If the task manager has been destroyed, we must
- # be shutting down. Get out of here.
- self.notify.warning("Task Manager destroyed, aborting patch %s" % (origPathname))
- yield self.stepFailed; return
-
- yield self.stepContinue
- ret = p.run()
- del p
- patchPathname.unlink()
-
- if ret < 0:
- self.notify.warning("Patching of %s failed." % (origPathname))
- result.unlink()
- yield self.stepFailed; return
-
- if not result.renameTo(origPathname):
- self.notify.warning("Couldn't rename %s to %s" % (result, origPathname))
- yield self.stepFailed; return
-
- yield self.stepComplete; return
-
- def __uncompressArchive(self, step):
- """ Turns the compressed archive into the uncompressed
- archive. Yields one of stepComplete, stepFailed,
- restartDownload, or stepContinue. """
-
- if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
- # We're not allowed to!
- yield self.stepFailed; return
-
- self.updated = True
-
- sourcePathname = Filename(self.getPackageDir(), self.compressedArchive.filename)
- targetPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
- targetPathname.unlink()
- self.notify.info("Uncompressing %s to %s" % (sourcePathname, targetPathname))
- decompressor = Decompressor()
- decompressor.initiate(sourcePathname, targetPathname)
- totalBytes = self.uncompressedArchive.size
- result = decompressor.run()
- while result == EUOk:
- step.bytesDone = int(totalBytes * decompressor.getProgress())
- self.__updateStepProgress(step)
- result = decompressor.run()
- if taskMgr.destroyed:
- # If the task manager has been destroyed, we must
- # be shutting down. Get out of here.
- self.notify.warning("Task Manager destroyed, aborting decompresss %s" % (sourcePathname))
- yield self.stepFailed; return
-
- yield self.stepContinue
-
- if result != EUSuccess:
- yield self.stepFailed; return
-
- step.bytesDone = totalBytes
- self.__updateStepProgress(step)
-
- if not self.uncompressedArchive.quickVerify(self.getPackageDir(), notify= self.notify):
- self.notify.warning("after uncompressing, %s still incorrect" % (
- self.uncompressedArchive.filename))
- yield self.stepFailed; return
-
- # Now that we've verified the archive, make it read-only.
- os.chmod(targetPathname.toOsSpecific(), 0o444)
-
- # Now we can safely remove the compressed archive.
- sourcePathname.unlink()
- yield self.stepComplete; return
-
- def __unpackArchive(self, step):
- """ Unpacks any files in the archive that want to be unpacked
- to disk. Yields one of stepComplete, stepFailed,
- restartDownload, or stepContinue. """
-
- if not self.extracts:
- # Nothing to extract.
- self.hasPackage = True
- yield self.stepComplete; return
-
- if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
- # We're not allowed to!
- yield self.stepFailed; return
-
- self.updated = True
-
- mfPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
- self.notify.info("Unpacking %s" % (mfPathname))
- mf = Multifile()
- if not mf.openRead(mfPathname):
- self.notify.warning("Couldn't open %s" % (mfPathname))
- yield self.stepFailed; return
-
- allExtractsOk = True
- step.bytesDone = 0
- for file in self.extracts:
- i = mf.findSubfile(file.filename)
- if i == -1:
- self.notify.warning("Not in Multifile: %s" % (file.filename))
- allExtractsOk = False
- continue
-
- targetPathname = Filename(self.getPackageDir(), file.filename)
- targetPathname.setBinary()
- targetPathname.unlink()
- if not mf.extractSubfile(i, targetPathname):
- self.notify.warning("Couldn't extract: %s" % (file.filename))
- allExtractsOk = False
- continue
-
- if not file.quickVerify(self.getPackageDir(), notify = self.notify):
- self.notify.warning("After extracting, still incorrect: %s" % (file.filename))
- allExtractsOk = False
- continue
-
- # Make sure it's executable, and not writable.
- os.chmod(targetPathname.toOsSpecific(), 0o555)
-
- step.bytesDone += file.size
- self.__updateStepProgress(step)
- if taskMgr.destroyed:
- # If the task manager has been destroyed, we must
- # be shutting down. Get out of here.
- self.notify.warning("Task Manager destroyed, aborting unpacking %s" % (mfPathname))
- yield self.stepFailed; return
-
- yield self.stepContinue
-
- if not allExtractsOk:
- yield self.stepFailed; return
-
- self.hasPackage = True
- yield self.stepComplete; return
-
- def installPackage(self, appRunner):
- """ Mounts the package and sets up system paths so it becomes
- available for use. Returns true on success, false on failure. """
-
- assert self.hasPackage
- if self.installed:
- # Already installed.
- return True
- assert self not in appRunner.installedPackages
-
- mfPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
- mf = Multifile()
- if not mf.openRead(mfPathname):
- self.notify.warning("Couldn't open %s" % (mfPathname))
- return False
-
- # We mount it under its actual location on disk.
- root = self.getPackageDir()
-
- vfs = VirtualFileSystem.getGlobalPtr()
- vfs.mount(mf, root, vfs.MFReadOnly)
-
- # Add this to the Python search path, if it's not already
- # there. We have to take a bit of care to check if it's
- # already there, since there can be some ambiguity in
- # os-specific path strings.
- osRoot = self.getPackageDir().toOsSpecific()
- foundOnPath = False
- for p in sys.path:
- if osRoot == p:
- # Already here, exactly.
- foundOnPath = True
- break
- elif osRoot == Filename.fromOsSpecific(p).toOsSpecific():
- # Already here, with some futzing.
- foundOnPath = True
- break
-
- if not foundOnPath:
- # Not already here; add it.
- sys.path.append(osRoot)
-
- # Put it on the model-path, too. We do this indiscriminantly,
- # because the Panda3D runtime won't be adding things to the
- # model-path, so it shouldn't be already there.
- getModelPath().appendDirectory(self.getPackageDir())
-
- # Set the environment variable to reference the package root.
- envvar = '%s_ROOT' % (self.packageName.upper())
- ExecutionEnvironment.setEnvironmentVariable(envvar, osRoot)
-
- # Add the package root to the system paths.
- if sys.platform.startswith('win'):
- path = os.environ.get('PATH', '')
- os.environ['PATH'] = "%s;%s" % (osRoot, path)
- else:
- path = os.environ.get('PATH', '')
- os.environ['PATH'] = "%s:%s" % (osRoot, path)
- path = os.environ.get('LD_LIBRARY_PATH', '')
- os.environ['LD_LIBRARY_PATH'] = "%s:%s" % (osRoot, path)
-
- if sys.platform == "darwin":
- path = os.environ.get('DYLD_LIBRARY_PATH', '')
- os.environ['DYLD_LIBRARY_PATH'] = "%s:%s" % (osRoot, path)
-
- # Now that the environment variable is set, read all of the
- # prc files in the package.
- appRunner.loadMultifilePrcFiles(mf, self.getPackageDir())
-
- # Also, find any toplevel Python packages, and add these as
- # shared packages. This will allow different packages
- # installed in different directories to share Python files as
- # if they were all in the same directory.
- for filename in mf.getSubfileNames():
- if filename.endswith('/__init__.pyc') or \
- filename.endswith('/__init__.pyo') or \
- filename.endswith('/__init__.py'):
- components = filename.split('/')[:-1]
- moduleName = '.'.join(components)
- VFSImporter.sharedPackages[moduleName] = True
-
- # Fix up any shared directories so we can load packages from
- # disparate locations.
- VFSImporter.reloadSharedPackages()
-
- self.installed = True
- appRunner.installedPackages.append(self)
-
- self.markUsed()
-
- return True
-
- def __measureDiskSpace(self):
- """ Returns the amount of space used by this package, in
- bytes, as determined by examining the actual contents of the
- package directory and its subdirectories. """
-
- thisDir = ScanDirectoryNode(self.getPackageDir(), ignoreUsageXml = True)
- diskSpace = thisDir.getTotalSize()
- self.notify.info("Package %s uses %s MB" % (
- self.packageName, (diskSpace + 524288) // 1048576))
- return diskSpace
-
- def markUsed(self):
- """ Marks the package as having been used. This is normally
- called automatically by installPackage(). """
-
- if not hasattr(core, 'TiXmlDocument'):
- return
-
- if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
- # Not allowed to write any files to the package directory.
- return
-
- if self.updated:
- # If we've just installed a new version of the package,
- # re-measure the actual disk space used.
- self.diskSpace = self.__measureDiskSpace()
-
- filename = Filename(self.getPackageDir(), self.UsageBasename)
- doc = TiXmlDocument(filename.toOsSpecific())
- if not doc.LoadFile():
- decl = TiXmlDeclaration("1.0", "utf-8", "")
- doc.InsertEndChild(decl)
-
- xusage = doc.FirstChildElement('usage')
- if not xusage:
- doc.InsertEndChild(TiXmlElement('usage'))
- xusage = doc.FirstChildElement('usage')
-
- now = int(time.time())
-
- count = xusage.Attribute('count_app')
- try:
- count = int(count or '')
- except ValueError:
- count = 0
- xusage.SetAttribute('first_use', str(now))
- count += 1
- xusage.SetAttribute('count_app', str(count))
-
- xusage.SetAttribute('last_use', str(now))
-
- if self.updated:
- xusage.SetAttribute('last_update', str(now))
- self.updated = False
- else:
- # Since we haven't changed the disk space, we can just
- # read it from the previous xml file.
- diskSpace = xusage.Attribute('disk_space')
- try:
- diskSpace = int(diskSpace or '')
- except ValueError:
- # Unless it wasn't set already.
- self.diskSpace = self.__measureDiskSpace()
-
- xusage.SetAttribute('disk_space', str(self.diskSpace))
-
- # Write the file to a temporary filename, then atomically move
- # it to its actual filename, to avoid race conditions when
- # updating this file.
- tfile = Filename.temporary(str(self.getPackageDir()), '.xml')
- if doc.SaveFile(tfile.toOsSpecific()):
- tfile.renameTo(filename)
-
- def getUsage(self):
- """ Returns the xusage element that is read from the usage.xml
- file, or None if there is no usage.xml file. """
-
- if not hasattr(core, 'TiXmlDocument'):
- return None
-
- filename = Filename(self.getPackageDir(), self.UsageBasename)
- doc = TiXmlDocument(filename.toOsSpecific())
- if not doc.LoadFile():
- return None
-
- xusage = doc.FirstChildElement('usage')
- if not xusage:
- return None
-
- return copy.copy(xusage)
-
diff --git a/direct/src/p3d/PackageInstaller.py b/direct/src/p3d/PackageInstaller.py
deleted file mode 100644
index cec1e07b11..0000000000
--- a/direct/src/p3d/PackageInstaller.py
+++ /dev/null
@@ -1,640 +0,0 @@
-__all__ = ["PackageInstaller"]
-
-from direct.showbase.DirectObject import DirectObject
-from direct.stdpy.threading import Lock, RLock
-from direct.showbase.MessengerGlobal import messenger
-from direct.task.TaskManagerGlobal import taskMgr
-from direct.p3d.PackageInfo import PackageInfo
-from panda3d.core import TPLow, PStatCollector
-from direct.directnotify.DirectNotifyGlobal import directNotify
-
-class PackageInstaller(DirectObject):
-
- """ This class is used in a p3d runtime environment to manage the
- asynchronous download and installation of packages. If you just
- want to install a package synchronously, see
- appRunner.installPackage() for a simpler interface.
-
- To use this class, you should subclass from it and override any of
- the six callback methods: downloadStarted(), packageStarted(),
- packageProgress(), downloadProgress(), packageFinished(),
- downloadFinished().
-
- Also see DWBPackageInstaller, which does exactly this, to add a
- DirectWaitBar GUI.
-
- """
-
- notify = directNotify.newCategory("PackageInstaller")
-
- globalLock = Lock()
- nextUniqueId = 1
-
- # This is a chain of state values progressing forward in time.
- S_initial = 0 # addPackage() calls are being made
- S_ready = 1 # donePackages() has been called
- S_started = 2 # download has started
- S_done = 3 # download is over
-
- class PendingPackage:
- """ This class describes a package added to the installer for
- download. """
-
- notify = directNotify.newCategory("PendingPackage")
-
- def __init__(self, packageName, version, host):
- self.packageName = packageName
- self.version = version
- self.host = host
-
- # This will be filled in properly by checkDescFile() or
- # getDescFile(); in the meantime, set a placeholder.
- self.package = PackageInfo(host, packageName, version)
-
- # Set true when the package has finished downloading,
- # either successfully or unsuccessfully.
- self.done = False
-
- # Set true or false when self.done has been set.
- self.success = False
-
- # Set true when the packageFinished() callback has been
- # delivered.
- self.notified = False
-
- # These are used to ensure the callbacks only get
- # delivered once for a particular package.
- self.calledPackageStarted = False
- self.calledPackageFinished = False
-
- # This is the amount of stuff we have to process to
- # install this package, and the amount of stuff we have
- # processed so far. "Stuff" includes bytes downloaded,
- # bytes uncompressed, and bytes extracted; and each of
- # which is weighted differently into one grand total. So,
- # the total doesn't really represent bytes; it's a
- # unitless number, which means something only as a ratio
- # to other packages. Filled in by checkDescFile() or
- # getDescFile().
- self.downloadEffort = 0
-
- # Similar, but this is the theoretical effort if the
- # package were already downloaded.
- self.prevDownloadedEffort = 0
-
- def __cmp__(self, pp):
- """ Python comparision function. This makes all
- PendingPackages withe same (packageName, version, host)
- combination be deemed equivalent. """
- return cmp((self.packageName, self.version, self.host),
- (pp.packageName, pp.version, pp.host))
-
- def getProgress(self):
- """ Returns the download progress of this package in the
- range 0..1. """
-
- return self.package.downloadProgress
-
- def checkDescFile(self):
- """ Returns true if the desc file is already downloaded
- and good, or false if it needs to be downloaded. """
-
- if not self.host.hasCurrentContentsFile():
- # If the contents file isn't ready yet, we can't check
- # the desc file yet.
- return False
-
- # All right, get the package info now.
- package = self.host.getPackage(self.packageName, self.version)
- if not package:
- self.notify.warning("Package %s %s not known on %s" % (
- self.packageName, self.version, self.host.hostUrl))
- return False
-
- self.package = package
- self.package.checkStatus()
-
- if not self.package.hasDescFile:
- return False
-
- self.downloadEffort = self.package.getDownloadEffort()
- self.prevDownloadEffort = 0
- if self.downloadEffort == 0:
- self.prevDownloadedEffort = self.package.getPrevDownloadedEffort()
-
- return True
-
-
- def getDescFile(self, http):
- """ Synchronously downloads the desc files required for
- the package. """
-
- if not self.host.downloadContentsFile(http):
- return False
-
- # All right, get the package info now.
- package = self.host.getPackage(self.packageName, self.version)
- if not package:
- self.notify.warning("Package %s %s not known on %s" % (
- self.packageName, self.version, self.host.hostUrl))
- return False
-
- self.package = package
- if not self.package.downloadDescFile(http):
- return False
-
- self.package.checkStatus()
- self.downloadEffort = self.package.getDownloadEffort()
- self.prevDownloadEffort = 0
- if self.downloadEffort == 0:
- self.prevDownloadedEffort = self.package.getPrevDownloadedEffort()
-
- return True
-
- def __init__(self, appRunner, taskChain = 'default'):
- self.globalLock.acquire()
- try:
- self.uniqueId = PackageInstaller.nextUniqueId
- PackageInstaller.nextUniqueId += 1
- finally:
- self.globalLock.release()
-
- self.appRunner = appRunner
- self.taskChain = taskChain
-
- # If we're to be running on an asynchronous task chain, and
- # the task chain hasn't yet been set up already, create the
- # default parameters now.
- if taskChain != 'default' and not taskMgr.hasTaskChain(self.taskChain):
- taskMgr.setupTaskChain(self.taskChain, numThreads = 1,
- threadPriority = TPLow)
-
- self.callbackLock = Lock()
- self.calledDownloadStarted = False
- self.calledDownloadFinished = False
-
- # A list of all packages that have been added to the
- # installer.
- self.packageLock = RLock()
- self.packages = []
- self.state = self.S_initial
-
- # A list of packages that are waiting for their desc files.
- self.needsDescFile = []
- self.descFileTask = None
-
- # A list of packages that are waiting to be downloaded and
- # installed.
- self.needsDownload = []
- self.downloadTask = None
-
- # A list of packages that were already done at the time they
- # were passed to addPackage().
- self.earlyDone = []
-
- # A list of packages that have been successfully installed, or
- # packages that have failed.
- self.done = []
- self.failed = []
-
- # This task is spawned on the default task chain, to update
- # the status during the download.
- self.progressTask = None
-
- self.accept('PackageInstaller-%s-allHaveDesc' % self.uniqueId,
- self.__allHaveDesc)
- self.accept('PackageInstaller-%s-packageStarted' % self.uniqueId,
- self.__packageStarted)
- self.accept('PackageInstaller-%s-packageDone' % self.uniqueId,
- self.__packageDone)
-
- def destroy(self):
- """ Interrupts all pending downloads. No further callbacks
- will be made. """
- self.cleanup()
-
- def cleanup(self):
- """ Interrupts all pending downloads. No further callbacks
- will be made. """
-
- self.packageLock.acquire()
- try:
- if self.descFileTask:
- taskMgr.remove(self.descFileTask)
- self.descFileTask = None
- if self.downloadTask:
- taskMgr.remove(self.downloadTask)
- self.downloadTask = None
- finally:
- self.packageLock.release()
-
- if self.progressTask:
- taskMgr.remove(self.progressTask)
- self.progressTask = None
-
- self.ignoreAll()
-
- def addPackage(self, packageName, version = None, hostUrl = None):
- """ Adds the named package to the list of packages to be
- downloaded. Call donePackages() to finish the list. """
-
- if self.state != self.S_initial:
- raise ValueError('addPackage called after donePackages')
-
- host = self.appRunner.getHostWithAlt(hostUrl)
- pp = self.PendingPackage(packageName, version, host)
-
- self.packageLock.acquire()
- try:
- self.__internalAddPackage(pp)
- finally:
- self.packageLock.release()
-
- def __internalAddPackage(self, pp):
- """ Adds the indicated "pending package" to the appropriate
- list(s) for downloading and installing. Assumes packageLock
- is already held."""
-
- if pp in self.packages:
- # Already added.
- return
-
- self.packages.append(pp)
-
- # We always add the package to needsDescFile, even if we
- # already have its desc file; this guarantees that packages
- # are downloaded in the order they are added.
- self.needsDescFile.append(pp)
- if not self.descFileTask:
- self.descFileTask = taskMgr.add(
- self.__getDescFileTask, 'getDescFile',
- taskChain = self.taskChain)
-
- def donePackages(self):
- """ After calling addPackage() for each package to be
- installed, call donePackages() to mark the end of the list.
- This is necessary to determine what the complete set of
- packages is (and therefore how large the total download size
- is). None of the low-level callbacks will be made before this
- call. """
-
- if self.state != self.S_initial:
- # We've already been here.
- return
-
- # Throw the messages for packages that were already done
- # before we started.
- for pp in self.earlyDone:
- self.__donePackage(pp, True)
- self.earlyDone = []
-
- self.packageLock.acquire()
- try:
- if self.state != self.S_initial:
- return
- self.state = self.S_ready
- if not self.needsDescFile:
- # All package desc files are already available; so begin.
- self.__prepareToStart()
- finally:
- self.packageLock.release()
-
- if not self.packages:
- # Trivial no-op.
- self.__callDownloadFinished(True)
-
- def downloadStarted(self):
- """ This callback is made at some point after donePackages()
- is called; at the time of this callback, the total download
- size is known, and we can sensibly report progress through the
- whole. """
-
- self.notify.info("downloadStarted")
-
- def packageStarted(self, package):
- """ This callback is made for each package between
- downloadStarted() and downloadFinished() to indicate the start
- of a new package. """
-
- self.notify.debug("packageStarted: %s" % (package.packageName))
-
- def packageProgress(self, package, progress):
- """ This callback is made repeatedly between packageStarted()
- and packageFinished() to update the current progress on the
- indicated package only. The progress value ranges from 0
- (beginning) to 1 (complete). """
-
- self.notify.debug("packageProgress: %s %s" % (package.packageName, progress))
-
- def downloadProgress(self, overallProgress):
- """ This callback is made repeatedly between downloadStarted()
- and downloadFinished() to update the current progress through
- all packages. The progress value ranges from 0 (beginning) to
- 1 (complete). """
-
- self.notify.debug("downloadProgress: %s" % (overallProgress))
-
- def packageFinished(self, package, success):
- """ This callback is made for each package between
- downloadStarted() and downloadFinished() to indicate that a
- package has finished downloading. If success is true, there
- were no problems and the package is now installed.
-
- If this package did not require downloading (because it was
- already downloaded), this callback will be made immediately,
- *without* a corresponding call to packageStarted(), and may
- even be made before downloadStarted(). """
-
- self.notify.info("packageFinished: %s %s" % (package.packageName, success))
-
- def downloadFinished(self, success):
- """ This callback is made when all of the packages have been
- downloaded and installed (or there has been some failure). If
- all packages where successfully installed, success is True.
-
- If there were no packages that required downloading, this
- callback will be made immediately, *without* a corresponding
- call to downloadStarted(). """
-
- self.notify.info("downloadFinished: %s" % (success))
-
- def __prepareToStart(self):
- """ This is called internally when transitioning from S_ready
- to S_started. It sets up whatever initial values are
- needed. Assumes self.packageLock is held. Returns False if
- there were no packages to download, and the state was
- therefore transitioned immediately to S_done. """
-
- if not self.needsDownload:
- self.state = self.S_done
- return False
-
- self.state = self.S_started
-
- assert not self.downloadTask
- self.downloadTask = taskMgr.add(
- self.__downloadPackageTask, 'downloadPackage',
- taskChain = self.taskChain)
-
- assert not self.progressTask
- self.progressTask = taskMgr.add(
- self.__progressTask, 'packageProgress')
-
- return True
-
- def __allHaveDesc(self):
- """ This method is called internally when all of the pending
- packages have their desc info. """
- working = True
-
- self.packageLock.acquire()
- try:
- if self.state == self.S_ready:
- # We've already called donePackages(), so move on now.
- working = self.__prepareToStart()
- finally:
- self.packageLock.release()
-
- if not working:
- self.__callDownloadFinished(True)
-
- def __packageStarted(self, pp):
- """ This method is called when a single package is beginning
- to download. """
-
- self.__callDownloadStarted()
- self.__callPackageStarted(pp)
-
- def __packageDone(self, pp):
- """ This method is called when a single package has been
- downloaded and installed, or has failed. """
-
- self.__callPackageFinished(pp, pp.success)
- pp.notified = True
-
- # See if there are more packages to go.
- success = True
- allDone = True
- self.packageLock.acquire()
- try:
- for pp in self.packages:
- if pp.notified:
- success = success and pp.success
- else:
- allDone = False
- finally:
- self.packageLock.release()
-
- if allDone:
- self.__callDownloadFinished(success)
-
- def __callPackageStarted(self, pp):
- """ Calls the packageStarted() callback for a particular
- package if it has not already been called, being careful to
- avoid race conditions. """
-
- self.callbackLock.acquire()
- try:
- if not pp.calledPackageStarted:
- self.packageStarted(pp.package)
- self.packageProgress(pp.package, 0)
- pp.calledPackageStarted = True
- finally:
- self.callbackLock.release()
-
- def __callPackageFinished(self, pp, success):
- """ Calls the packageFinished() callback for a paricular
- package if it has not already been called, being careful to
- avoid race conditions. """
-
- self.callbackLock.acquire()
- try:
- if not pp.calledPackageFinished:
- if success:
- self.packageProgress(pp.package, 1)
- self.packageFinished(pp.package, success)
- pp.calledPackageFinished = True
- finally:
- self.callbackLock.release()
-
- def __callDownloadStarted(self):
- """ Calls the downloadStarted() callback if it has not already
- been called, being careful to avoid race conditions. """
-
- self.callbackLock.acquire()
- try:
- if not self.calledDownloadStarted:
- self.downloadStarted()
- self.downloadProgress(0)
- self.calledDownloadStarted = True
- finally:
- self.callbackLock.release()
-
- def __callDownloadFinished(self, success):
- """ Calls the downloadFinished() callback if it has not
- already been called, being careful to avoid race
- conditions. """
-
- self.callbackLock.acquire()
- try:
- if not self.calledDownloadFinished:
- if success:
- self.downloadProgress(1)
- self.downloadFinished(success)
- self.calledDownloadFinished = True
- finally:
- self.callbackLock.release()
-
- def __getDescFileTask(self, task):
-
- """ This task runs on the aysynchronous task chain; each pass,
- it extracts one package from self.needsDescFile and downloads
- its desc file. On success, it adds the package to
- self.needsDownload. """
-
- self.packageLock.acquire()
- try:
- # If we've finished all of the packages that need desc
- # files, stop the task.
- if not self.needsDescFile:
- self.descFileTask = None
-
- eventName = 'PackageInstaller-%s-allHaveDesc' % self.uniqueId
- messenger.send(eventName, taskChain = 'default')
-
- return task.done
- pp = self.needsDescFile[0]
- del self.needsDescFile[0]
- finally:
- self.packageLock.release()
-
- # Now serve this one package.
- if not pp.checkDescFile():
- if not pp.getDescFile(self.appRunner.http):
- self.__donePackage(pp, False)
- return task.cont
-
- # This package is now ready to be downloaded. We always add
- # it to needsDownload, even if it's already downloaded, to
- # guarantee ordering of packages.
-
- self.packageLock.acquire()
- try:
- # Also add any packages required by this one.
- for packageName, version, host in pp.package.requires:
- pp2 = self.PendingPackage(packageName, version, host)
- self.__internalAddPackage(pp2)
- self.needsDownload.append(pp)
- finally:
- self.packageLock.release()
-
- return task.cont
-
- def __downloadPackageTask(self, task):
-
- """ This task runs on the aysynchronous task chain; each pass,
- it extracts one package from self.needsDownload and downloads
- it. """
-
- while True:
- self.packageLock.acquire()
- try:
- # If we're done downloading, stop the task.
- if self.state == self.S_done or not self.needsDownload:
- self.downloadTask = None
- self.packageLock.release()
- yield task.done; return
-
- assert self.state == self.S_started
- pp = self.needsDownload[0]
- del self.needsDownload[0]
- except:
- self.packageLock.release()
- raise
- self.packageLock.release()
-
- # Now serve this one package.
- eventName = 'PackageInstaller-%s-packageStarted' % self.uniqueId
- messenger.send(eventName, [pp], taskChain = 'default')
-
- if not pp.package.hasPackage:
- for token in pp.package.downloadPackageGenerator(self.appRunner.http):
- if token == pp.package.stepContinue:
- yield task.cont
- else:
- break
-
- if token != pp.package.stepComplete:
- pc = PStatCollector(':App:PackageInstaller:donePackage:%s' % (pp.package.packageName))
- pc.start()
- self.__donePackage(pp, False)
- pc.stop()
- yield task.cont
- continue
-
- # Successfully downloaded and installed.
- pc = PStatCollector(':App:PackageInstaller:donePackage:%s' % (pp.package.packageName))
- pc.start()
- self.__donePackage(pp, True)
- pc.stop()
-
- # Continue the loop without yielding, so we pick up the
- # next package within this same frame.
-
- def __donePackage(self, pp, success):
- """ Marks the indicated package as done, either successfully
- or otherwise. """
- assert not pp.done
-
- if success:
- pc = PStatCollector(':App:PackageInstaller:install:%s' % (pp.package.packageName))
- pc.start()
- pp.package.installPackage(self.appRunner)
- pc.stop()
-
- self.packageLock.acquire()
- try:
- pp.done = True
- pp.success = success
- if success:
- self.done.append(pp)
- else:
- self.failed.append(pp)
- finally:
- self.packageLock.release()
-
- eventName = 'PackageInstaller-%s-packageDone' % self.uniqueId
- messenger.send(eventName, [pp], taskChain = 'default')
-
- def __progressTask(self, task):
- self.callbackLock.acquire()
- try:
- if not self.calledDownloadStarted:
- # We haven't yet officially started the download.
- return task.cont
-
- if self.calledDownloadFinished:
- # We've officially ended the download.
- self.progressTask = None
- return task.done
-
- downloadEffort = 0
- currentDownloadSize = 0
- for pp in self.packages:
- downloadEffort += pp.downloadEffort + pp.prevDownloadedEffort
- packageProgress = pp.getProgress()
- currentDownloadSize += pp.downloadEffort * packageProgress + pp.prevDownloadedEffort
- if pp.calledPackageStarted and not pp.calledPackageFinished:
- self.packageProgress(pp.package, packageProgress)
-
- if not downloadEffort:
- progress = 1
- else:
- progress = float(currentDownloadSize) / float(downloadEffort)
- self.downloadProgress(progress)
-
- finally:
- self.callbackLock.release()
-
- return task.cont
-
diff --git a/direct/src/p3d/PackageMerger.py b/direct/src/p3d/PackageMerger.py
deleted file mode 100644
index a22d8f5f2d..0000000000
--- a/direct/src/p3d/PackageMerger.py
+++ /dev/null
@@ -1,306 +0,0 @@
-__all__ = ["PackageMerger", "PackageMergerError"]
-
-from direct.p3d.FileSpec import FileSpec
-from direct.p3d.SeqValue import SeqValue
-from direct.directnotify.DirectNotifyGlobal import *
-from panda3d.core import *
-import shutil
-import os
-
-class PackageMergerError(Exception):
- pass
-
-class PackageMerger:
- """ This class will combine two or more separately-built stage
- directories, the output of Packager.py or the ppackage tool, into
- a single output directory. It assumes that the clocks on all
- hosts are in sync, so that the file across all builds with the
- most recent timestamp (indicated in the contents.xml file) is
- always the most current version of the file. """
-
- notify = directNotify.newCategory("PackageMerger")
-
- class PackageEntry:
- """ This corresponds to a entry in the contents.xml
- file. """
-
- def __init__(self, xpackage, sourceDir):
- self.sourceDir = sourceDir
- self.loadXml(xpackage)
-
- def getKey(self):
- """ Returns a tuple used for sorting the PackageEntry
- objects uniquely per package. """
- return (self.packageName, self.platform, self.version)
-
- def isNewer(self, other):
- return self.descFile.timestamp > other.descFile.timestamp
-
- def loadXml(self, xpackage):
- self.packageName = xpackage.Attribute('name')
- self.platform = xpackage.Attribute('platform')
- self.version = xpackage.Attribute('version')
- solo = xpackage.Attribute('solo')
- self.solo = int(solo or '0')
- perPlatform = xpackage.Attribute('per_platform')
- self.perPlatform = int(perPlatform or '0')
-
- self.descFile = FileSpec()
- self.descFile.loadXml(xpackage)
-
- self.validatePackageContents()
-
- self.descFile.quickVerify(packageDir = self.sourceDir, notify = PackageMerger.notify, correctSelf = True)
-
- self.packageSeq = SeqValue()
- self.packageSeq.loadXml(xpackage, 'seq')
- self.packageSetVer = SeqValue()
- self.packageSetVer.loadXml(xpackage, 'set_ver')
-
- self.importDescFile = None
- ximport = xpackage.FirstChildElement('import')
- if ximport:
- self.importDescFile = FileSpec()
- self.importDescFile.loadXml(ximport)
- self.importDescFile.quickVerify(packageDir = self.sourceDir, notify = PackageMerger.notify, correctSelf = True)
-
- def makeXml(self):
- """ Returns a new TiXmlElement. """
- xpackage = TiXmlElement('package')
- xpackage.SetAttribute('name', self.packageName)
- if self.platform:
- xpackage.SetAttribute('platform', self.platform)
- if self.version:
- xpackage.SetAttribute('version', self.version)
- if self.solo:
- xpackage.SetAttribute('solo', '1')
- if self.perPlatform:
- xpackage.SetAttribute('per_platform', '1')
-
- self.descFile.storeXml(xpackage)
- self.packageSeq.storeXml(xpackage, 'seq')
- self.packageSetVer.storeXml(xpackage, 'set_ver')
-
- if self.importDescFile:
- ximport = TiXmlElement('import')
- self.importDescFile.storeXml(ximport)
- xpackage.InsertEndChild(ximport)
-
- return xpackage
-
- def validatePackageContents(self):
- """ Validates the contents of the package directory itself
- against the expected hashes and timestamps. Updates
- hashes and timestamps where needed. """
-
- if self.solo:
- return
-
- needsChange = False
- packageDescFullpath = Filename(self.sourceDir, self.descFile.filename)
- packageDir = Filename(packageDescFullpath.getDirname())
- doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
- if not doc.LoadFile():
- message = "Could not read XML file: %s" % (self.descFile.filename)
- raise OSError(message)
-
- xpackage = doc.FirstChildElement('package')
- if not xpackage:
- message = "No package definition: %s" % (self.descFile.filename)
- raise OSError(message)
-
- xcompressed = xpackage.FirstChildElement('compressed_archive')
- if xcompressed:
- spec = FileSpec()
- spec.loadXml(xcompressed)
- if not spec.quickVerify(packageDir = packageDir, notify = PackageMerger.notify, correctSelf = True):
- spec.storeXml(xcompressed)
- needsChange = True
-
- xpatch = xpackage.FirstChildElement('patch')
- while xpatch:
- spec = FileSpec()
- spec.loadXml(xpatch)
- if not spec.quickVerify(packageDir = packageDir, notify = PackageMerger.notify, correctSelf = True):
- spec.storeXml(xpatch)
- needsChange = True
-
- xpatch = xpatch.NextSiblingElement('patch')
-
- if needsChange:
- PackageMerger.notify.info("Rewriting %s" % (self.descFile.filename))
- doc.SaveFile()
- self.descFile.quickVerify(packageDir = self.sourceDir, notify = PackageMerger.notify, correctSelf = True)
-
- # PackageMerger constructor
- def __init__(self, installDir):
- self.installDir = installDir
- self.xhost = None
- self.contents = {}
- self.maxAge = None
- self.contentsSeq = SeqValue()
-
- # We allow the first one to fail quietly.
- self.__readContentsFile(self.installDir, None)
-
- def __readContentsFile(self, sourceDir, packageNames):
- """ Reads the contents.xml file from the indicated sourceDir,
- and updates the internal set of packages appropriately. """
-
- assert sourceDir != None, "No source directory was specified!"
- contentsFilename = Filename(sourceDir, 'contents.xml')
- doc = TiXmlDocument(contentsFilename.toOsSpecific())
- if not doc.LoadFile():
- # Couldn't read file.
- return False
-
- xcontents = doc.FirstChildElement('contents')
- if xcontents:
- maxAge = xcontents.Attribute('max_age')
- if maxAge:
- maxAge = int(maxAge)
- if self.maxAge is None:
- self.maxAge = maxAge
- else:
- self.maxAge = min(self.maxAge, maxAge)
-
- contentsSeq = SeqValue()
- if contentsSeq.loadXml(xcontents):
- self.contentsSeq = max(self.contentsSeq, contentsSeq)
-
- xhost = xcontents.FirstChildElement('host')
- if xhost:
- self.xhost = xhost.Clone()
-
- xpackage = xcontents.FirstChildElement('package')
- while xpackage:
- pe = self.PackageEntry(xpackage, sourceDir)
-
- # Filter out any packages not listed in
- # packageNames (unless packageNames is None,
- # in which case don't filter anything).
- if packageNames is None or pe.packageName in packageNames:
- other = self.contents.get(pe.getKey(), None)
- if not other or pe.isNewer(other):
- # Store this package in the resulting output.
- self.contents[pe.getKey()] = pe
-
- xpackage = xpackage.NextSiblingElement('package')
-
- self.contentsDoc = doc
-
- return True
-
- def __writeContentsFile(self):
- """ Writes the contents.xml file at the end of processing. """
-
- filename = Filename(self.installDir, 'contents.xml')
- doc = TiXmlDocument(filename.toOsSpecific())
- decl = TiXmlDeclaration("1.0", "utf-8", "")
- doc.InsertEndChild(decl)
-
- xcontents = TiXmlElement('contents')
- if self.xhost:
- xcontents.InsertEndChild(self.xhost)
-
- if self.maxAge is not None:
- xcontents.SetAttribute('max_age', str(self.maxAge))
- self.contentsSeq.storeXml(xcontents)
-
- contents = list(self.contents.items())
- contents.sort()
- for key, pe in contents:
- xpackage = pe.makeXml()
- xcontents.InsertEndChild(xpackage)
-
- doc.InsertEndChild(xcontents)
- doc.SaveFile()
-
- def __copySubdirectory(self, pe):
- """ Copies the subdirectory referenced in the indicated
- PackageEntry object into the installDir, replacing the
- contents of any similarly-named subdirectory already
- there. """
-
- dirname = Filename(pe.descFile.filename).getDirname()
- self.notify.info("copying %s" % (dirname))
- sourceDirname = Filename(pe.sourceDir, dirname)
- targetDirname = Filename(self.installDir, dirname)
-
- self.__rCopyTree(sourceDirname, targetDirname)
-
- def __rCopyTree(self, sourceFilename, targetFilename):
- """ Recursively copies the contents of sourceDirname onto
- targetDirname. This behaves like shutil.copytree, but it does
- not remove pre-existing subdirectories. """
-
- if targetFilename.exists():
- if not targetFilename.isDirectory():
- # Delete any regular files in the way.
- targetFilename.unlink()
-
- elif not sourceFilename.isDirectory():
- # If the source file is a regular file, but the target
- # file is a directory, completely remove the target
- # file.
- shutil.rmtree(targetFilename.toOsSpecific())
-
- else:
- # Both the source file and target file are
- # directories.
-
- # We have to clean out the target directory first.
- # Instead of using shutil.rmtree(), remove the files in
- # this directory one at a time, so we don't inadvertently
- # clean out subdirectories too.
- files = os.listdir(targetFilename.toOsSpecific())
- for file in files:
- f = Filename(targetFilename, file)
- if f.isRegularFile():
- f.unlink()
-
- if sourceFilename.isDirectory():
- # Recursively copying a directory.
- Filename(targetFilename, '').makeDir()
- files = os.listdir(sourceFilename.toOsSpecific())
- for file in files:
- self.__rCopyTree(Filename(sourceFilename, file),
- Filename(targetFilename, file))
- else:
- # Copying a regular file.
- sourceFilename.copyTo(targetFilename)
-
- # Also try to copy the timestamp, but don't fuss too much
- # if it doesn't work.
- try:
- st = os.stat(sourceFilename.toOsSpecific())
- os.utime(targetFilename.toOsSpecific(), (st.st_atime, st.st_mtime))
- except OSError:
- pass
-
- def merge(self, sourceDir, packageNames = None):
- """ Adds the contents of the indicated source directory into
- the current pool. If packageNames is not None, it is a list
- of package names that we wish to include from the source;
- packages not named in this list will be unchanged. """
-
- if not self.__readContentsFile(sourceDir, packageNames):
- message = "Couldn't read %s" % (sourceDir)
- raise PackageMergerError(message)
-
- def close(self):
- """ Finalizes the results of all of the previous calls to
- merge(), writes the new contents.xml file, and copies in all
- of the new contents. """
-
- dirname = Filename(self.installDir, '')
- dirname.makeDir()
-
- for pe in self.contents.values():
- if pe.sourceDir != self.installDir:
- # Here's a new subdirectory we have to copy in.
- self.__copySubdirectory(pe)
-
- self.contentsSeq += 1
- self.__writeContentsFile()
-
diff --git a/direct/src/p3d/Packager.py b/direct/src/p3d/Packager.py
deleted file mode 100644
index 4e8a303f7f..0000000000
--- a/direct/src/p3d/Packager.py
+++ /dev/null
@@ -1,3962 +0,0 @@
-""" This module is used to build a "Package", a collection of files
-within a Panda3D Multifile, which can be easily be downloaded and/or
-patched onto a client machine, for the purpose of running a large
-application. """
-
-__all__ = ["Packager", "PackagerError", "OutsideOfPackageError", "ArgumentError"]
-
-# Important to import panda3d first, to avoid naming conflicts with
-# Python's "string" and "Loader" names that are imported later.
-from panda3d.core import *
-import sys
-import os
-import glob
-import struct
-import subprocess
-import copy
-from direct.p3d.FileSpec import FileSpec
-from direct.p3d.SeqValue import SeqValue
-from direct.p3d.HostInfo import HostInfo
-from direct.showbase import Loader
-from direct.showbase import AppRunnerGlobal
-from direct.dist import FreezeTool
-from direct.directnotify.DirectNotifyGlobal import *
-
-vfs = VirtualFileSystem.getGlobalPtr()
-
-class PackagerError(Exception):
- pass
-
-class OutsideOfPackageError(PackagerError):
- pass
-
-class ArgumentError(PackagerError):
- pass
-
-class Packager:
- notify = directNotify.newCategory("Packager")
-
- class PackFile:
- def __init__(self, package, filename,
- newName = None, deleteTemp = False,
- explicit = False, compress = None, extract = None,
- text = None, unprocessed = None,
- executable = None, dependencyDir = None,
- platformSpecific = None, required = False):
- assert isinstance(filename, Filename)
- self.filename = Filename(filename)
- self.newName = newName
- self.deleteTemp = deleteTemp
- self.explicit = explicit
- self.compress = compress
- self.extract = extract
- self.text = text
- self.unprocessed = unprocessed
- self.executable = executable
- self.dependencyDir = dependencyDir
- self.platformSpecific = platformSpecific
- self.required = required
-
- if not self.newName:
- self.newName = str(self.filename)
-
- ext = Filename(self.newName).getExtension()
- if ext == 'pz' or ext == 'gz':
- # Strip off a .pz extension; we can compress files
- # within the Multifile without it.
- filename = Filename(self.newName)
- filename.setExtension('')
- self.newName = str(filename)
- ext = Filename(self.newName).getExtension()
- if self.compress is None:
- self.compress = True
-
- packager = package.packager
- if self.compress is None:
- self.compress = (ext not in packager.uncompressibleExtensions and ext not in packager.imageExtensions)
-
- if self.executable is None:
- self.executable = (ext in packager.executableExtensions)
-
- if self.executable and self.dependencyDir is None:
- # By default, install executable dependencies in the
- # root directory, which is the one that's added to PATH.
- self.dependencyDir = ''
-
- if self.extract is None:
- self.extract = self.executable or (ext in packager.extractExtensions)
- if self.platformSpecific is None:
- self.platformSpecific = self.executable or (ext in packager.platformSpecificExtensions)
-
- if self.unprocessed is None:
- self.unprocessed = self.executable or (ext in packager.unprocessedExtensions)
-
- if self.executable:
- # Look up the filename along the system PATH, if necessary.
- if not packager.resolveLibrary(self.filename):
- # If it wasn't found, try looking it up under its
- # basename only. Sometimes a Mac user will copy
- # the library file out of a framework and put that
- # along the PATH, instead of the framework itself.
- basename = Filename(self.filename.getBasename())
- if packager.resolveLibrary(basename):
- self.filename = basename
-
- if ext in packager.textExtensions and not self.executable:
- self.filename.setText()
- else:
- self.filename.setBinary()
-
- # Convert the filename to an unambiguous filename for
- # searching.
- self.filename.makeTrueCase()
- if self.filename.exists() or not self.filename.isLocal():
- self.filename.makeCanonical()
-
- def isExcluded(self, package):
- """ Returns true if this file should be excluded or
- skipped, false otherwise. """
-
- if self.newName.lower() in package.skipFilenames:
- return True
-
- if not self.explicit:
- # Make sure it's not one of our auto-excluded system
- # files. (But only make this check if this file was
- # not explicitly added.)
-
- basename = Filename(self.newName).getBasename()
- if not package.packager.caseSensitive:
- basename = basename.lower()
- if basename in package.packager.excludeSystemFiles:
- return True
- for exclude in package.packager.excludeSystemGlobs:
- if exclude.matches(basename):
- return True
-
- # Also check if it was explicitly excluded. As above,
- # omit this check for an explicitly-added file: if you
- # both include and exclude a file, the file is
- # included.
- for exclude in package.excludedFilenames:
- if exclude.matches(self.filename):
- return True
-
- # A platform-specific file is implicitly excluded from
- # not-platform-specific packages.
- if self.platformSpecific and package.platformSpecificConfig is False:
- return True
-
- return False
-
- class ExcludeFilename:
- def __init__(self, packager, filename, caseSensitive):
- self.packager = packager
- self.localOnly = (not filename.getDirname())
- if not self.localOnly:
- filename = Filename(filename)
- filename.makeCanonical()
- self.glob = GlobPattern(str(filename))
-
- if self.packager.platform.startswith('win'):
- self.glob.setCaseSensitive(False)
- elif self.packager.platform.startswith('osx'):
- self.glob.setCaseSensitive(False)
-
- def matches(self, filename):
- if self.localOnly:
- return self.glob.matches(filename.getBasename())
- else:
- return self.glob.matches(str(filename))
-
- class PackageEntry:
- """ This corresponds to a entry in the contents.xml
- file. """
-
- def __init__(self):
- # The "seq" value increments automatically with each publish.
- self.packageSeq = SeqValue()
-
- # The "set_ver" value is optionally specified in the pdef
- # file and does not change unless the user says it does.
- self.packageSetVer = SeqValue()
-
- def getKey(self):
- """ Returns a tuple used for sorting the PackageEntry
- objects uniquely per package. """
- return (self.packageName, self.platform or "", self.version or "")
-
- def fromFile(self, packageName, platform, version, solo, perPlatform,
- installDir, descFilename, importDescFilename):
- self.packageName = packageName
- self.platform = platform
- self.version = version
- self.solo = solo
- self.perPlatform = perPlatform
-
- self.descFile = FileSpec()
- self.descFile.fromFile(installDir, descFilename)
-
- self.importDescFile = None
- if importDescFilename:
- self.importDescFile = FileSpec()
- self.importDescFile.fromFile(installDir, importDescFilename)
-
- def loadXml(self, xpackage):
- self.packageName = xpackage.Attribute('name')
- self.platform = xpackage.Attribute('platform')
- self.version = xpackage.Attribute('version')
- solo = xpackage.Attribute('solo')
- self.solo = int(solo or '0')
- perPlatform = xpackage.Attribute('per_platform')
- self.perPlatform = int(perPlatform or '0')
-
- self.packageSeq = SeqValue()
- self.packageSeq.loadXml(xpackage, 'seq')
-
- self.packageSetVer = SeqValue()
- self.packageSetVer.loadXml(xpackage, 'set_ver')
-
- self.descFile = FileSpec()
- self.descFile.loadXml(xpackage)
-
- self.importDescFile = None
- ximport = xpackage.FirstChildElement('import')
- if ximport:
- self.importDescFile = FileSpec()
- self.importDescFile.loadXml(ximport)
-
-
- def makeXml(self):
- """ Returns a new TiXmlElement. """
- xpackage = TiXmlElement('package')
- xpackage.SetAttribute('name', self.packageName)
- if self.platform:
- xpackage.SetAttribute('platform', self.platform)
- if self.version:
- xpackage.SetAttribute('version', self.version)
- if self.solo:
- xpackage.SetAttribute('solo', '1')
- if self.perPlatform:
- xpackage.SetAttribute('per_platform', '1')
-
- self.packageSeq.storeXml(xpackage, 'seq')
- self.packageSetVer.storeXml(xpackage, 'set_ver')
- self.descFile.storeXml(xpackage)
-
- if self.importDescFile:
- ximport = TiXmlElement('import')
- self.importDescFile.storeXml(ximport)
- xpackage.InsertEndChild(ximport)
-
- return xpackage
-
- class HostEntry:
- def __init__(self, url = None, downloadUrl = None,
- descriptiveName = None, hostDir = None,
- mirrors = None):
- self.url = url
- self.downloadUrl = downloadUrl
- self.descriptiveName = descriptiveName
- self.hostDir = hostDir
- self.mirrors = mirrors or []
- self.altHosts = {}
-
- def loadXml(self, xhost, packager):
- self.url = xhost.Attribute('url')
- self.downloadUrl = xhost.Attribute('download_url')
- self.descriptiveName = xhost.Attribute('descriptive_name')
- self.hostDir = xhost.Attribute('host_dir')
- self.mirrors = []
- xmirror = xhost.FirstChildElement('mirror')
- while xmirror:
- url = xmirror.Attribute('url')
- self.mirrors.append(url)
- xmirror = xmirror.NextSiblingElement('mirror')
-
- xalthost = xhost.FirstChildElement('alt_host')
- while xalthost:
- url = xalthost.Attribute('url')
- he = packager.addHost(url)
- he.loadXml(xalthost, packager)
- xalthost = xalthost.NextSiblingElement('alt_host')
-
- def makeXml(self, packager = None):
- """ Returns a new TiXmlElement. """
- xhost = TiXmlElement('host')
- xhost.SetAttribute('url', self.url)
- if self.downloadUrl and self.downloadUrl != self.url:
- xhost.SetAttribute('download_url', self.downloadUrl)
- if self.descriptiveName:
- xhost.SetAttribute('descriptive_name', self.descriptiveName)
- if self.hostDir:
- xhost.SetAttribute('host_dir', self.hostDir)
-
- for mirror in self.mirrors:
- xmirror = TiXmlElement('mirror')
- xmirror.SetAttribute('url', mirror)
- xhost.InsertEndChild(xmirror)
-
- if packager:
- altHosts = sorted(self.altHosts.items())
- for keyword, alt in altHosts:
- he = packager.hosts.get(alt, None)
- if he:
- xalthost = he.makeXml()
- xalthost.SetValue('alt_host')
- xalthost.SetAttribute('keyword', keyword)
- xhost.InsertEndChild(xalthost)
-
- return xhost
-
-
- class Package:
- """ This is the full information on a particular package we
- are constructing. Don't confuse it with PackageEntry, above,
- which contains only the information found in the toplevel
- contents.xml file."""
-
- def __init__(self, packageName, packager):
- self.packageName = packageName
- self.packager = packager
- self.notify = packager.notify
-
- # The platform is initially None until we know the file is
- # platform-specific.
- self.platform = None
-
- # This is always true on modern packages.
- self.perPlatform = True
-
- # The arch string, though, is pre-loaded from the system
- # arch string, so we can sensibly call otool.
- self.arch = self.packager.arch
-
- self.version = None
- self.host = None
- self.p3dApplication = False
- self.solo = False
- self.compressionLevel = 0
- self.importedMapsDir = 'imported_maps'
- self.mainModule = None
- self.signParams = []
- self.requires = []
-
- # This may be set explicitly in the pdef file to a
- # particular sequence value.
- self.packageSetVer = SeqValue()
-
- # This is the set of config variables assigned to the
- # package.
- self.configs = {}
-
- # This is the set of files and modules, already included
- # by required packages, that we can skip.
- self.skipFilenames = {}
- self.skipModules = {}
-
- # This is a list of ExcludeFilename objects, representing
- # the files that have been explicitly excluded.
- self.excludedFilenames = []
-
- # This is the list of files we will be adding, and a pair
- # of cross references.
- self.files = []
- self.sourceFilenames = {}
- self.targetFilenames = {}
-
- # This is the set of files and modules that are
- # required and may not be excluded from the package.
- self.requiredFilenames = []
- self.requiredModules = []
-
- # A list of required packages that were missing.
- self.missingPackages = []
-
- # This records the current list of modules we have added so
- # far.
- self.freezer = FreezeTool.Freezer(platform = self.packager.platform)
- self.freezer.storePythonSource = self.packager.storePythonSource
-
- # Map of extensions to files to number (ignored by dir)
- self.ignoredDirFiles = {}
-
- def close(self):
- """ Writes out the contents of the current package. Returns True
- if the package was constructed successfully, False if one or more
- required files or modules are missing. """
-
- if not self.p3dApplication and not self.packager.allowPackages:
- message = 'Cannot generate packages without an installDir; use -i'
- raise PackagerError(message)
-
- if self.ignoredDirFiles:
- exts = sorted(self.ignoredDirFiles.keys())
- total = sum([x for x in self.ignoredDirFiles.values()])
- self.notify.warning("excluded %s files not marked for inclusion: %s" \
- % (total, ", ".join(["'" + ext + "'" for ext in exts])))
-
- if not self.host:
- self.host = self.packager.host
-
- # Check the version config variable.
- version = self.configs.get('version', None)
- if version is not None:
- self.version = version
- del self.configs['version']
-
- # Check the platform_specific config variable. This has
- # only three settings: None (unset), True, or False.
- self.platformSpecificConfig = self.configs.get('platform_specific', None)
- if self.platformSpecificConfig is not None:
- # First, convert it to an int, in case it's "0" or "1".
- try:
- self.platformSpecificConfig = int(self.platformSpecificConfig)
- except ValueError:
- pass
- # Then, make it a bool.
- self.platformSpecificConfig = bool(self.platformSpecificConfig)
- del self.configs['platform_specific']
-
- # A special case when building the "panda3d" package. We
- # enforce that the version number matches what we've been
- # compiled with.
- if self.packageName == 'panda3d':
- if self.version is None:
- self.version = PandaSystem.getPackageVersionString()
-
- if self.version != PandaSystem.getPackageVersionString():
- message = 'mismatched Panda3D version: requested %s, but Panda3D is built as %s' % (self.version, PandaSystem.getPackageVersionString())
- raise PackagerError(message)
-
- if self.host != PandaSystem.getPackageHostUrl():
- message = 'mismatched Panda3D host: requested %s, but Panda3D is built as %s' % (self.host, PandaSystem.getPackageHostUrl())
- raise PackagerError(message)
-
- if self.p3dApplication:
- # Default compression level for an app.
- self.compressionLevel = 6
-
- # Every p3dapp requires panda3d.
- if 'panda3d' not in [p.packageName for p in self.requires]:
- assert not self.packager.currentPackage
- self.packager.currentPackage = self
- self.packager.do_require('panda3d')
- self.packager.currentPackage = None
-
- # If this flag is set, enable allow_python_dev.
- if self.packager.allowPythonDev:
- self.configs['allow_python_dev'] = True
-
- if not self.p3dApplication and not self.version:
- # If we don't have an implicit version, inherit the
- # version from the 'panda3d' package on our require
- # list.
- for p2 in self.requires:
- if p2.packageName == 'panda3d' and p2.version:
- self.version = p2.version
- break
-
- if self.solo:
- result = self.installSolo()
- else:
- result = self.installMultifile()
-
- if self.p3dApplication:
- allowPythonDev = self.configs.get('allow_python_dev', 0)
- if int(allowPythonDev):
- print("\n*** Generating %s.p3d with allow_python_dev enabled ***\n" % (self.packageName))
-
- return result
-
-
- def considerPlatform(self):
- # Check to see if any of the files are platform-specific,
- # making the overall package platform-specific.
-
- platformSpecific = self.platformSpecificConfig
- for file in self.files:
- if file.isExcluded(self):
- # Skip this file.
- continue
- if file.platformSpecific:
- platformSpecific = True
-
- if platformSpecific and self.platformSpecificConfig is not False:
- if not self.platform:
- self.platform = self.packager.platform
-
- if self.platform and self.platform.startswith('osx_'):
- # Get the OSX "arch" specification.
- self.arch = self.platform[4:]
-
-
- def installMultifile(self):
- """ Installs the package, either as a p3d application, or
- as a true package. Either is implemented with a
- Multifile. """
-
- if self.missingPackages:
- missing = ', '.join([name for name, version in self.missingPackages])
- self.notify.warning("Cannot build package %s due to missing dependencies: %s" % (self.packageName, missing))
- self.cleanup()
- return False
-
- self.multifile = Multifile()
-
- # Write the multifile to a temporary filename until we
- # know enough to determine the output filename.
- multifileFilename = Filename.temporary('', self.packageName + '.', '.mf')
- self.multifile.openReadWrite(multifileFilename)
-
- if self.p3dApplication:
- # p3d files should be tagged to make them executable.
- self.multifile.setHeaderPrefix('#! /usr/bin/env panda3d\n')
- else:
- # Package multifiles might be patched, and therefore
- # don't want to record an internal timestamp, which
- # would make patching less efficient.
- self.multifile.setRecordTimestamp(False)
-
- # Make sure that all required files are present.
- missing = []
- for file in self.requiredFilenames:
- if file not in self.files or file.isExcluded(self):
- missing.append(file.filename.getBasename())
- if len(missing) > 0:
- self.notify.warning("Cannot build package %s, missing required files: %r" % (self.packageName, missing))
- self.cleanup()
- return False
-
- self.extracts = []
- self.components = []
-
- # Add the explicit py files that were requested by the
- # pdef file. These get turned into Python modules.
- for file in self.files:
- if file.isExcluded(self):
- # Skip this file.
- continue
- if file.unprocessed:
- # Unprocessed files get dealt with below.
- continue
-
- ext = Filename(file.newName).getExtension()
- if ext == 'dc':
- # Add the modules named implicitly in the dc file.
- self.addDcImports(file)
-
- elif ext == 'py':
- self.addPyFile(file)
-
- # Add the main module, if any.
- if not self.mainModule and self.p3dApplication:
- message = 'No main_module specified for application %s' % (self.packageName)
- raise PackagerError(message)
- if self.mainModule:
- moduleName, newName = self.mainModule
- if newName not in self.freezer.modules:
- self.freezer.addModule(moduleName, newName = newName)
-
- # Now all module files have been added. Exclude modules
- # already imported in a required package, and not
- # explicitly included by this package.
- for moduleName, mdef in self.skipModules.items():
- if moduleName not in self.freezer.modules:
- self.freezer.excludeModule(
- moduleName, allowChildren = mdef.allowChildren,
- forbid = mdef.forbid, fromSource = 'skip')
-
- # Pick up any unfrozen Python files.
- self.freezer.done()
-
- # But first, make sure that all required modules are present.
- missing = []
- moduleDict = dict(self.freezer.getModuleDefs())
- for module in self.requiredModules:
- if module not in moduleDict:
- missing.append(module)
- if len(missing) > 0:
- self.notify.warning("Cannot build package %s, missing required modules: %r" % (self.packageName, missing))
- self.cleanup()
- return False
-
- # OK, we can add it.
- self.freezer.addToMultifile(self.multifile, self.compressionLevel)
- self.addExtensionModules()
-
- # Add known module names.
- self.moduleNames = {}
- modules = sorted(self.freezer.modules.items())
- for newName, mdef in modules:
- if mdef.guess:
- # Not really a module.
- continue
-
- if mdef.fromSource == 'skip':
- # This record already appeared in a required
- # module; don't repeat it now.
- continue
-
- if mdef.exclude and mdef.implicit:
- # Don't bother mentioning implicitly-excluded
- # (i.e. missing) modules.
- continue
-
- #if newName == '__main__':
- # # Ignore this special case.
- # continue
-
- self.moduleNames[newName] = mdef
-
- xmodule = TiXmlElement('module')
- xmodule.SetAttribute('name', newName)
- if mdef.exclude:
- xmodule.SetAttribute('exclude', '1')
- if mdef.forbid:
- xmodule.SetAttribute('forbid', '1')
- if mdef.exclude and mdef.allowChildren:
- xmodule.SetAttribute('allowChildren', '1')
- self.components.append(('m', newName.lower(), xmodule))
-
- # Now look for implicit shared-library dependencies.
- if self.packager.platform.startswith('win'):
- self.__addImplicitDependenciesWindows()
- elif self.packager.platform.startswith('osx'):
- self.__addImplicitDependenciesOSX()
- else:
- self.__addImplicitDependenciesPosix()
-
- # Now add all the real, non-Python files (except model
- # files). This will include the extension modules we just
- # discovered above.
- for file in self.files:
- if file.isExcluded(self):
- # Skip this file.
- continue
- ext = Filename(file.newName).getExtension()
- if file.unprocessed:
- # Add an unprocessed file verbatim.
- self.addComponent(file)
- elif ext == 'py':
- # Already handled, above.
- pass
- elif file.isExcluded(self):
- # Skip this file.
- pass
- elif ext == 'egg' or ext == 'bam':
- # Skip model files this pass.
- pass
- elif ext == 'dc':
- # dc files get a special treatment.
- self.addDcFile(file)
- elif ext == 'prc':
- # So do prc files.
- self.addPrcFile(file)
- else:
- # Any other file.
- self.addComponent(file)
-
- # Now add the model files. It's important to add these
- # after we have added all of the texture files, so we can
- # determine which textures need to be implicitly pulled
- # in.
-
- # We walk through a copy of the files list, since we might
- # be adding more files (textures) to this list as we
- # discover them in model files referenced in this list.
- for file in self.files[:]:
- if file.isExcluded(self):
- # Skip this file.
- continue
- ext = Filename(file.newName).getExtension()
- if file.unprocessed:
- # Already handled, above.
- pass
- elif ext == 'py':
- # Already handled, above.
- pass
- elif file.isExcluded(self):
- # Skip this file.
- pass
- elif ext == 'egg':
- self.addEggFile(file)
- elif ext == 'bam':
- self.addBamFile(file)
- else:
- # Handled above.
- pass
-
- # Check to see if we should be platform-specific.
- self.considerPlatform()
-
- # Now that we've processed all of the component files,
- # (and set our platform if necessary), we can generate the
- # output filename and write the output files.
-
- self.packageBasename = self.packageName
- packageDir = self.packageName
- if self.version:
- self.packageBasename += '.' + self.version
- packageDir += '/' + self.version
- if self.platform:
- self.packageBasename += '.' + self.platform
- packageDir += '/' + self.platform
-
- self.packageDesc = self.packageBasename + '.xml'
- self.packageImportDesc = self.packageBasename + '.import.xml'
- if self.p3dApplication:
- self.packageBasename += self.packager.p3dSuffix
- self.packageBasename += '.p3d'
- packageDir = ''
- else:
- self.packageBasename += '.mf'
- packageDir += '/'
-
- self.packageDir = packageDir
- self.packageFilename = packageDir + self.packageBasename
- self.packageDesc = packageDir + self.packageDesc
- self.packageImportDesc = packageDir + self.packageImportDesc
-
- print("Generating %s" % (self.packageFilename))
-
- if self.p3dApplication:
- self.packageFullpath = Filename(self.packager.p3dInstallDir, self.packageFilename)
- self.packageFullpath.makeDir()
- self.makeP3dInfo()
- else:
- self.packageFullpath = Filename(self.packager.installDir, self.packageFilename)
- self.packageFullpath.makeDir()
-
- self.multifile.repack()
-
- # Also sign the multifile before we close it.
- for certificate, chain, pkey, password in self.signParams:
- self.multifile.addSignature(certificate, chain or '', pkey or '', password or '')
-
- self.multifile.close()
-
- if not multifileFilename.renameTo(self.packageFullpath):
- self.notify.error("Cannot move %s to %s" % (multifileFilename, self.packageFullpath))
-
- if self.p3dApplication:
- # No patches for an application; just move it into place.
- # Make the application file executable.
- os.chmod(self.packageFullpath.toOsSpecific(), 0o755)
- else:
- self.readDescFile()
- self.packageSeq += 1
- self.perPlatform = True # always true on modern packages.
- self.compressMultifile()
- self.writeDescFile()
- self.writeImportDescFile()
-
- # Now that we've written out the desc file, we don't
- # need to keep around the uncompressed archive
- # anymore.
- self.packageFullpath.unlink()
-
- # Replace or add the entry in the contents.
- pe = Packager.PackageEntry()
- pe.fromFile(self.packageName, self.platform, self.version,
- False, self.perPlatform, self.packager.installDir,
- self.packageDesc, self.packageImportDesc)
- pe.packageSeq = self.packageSeq
- pe.packageSetVer = self.packageSetVer
-
- self.packager.contents[pe.getKey()] = pe
- self.packager.contentsChanged = True
-
- self.cleanup()
- return True
-
- def installSolo(self):
- """ Installs the package as a "solo", which means we
- simply copy the one file into the install directory. This
- is primarily intended for the "coreapi" plugin, which is
- just a single dll and a jpg file; but it can support other
- kinds of similar "solo" packages as well. """
-
- self.considerPlatform()
- self.perPlatform = False # Not true on "solo" packages.
-
- packageDir = self.packageName
- if self.platform:
- packageDir += '/' + self.platform
- if self.version:
- packageDir += '/' + self.version
-
- if not self.packager.allowPackages:
- message = 'Cannot generate packages without an installDir; use -i'
- raise PackagerError(message)
-
- installPath = Filename(self.packager.installDir, packageDir)
- # Remove any files already in the installPath.
- origFiles = vfs.scanDirectory(installPath)
- if origFiles:
- for origFile in origFiles:
- origFile.getFilename().unlink()
-
- files = []
- for file in self.files:
- if file.isExcluded(self):
- # Skip this file.
- continue
- files.append(file)
-
- if not files:
- # No files, never mind.
- return
-
- if len(files) != 1:
- raise PackagerError('Multiple files in "solo" package %s' % (self.packageName))
-
- Filename(installPath, '').makeDir()
-
- file = files[0]
- targetPath = Filename(installPath, file.newName)
- targetPath.setBinary()
- file.filename.setBinary()
- if not file.filename.copyTo(targetPath):
- self.notify.warning("Could not copy %s to %s" % (
- file.filename, targetPath))
-
- # Replace or add the entry in the contents.
- pe = Packager.PackageEntry()
- pe.fromFile(self.packageName, self.platform, self.version,
- True, self.perPlatform, self.packager.installDir,
- Filename(packageDir, file.newName), None)
- peOrig = self.packager.contents.get(pe.getKey(), None)
- if peOrig:
- pe.packageSeq = peOrig.packageSeq + 1
- pe.packageSetVer = peOrig.packageSetVer
- if self.packageSetVer:
- pe.packageSetVer = self.packageSetVer
-
- self.packager.contents[pe.getKey()] = pe
- self.packager.contentsChanged = True
-
- # Hack for coreapi package, to preserve backward compatibility
- # with old versions of the runtime, which still called the
- # 32-bit Windows platform "win32".
- if self.packageName == "coreapi" and self.platform == "win_i386":
- pe2 = copy.copy(pe)
- pe2.platform = "win32"
- self.packager.contents[pe2.getKey()] = pe2
-
- self.cleanup()
- return True
-
- def cleanup(self):
- # Now that all the files have been packed, we can delete
- # the temporary files.
- for file in self.files:
- if file.deleteTemp:
- file.filename.unlink()
-
- def addFile(self, *args, **kw):
- """ Adds the named file to the package. Returns the file
- object, or None if it was not added by this call. """
-
- file = Packager.PackFile(self, *args, **kw)
- if file.filename in self.sourceFilenames:
- # Don't bother, it's already here.
- return None
-
- lowerName = file.newName.lower()
- if lowerName in self.targetFilenames:
- # Another file is already in the same place.
- file2 = self.targetFilenames[lowerName]
- self.packager.notify.warning(
- "%s is shadowing %s" % (file2.filename, file.filename))
- return None
-
- self.sourceFilenames[file.filename] = file
- if file.required:
- self.requiredFilenames.append(file)
-
- if file.text is None and not file.filename.exists():
- if not file.isExcluded(self):
- self.packager.notify.warning("No such file: %s" % (file.filename))
- return None
-
- self.files.append(file)
- self.targetFilenames[lowerName] = file
-
- return file
-
- def excludeFile(self, filename):
- """ Excludes the named file (or glob pattern) from the
- package. """
- xfile = Packager.ExcludeFilename(self.packager, filename, self.packager.caseSensitive)
- self.excludedFilenames.append(xfile)
-
- def __addImplicitDependenciesWindows(self):
- """ Walks through the list of files, looking for dll's and
- exe's that might include implicit dependencies on other
- dll's and assembly manifests. Tries to determine those
- dependencies, and adds them back into the filelist. """
-
- # We walk through the list as we modify it. That's OK,
- # because we want to follow the transitive closure of
- # dependencies anyway.
- for file in self.files:
- if not file.executable:
- continue
-
- if file.isExcluded(self):
- # Skip this file.
- continue
-
- if file.filename.getExtension().lower() == "manifest":
- filenames = self.__parseManifest(file.filename)
- if filenames is None:
- self.notify.warning("Unable to determine dependent assemblies from %s" % (file.filename))
- continue
-
- else:
- tempFile = Filename.temporary('', 'p3d_', '.txt')
- command = 'dumpbin /dependents "%s" >"%s"' % (
- file.filename.toOsSpecific(),
- tempFile.toOsSpecific())
- try:
- os.system(command)
- except:
- pass
- filenames = None
-
- if tempFile.exists():
- filenames = self.__parseDependenciesWindows(tempFile)
- tempFile.unlink()
- if filenames is None:
- self.notify.warning("Unable to determine dependencies from %s" % (file.filename))
- filenames = []
-
- # Extract the manifest file so we can figure out
- # the dependent assemblies.
- tempFile = Filename.temporary('', 'p3d_', '.manifest')
- resindex = 2
- if file.filename.getExtension().lower() == "exe":
- resindex = 1
- command = 'mt -inputresource:"%s";#%d -out:"%s" > nul' % (
- file.filename.toOsSpecific(),
- resindex, tempFile.toOsSpecific())
- try:
- out = os.system(command)
- except:
- pass
- afilenames = None
-
- if tempFile.exists():
- afilenames = self.__parseManifest(tempFile)
- tempFile.unlink()
-
- # Also check for an explicit private-assembly
- # manifest file on disk.
- mfile = file.filename + '.manifest'
- if mfile.exists():
- if afilenames is None:
- afilenames = []
- afilenames += self.__parseManifest(mfile)
- # Since it's an explicit manifest file, it
- # means we should include the manifest
- # file itself in the package.
- newName = Filename(file.dependencyDir, mfile.getBasename())
- self.addFile(mfile, newName = str(newName),
- explicit = False, executable = True)
-
- if afilenames is None and out != 31:
- self.notify.warning("Unable to determine dependent assemblies from %s" % (file.filename))
-
- if afilenames is not None:
- filenames += afilenames
-
- # Attempt to resolve the dependent filename relative
- # to the original filename, before we resolve it along
- # the PATH.
- path = DSearchPath(Filename(file.filename.getDirname()))
-
- for filename in filenames:
- filename = Filename.fromOsSpecific(filename)
- filename.resolveFilename(path)
- filename.makeTrueCase()
-
- newName = Filename(file.dependencyDir, filename.getBasename())
- self.addFile(filename, newName = str(newName),
- explicit = False, executable = True)
-
- def __parseDependenciesWindows(self, tempFile):
- """ Reads the indicated temporary file, the output from
- dumpbin /dependents, to determine the list of dll's this
- executable file depends on. """
-
- lines = open(tempFile.toOsSpecific(), 'rU').readlines()
- li = 0
- while li < len(lines):
- line = lines[li]
- li += 1
- if line.find(' has the following dependencies') != -1:
- break
-
- if li < len(lines):
- line = lines[li]
- if line.strip() == '':
- # Skip a blank line.
- li += 1
-
- # Now we're finding filenames, until the next blank line.
- filenames = []
- while li < len(lines):
- line = lines[li]
- li += 1
- line = line.strip()
- if line == '':
- # We're done.
- return filenames
- filenames.append(line)
-
- # Hmm, we ran out of data. Oh well.
- if not filenames:
- # Some parse error.
- return None
-
- # At least we got some data.
- return filenames
-
- def __parseManifest(self, tempFile):
- """ Reads the indicated application manifest file, to
- determine the list of dependent assemblies this
- executable file depends on. """
-
- doc = TiXmlDocument(tempFile.toOsSpecific())
- if not doc.LoadFile():
- return None
-
- assembly = doc.FirstChildElement("assembly")
- if not assembly:
- return None
-
- # Pick up assemblies that it depends on
- filenames = []
- dependency = assembly.FirstChildElement("dependency")
- while dependency:
- depassembly = dependency.FirstChildElement("dependentAssembly")
- if depassembly:
- ident = depassembly.FirstChildElement("assemblyIdentity")
- if ident:
- name = ident.Attribute("name")
- if name:
- filenames.append(name + ".manifest")
-
- dependency = dependency.NextSiblingElement("dependency")
-
- # Pick up direct dll dependencies that it lists
- dfile = assembly.FirstChildElement("file")
- while dfile:
- name = dfile.Attribute("name")
- if name:
- filenames.append(name)
- dfile = dfile.NextSiblingElement("file")
-
- return filenames
-
- def __locateFrameworkLibrary(self, library):
- """ Locates the given library inside its framework on the
- default framework paths, and returns its location as Filename. """
-
- # If it's already a full existing path, we
- # don't search for it anymore, of course.
- if Filename.fromOsSpecific(library).exists():
- return Filename.fromOsSpecific(library)
-
- # DSearchPath appears not to work for directories.
- fpath = []
- fpath.append(Filename("/Library/Frameworks"))
- fpath.append(Filename("/System/Library/Frameworks"))
- fpath.append(Filename("/Developer/Library/Frameworks"))
- fpath.append(Filename(os.path.expanduser("~"), "Library/Frameworks"))
- if "HOME" in os.environ:
- fpath.append(Filename(os.environ["HOME"], "Library/Frameworks"))
- ffilename = Filename(library.split('.framework/', 1)[0].split('/')[-1] + '.framework')
- ffilename = Filename(ffilename, library.split('.framework/', 1)[-1])
-
- # Look under the system root first, if supplied.
- if self.packager.systemRoot:
- for i in fpath:
- fw = Filename(self.packager.systemRoot, i)
- if Filename(fw, ffilename).exists():
- return Filename(fw, ffilename)
-
- for i in fpath:
- if Filename(i, ffilename).exists():
- return Filename(i, ffilename)
-
- # Not found? Well, let's just return the framework + file
- # path, the user will be presented with a warning later.
- return ffilename
-
- def __alterFrameworkDependencies(self, file, framework_deps):
- """ Copies the given library file to a temporary directory,
- and alters the dependencies so that it doesn't contain absolute
- framework dependencies. """
-
- if not file.deleteTemp:
- # Copy the file to a temporary location because we
- # don't want to modify the original (there's a big
- # chance that we break it).
-
- # Copy it every time, because the source file might
- # have changed since last time we ran.
- assert file.filename.exists(), "File doesn't exist: %s" % file.filename
- tmpfile = Filename.temporary('', "p3d_" + file.filename.getBasename())
- tmpfile.setBinary()
- file.filename.copyTo(tmpfile)
- file.filename = tmpfile
- file.deleteTemp = True
-
- # Alter the dependencies to have a relative path rather than absolute
- for filename in framework_deps:
- loc = self.__locateFrameworkLibrary(filename)
-
- if loc == file.filename:
- os.system('install_name_tool -id "%s" "%s"' % (os.path.basename(filename), file.filename.toOsSpecific()))
- elif "/System/" in loc.toOsSpecific():
- # Let's keep references to system frameworks absolute
- os.system('install_name_tool -change "%s" "%s" "%s"' % (filename, loc.toOsSpecific(), file.filename.toOsSpecific()))
- else:
- os.system('install_name_tool -change "%s" "%s" "%s"' % (filename, os.path.basename(filename), file.filename.toOsSpecific()))
-
- def __addImplicitDependenciesOSX(self):
- """ Walks through the list of files, looking for dylib's
- and executables that might include implicit dependencies
- on other dylib's. Tries to determine those dependencies,
- and adds them back into the filelist. """
-
- # We walk through the list as we modify it. That's OK,
- # because we want to follow the transitive closure of
- # dependencies anyway.
- for file in self.files:
- if not file.executable:
- continue
-
- if file.isExcluded(self):
- # Skip this file.
- continue
-
- origFilename = Filename(file.filename)
-
- tempFile = Filename.temporary('', 'p3d_', '.txt')
- command = '/usr/bin/otool -arch all -L "%s" >"%s"' % (
- origFilename.toOsSpecific(),
- tempFile.toOsSpecific())
- if self.arch:
- arch = self.arch
- if arch == "amd64":
- arch = "x86_64"
- command = '/usr/bin/otool -arch %s -L "%s" >"%s"' % (
- arch,
- origFilename.toOsSpecific(),
- tempFile.toOsSpecific())
- exitStatus = os.system(command)
- if exitStatus != 0:
- self.notify.warning('Command failed: %s' % (command))
- filenames = None
-
- if tempFile.exists():
- filenames = self.__parseDependenciesOSX(tempFile)
- tempFile.unlink()
- if filenames is None:
- self.notify.warning("Unable to determine dependencies from %s" % (origFilename))
- continue
-
- # Attempt to resolve the dependent filename relative
- # to the original filename, before we resolve it along
- # the PATH.
- path = DSearchPath(Filename(origFilename.getDirname()))
-
- # Find the dependencies that are referencing a framework
- framework_deps = []
- for filename in filenames:
- if '.framework/' in filename:
- framework_deps.append(filename)
-
- if len(framework_deps) > 0:
- # Fixes dependencies like @executable_path/../Library/Frameworks/Cg.framework/Cg
- self.__alterFrameworkDependencies(file, framework_deps)
-
- for filename in filenames:
- if '@loader_path' in filename:
- filename = filename.replace('@loader_path', origFilename.getDirname())
-
- if False and '.framework/' in filename:
- # It references a framework, and besides the fact
- # that those often contain absolute paths, they
- # aren't commonly on the library path either.
- filename = self.__locateFrameworkLibrary(filename)
- filename.setBinary()
- else:
- # It's just a normal library - find it on the path.
- filename = Filename.fromOsSpecific(filename)
- filename.setBinary()
-
- if filename.isLocal():
- filename.resolveFilename(path)
- else:
- # It's a fully-specified filename; look
- # for it under the system root first.
- if self.packager.systemRoot:
- f2 = Filename(self.packager.systemRoot, filename)
- if f2.exists():
- filename = f2
-
- # Skip libraries and frameworks in system directory
- if "/System/" in filename.toOsSpecific():
- continue
-
- newName = Filename(file.dependencyDir, filename.getBasename())
- self.addFile(filename, newName = str(newName),
- explicit = False, executable = True)
-
- def __parseDependenciesOSX(self, tempFile):
- """ Reads the indicated temporary file, the output from
- otool -L, to determine the list of dylibs this
- executable file depends on. """
-
- lines = open(tempFile.toOsSpecific(), 'rU').readlines()
-
- filenames = []
- for line in lines:
- if not line[0].isspace():
- continue
- line = line.strip()
- s = line.find(' (compatibility')
- if s != -1:
- line = line[:s]
- else:
- s = line.find('.dylib')
- if s != -1:
- line = line[:s + 6]
- else:
- continue
- filenames.append(line)
-
- return filenames
-
- def __readAndStripELF(self, file):
- """ Reads the indicated ELF binary, and returns a list with
- dependencies. If it contains data that should be stripped,
- it writes the stripped library to a temporary file. Returns
- None if the file failed to read (e.g. not an ELF file). """
-
- # Read the first 16 bytes, which identify the ELF file.
- elf = open(file.filename.toOsSpecific(), 'rb')
- try:
- ident = elf.read(16)
- except IOError:
- elf.close()
- return None
-
- if not ident.startswith(b"\177ELF"):
- # No elf magic! Beware of orcs.
- return None
-
- # Make sure we read in the correct endianness and integer size
- byteOrder = "<>"[ord(ident[5:6]) - 1]
- elfClass = ord(ident[4:5]) - 1 # 0 = 32-bits, 1 = 64-bits
- headerStruct = byteOrder + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[elfClass]
- sectionStruct = byteOrder + ("4xI8xIII8xI", "4xI16xQQI12xQ")[elfClass]
- dynamicStruct = byteOrder + ("iI", "qQ")[elfClass]
-
- type, machine, version, entry, phoff, shoff, flags, ehsize, phentsize, phnum, shentsize, shnum, shstrndx \
- = struct.unpack(headerStruct, elf.read(struct.calcsize(headerStruct)))
- dynamicSections = []
- stringTables = {}
-
- # Seek to the section header table and find the .dynamic section.
- elf.seek(shoff)
- for i in range(shnum):
- type, offset, size, link, entsize = struct.unpack_from(sectionStruct, elf.read(shentsize))
- if type == 6 and link != 0: # DYNAMIC type, links to string table
- dynamicSections.append((offset, size, link, entsize))
- stringTables[link] = None
-
- # Read the relevant string tables.
- for idx in stringTables.keys():
- elf.seek(shoff + idx * shentsize)
- type, offset, size, link, entsize = struct.unpack_from(sectionStruct, elf.read(shentsize))
- if type != 3: continue
- elf.seek(offset)
- stringTables[idx] = elf.read(size)
-
- # Loop through the dynamic sections and rewrite it if it has an rpath/runpath.
- rewriteSections = []
- filenames = []
- rpath = []
- for offset, size, link, entsize in dynamicSections:
- elf.seek(offset)
- data = elf.read(entsize)
- tag, val = struct.unpack_from(dynamicStruct, data)
- newSectionData = b""
- startReplace = None
- pad = 0
-
- # Read tags until we find a NULL tag.
- while tag != 0:
- if tag == 1: # A NEEDED entry. Read it from the string table.
- filenames.append(stringTables[link][val : stringTables[link].find(b'\0', val)])
-
- elif tag == 15 or tag == 29:
- rpath += stringTables[link][val : stringTables[link].find(b'\0', val)].split(b':')
- # An RPATH or RUNPATH entry.
- if not startReplace:
- startReplace = elf.tell() - entsize
- if startReplace:
- pad += entsize
-
- elif startReplace is not None:
- newSectionData += data
-
- data = elf.read(entsize)
- tag, val = struct.unpack_from(dynamicStruct, data)
-
- if startReplace is not None:
- newSectionData += data + (b"\0" * pad)
- rewriteSections.append((startReplace, newSectionData))
- elf.close()
-
- # No rpaths/runpaths found, so nothing to do any more.
- if len(rewriteSections) == 0:
- return filenames
-
- # Attempt to resolve any of the directly
- # dependent filenames along the RPATH.
- for f in range(len(filenames)):
- filename = filenames[f]
- for rdir in rpath:
- if os.path.isfile(os.path.join(rdir, filename)):
- filenames[f] = os.path.join(rdir, filename)
- break
-
- if not file.deleteTemp:
- # Copy the file to a temporary location because we
- # don't want to modify the original (there's a big
- # chance that we break it).
-
- tmpfile = Filename.temporary('', "p3d_" + file.filename.getBasename())
- tmpfile.setBinary()
- file.filename.copyTo(tmpfile)
- file.filename = tmpfile
- file.deleteTemp = True
-
- # Open the temporary file and rewrite the dynamic sections.
- elf = open(file.filename.toOsSpecific(), 'r+b')
- for offset, data in rewriteSections:
- elf.seek(offset)
- elf.write(data)
- elf.write(b"\0" * pad)
- elf.close()
- return filenames
-
- def __addImplicitDependenciesPosix(self):
- """ Walks through the list of files, looking for so's
- and executables that might include implicit dependencies
- on other so's. Tries to determine those dependencies,
- and adds them back into the filelist. """
-
- # We walk through the list as we modify it. That's OK,
- # because we want to follow the transitive closure of
- # dependencies anyway.
- for file in self.files:
- if not file.executable:
- continue
-
- if file.isExcluded(self):
- # Skip this file.
- continue
-
- # Check if this is an ELF binary.
- filenames = self.__readAndStripELF(file)
-
- # If that failed, perhaps ldd will help us.
- if filenames is None:
- self.notify.warning("Reading ELF library %s failed, using ldd instead" % (file.filename))
- tempFile = Filename.temporary('', 'p3d_', '.txt')
- command = 'ldd "%s" >"%s"' % (
- file.filename.toOsSpecific(),
- tempFile.toOsSpecific())
- try:
- os.system(command)
- except:
- pass
-
- if tempFile.exists():
- filenames = self.__parseDependenciesPosix(tempFile)
- tempFile.unlink()
-
- if filenames is None:
- self.notify.warning("Unable to determine dependencies from %s" % (file.filename))
- continue
-
- # Attempt to resolve the dependent filename relative
- # to the original filename, before we resolve it along
- # the PATH.
- path = DSearchPath(Filename(file.filename.getDirname()))
-
- for filename in filenames:
- # These vDSO's provided by Linux aren't
- # supposed to be anywhere on the system.
- if filename in ["linux-gate.so.1", "linux-vdso.so.1"]:
- continue
-
- filename = Filename.fromOsSpecific(filename)
- filename.resolveFilename(path)
- filename.setBinary()
-
- newName = Filename(file.dependencyDir, filename.getBasename())
- self.addFile(filename, newName = str(newName),
- explicit = False, executable = True)
-
- def __parseDependenciesPosix(self, tempFile):
- """ Reads the indicated temporary file, the output from
- ldd, to determine the list of so's this executable file
- depends on. """
-
- lines = open(tempFile.toOsSpecific(), 'rU').readlines()
-
- filenames = []
- for line in lines:
- line = line.strip()
- s = line.find(' => ')
- if s == -1:
- continue
-
- line = line[:s].strip()
- filenames.append(line)
-
- return filenames
-
- def addExtensionModules(self):
- """ Adds the extension modules detected by the freezer to
- the current list of files. """
-
- freezer = self.freezer
- for moduleName, filename in freezer.extras:
- filename = Filename.fromOsSpecific(filename)
- newName = filename.getBasename()
- if '.' in moduleName:
- newName = '/'.join(moduleName.split('.')[:-1])
- newName += '/' + filename.getBasename()
- # Sometimes the PYTHONPATH has the wrong case in it.
- filename.makeTrueCase()
- self.addFile(filename, newName = newName,
- explicit = False, extract = True,
- executable = True,
- platformSpecific = True)
- freezer.extras = []
-
-
- def makeP3dInfo(self):
- """ Makes the p3d_info.xml file that defines the
- application startup parameters and such. """
-
- doc = TiXmlDocument()
- decl = TiXmlDeclaration("1.0", "utf-8", "")
- doc.InsertEndChild(decl)
-
- xpackage = TiXmlElement('package')
- xpackage.SetAttribute('name', self.packageName)
- if self.platform:
- xpackage.SetAttribute('platform', self.platform)
- if self.version:
- xpackage.SetAttribute('version', self.version)
-
- xpackage.SetAttribute('main_module', self.mainModule[1])
-
- self.__addConfigs(xpackage)
-
- requireHosts = {}
- for package in self.requires:
- xrequires = TiXmlElement('requires')
- xrequires.SetAttribute('name', package.packageName)
- if package.version:
- xrequires.SetAttribute('version', package.version)
- xrequires.SetAttribute('host', package.host)
- package.packageSeq.storeXml(xrequires, 'seq')
- package.packageSetVer.storeXml(xrequires, 'set_ver')
- requireHosts[package.host] = True
- xpackage.InsertEndChild(xrequires)
-
- for host in requireHosts.keys():
- he = self.packager.hosts.get(host, None)
- if he:
- xhost = he.makeXml(packager = self.packager)
- xpackage.InsertEndChild(xhost)
-
- self.extracts.sort()
- for name, xextract in self.extracts:
- xpackage.InsertEndChild(xextract)
-
- doc.InsertEndChild(xpackage)
-
- # Write the xml file to a temporary file on disk, so we
- # can add it to the multifile.
- filename = Filename.temporary('', 'p3d_', '.xml')
-
- # This should really be setText() for an xml file, but it
- # doesn't really matter that much since tinyxml can read
- # it either way; and if we use setBinary() it will remain
- # compatible with older versions of the core API that
- # didn't understand the SF_text flag.
- filename.setBinary()
-
- doc.SaveFile(filename.toOsSpecific())
-
- # It's important not to compress this file: the core API
- # runtime can't decode compressed subfiles.
- self.multifile.addSubfile('p3d_info.xml', filename, 0)
-
- self.multifile.flush()
- filename.unlink()
-
-
- def compressMultifile(self):
- """ Compresses the .mf file into an .mf.pz file. """
-
- if self.oldCompressedBasename:
- # Remove the previous compressed file first.
- compressedPath = Filename(self.packager.installDir, Filename(self.packageDir, self.oldCompressedBasename))
- compressedPath.unlink()
-
- newCompressedFilename = '%s.pz' % (self.packageFilename)
-
- # Now build the new version.
- compressedPath = Filename(self.packager.installDir, newCompressedFilename)
- if not compressFile(self.packageFullpath, compressedPath, 6):
- message = 'Unable to write %s' % (compressedPath)
- raise PackagerError(message)
-
- def readDescFile(self):
- """ Reads the existing package.xml file before rewriting
- it. We need this to preserve the list of patches, and
- similar historic data, between sessions. """
-
- self.packageSeq = SeqValue()
- self.packageSetVer = SeqValue()
- self.patchVersion = None
- self.patches = []
-
- self.oldCompressedBasename = None
-
- packageDescFullpath = Filename(self.packager.installDir, self.packageDesc)
- doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
- if not doc.LoadFile():
- return
-
- xpackage = doc.FirstChildElement('package')
- if not xpackage:
- return
-
- perPlatform = xpackage.Attribute('per_platform')
- self.perPlatform = int(perPlatform or '0')
-
- self.packageSeq.loadXml(xpackage, 'seq')
- self.packageSetVer.loadXml(xpackage, 'set_ver')
-
- xcompressed = xpackage.FirstChildElement('compressed_archive')
- if xcompressed:
- compressedFilename = xcompressed.Attribute('filename')
- if compressedFilename:
- self.oldCompressedBasename = compressedFilename
-
- patchVersion = xpackage.Attribute('patch_version')
- if not patchVersion:
- patchVersion = xpackage.Attribute('last_patch_version')
- if patchVersion:
- self.patchVersion = patchVersion
-
- # Extract the base_version, top_version, and patch
- # entries, if any, and preserve these entries verbatim for
- # the next version.
- xbase = xpackage.FirstChildElement('base_version')
- if xbase:
- self.patches.append(xbase.Clone())
- xtop = xpackage.FirstChildElement('top_version')
- if xtop:
- self.patches.append(xtop.Clone())
-
- xpatch = xpackage.FirstChildElement('patch')
- while xpatch:
- self.patches.append(xpatch.Clone())
- xpatch = xpatch.NextSiblingElement('patch')
-
- def writeDescFile(self):
- """ Makes the package.xml file that describes the package
- and its contents, for download. """
-
- packageDescFullpath = Filename(self.packager.installDir, self.packageDesc)
- doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
- decl = TiXmlDeclaration("1.0", "utf-8", "")
- doc.InsertEndChild(decl)
-
- xpackage = TiXmlElement('package')
- xpackage.SetAttribute('name', self.packageName)
- if self.platform:
- xpackage.SetAttribute('platform', self.platform)
- if self.version:
- xpackage.SetAttribute('version', self.version)
- if self.perPlatform:
- xpackage.SetAttribute('per_platform', '1')
-
- if self.patchVersion:
- xpackage.SetAttribute('last_patch_version', self.patchVersion)
-
- self.packageSeq.storeXml(xpackage, 'seq')
- self.packageSetVer.storeXml(xpackage, 'set_ver')
-
- self.__addConfigs(xpackage)
-
- for package in self.requires:
- xrequires = TiXmlElement('requires')
- xrequires.SetAttribute('name', package.packageName)
- if self.platform and package.platform:
- xrequires.SetAttribute('platform', package.platform)
- if package.version:
- xrequires.SetAttribute('version', package.version)
- package.packageSeq.storeXml(xrequires, 'seq')
- package.packageSetVer.storeXml(xrequires, 'set_ver')
- xrequires.SetAttribute('host', package.host)
- xpackage.InsertEndChild(xrequires)
-
- xuncompressedArchive = self.getFileSpec(
- 'uncompressed_archive', self.packageFullpath,
- self.packageBasename)
- xpackage.InsertEndChild(xuncompressedArchive)
-
- xcompressedArchive = self.getFileSpec(
- 'compressed_archive', self.packageFullpath + '.pz',
- self.packageBasename + '.pz')
- xpackage.InsertEndChild(xcompressedArchive)
-
- # Copy in the patch entries read from the previous version
- # of the desc file.
- for xpatch in self.patches:
- xpackage.InsertEndChild(xpatch)
-
- self.extracts.sort()
- for name, xextract in self.extracts:
- xpackage.InsertEndChild(xextract)
-
- doc.InsertEndChild(xpackage)
- doc.SaveFile()
-
- def __addConfigs(self, xpackage):
- """ Adds the XML config values defined in self.configs to
- the indicated XML element. """
-
- if self.configs:
- xconfig = TiXmlElement('config')
-
- for variable, value in self.configs.items():
- if sys.version_info < (3, 0) and isinstance(value, unicode):
- xconfig.SetAttribute(variable, value.encode('utf-8'))
- elif isinstance(value, bool):
- # True or False must be encoded as 1 or 0.
- xconfig.SetAttribute(variable, str(int(value)))
- else:
- xconfig.SetAttribute(variable, str(value))
-
- xpackage.InsertEndChild(xconfig)
-
- def writeImportDescFile(self):
- """ Makes the package.import.xml file that describes the
- package and its contents, for other packages and
- applications that may wish to "require" this one. """
-
- packageImportDescFullpath = Filename(self.packager.installDir, self.packageImportDesc)
- doc = TiXmlDocument(packageImportDescFullpath.toOsSpecific())
- decl = TiXmlDeclaration("1.0", "utf-8", "")
- doc.InsertEndChild(decl)
-
- xpackage = TiXmlElement('package')
- xpackage.SetAttribute('name', self.packageName)
- if self.platform:
- xpackage.SetAttribute('platform', self.platform)
- if self.version:
- xpackage.SetAttribute('version', self.version)
- xpackage.SetAttribute('host', self.host)
-
- self.packageSeq.storeXml(xpackage, 'seq')
- self.packageSetVer.storeXml(xpackage, 'set_ver')
-
- requireHosts = {}
- requireHosts[self.host] = True
-
- for package in self.requires:
- xrequires = TiXmlElement('requires')
- xrequires.SetAttribute('name', package.packageName)
- if self.platform and package.platform:
- xrequires.SetAttribute('platform', package.platform)
- if package.version:
- xrequires.SetAttribute('version', package.version)
- xrequires.SetAttribute('host', package.host)
- package.packageSeq.storeXml(xrequires, 'seq')
- package.packageSetVer.storeXml(xrequires, 'set_ver')
- requireHosts[package.host] = True
- xpackage.InsertEndChild(xrequires)
-
- # Make sure we also write the full host descriptions for
- # any hosts we reference, so we can find these guys later.
- for host in requireHosts.keys():
- he = self.packager.hosts.get(host, None)
- if he:
- xhost = he.makeXml(packager = self.packager)
- xpackage.InsertEndChild(xhost)
-
- self.components.sort()
- for type, name, xcomponent in self.components:
- xpackage.InsertEndChild(xcomponent)
-
- doc.InsertEndChild(xpackage)
- doc.SaveFile()
-
- def readImportDescFile(self, filename):
- """ Reads the import desc file. Returns True on success,
- False on failure. """
-
- self.packageSeq = SeqValue()
- self.packageSetVer = SeqValue()
-
- doc = TiXmlDocument(filename.toOsSpecific())
- if not doc.LoadFile():
- return False
- xpackage = doc.FirstChildElement('package')
- if not xpackage:
- return False
-
- self.packageName = xpackage.Attribute('name')
- self.platform = xpackage.Attribute('platform')
- self.version = xpackage.Attribute('version')
- self.host = xpackage.Attribute('host')
-
- # Get any new host descriptors.
- xhost = xpackage.FirstChildElement('host')
- while xhost:
- he = self.packager.HostEntry()
- he.loadXml(xhost, self)
- if he.url not in self.packager.hosts:
- self.packager.hosts[he.url] = he
- xhost = xhost.NextSiblingElement('host')
-
- self.packageSeq.loadXml(xpackage, 'seq')
- self.packageSetVer.loadXml(xpackage, 'set_ver')
-
- self.requires = []
- xrequires = xpackage.FirstChildElement('requires')
- while xrequires:
- packageName = xrequires.Attribute('name')
- platform = xrequires.Attribute('platform')
- version = xrequires.Attribute('version')
- host = xrequires.Attribute('host')
- if packageName:
- package = self.packager.findPackage(
- packageName, platform = platform, version = version,
- host = host, requires = self.requires)
- if package:
- self.requires.append(package)
- xrequires = xrequires.NextSiblingElement('requires')
-
- self.targetFilenames = {}
- xcomponent = xpackage.FirstChildElement('component')
- while xcomponent:
- name = xcomponent.Attribute('filename')
- if name:
- self.targetFilenames[name.lower()] = True
- xcomponent = xcomponent.NextSiblingElement('component')
-
- self.moduleNames = {}
- xmodule = xpackage.FirstChildElement('module')
- while xmodule:
- moduleName = xmodule.Attribute('name')
- exclude = int(xmodule.Attribute('exclude') or 0)
- forbid = int(xmodule.Attribute('forbid') or 0)
- allowChildren = int(xmodule.Attribute('allowChildren') or 0)
-
- if moduleName:
- mdef = FreezeTool.Freezer.ModuleDef(
- moduleName, exclude = exclude, forbid = forbid,
- allowChildren = allowChildren)
- self.moduleNames[moduleName] = mdef
- xmodule = xmodule.NextSiblingElement('module')
-
- return True
-
- def getFileSpec(self, element, pathname, newName):
- """ Returns an xcomponent or similar element with the file
- information for the indicated file. """
-
- xspec = TiXmlElement(element)
-
- size = pathname.getFileSize()
- timestamp = pathname.getTimestamp()
-
- hv = HashVal()
- hv.hashFile(pathname)
- hash = hv.asHex()
-
- xspec.SetAttribute('filename', newName)
- xspec.SetAttribute('size', str(size))
- xspec.SetAttribute('timestamp', str(timestamp))
- xspec.SetAttribute('hash', hash)
-
- return xspec
-
- def addPyFile(self, file):
- """ Adds the indicated python file, identified by filename
- instead of by module name, to the package. """
-
- # Convert the raw filename back to a module name, so we
- # can see if we've already loaded this file. We assume
- # that all Python files within the package will be rooted
- # at the top of the package.
-
- filename = file.newName.rsplit('.', 1)[0]
- moduleName = filename.replace("/", ".")
- if moduleName.endswith('.__init__'):
- moduleName = moduleName.rsplit('.', 1)[0]
-
- if moduleName in self.freezer.modules:
- # This Python file is already known. We don't have to
- # deal with it again.
- return
-
- # Make sure that it is actually in a package.
- parentName = moduleName
- while '.' in parentName:
- parentName = parentName.rsplit('.', 1)[0]
- if parentName not in self.freezer.modules:
- message = 'Cannot add Python file %s; not in package' % (file.newName)
- if file.required or file.explicit:
- raise Exception(message)
- else:
- self.notify.warning(message)
- return
-
- if file.text:
- self.freezer.addModule(moduleName, filename = file.filename, text = file.text)
- else:
- self.freezer.addModule(moduleName, filename = file.filename)
-
- def addEggFile(self, file):
- # Precompile egg files to bam's.
- np = self.packager.loader.loadModel(file.filename, self.packager.loaderOptions)
- if not np:
- raise Exception('Could not read egg file %s' % (file.filename))
-
- bamName = Filename(file.newName)
- bamName.setExtension('bam')
- self.addNode(np.node(), file.filename, str(bamName))
-
- def addBamFile(self, file):
- # Load the bam file so we can massage its textures.
- bamFile = BamFile()
- if not bamFile.openRead(file.filename):
- raise Exception('Could not read bam file %s' % (file.filename))
-
- bamFile.getReader().setLoaderOptions(self.packager.loaderOptions)
-
- if not bamFile.resolve():
- raise Exception('Could not resolve bam file %s' % (file.filename))
-
- node = bamFile.readNode()
- if not node:
- raise Exception('Not a model file: %s' % (file.filename))
-
- self.addNode(node, file.filename, file.newName)
-
- def addNode(self, node, filename, newName):
- """ Converts the indicated node to a bam stream, and adds the
- bam file to the multifile under the indicated newName. """
-
- # If the Multifile already has a file by this name, don't
- # bother adding it again.
- if self.multifile.findSubfile(newName) >= 0:
- return
-
- # Be sure to import all of the referenced textures, and tell
- # them their new location within the multifile.
-
- for tex in NodePath(node).findAllTextures():
- if not tex.hasFullpath() and tex.hasRamImage():
- # We need to store this texture as a raw-data image.
- # Clear the newName so this will happen
- # automatically.
- tex.clearFilename()
- tex.clearAlphaFilename()
-
- else:
- # We can store this texture as a file reference to its
- # image. Copy the file into our multifile, and rename
- # its reference in the texture.
- if tex.hasFilename():
- tex.setFilename(self.addFoundTexture(tex.getFullpath()))
- if tex.hasAlphaFilename():
- tex.setAlphaFilename(self.addFoundTexture(tex.getAlphaFullpath()))
-
- # Now generate an in-memory bam file. Tell the bam writer to
- # keep the textures referenced by their in-multifile path.
- bamFile = BamFile()
- stream = StringStream()
- bamFile.openWrite(stream)
- bamFile.getWriter().setFileTextureMode(bamFile.BTMUnchanged)
- bamFile.writeObject(node)
- bamFile.close()
-
- # Clean the node out of memory.
- node.removeAllChildren()
-
- # Now we have an in-memory bam file.
- stream.seekg(0)
- self.multifile.addSubfile(newName, stream, self.compressionLevel)
-
- # Flush it so the data gets written to disk immediately, so we
- # don't have to keep it around in ram.
- self.multifile.flush()
-
- xcomponent = TiXmlElement('component')
- xcomponent.SetAttribute('filename', newName)
- self.components.append(('c', newName.lower(), xcomponent))
-
- def addFoundTexture(self, filename):
- """ Adds the newly-discovered texture to the output, if it has
- not already been included. Returns the new name within the
- package tree. """
-
- filename = Filename(filename)
- filename.makeCanonical()
-
- file = self.sourceFilenames.get(filename, None)
- if file:
- # Never mind, it's already on the list.
- return file.newName
-
- # We have to copy the image into the plugin tree somewhere.
- newName = self.importedMapsDir + '/' + filename.getBasename()
- uniqueId = 0
- while newName.lower() in self.targetFilenames:
- uniqueId += 1
- newName = '%s/%s_%s.%s' % (
- self.importedMapsDir, filename.getBasenameWoExtension(),
- uniqueId, filename.getExtension())
-
- file = self.addFile(
- filename, newName = newName, explicit = False,
- compress = False)
-
- if file:
- # If we added the file in this pass, then also
- # immediately add it to the multifile (because we
- # won't be visiting the files list again).
- self.addComponent(file)
-
- return newName
-
- def addDcFile(self, file):
- """ Adds a dc file to the archive. A dc file gets its
- internal comments and parameter names stripped out of the
- final result automatically. This is as close as we can
- come to "compiling" a dc file, since all of the remaining
- symbols are meaningful at runtime. """
-
- # First, read in the dc file
- from panda3d.direct import DCFile
- dcFile = DCFile()
- if not dcFile.read(file.filename):
- self.notify.error("Unable to parse %s." % (file.filename))
-
- # And then write it out without the comments and such.
- stream = StringStream()
- if not dcFile.write(stream, True):
- self.notify.error("Unable to write %s." % (file.filename))
-
- file.text = stream.getData()
- self.addComponent(file)
-
- def addDcImports(self, file):
- """ Adds the Python modules named by the indicated dc
- file. """
-
- from panda3d.direct import DCFile
- dcFile = DCFile()
- if not dcFile.read(file.filename):
- self.notify.error("Unable to parse %s." % (file.filename))
-
- for n in range(dcFile.getNumImportModules()):
- moduleName = dcFile.getImportModule(n)
- moduleSuffixes = []
- if '/' in moduleName:
- moduleName, suffixes = moduleName.split('/', 1)
- moduleSuffixes = suffixes.split('/')
- self.freezer.addModule(moduleName)
-
- for suffix in self.packager.dcClientSuffixes:
- if suffix in moduleSuffixes:
- self.freezer.addModule(moduleName + suffix)
-
- for i in range(dcFile.getNumImportSymbols(n)):
- symbolName = dcFile.getImportSymbol(n, i)
- symbolSuffixes = []
- if '/' in symbolName:
- symbolName, suffixes = symbolName.split('/', 1)
- symbolSuffixes = suffixes.split('/')
-
- # "from moduleName import symbolName".
-
- # Maybe this symbol is itself a module; if that's
- # the case, we need to add it to the list also.
- self.freezer.addModule('%s.%s' % (moduleName, symbolName),
- implicit = True)
- for suffix in self.packager.dcClientSuffixes:
- if suffix in symbolSuffixes:
- self.freezer.addModule('%s.%s%s' % (moduleName, symbolName, suffix),
- implicit = True)
-
-
- def addPrcFile(self, file):
- """ Adds a prc file to the archive. Like the dc file,
- this strips comments and such before adding. It's also
- possible to set prcEncryptionKey and/or prcSignCommand to
- further manipulate prc files during processing. """
-
- # First, read it in.
- if file.text:
- textLines = file.text.split('\n')
- else:
- textLines = open(file.filename.toOsSpecific(), 'rU').readlines()
-
- # Then write it out again, without the comments.
- tempFilename = Filename.temporary('', 'p3d_', '.prc')
- tempFilename.setBinary() # Binary is more reliable for signing.
- temp = open(tempFilename.toOsSpecific(), 'w')
- for line in textLines:
- line = line.strip()
- if line and line[0] != '#':
- # Write the line out only if it's not a comment.
- temp.write(line + '\n')
- temp.close()
-
- if self.packager.prcSignCommand:
- # Now sign the file.
- command = '%s -n "%s"' % (
- self.packager.prcSignCommand, tempFilename.toOsSpecific())
- self.notify.info(command)
- exitStatus = os.system(command)
- if exitStatus != 0:
- self.notify.error('Command failed: %s' % (command))
-
- if self.packager.prcEncryptionKey:
- # And now encrypt it.
- if file.newName.endswith('.prc'):
- # Change .prc -> .pre
- file.newName = file.newName[:-1] + 'e'
-
- preFilename = Filename.temporary('', 'p3d_', '.pre')
- preFilename.setBinary()
- tempFilename.setText()
- encryptFile(tempFilename, preFilename, self.packager.prcEncryptionKey)
- tempFilename.unlink()
- tempFilename = preFilename
-
- if file.deleteTemp:
- file.filename.unlink()
-
- file.filename = tempFilename
- file.text = None
- file.deleteTemp = True
-
- self.addComponent(file)
-
- def addComponent(self, file):
- compressionLevel = 0
- if file.compress:
- compressionLevel = self.compressionLevel
-
- if file.text:
- stream = StringStream(file.text)
- self.multifile.addSubfile(file.newName, stream, compressionLevel)
- self.multifile.flush()
-
- elif file.executable and self.arch:
- if not self.__addOsxExecutable(file):
- return
-
- else:
- # Copy an ordinary file into the multifile.
- self.multifile.addSubfile(file.newName, file.filename, compressionLevel)
- if file.extract:
- if file.text:
- # Better write it to a temporary file, so we can
- # get its hash.
- tfile = Filename.temporary('', 'p3d_')
- open(tfile.toOsSpecific(), 'wb').write(file.text)
- xextract = self.getFileSpec('extract', tfile, file.newName)
- tfile.unlink()
-
- else:
- # The file data exists on disk already.
- xextract = self.getFileSpec('extract', file.filename, file.newName)
- self.extracts.append((file.newName.lower(), xextract))
-
- xcomponent = TiXmlElement('component')
- xcomponent.SetAttribute('filename', file.newName)
- self.components.append(('c', file.newName.lower(), xcomponent))
-
- def __addOsxExecutable(self, file):
- """ Adds an executable or shared library to the multifile,
- with respect to OSX's fat-binary features. Returns true
- on success, false on failure. """
-
- compressionLevel = 0
- if file.compress:
- compressionLevel = self.compressionLevel
-
- # If we're on OSX and adding only files for a
- # particular architecture, use lipo to strip out the
- # part of the file for that architecture.
-
- arch = self.arch
- if arch == "amd64":
- arch = "x86_64"
-
- # First, we need to verify that it is in fact a
- # universal binary.
- tfile = Filename.temporary('', 'p3d_')
- tfile.setBinary()
- command = '/usr/bin/lipo -info "%s" >"%s"' % (
- file.filename.toOsSpecific(),
- tfile.toOsSpecific())
- exitStatus = os.system(command)
- if exitStatus != 0:
- self.notify.warning("Not an executable file: %s" % (file.filename))
- # Just add it anyway.
- file.filename.setBinary()
- self.multifile.addSubfile(file.newName, file.filename, compressionLevel)
- return True
-
- # The lipo command succeeded, so it really is an
- # executable file. Parse the lipo output to figure out
- # which architectures the file supports.
- arches = []
- lipoData = open(tfile.toOsSpecific(), 'r').read()
- tfile.unlink()
- if ':' in lipoData:
- arches = lipoData.rsplit(':', 1)[1]
- arches = arches.split()
-
- if arches == [arch]:
- # The file only contains the one architecture that
- # we want anyway.
- file.filename.setBinary()
- self.multifile.addSubfile(file.newName, file.filename, compressionLevel)
- return True
-
- if arch not in arches:
- # The file doesn't support the architecture that we
- # want at all. Omit the file.
- self.notify.warning("%s doesn't support architecture %s" % (
- file.filename, self.arch))
- return False
-
- # The file contains multiple architectures. Get
- # out just the one we want.
- command = '/usr/bin/lipo -thin %s -output "%s" "%s"' % (
- arch, tfile.toOsSpecific(),
- file.filename.toOsSpecific())
- exitStatus = os.system(command)
- if exitStatus != 0:
- self.notify.error('Command failed: %s' % (command))
- self.multifile.addSubfile(file.newName, tfile, compressionLevel)
- if file.deleteTemp:
- file.filename.unlink()
- file.filename = tfile
- file.deleteTemp = True
- return True
-
-
- def requirePackage(self, package):
- """ Indicates a dependency on the given package. This
- also implicitly requires all of the package's requirements
- as well (though this transitive requirement happens at
- runtime, not here at build time). """
-
- if package not in self.requires:
- self.requires.append(package)
- for lowerName in package.targetFilenames:
- ext = Filename(lowerName).getExtension()
- if ext not in self.packager.nonuniqueExtensions:
- self.skipFilenames[lowerName] = True
-
- for moduleName, mdef in package.moduleNames.items():
- if not mdef.exclude:
- self.skipModules[moduleName] = mdef
-
- # Packager constructor
- def __init__(self, platform = None):
-
- # The following are config settings that the caller may adjust
- # before calling any of the command methods.
-
- # The platform string.
- self.setPlatform(platform)
-
- # This should be set to a Filename.
- self.installDir = None
-
- # If specified, this is a directory to search first for any
- # library references, before searching the system.
- # Particularly useful on OSX to reference the universal SDK.
- self.systemRoot = None
-
- # Set this true to treat setHost() the same as addHost(), thus
- # ignoring any request to specify a particular download host,
- # e.g. for testing and development.
- self.ignoreSetHost = False
-
- # Set this to true to verbosely log files ignored by dir().
- self.verbosePrint = False
-
- # This will be appended to the basename of any .p3d package,
- # before the .p3d extension.
- self.p3dSuffix = ''
-
- # The download URL at which these packages will eventually be
- # hosted.
- self.hosts = {}
- self.host = PandaSystem.getPackageHostUrl()
- self.addHost(self.host)
-
- # This will be used when we're not compiling in the packaged
- # environment.
- self.__hostInfos = {}
- self.http = HTTPClient.getGlobalPtr()
-
- # The maximum amount of time a client should cache the
- # contents.xml before re-querying the server, in seconds.
- self.maxAge = 0
-
- # The contents seq: a tuple of integers, representing the
- # current seq value. The contents seq generally increments
- # with each modification to the contents.xml file. There is
- # also a package seq for each package, which generally
- # increments with each modification to the package.
-
- # The contents seq and package seq are used primarily for
- # documentation purposes, to note when a new version is
- # released. The package seq value can also be used to verify
- # that the contents.xml, desc.xml, and desc.import.xml files
- # were all built at the same time.
-
- # Although the package seqs are used at runtime to verify that
- # the latest contents.xml file has been downloaded, they are
- # not otherwise used at runtime, and they are not binding on
- # the download version. The md5 hash, not the package seq, is
- # actually used to differentiate different download versions.
- self.contentsSeq = SeqValue()
-
- # A search list for previously-built local packages.
-
- # We use a bit of caution to read the Filenames out of the
- # config variable. Since cvar.getDirectories() returns a list
- # of references to Filename objects stored within the config
- # variable itself, we have to make a copy of each Filename
- # returned, so they will persist beyond the lifespan of the
- # config variable.
- cvar = ConfigVariableSearchPath('pdef-path')
- self.installSearch = list(map(Filename, cvar.getDirectories()))
-
- # This is where we cache the location of libraries.
- self.libraryCache = {}
-
- # The system PATH, for searching dll's and exe's.
- self.executablePath = DSearchPath()
-
- # By convention, we include sys.path at the front of
- # self.executablePath, mainly to aid makepanda when building
- # an rtdist build.
- for dirname in sys.path:
- self.executablePath.appendDirectory(Filename.fromOsSpecific(dirname))
-
- # Now add the actual system search path.
- if self.platform.startswith('win'):
- self.addWindowsSearchPath(self.executablePath, "PATH")
-
- else:
- if self.platform.startswith('osx'):
- self.addPosixSearchPath(self.executablePath, "DYLD_LIBRARY_PATH")
-
- self.addPosixSearchPath(self.executablePath, "LD_LIBRARY_PATH")
- self.addPosixSearchPath(self.executablePath, "PATH")
-
- if self.platform.startswith('linux'):
- # It used to be okay to just add some common paths on Linux.
- # But nowadays, each distribution has their own convention for
- # where they put their libraries. Instead, we query the ldconfig
- # cache, which contains the location of all libraries.
-
- if not self.loadLdconfigCache():
- # Ugh, failure. All that remains is to guess. This should
- # work for the most common Debian configurations.
- multiarchDir = "/lib/%s-linux-gnu" % (os.uname()[4])
- if os.path.isdir(multiarchDir):
- self.executablePath.appendDirectory(multiarchDir)
- if os.path.isdir("/usr/" + multiarchDir):
- self.executablePath.appendDirectory("/usr/" + multiarchDir)
-
- else:
- # FreeBSD, or some other system that still makes sense.
- self.executablePath.appendDirectory('/lib')
- self.executablePath.appendDirectory('/usr/lib')
- self.executablePath.appendDirectory('/usr/local/lib')
-
- if self.platform.startswith('freebsd') and os.uname()[1] == "pcbsd":
- self.executablePath.appendDirectory('/usr/PCBSD/local/lib')
-
- # Set this flag true to automatically add allow_python_dev to
- # any applications.
- self.allowPythonDev = False
-
- # Set this flag to store the original Python source files,
- # without compiling them to .pyc or .pyo.
- self.storePythonSource = False
-
- # Fill this with a list of (certificate, chain, pkey,
- # password) tuples to automatically sign each p3d file
- # generated.
- self.signParams = []
-
- # Optional signing and encrypting features.
- self.encryptionKey = None
- self.prcEncryptionKey = None
- self.prcSignCommand = None
-
- # This is a list of filename extensions and/or basenames that
- # indicate files that should be encrypted within the
- # multifile. This provides obfuscation only, not real
- # security, since the decryption key must be part of the
- # client and is therefore readily available to any hacker.
- # Not only is this feature useless, but using it also
- # increases the size of your patchfiles, since encrypted files
- # can't really be patched. But it's here if you really want
- # it. ** Note: Actually, this isn't implemented yet.
- #self.encryptExtensions = []
- #self.encryptFiles = []
-
- # This is the list of DC import suffixes that should be
- # available to the client. Other suffixes, like AI and UD,
- # are server-side only and should be ignored by the Scrubber.
- self.dcClientSuffixes = ['OV']
-
- # Is this file system case-sensitive?
- self.caseSensitive = True
- if self.platform.startswith('win'):
- self.caseSensitive = False
- elif self.platform.startswith('osx'):
- self.caseSensitive = False
-
- # Get the list of filename extensions that are recognized as
- # image files.
- self.imageExtensions = []
- for type in PNMFileTypeRegistry.getGlobalPtr().getTypes():
- self.imageExtensions += type.getExtensions()
-
- # Other useful extensions. The .pz extension is implicitly
- # stripped.
-
- # Model files.
- self.modelExtensions = [ 'egg', 'bam' ]
-
- # Text files that are copied (and compressed) to the package
- # with end-of-line conversion.
- self.textExtensions = [ 'prc', 'ptf', 'txt', 'cg', 'sha', 'dc', 'xml' ]
-
- # Binary files that are copied (and compressed) without
- # processing.
- self.binaryExtensions = [ 'ttf', 'TTF', 'mid', 'ico', 'cur' ]
-
- # Files that can have an existence in multiple different
- # packages simultaneously without conflict.
- self.nonuniqueExtensions = [ 'prc' ]
-
- # Files that represent an executable or shared library.
- if self.platform.startswith('win'):
- self.executableExtensions = [ 'dll', 'pyd', 'exe' ]
- elif self.platform.startswith('osx'):
- self.executableExtensions = [ 'so', 'dylib' ]
- else:
- self.executableExtensions = [ 'so' ]
-
- # Files that represent a Windows "manifest" file. These files
- # must be explicitly extracted to disk so the OS can find
- # them.
- if self.platform.startswith('win'):
- self.manifestExtensions = [ 'manifest' ]
- else:
- self.manifestExtensions = [ ]
-
- # Extensions that are automatically remapped by convention.
- self.remapExtensions = {}
- if self.platform.startswith('win'):
- pass
- elif self.platform.startswith('osx'):
- self.remapExtensions = {
- 'dll' : 'dylib',
- 'pyd' : 'so',
- 'exe' : ''
- }
- else:
- self.remapExtensions = {
- 'dll' : 'so',
- 'pyd' : 'so',
- 'exe' : ''
- }
-
- # Files that should be extracted to disk.
- self.extractExtensions = self.executableExtensions[:] + self.manifestExtensions[:] + [ 'ico', 'cur' ]
-
- # Files that indicate a platform dependency.
- self.platformSpecificExtensions = self.executableExtensions[:]
-
- # Binary files that are considered uncompressible, and are
- # copied without compression.
- self.uncompressibleExtensions = [ 'mp3', 'ogg', 'ogv', 'wav', 'rml', 'rcss', 'otf' ]
- # wav files are compressible, but p3openal_audio won't load
- # them compressed.
- # rml, rcss and otf files must be added here because
- # libRocket wants to be able to seek in these files.
-
- # Files which are not to be processed further, but which
- # should be added exactly byte-for-byte as they are.
- self.unprocessedExtensions = []
-
- # Files for which warnings should be suppressed when they are
- # not handled by dir()
- self.suppressWarningForExtensions = ['', 'pyc', 'pyo',
- 'p3d', 'pdef',
- 'c', 'C', 'cxx', 'cpp', 'h', 'H',
- 'hpp', 'pp', 'I', 'pem', 'p12', 'crt',
- 'o', 'obj', 'a', 'lib', 'bc', 'll']
-
- # System files that should never be packaged. For
- # case-insensitive filesystems (like Windows and OSX), put the
- # lowercase filename here. Case-sensitive filesystems should
- # use the correct case.
- self.excludeSystemFiles = [
- 'kernel32.dll', 'user32.dll', 'wsock32.dll', 'ws2_32.dll',
- 'advapi32.dll', 'opengl32.dll', 'glu32.dll', 'gdi32.dll',
- 'shell32.dll', 'ntdll.dll', 'ws2help.dll', 'rpcrt4.dll',
- 'imm32.dll', 'ddraw.dll', 'shlwapi.dll', 'secur32.dll',
- 'dciman32.dll', 'comdlg32.dll', 'comctl32.dll', 'ole32.dll',
- 'oleaut32.dll', 'gdiplus.dll', 'winmm.dll', 'iphlpapi.dll',
- 'msvcrt.dll', 'kernelbase.dll', 'msimg32.dll', 'msacm32.dll',
-
- 'libsystem.b.dylib', 'libmathcommon.a.dylib', 'libmx.a.dylib',
- 'libstdc++.6.dylib', 'libobjc.a.dylib', 'libauto.dylib',
- ]
-
- # As above, but with filename globbing to catch a range of
- # filenames.
- self.excludeSystemGlobs = [
- GlobPattern('d3dx9_*.dll'),
- GlobPattern('api-ms-win-*.dll'),
-
- GlobPattern('libGL.so*'),
- GlobPattern('libGLU.so*'),
- GlobPattern('libGLcore.so*'),
- GlobPattern('libGLES*.so*'),
- GlobPattern('libEGL.so*'),
- GlobPattern('libX11.so*'),
- GlobPattern('libXau.so*'),
- GlobPattern('libXdmcp.so*'),
- GlobPattern('libxcb*.so*'),
- GlobPattern('libc.so*'),
- GlobPattern('libgcc_s.so*'),
- GlobPattern('libdl.so*'),
- GlobPattern('libm.so*'),
- GlobPattern('libnvidia*.so*'),
- GlobPattern('libpthread.so*'),
- GlobPattern('libthr.so*'),
- GlobPattern('ld-linux.so*'),
- GlobPattern('ld-linux-*.so*'),
- GlobPattern('librt.so*'),
- ]
-
- # A Loader for loading models.
- self.loader = Loader.Loader(self)
- self.sfxManagerList = None
- self.musicManager = None
-
- # These options will be used when loading models and textures. By
- # default we don't load textures beyond the header and don't store
- # models in the RAM cache in order to conserve on memory usage.
- opts = LoaderOptions()
- opts.setFlags(opts.getFlags() | LoaderOptions.LFNoRamCache)
- opts.setTextureFlags(opts.getTextureFlags() & ~LoaderOptions.TFPreload)
- self.loaderOptions = opts
-
- # This is filled in during readPackageDef().
- self.packageList = []
-
- # A table of all known packages by name.
- self.packages = {}
-
- # A list of PackageEntry objects read from the contents.xml
- # file.
- self.contents = {}
-
- def loadLdconfigCache(self):
- """ On GNU/Linux, runs ldconfig -p to find out where all the
- libraries on the system are located. Assumes that the platform
- has already been set. """
-
- if not os.path.isfile('/sbin/ldconfig'):
- return False
-
- handle = subprocess.Popen(['/sbin/ldconfig', '-p'], stdout=subprocess.PIPE, universal_newlines=True)
- out, err = handle.communicate()
-
- if handle.returncode != 0:
- self.notify.warning("/sbin/ldconfig -p returned code %d" %(handle.returncode))
- return False
-
- for line in out.splitlines():
- if '=>' not in line:
- continue
-
- prefix, location = line.rsplit('=>', 1)
- prefix = prefix.strip()
- location = location.strip()
-
- if not location or not prefix or ' ' not in prefix:
- self.notify.warning("Ignoring malformed ldconfig -p line: " + line)
- continue
-
- lib, opts = prefix.split(' ', 1)
- if ('x86-64' in opts) != self.platform.endswith('_amd64'):
- # This entry isn't meant for our architecture. I think
- # x86-64 is the only platform where ldconfig supplies
- # this extra arch string.
- continue
-
- self.libraryCache[lib] = Filename.fromOsSpecific(location)
-
- return True
-
- def resolveLibrary(self, filename):
- """ Resolves the given shared library filename along the executable path,
- or by cross-referencing it with the library cache. """
-
- path = str(filename)
-
- if path in self.libraryCache:
- filename.setFullpath(self.libraryCache[path].getFullpath())
- return True
-
- if filename.resolveFilename(self.executablePath):
- self.libraryCache[path] = Filename(filename)
- return True
-
- return False
-
- def setPlatform(self, platform = None):
- """ Sets the platform that this Packager will compute for. On
- OSX, this can be used to specify the particular architecture
- we are building; on other platforms, it is probably a mistake
- to set this.
-
- You should call this before doing anything else with the
- Packager. It's even better to pass the platform string to the
- constructor. """
-
- self.platform = platform or PandaSystem.getPlatform()
-
- # OSX uses this "arch" string for the otool and lipo commands.
- self.arch = None
- if self.platform.startswith('osx_'):
- self.arch = self.platform[4:]
-
-
- def setHost(self, host, downloadUrl = None,
- descriptiveName = None, hostDir = None,
- mirrors = None):
- """ Specifies the URL that will ultimately host these
- contents. """
-
- if not self.ignoreSetHost:
- self.host = host
-
- self.addHost(host, downloadUrl = downloadUrl,
- descriptiveName = descriptiveName, hostDir = hostDir,
- mirrors = mirrors)
-
- def addHost(self, host, downloadUrl = None, descriptiveName = None,
- hostDir = None, mirrors = None):
- """ Adds a host to the list of known download hosts. This
- information will be written into any p3d files that reference
- this host; this can be used to pre-define the possible mirrors
- for a given host, for instance. Returns the newly-created
- HostEntry object."""
-
- scheme = URLSpec(host).getScheme()
- if scheme == 'https' and downloadUrl is None:
- # If we specified an SSL-protected host URL, but no
- # explicit download URL, then assume the download URL is
- # the same, over cleartext.
- url = URLSpec(host)
- url.setScheme('http')
- downloadUrl = url.getUrl()
-
- he = self.hosts.get(host, None)
- if he is None:
- # Define a new host entry
- he = self.HostEntry(host, downloadUrl = downloadUrl,
- descriptiveName = descriptiveName,
- hostDir = hostDir, mirrors = mirrors)
- self.hosts[host] = he
- else:
- # Update an existing host entry
- if downloadUrl is not None:
- he.downloadUrl = downloadUrl
- if descriptiveName is not None:
- he.descriptiveName = descriptiveName
- if hostDir is not None:
- he.hostDir = hostDir
- if mirrors is not None:
- he.mirrors = mirrors
-
- return he
-
- def addAltHost(self, keyword, altHost, origHost = None,
- downloadUrl = None, descriptiveName = None,
- hostDir = None, mirrors = None):
- """ Adds an alternate host to any already-known host. This
- defines an alternate server that may be contacted, if
- specified on the HTML page, which hosts a different version of
- the server's contents. (This is different from a mirror,
- which hosts an identical version of the server's contents.)
- """
-
- if not origHost:
- origHost = self.host
-
- self.addHost(altHost, downloadUrl = downloadUrl,
- descriptiveName = descriptiveName, hostDir = hostDir,
- mirrors = mirrors)
- he = self.addHost(origHost)
- he.altHosts[keyword] = altHost
-
- def addWindowsSearchPath(self, searchPath, varname):
- """ Expands $varname, interpreting as a Windows-style search
- path, and adds its contents to the indicated DSearchPath. """
-
- path = ExecutionEnvironment.getEnvironmentVariable(varname)
- if len(path) == 0:
- if varname not in os.environ:
- return
- path = os.environ[varname]
- for dirname in path.split(';'):
- dirname = Filename.fromOsSpecific(dirname)
- if dirname.makeTrueCase():
- searchPath.appendDirectory(dirname)
-
- def addPosixSearchPath(self, searchPath, varname):
- """ Expands $varname, interpreting as a Posix-style search
- path, and adds its contents to the indicated DSearchPath. """
-
- path = ExecutionEnvironment.getEnvironmentVariable(varname)
- if len(path) == 0:
- if varname not in os.environ:
- return
- path = os.environ[varname]
- for dirname in path.split(':'):
- dirname = Filename.fromOsSpecific(dirname)
- if dirname.makeTrueCase():
- searchPath.appendDirectory(dirname)
-
- def _ensureExtensions(self):
- self.knownExtensions = \
- self.imageExtensions + \
- self.modelExtensions + \
- self.textExtensions + \
- self.binaryExtensions + \
- self.uncompressibleExtensions + \
- self.unprocessedExtensions
-
- def setup(self):
- """ Call this method to initialize the class after filling in
- some of the values in the constructor. """
-
- self._ensureExtensions()
-
- self.currentPackage = None
-
- if self.installDir:
- # If we were given an install directory, we can build
- # packages as well as plain p3d files, and it all goes
- # into the specified directory.
- self.p3dInstallDir = self.installDir
- self.allowPackages = True
- else:
- # If we don't have an actual install directory, we can
- # only build p3d files, and we drop them into the current
- # directory.
- self.p3dInstallDir = '.'
- self.allowPackages = False
-
- if not PandaSystem.getPackageVersionString() or not PandaSystem.getPackageHostUrl():
- raise PackagerError('This script must be run using a version of Panda3D that has been built\nfor distribution. Try using ppackage.p3d or packp3d.p3d instead.\nIf you are running this script for development purposes, you may also\nset the Config variable panda-package-host-url to the URL you expect\nto download these contents from (for instance, a file:// URL).')
-
- self.readContentsFile()
-
- def close(self):
- """ Called after reading all of the package def files, this
- performs any final cleanup appropriate. """
-
- self.writeContentsFile()
-
- def buildPatches(self, packages):
- """ Call this after calling close(), to build patches for the
- indicated packages. """
-
- # We quietly ignore any p3d applications or solo packages
- # passed in the packages list; we only build patches for
- # actual Multifile-based packages.
- packageNames = []
- for package in packages:
- if not package.p3dApplication and not package.solo:
- packageNames.append(package.packageName)
-
- if packageNames:
- from .PatchMaker import PatchMaker
- pm = PatchMaker(self.installDir)
- pm.buildPatches(packageNames = packageNames)
-
- def readPackageDef(self, packageDef, packageNames = None):
- """ Reads the named .pdef file and constructs the named
- packages, or all packages if packageNames is None. Raises an
- exception if the pdef file is invalid. Returns the list of
- packages constructed. """
-
- self.notify.info('Reading %s' % (packageDef))
-
- # We use exec to "read" the .pdef file. This has the nice
- # side-effect that the user can put arbitrary Python code in
- # there to control conditional execution, and such.
-
- # Set up the namespace dictionary for exec.
- globals = {}
- globals['__name__'] = packageDef.getBasenameWoExtension()
- globals['__dir__'] = Filename(packageDef.getDirname()).toOsSpecific()
- globals['__file__'] = packageDef.toOsSpecific()
- globals['packageDef'] = packageDef
-
- globals['platform'] = self.platform
- globals['packager'] = self
-
- # We'll stuff all of the predefined functions, and the
- # predefined classes, in the global dictionary, so the pdef
- # file can reference them.
-
- # By convention, the existence of a method of this class named
- # do_foo(self) is sufficient to define a pdef method call
- # foo().
- for methodName in list(self.__class__.__dict__.keys()):
- if methodName.startswith('do_'):
- name = methodName[3:]
- c = func_closure(name)
- globals[name] = c.generic_func
-
- globals['p3d'] = class_p3d
- globals['package'] = class_package
- globals['solo'] = class_solo
-
- # Now exec the pdef file. Assuming there are no syntax
- # errors, and that the pdef file doesn't contain any really
- # crazy Python code, all this will do is fill in the
- # '__statements' list in the module scope.
- fn = packageDef.toOsSpecific()
- f = open(fn)
- code = compile(f.read(), fn, 'exec')
- f.close()
-
- # It appears that having a separate globals and locals
- # dictionary causes problems with resolving symbols within a
- # class scope. So, we just use one dictionary, the globals.
- exec(code, globals)
-
- packages = []
-
- # Now iterate through the statements and operate on them.
- statements = globals.get('__statements', [])
- if not statements:
- self.notify.info("No packages defined.")
-
- try:
- for (lineno, stype, name, args, kw) in statements:
- if stype == 'class':
- if packageNames is None or name in packageNames:
- classDef = globals[name]
- p3dApplication = (class_p3d in classDef.__bases__)
- solo = (class_solo in classDef.__bases__)
- self.beginPackage(name, p3dApplication = p3dApplication,
- solo = solo)
- statements = classDef.__dict__.get('__statements', [])
- if not statements:
- self.notify.info("No files added to %s" % (name))
- for (lineno, stype, sname, args, kw) in statements:
- if stype == 'class':
- raise PackagerError('Nested classes not allowed')
- self.__evalFunc(sname, args, kw)
- package = self.endPackage()
- if package is not None:
- packages.append(package)
- elif packageNames is not None:
- # If the name is explicitly specified, this means
- # we should abort if the package faild to construct.
- raise PackagerError('Failed to construct %s' % name)
- else:
- self.__evalFunc(name, args, kw)
- except PackagerError:
- # Append the line number and file name to the exception
- # error message.
- inst = sys.exc_info()[1]
- if not inst.args:
- inst.args = ('Error',)
-
- inst.args = (inst.args[0] + ' on line %s of %s' % (lineno, packageDef),)
- raise
-
- return packages
-
- def __evalFunc(self, name, args, kw):
- """ This is called from readPackageDef(), above, to call the
- function do_name(*args, **kw), as extracted from the pdef
- file. """
-
- funcname = 'do_%s' % (name)
- func = getattr(self, funcname)
- try:
- func(*args, **kw)
- except OutsideOfPackageError:
- message = '%s encountered outside of package definition' % (name)
- raise OutsideOfPackageError(message)
-
- def __expandTabs(self, line, tabWidth = 8):
- """ Expands tab characters in the line to 8 spaces. """
- p = 0
- while p < len(line):
- if line[p] == '\t':
- # Expand a tab.
- nextStop = ((p + tabWidth) / tabWidth) * tabWidth
- numSpaces = nextStop - p
- line = line[:p] + ' ' * numSpaces + line[p + 1:]
- p = nextStop
- else:
- p += 1
-
- return line
-
- def __countLeadingWhitespace(self, line):
- """ Returns the number of leading whitespace characters in the
- line. """
-
- line = self.__expandTabs(line)
- return len(line) - len(line.lstrip())
-
- def __stripLeadingWhitespace(self, line, whitespaceCount):
- """ Removes the indicated number of whitespace characters, but
- no more. """
-
- line = self.__expandTabs(line)
- line = line[:whitespaceCount].lstrip() + line[whitespaceCount:]
- return line
-
- def __parseArgs(self, words, argList):
- args = {}
-
- while len(words) > 1:
- arg = words[-1]
- if '=' not in arg:
- return args
-
- parameter, value = arg.split('=', 1)
- parameter = parameter.strip()
- value = value.strip()
- if parameter not in argList:
- message = 'Unknown parameter %s' % (parameter)
- raise PackagerError(message)
- if parameter in args:
- message = 'Duplicate parameter %s' % (parameter)
- raise PackagerError(message)
-
- args[parameter] = value
-
- del words[-1]
-
-
- def beginPackage(self, packageName, p3dApplication = False,
- solo = False):
- """ Begins a new package specification. packageName is the
- basename of the package. Follow this with a number of calls
- to file() etc., and close the package with endPackage(). """
-
- if self.currentPackage:
- raise PackagerError('unclosed endPackage %s' % (self.currentPackage.packageName))
-
- package = self.Package(packageName, self)
- self.currentPackage = package
-
- package.p3dApplication = p3dApplication
- package.solo = solo
-
- if not package.p3dApplication and not self.allowPackages:
- message = 'Cannot generate packages without an installDir; use -i'
- raise PackagerError(message)
-
-
- def endPackage(self):
- """ Closes the current package specification. This actually
- generates the package file. Returns the finished package,
- or None if the package failed to close (e.g. missing files). """
-
- if not self.currentPackage:
- raise PackagerError('unmatched endPackage')
-
- package = self.currentPackage
- package.signParams += self.signParams[:]
-
- self.currentPackage = None
- if not package.close():
- return None
-
- self.packageList.append(package)
- self.packages[(package.packageName, package.platform, package.version)] = package
- self.currentPackage = None
-
- return package
-
- def findPackage(self, packageName, platform = None, version = None,
- host = None, requires = None):
- """ Searches for the named package from a previous publish
- operation along the install search path.
-
- If requires is not None, it is a list of Package objects that
- are already required. The new Package object must be
- compatible with the existing Packages, or an error is
- returned. This is also useful for determining the appropriate
- package version to choose when a version is not specified.
-
- Returns the Package object, or None if the package cannot be
- located. """
-
- # Is it a package we already have resident?
- package = self.packages.get((packageName, platform or self.platform, version, host), None)
- if package:
- return package
-
- # Look on the searchlist.
- for dirname in self.installSearch:
- package = self.__scanPackageDir(dirname, packageName, platform or self.platform, version, host, requires = requires)
- if not package:
- package = self.__scanPackageDir(dirname, packageName, platform, version, host, requires = requires)
-
- if package and host and package.host != host:
- # Wrong host.
- package = None
-
- if package:
- break
-
- if not package:
- # Query the indicated host.
- package = self.__findPackageOnHost(packageName, platform or self.platform, version or None, host, requires = requires)
- if not package:
- package = self.__findPackageOnHost(packageName, platform, version, host, requires = requires)
-
- if package:
- package = self.packages.setdefault((package.packageName, package.platform, package.version, package.host), package)
- self.packages[(packageName, platform or self.platform, version, host)] = package
- return package
-
- return None
-
- def __scanPackageDir(self, rootDir, packageName, platform, version,
- host, requires = None):
- """ Scans a directory on disk, looking for *.import.xml files
- that match the indicated packageName and optional version. If a
- suitable xml file is found, reads it and returns the assocated
- Package definition.
-
- If a version is not specified, and multiple versions are
- available, the highest-numbered version that matches will be
- selected.
- """
-
- packages = []
-
- if version:
- # A specific version package.
- versionList = [version]
- else:
- # An unversioned package, or any old version.
- versionList = [None, '*']
-
- for version in versionList:
- packageDir = Filename(rootDir, packageName)
- basename = packageName
-
- if version:
- # A specific or nonspecific version package.
- packageDir = Filename(packageDir, version)
- basename += '.%s' % (version)
-
- if platform:
- packageDir = Filename(packageDir, platform)
- basename += '.%s' % (platform)
-
- # Actually, the host means little for this search, since we're
- # only looking in a local directory at this point.
-
- basename += '.import.xml'
- filename = Filename(packageDir, basename)
- filelist = glob.glob(filename.toOsSpecific())
- if not filelist:
- # It doesn't exist in the nested directory; try the root
- # directory.
- filename = Filename(rootDir, basename)
- filelist = glob.glob(filename.toOsSpecific())
-
- for file in filelist:
- package = self.__readPackageImportDescFile(Filename.fromOsSpecific(file))
- packages.append(package)
-
- self.__sortImportPackages(packages)
- for package in packages:
- if package and self.__packageIsValid(package, requires, platform):
- return package
-
- return None
-
- def __findPackageOnHost(self, packageName, platform, version, hostUrl, requires = None):
- appRunner = AppRunnerGlobal.appRunner
-
- # Make sure we have a fresh version of the contents file.
- host = self.__getHostInfo(hostUrl)
- if not host.downloadContentsFile(self.http):
- return None
-
- packageInfos = []
- packageInfo = host.getPackage(packageName, version, platform = platform)
- if not packageInfo and not version:
- # No explicit version is specified, first fallback: look
- # for the compiled-in version.
- packageInfo = host.getPackage(packageName, PandaSystem.getPackageVersionString(), platform = platform)
-
- if not packageInfo and not version:
- # No explicit version is specified, second fallback: get
- # the highest-numbered version available.
- packageInfos = host.getPackages(packageName, platform = platform)
- self.__sortPackageInfos(packageInfos)
-
- if packageInfo and not packageInfos:
- packageInfos = [packageInfo]
-
- for packageInfo in packageInfos:
- if not packageInfo or not packageInfo.importDescFile:
- continue
-
- # Now we've retrieved a PackageInfo. Get the import desc file
- # from it.
- if host.hostDir:
- filename = Filename(host.hostDir, 'imports/' + packageInfo.importDescFile.basename)
- else:
- # We're not running in the packaged environment, so download
- # to a temporary file instead of the host directory.
- filename = Filename.temporary('', 'import_' + packageInfo.importDescFile.basename, '.xml')
-
- if not host.freshenFile(self.http, packageInfo.importDescFile, filename):
- self.notify.error("Couldn't download import file.")
- continue
-
- # Now that we have the import desc file, use it to load one of
- # our Package objects.
- package = self.Package('', self)
- success = package.readImportDescFile(filename)
-
- if not host.hostDir:
- # Don't forget to delete the temporary file we created.
- filename.unlink()
-
- if success and self.__packageIsValid(package, requires, platform):
- return package
-
- # Couldn't find a suitable package.
- return None
-
- def __getHostInfo(self, hostUrl = None):
- """ This shadows appRunner.getHost(), for the purpose of running
- outside the packaged environment. """
-
- if not hostUrl:
- hostUrl = PandaSystem.getPackageHostUrl()
-
- if AppRunnerGlobal.appRunner:
- return AppRunnerGlobal.appRunner.getHost(hostUrl)
-
- host = self.__hostInfos.get(hostUrl, None)
- if not host:
- host = HostInfo(hostUrl)
- self.__hostInfos[hostUrl] = host
- return host
-
- def __sortImportPackages(self, packages):
- """ Given a list of Packages read from *.import.xml filenames,
- sorts them in reverse order by version, so that the
- highest-numbered versions appear first in the list. """
-
- tuples = []
- for package in packages:
- version = self.__makeVersionTuple(package.version)
- tuples.append((version, package))
- tuples.sort(reverse = True)
-
- return [t[1] for t in tuples]
-
- def __sortPackageInfos(self, packages):
- """ Given a list of PackageInfos retrieved from a Host, sorts
- them in reverse order by version, so that the highest-numbered
- versions appear first in the list. """
-
- tuples = []
- for package in packages:
- version = self.__makeVersionTuple(package.packageVersion)
- tuples.append((version, package))
- tuples.sort(reverse = True)
-
- return [t[1] for t in tuples]
-
- def __makeVersionTuple(self, version):
- """ Converts a version string into a tuple for sorting, by
- separating out numbers into separate numeric fields, so that
- version numbers sort numerically where appropriate. """
-
- if not version:
- return ('',)
-
- words = []
- p = 0
- while p < len(version):
- # Scan to the first digit.
- w = ''
- while p < len(version) and not version[p].isdigit():
- w += version[p]
- p += 1
- words.append(w)
-
- # Scan to the end of the string of digits.
- w = ''
- while p < len(version) and version[p].isdigit():
- w += version[p]
- p += 1
- if w:
- words.append(int(w))
-
- return tuple(words)
-
- def __packageIsValid(self, package, requires, platform):
- """ Returns true if the package is valid, meaning it can be
- imported without conflicts with existing packages already
- required (such as different versions of panda3d). """
-
- if package.platform and package.platform != platform:
- # Incorrect platform.
- return False
-
- if not requires:
- # No other restrictions.
- return True
-
- # Really, we only check the panda3d package. The other
- # packages will list this as a dependency, and this is all
- # that matters.
-
- panda1 = self.__findPackageInRequires('panda3d', [package] + package.requires)
- panda2 = self.__findPackageInRequires('panda3d', requires)
-
- if not panda1 or not panda2:
- return True
-
- if panda1.version == panda2.version:
- return True
-
- print('Rejecting package %s, version "%s": depends on %s, version "%s" instead of version "%s"' % (
- package.packageName, package.version,
- panda1.packageName, panda1.version, panda2.version))
- return False
-
- def __findPackageInRequires(self, packageName, list):
- """ Returns the first package with the indicated name in the
- list of packages, or in the list of packages required by the
- packages in the list. """
-
- for package in list:
- if package.packageName == packageName:
- return package
- p2 = self.__findPackageInRequires(packageName, package.requires)
- if p2:
- return p2
-
- return None
-
- def __readPackageImportDescFile(self, filename):
- """ Reads the named xml file as a Package, and returns it if
- valid, or None otherwise. """
-
- package = self.Package('', self)
- if package.readImportDescFile(filename):
- return package
-
- return None
-
- def do_setVer(self, value):
- """ Sets an explicit set_ver number for the package, as a tuple
- of integers, or as a string of dot-separated integers. """
-
- self.currentPackage.packageSetVer = SeqValue(value)
-
- def do_config(self, **kw):
- """ Called with any number of keyword parameters. For each
- keyword parameter, sets the corresponding p3d config variable
- to the given value. This will be written into the
- p3d_info.xml file at the top of the application, or to the
- package desc file for a package file. """
-
- if not self.currentPackage:
- raise OutsideOfPackageError
-
- for keyword, value in list(kw.items()):
- self.currentPackage.configs[keyword] = value
-
- def do_require(self, *args, **kw):
- """ Indicates a dependency on the named package(s), supplied
- as a name.
-
- Attempts to install this package will implicitly install the
- named package also. Files already included in the named
- package will be omitted from this one when building it. """
-
- self.requirePackagesNamed(args, **kw)
-
- def requirePackagesNamed(self, names, version = None, host = None):
- """ Indicates a dependency on the named package(s), supplied
- as a name.
-
- Attempts to install this package will implicitly install the
- named package also. Files already included in the named
- package will be omitted from this one when building it. """
-
- if not self.currentPackage:
- raise OutsideOfPackageError
-
- for packageName in names:
- # A special case when requiring the "panda3d" package. We
- # supply the version number which we've been compiled with
- # as a default.
- pversion = version
- phost = host
- if packageName == 'panda3d':
- if not pversion:
- pversion = PandaSystem.getPackageVersionString()
- if not phost:
- phost = PandaSystem.getPackageHostUrl()
-
- package = self.findPackage(packageName, version = pversion, host = phost,
- requires = self.currentPackage.requires)
- if not package:
- message = 'Unknown package %s, version "%s"' % (packageName, version)
- self.notify.warning(message)
- self.currentPackage.missingPackages.append((packageName, pversion))
- continue
-
- self.requirePackage(package)
-
- def requirePackage(self, package):
- """ Indicates a dependency on the indicated package, supplied
- as a Package object.
-
- Attempts to install this package will implicitly install the
- named package also. Files already included in the named
- package will be omitted from this one. """
-
- if not self.currentPackage:
- raise OutsideOfPackageError
-
- # A special case when requiring the "panda3d" package. We
- # complain if the version number doesn't match what we've been
- # compiled with.
- if package.packageName == 'panda3d':
- if package.version != PandaSystem.getPackageVersionString():
- self.notify.warning("Requiring panda3d version %s, which does not match the current build of Panda, which is version %s." % (package.version, PandaSystem.getPackageVersionString()))
- elif package.host != PandaSystem.getPackageHostUrl():
- self.notify.warning("Requiring panda3d host %s, which does not match the current build of Panda, which is host %s." % (package.host, PandaSystem.getPackageHostUrl()))
-
- self.currentPackage.requirePackage(package)
-
- def do_module(self, *args, **kw):
- """ Adds the indicated Python module(s) to the current package. """
- self.addModule(args, **kw)
-
- def addModule(self, moduleNames, newName = None, filename = None, required = False):
- if not self.currentPackage:
- raise OutsideOfPackageError
-
- if (newName or filename) and len(moduleNames) != 1:
- raise PackagerError('Cannot specify newName with multiple modules')
-
- if required:
- self.currentPackage.requiredModules += moduleNames
-
- for moduleName in moduleNames:
- self.currentPackage.freezer.addModule(moduleName, newName = newName, filename = filename)
-
- def do_excludeModule(self, *args):
- """ Marks the indicated Python module as not to be included. """
-
- if not self.currentPackage:
- raise OutsideOfPackageError
-
- for moduleName in args:
- self.currentPackage.freezer.excludeModule(moduleName)
-
- def do_main(self, filename):
- """ Includes the indicated file as __main__ module of the application.
- Also updates mainModule to point to this module. """
-
- self.addModule(['__main__'], '__main__', filename, required = True)
- self.currentPackage.mainModule = ('__main__', '__main__')
-
- def do_mainModule(self, moduleName, newName = None, filename = None):
- """ Names the indicated module as the "main" module of the
- application or exe. In most cases, you will want to use main()
- instead. """
-
- if not self.currentPackage:
- raise OutsideOfPackageError
-
- if self.currentPackage.mainModule and self.currentPackage.mainModule[0] != moduleName:
- self.notify.warning("Replacing mainModule %s with %s" % (
- self.currentPackage.mainModule[0], moduleName))
-
- if not newName:
- newName = moduleName
-
- if filename:
- filename = Filename(filename)
- newFilename = Filename('/'.join(moduleName.split('.')))
- newFilename.setExtension(filename.getExtension())
- self.currentPackage.addFile(
- filename, newName = str(newFilename),
- explicit = True, extract = True, required = True)
-
- self.currentPackage.mainModule = (moduleName, newName)
-
- def do_sign(self, certificate, chain = None, pkey = None, password = None):
- """ Signs the resulting p3d file (or package multifile) with
- the indicated certificate. If needed, the chain file should
- contain the list of additional certificate authorities needed
- to validate the signing certificate. The pkey file should
- contain the private key.
-
- It is also legal for the certificate file to contain the chain
- and private key embedded within it.
-
- If the private key is encrypted, the password should be
- supplied. """
-
- self.currentPackage.signParams.append((certificate, chain, pkey, password))
-
- def do_setupPanda3D(self, p3dpythonName=None, p3dpythonwName=None):
- """ A special convenience command that adds the minimum
- startup modules for a panda3d package, intended for developers
- producing their own custom panda3d for download. Should be
- called before any other Python modules are named. """
-
- # This module and all its dependencies come frozen into p3dpython.
- # We should mark them as having already been added so that we don't
- # add them again to the Multifile.
- self.do_module('direct.showbase.VFSImporter')
- self.currentPackage.freezer.done(addStartupModules=True)
- self.currentPackage.freezer.writeCode(None)
- self.currentPackage.addExtensionModules()
- self.currentPackage.freezer.reset()
-
- self.do_file('panda3d/core.pyd', newDir='panda3d')
-
- # This is the key Python module that is imported at runtime to
- # start an application running.
- self.do_module('direct.p3d.AppRunner')
-
- # This is the main program that drives the runtime Python. It
- # is responsible for importing direct.p3d.AppRunner to start an
- # application running. The program comes in two parts: an
- # executable, and an associated dynamic library. Note that the
- # .exe and .dll extensions are automatically replaced with the
- # appropriate platform-specific extensions.
-
- if self.platform.startswith('osx'):
- # On Mac, we package up a P3DPython.app bundle. This
- # includes specifications in the plist file to avoid
- # creating a dock icon and stuff.
-
- resources = []
-
- # Find p3dpython.plist in the direct source tree.
- import direct
- plist = Filename(direct.__path__[0], 'plugin/p3dpython.plist')
-
-## # Find panda3d.icns in the models tree.
-## filename = Filename('plugin_images/panda3d.icns')
-## found = filename.resolveFilename(getModelPath().getValue())
-## if not found:
-## found = filename.resolveFilename("models")
-## if found:
-## resources.append(filename)
-
- self.do_makeBundle('P3DPython.app', plist, executable = 'p3dpython',
- resources = resources, dependencyDir = '')
-
- else:
- # Anywhere else, we just ship the executable file p3dpython.exe.
- if p3dpythonName is None:
- p3dpythonName = 'p3dpython'
- else:
- self.do_config(p3dpython_name=p3dpythonName)
-
- if self.platform.startswith('win'):
- self.do_file('p3dpython.exe', newName=p3dpythonName+'.exe')
- else:
- self.do_file('p3dpython.exe', newName=p3dpythonName)
-
- # The "Windows" executable appends a 'w' to whatever name is used
- # above, unless an override name is explicitly specified.
- if self.platform.startswith('win'):
- if p3dpythonwName is None:
- p3dpythonwName = p3dpythonName+'w'
- else:
- self.do_config(p3dpythonw_name=p3dpythonwName)
-
- if self.platform.startswith('win'):
- self.do_file('p3dpythonw.exe', newName=p3dpythonwName+'.exe')
- else:
- self.do_file('p3dpythonw.exe', newName=p3dpythonwName)
-
- self.do_file('libp3dpython.dll')
-
- def do_freeze(self, filename, compileToExe = False):
- """ Freezes all of the current Python code into either an
- executable (if compileToExe is true) or a dynamic library (if
- it is false). The resulting compiled binary is added to the
- current package under the indicated filename. The filename
- should not include an extension; that will be added. """
-
- if not self.currentPackage:
- raise OutsideOfPackageError
-
- package = self.currentPackage
- freezer = package.freezer
-
- if package.mainModule and not compileToExe:
- self.notify.warning("Ignoring main_module for dll %s" % (filename))
- package.mainModule = None
- if not package.mainModule and compileToExe:
- message = "No main_module specified for exe %s" % (filename)
- raise PackagerError(message)
-
- if package.mainModule:
- moduleName, newName = package.mainModule
- if compileToExe:
- # If we're producing an exe, the main module must
- # be called "__main__".
- newName = '__main__'
- package.mainModule = (moduleName, newName)
-
- if newName not in freezer.modules:
- freezer.addModule(moduleName, newName = newName)
- else:
- freezer.modules[newName] = freezer.modules[moduleName]
- freezer.done(addStartupModules = compileToExe)
-
- dirname = ''
- basename = filename
- if '/' in basename:
- dirname, basename = filename.rsplit('/', 1)
- dirname += '/'
-
- basename = freezer.generateCode(basename, compileToExe = compileToExe)
-
- package.addFile(Filename(basename), newName = dirname + basename,
- deleteTemp = True, explicit = True, extract = True)
- package.addExtensionModules()
- if not package.platform:
- package.platform = self.platform
-
- # Reset the freezer for more Python files.
- freezer.reset()
- package.mainModule = None
-
- def do_makeBundle(self, bundleName, plist, executable = None,
- resources = None, dependencyDir = None):
- """ Constructs a minimal OSX "bundle" consisting of an
- executable and a plist file, with optional resource files
- (such as icons), and adds it to the package under the given
- name. """
-
- contents = bundleName + '/Contents'
-
- self.addFiles([plist], newName = contents + '/Info.plist',
- extract = True)
- if executable:
- basename = Filename(executable).getBasename()
- self.addFiles([executable], newName = contents + '/MacOS/' + basename,
- extract = True, executable = True, dependencyDir = dependencyDir)
- if resources:
- self.addFiles(resources, newDir = contents + '/Resources',
- extract = True, dependencyDir = dependencyDir)
-
-
-
- def do_file(self, *args, **kw):
- """ Adds the indicated file or files to the current package.
- See addFiles(). """
-
- self.addFiles(args, **kw)
-
- def addFiles(self, filenames, text = None, newName = None,
- newDir = None, extract = None, executable = None,
- deleteTemp = False, literal = False,
- dependencyDir = None, required = False):
-
- """ Adds the indicated arbitrary files to the current package.
-
- filenames is a list of Filename or string objects, and each
- may include shell globbing characters.
-
- Each file is placed in the named directory, or the toplevel
- directory if no directory is specified.
-
- Certain special behavior is invoked based on the filename
- extension. For instance, .py files may be automatically
- compiled and stored as Python modules.
-
- If newDir is not None, it specifies the directory in which the
- file should be placed. In this case, all files matched by the
- filename expression are placed in the named directory.
-
- If newName is not None, it specifies a new filename. In this
- case, newDir is ignored, and the filename expression must
- match only one file.
-
- If newName and newDir are both None, the file is placed in the
- toplevel directory, regardless of its source directory.
-
- If text is nonempty, it contains the text of the file. In
- this case, the filename is not read, but the supplied text is
- used instead.
-
- If extract is true, the file is explicitly extracted at
- runtime.
-
- If executable is true, the file is marked as an executable
- filename, for special treatment.
-
- If deleteTemp is true, the file is a temporary file and will
- be deleted after its contents are copied to the package.
-
- If literal is true, then the file extension will be respected
- exactly as it appears, and glob characters will not be
- expanded. If this is false, then .dll or .exe files will be
- renamed to .dylib and no extension on OSX (or .so on Linux);
- and glob characters will be expanded.
-
- If required is true, then the file is marked a vital part of
- the package. The package will not be built if this file
- somehow cannot be added to the package.
-
- """
-
- if not self.currentPackage:
- raise OutsideOfPackageError
-
- files = []
- explicit = True
-
- for filename in filenames:
- filename = Filename(filename)
-
- if literal:
- thisFiles = [filename.toOsSpecific()]
-
- else:
- ext = filename.getExtension()
-
- # A special case, since OSX and Linux don't have a
- # standard extension for program files.
- if executable is None and ext == 'exe':
- executable = True
-
- newExt = self.remapExtensions.get(ext, None)
- if newExt is not None:
- filename.setExtension(newExt)
-
- thisFiles = glob.glob(filename.toOsSpecific())
- if not thisFiles:
- thisFiles = [filename.toOsSpecific()]
-
- if newExt == 'dll' or (ext == 'dll' and newExt is None):
- # Go through the dsoFilename interface on Windows,
- # to insert a _d if we are running on a debug
- # build.
- dllFilename = Filename(filename)
- dllFilename.setExtension('so')
- dllFilename = Filename.dsoFilename(str(dllFilename))
- if dllFilename != filename:
- thisFiles = glob.glob(filename.toOsSpecific())
- if not thisFiles:
- # We have to resolve this filename to
- # determine if it's a _d or not.
- if self.resolveLibrary(dllFilename):
- thisFiles = [dllFilename.toOsSpecific()]
- else:
- thisFiles = [filename.toOsSpecific()]
-
- if len(thisFiles) > 1:
- explicit = False
- files += thisFiles
-
- prefix = ''
- if newDir is not None:
- prefix = str(Filename(newDir))
- if prefix and prefix[-1] != '/':
- prefix += '/'
-
- if newName:
- if len(files) != 1:
- message = 'Cannot install multiple files on target filename %s' % (newName)
- raise PackagerError(message)
-
- if text:
- if len(files) != 1:
- message = 'Cannot install text to multiple files'
- raise PackagerError(message)
- if not newName:
- newName = str(filenames[0])
-
- for filename in files:
- filename = Filename.fromOsSpecific(filename)
- basename = filename.getBasename()
- name = newName
- if not name:
- name = prefix + basename
-
- self.currentPackage.addFile(
- filename, newName = name, extract = extract,
- explicit = explicit, executable = executable,
- text = text, deleteTemp = deleteTemp,
- dependencyDir = dependencyDir, required = required)
-
- def do_exclude(self, filename):
- """ Marks the indicated filename as not to be included. The
- filename may include shell globbing characters, and may or may
- not include a dirname. (If it does not include a dirname, it
- refers to any file with the given basename from any
- directory.)"""
-
- if not self.currentPackage:
- raise OutsideOfPackageError
-
- filename = Filename(filename)
- self.currentPackage.excludeFile(filename)
-
-
- def do_includeExtensions(self, executableExtensions = None, extractExtensions = None,
- imageExtensions = None, textExtensions = None,
- uncompressibleExtensions = None, unprocessedExtensions = None,
- suppressWarningForExtensions = None):
- """ Ensure that dir() will include files with the given extensions.
- The extensions should not have '.' prefixes.
-
- All except 'suppressWarningForExtensions' allow the given kinds of files
- to be packaged with their respective semantics (read the source).
-
- 'suppressWarningForExtensions' lists extensions *expected* to be ignored,
- so no warnings will be emitted for them.
- """
- if executableExtensions:
- self.executableExtensions += executableExtensions
-
- if extractExtensions:
- self.extractExtensions += extractExtensions
-
- if imageExtensions:
- self.imageExtensions += imageExtensions
-
- if textExtensions:
- self.textExtensions += textExtensions
-
- if uncompressibleExtensions:
- self.uncompressibleExtensions += uncompressibleExtensions
-
- if unprocessedExtensions:
- self.unprocessedExtensions += unprocessedExtensions
-
- if suppressWarningForExtensions:
- self.suppressWarningForExtensions += suppressWarningForExtensions
-
- self._ensureExtensions()
-
- def do_dir(self, dirname, newDir = None, unprocessed = None):
-
- """ Adds the indicated directory hierarchy to the current
- package. The directory hierarchy is walked recursively, and
- all files that match a known extension are added to the package.
-
- newDir specifies the directory name within the package which
- the contents of the named directory should be installed to.
- If it is omitted, the contents of the named directory are
- installed to the root of the package.
-
- If unprocessed is false (the default), bam files are loaded and
- scanned for textures, and these texture paths within the bam
- files are manipulated to point to the new paths within the
- package. If unprocessed is true, this operation is bypassed,
- and bam files are packed exactly as they are.
- """
-
- if not self.currentPackage:
- raise OutsideOfPackageError
-
- dirname = Filename(dirname)
- if not newDir:
- newDir = ''
-
- # Adding the directory to sys.path is a cheesy way to help the
- # modulefinder find it.
- sys.path.append(dirname.toOsSpecific())
- self.__recurseDir(dirname, newDir, unprocessed = unprocessed)
-
- def __recurseDir(self, filename, newName, unprocessed = None, packageTree = None):
- if filename.isDirectory():
- # It's a directory name. Recurse.
- prefix = newName
- if prefix and prefix[-1] != '/':
- prefix += '/'
-
- # First check if this is a Python package tree. If so, add it
- # implicitly as a module.
- dirList = vfs.scanDirectory(filename)
- for subfile in dirList:
- filename = subfile.getFilename()
- if filename.getBasename() == '__init__.py':
- moduleName = newName.replace("/", ".")
- self.addModule([moduleName], filename=filename)
-
- for subfile in dirList:
- filename = subfile.getFilename()
- self.__recurseDir(filename, prefix + filename.getBasename(),
- unprocessed = unprocessed)
- return
- elif not filename.exists():
- # It doesn't exist. Perhaps it's a virtual file. Ignore it.
- return
-
- # It's a file name. Add it.
- ext = filename.getExtension()
- if ext == 'py':
- self.currentPackage.addFile(filename, newName = newName,
- explicit = False, unprocessed = unprocessed)
- else:
- if ext == 'pz' or ext == 'gz':
- # Strip off an implicit .pz extension.
- newFilename = Filename(filename)
- newFilename.setExtension('')
- newFilename = Filename(str(newFilename))
- ext = newFilename.getExtension()
-
- if ext in self.knownExtensions:
- if ext in self.textExtensions:
- filename.setText()
- else:
- filename.setBinary()
- self.currentPackage.addFile(filename, newName = newName,
- explicit = False, unprocessed = unprocessed)
- elif not ext in self.suppressWarningForExtensions:
- newCount = self.currentPackage.ignoredDirFiles.get(ext, 0) + 1
- self.currentPackage.ignoredDirFiles[ext] = newCount
-
- if self.verbosePrint:
- self.notify.warning("ignoring file %s" % filename)
-
- def readContentsFile(self):
- """ Reads the contents.xml file at the beginning of
- processing. """
-
- self.hosts = {}
- # Since we've blown away the self.hosts map, we have to make
- # sure that our own host at least is added to the map.
- self.addHost(self.host)
-
- self.maxAge = 0
- self.contentsSeq = SeqValue()
- self.contents = {}
- self.contentsChanged = False
-
- if not self.allowPackages:
- # Don't bother.
- return
-
- contentsFilename = Filename(self.installDir, 'contents.xml')
- doc = TiXmlDocument(contentsFilename.toOsSpecific())
- if not doc.LoadFile():
- # Couldn't read file.
- return
-
- xcontents = doc.FirstChildElement('contents')
- if xcontents:
- maxAge = xcontents.Attribute('max_age')
- if maxAge:
- self.maxAge = int(maxAge)
-
- self.contentsSeq.loadXml(xcontents)
-
- xhost = xcontents.FirstChildElement('host')
- if xhost:
- he = self.HostEntry()
- he.loadXml(xhost, self)
- self.hosts[he.url] = he
- self.host = he.url
-
- xpackage = xcontents.FirstChildElement('package')
- while xpackage:
- pe = self.PackageEntry()
- pe.loadXml(xpackage)
- self.contents[pe.getKey()] = pe
- xpackage = xpackage.NextSiblingElement('package')
-
- def writeContentsFile(self):
- """ Rewrites the contents.xml file at the end of
- processing. """
-
- if not self.contentsChanged:
- # No need to rewrite.
- return
-
- contentsFilename = Filename(self.installDir, 'contents.xml')
- doc = TiXmlDocument(contentsFilename.toOsSpecific())
- decl = TiXmlDeclaration("1.0", "utf-8", "")
- doc.InsertEndChild(decl)
-
- xcontents = TiXmlElement('contents')
- if self.maxAge:
- xcontents.SetAttribute('max_age', str(self.maxAge))
-
- self.contentsSeq += 1
- self.contentsSeq.storeXml(xcontents)
-
- if self.host:
- he = self.hosts.get(self.host, None)
- if he:
- xhost = he.makeXml(packager = self)
- xcontents.InsertEndChild(xhost)
-
- contents = sorted(self.contents.items())
- for key, pe in contents:
- xpackage = pe.makeXml()
- xcontents.InsertEndChild(xpackage)
-
- doc.InsertEndChild(xcontents)
- doc.SaveFile()
-
-
-# The following class and function definitions represent a few sneaky
-# Python tricks to allow the pdef syntax to contain the pseudo-Python
-# code they do. These tricks bind the function and class definitions
-# into a bit table as they are parsed from the pdef file, so we can
-# walk through that table later and perform the operations requested
-# in order.
-
-class metaclass_def(type):
- """ A metaclass is invoked by Python when the class definition is
- read, for instance to define a child class. By defining a
- metaclass for class_p3d and class_package, we can get a callback
- when we encounter "class foo(p3d)" in the pdef file. The callback
- actually happens after all of the code within the class scope has
- been parsed first. """
-
- def __new__(self, name, bases, dict):
-
- # At the point of the callback, now, "name" is the name of the
- # class we are instantiating, "bases" is the list of parent
- # classes, and "dict" is the class dictionary we have just
- # parsed.
-
- # If "dict" contains __metaclass__, then we must be parsing
- # class_p3d or class_ppackage, below--skip it. But if it
- # doesn't contain __metaclass__, then we must be parsing
- # "class foo(p3d)" (or whatever) from the pdef file.
-
- if '__metaclass__' not in dict:
- # Get the context in which this class was created
- # (presumably, the module scope) out of the stack frame.
- frame = sys._getframe(1)
- mdict = frame.f_locals
- lineno = frame.f_lineno
-
- # Store the class name on a statements list in that
- # context, so we can later resolve the class names in
- # the order they appeared in the file.
- mdict.setdefault('__statements', []).append((lineno, 'class', name, None, None))
-
- return type.__new__(self, name, bases, dict)
-
-# Define these dynamically to stay compatible with Python 2 and 3.
-class_p3d = metaclass_def(str('class_p3d'), (), {})
-class_package = metaclass_def(str('class_package'), (), {})
-class_solo = metaclass_def(str('class_solo'), (), {})
-
-class func_closure:
-
- """ This class is used to create a closure on the function name,
- and also allows the *args, **kw syntax. In Python, the lambda
- syntax, used with default parameters, is used more often to create
- a closure (that is, a binding of one or more variables into a
- callable object), but that syntax doesn't work with **kw.
- Fortunately, a class method is also a form of a closure, because
- it binds self; and this doesn't have any syntax problems with
- **kw. """
-
- def __init__(self, name):
- self.name = name
-
- def generic_func(self, *args, **kw):
- """ This method is bound to all the functions that might be
- called from the pdef file. It's a special function; when it is
- called, it does nothing but store its name and arguments in the
- caller's local scope, where they can be pulled out later. """
-
- # Get the context in which this function was called (presumably,
- # the class dictionary) out of the stack frame.
- frame = sys._getframe(1)
- cldict = frame.f_locals
- lineno = frame.f_lineno
-
- # Store the function on a statements list in that context, so we
- # can later walk through the function calls for each class.
- cldict.setdefault('__statements', []).append((lineno, 'func', self.name, args, kw))
diff --git a/direct/src/p3d/PatchMaker.py b/direct/src/p3d/PatchMaker.py
deleted file mode 100644
index c1624b3cc1..0000000000
--- a/direct/src/p3d/PatchMaker.py
+++ /dev/null
@@ -1,814 +0,0 @@
-__all__ = ["PatchMaker"]
-
-from direct.p3d.FileSpec import FileSpec
-from direct.p3d.SeqValue import SeqValue
-from panda3d.core import *
-import copy
-
-class PatchMaker:
- """ This class will operate on an existing package install
- directory, as generated by the Packager, and create patchfiles
- between versions as needed. It is also used at runtime, to apply
- the downloaded patches. """
-
- class PackageVersion:
- """ A specific patch version of a package. This is not just
- the package's "version" string; it also corresponds to the
- particular patch version, which increments independently of
- the "version". """
-
- def __init__(self, packageName, platform, version, hostUrl, file):
- self.packageName = packageName
- self.platform = platform
- self.version = version
- self.hostUrl = hostUrl
- self.file = file
- self.printName = None
-
- # The Package object that produces this version, if this
- # is the current, base, or top form, respectively.
- self.packageCurrent = None
- self.packageBase = None
- self.packageTop = None
-
- # A list of patchfiles that can produce this version.
- self.fromPatches = []
-
- # A list of patchfiles that can start from this version.
- self.toPatches = []
-
- # A temporary file for re-creating the archive file for
- # this version.
- self.tempFile = None
-
- def cleanup(self):
- if self.tempFile:
- self.tempFile.unlink()
-
- def getPatchChain(self, startPv, alreadyVisited = []):
- """ Returns a list of patches that, when applied in
- sequence to the indicated PackageVersion object, will
- produce this PackageVersion object. Returns None if no
- chain can be found. """
-
- if self is startPv:
- # We're already here. A zero-length patch chain is
- # therefore the answer.
- return []
- if self in alreadyVisited:
- # We've already been here; this is a loop. Avoid
- # infinite recursion.
- return None
-
- alreadyVisited = alreadyVisited[:]
- alreadyVisited.append(self)
-
- bestPatchChain = None
- for patchfile in self.fromPatches:
- fromPv = patchfile.fromPv
- patchChain = fromPv.getPatchChain(startPv, alreadyVisited)
- if patchChain is not None:
- # There's a path through this patchfile.
- patchChain = patchChain + [patchfile]
- if bestPatchChain is None or len(patchChain) < len(bestPatchChain):
- bestPatchChain = patchChain
-
- # Return the shortest path found, or None if there were no
- # paths found.
- return bestPatchChain
-
- def getRecreateFilePlan(self, alreadyVisited = []):
- """ Returns the tuple (startFile, startPv, plan),
- describing how to recreate the archive file for this
- version. startFile and startPv is the Filename and
- packageVersion of the file to start with, and plan is a
- list of tuples (patchfile, pv), listing the patches to
- apply in sequence, and the packageVersion object
- associated with each patch. Returns (None, None, None) if
- there is no way to recreate this archive file. """
-
- if self.tempFile:
- return (self.tempFile, self, [])
-
- if self in alreadyVisited:
- # We've already been here; this is a loop. Avoid
- # infinite recursion.
- return (None, None, None)
-
- alreadyVisited = alreadyVisited[:]
- alreadyVisited.append(self)
-
- if self.packageCurrent:
- # This PackageVersion instance represents the current
- # version of some package.
- package = self.packageCurrent
- return (Filename(package.packageDir, package.compressedFilename), self, [])
-
- if self.packageBase:
- # This PackageVersion instance represents the base
- # (oldest) version of some package.
- package = self.packageBase
- return (Filename(package.packageDir, package.baseFile.filename + '.pz'), self, [])
-
- # We'll need to re-create the file.
- bestPlan = None
- bestStartFile = None
- bestStartPv = None
- for patchfile in self.fromPatches:
- fromPv = patchfile.fromPv
- startFile, startPv, plan = fromPv.getRecreateFilePlan(alreadyVisited)
- if plan is not None:
- # There's a path through this patchfile.
- plan = plan + [(patchfile, self)]
- if bestPlan is None or len(plan) < len(bestPlan):
- bestPlan = plan
- bestStartFile = startFile
- bestStartPv = startPv
-
- # Return the shortest path found, or None if there were no
- # paths found.
- return (bestStartFile, bestStartPv, bestPlan)
-
- def getFile(self):
- """ Returns the Filename of the archive file associated
- with this version. If the file doesn't actually exist on
- disk, a temporary file will be created. Returns None if
- the file can't be recreated. """
-
- startFile, startPv, plan = self.getRecreateFilePlan()
-
- if startFile.getExtension() in ('pz', 'gz'):
- # If the starting file is compressed, we have to
- # decompress it first.
- assert startPv.tempFile is None
- startPv.tempFile = Filename.temporary('', 'patch_')
- if not decompressFile(startFile, startPv.tempFile):
- # Failure trying to decompress the file.
- return None
- startFile = startPv.tempFile
-
- if not plan:
- # If plan is a zero-length list, we're already
- # here--return startFile. If plan is None, there's no
- # solution, and startFile is None. In either case, we
- # can return startFile.
- return startFile
-
- # If plan is a non-empty list, we have to walk the list to
- # apply the patch plan.
- prevFile = startFile
- for patchfile, pv in plan:
- fromPv = patchfile.fromPv
- patchFilename = Filename(patchfile.package.packageDir, patchfile.file.filename)
- result = self.applyPatch(prevFile, patchFilename)
- if not result:
- # Failure trying to re-create the file.
- return None
-
- pv.tempFile = result
- prevFile = result
-
- # Successfully patched.
- assert pv is self and prevFile is self.tempFile
- return prevFile
-
- def applyPatch(self, origFile, patchFilename):
- """ Applies the named patch to the indicated original
- file, storing the results in a temporary file, and returns
- that temporary Filename. Returns None on failure. """
-
- result = Filename.temporary('', 'patch_')
- p = Patchfile()
- if not p.apply(patchFilename, origFile, result):
- print("Internal patching failed: %s" % (patchFilename))
- return None
-
- return result
-
- def getNext(self, package):
- """ Gets the next patch in the chain towards this
- package. """
- for patch in self.toPatches:
- if patch.packageName == package.packageName and \
- patch.platform == package.platform and \
- patch.version == package.version and \
- patch.hostUrl == package.hostUrl:
- return patch.toPv
-
- return None
-
- class Patchfile:
- """ A single patchfile for a package. """
-
- def __init__(self, package):
- self.package = package
- self.packageName = package.packageName
- self.platform = package.platform
- self.version = package.version
- self.hostUrl = None
-
- # FileSpec for the patchfile itself
- self.file = None
-
- # FileSpec for the package file that the patch is applied to
- self.sourceFile = None
-
- # FileSpec for the package file that the patch generates
- self.targetFile = None
-
- # The PackageVersion corresponding to our sourceFile
- self.fromPv = None
-
- # The PackageVersion corresponding to our targetFile
- self.toPv = None
-
- def getSourceKey(self):
- """ Returns the key for locating the package that this
- patchfile can be applied to. """
- return (self.packageName, self.platform, self.version, self.hostUrl, self.sourceFile)
-
- def getTargetKey(self):
- """ Returns the key for locating the package that this
- patchfile will generate. """
- return (self.packageName, self.platform, self.version, self.hostUrl, self.targetFile)
-
- def fromFile(self, packageDir, patchFilename, sourceFile, targetFile):
- """ Creates the data structures from an existing patchfile
- on disk. """
-
- self.file = FileSpec()
- self.file.fromFile(packageDir, patchFilename)
- self.sourceFile = sourceFile
- self.targetFile = targetFile
-
- def loadXml(self, xpatch):
- """ Reads the data structures from an xml file. """
-
- self.packageName = xpatch.Attribute('name') or self.packageName
- self.platform = xpatch.Attribute('platform') or self.platform
- self.version = xpatch.Attribute('version') or self.version
- self.hostUrl = xpatch.Attribute('host') or self.hostUrl
-
- self.file = FileSpec()
- self.file.loadXml(xpatch)
-
- xsource = xpatch.FirstChildElement('source')
- if xsource:
- self.sourceFile = FileSpec()
- self.sourceFile.loadXml(xsource)
-
- xtarget = xpatch.FirstChildElement('target')
- if xtarget:
- self.targetFile = FileSpec()
- self.targetFile.loadXml(xtarget)
-
- def makeXml(self, package):
- xpatch = TiXmlElement('patch')
-
- if self.packageName != package.packageName:
- xpatch.SetAttribute('name', self.packageName)
- if self.platform != package.platform:
- xpatch.SetAttribute('platform', self.platform)
- if self.version != package.version:
- xpatch.SetAttribute('version', self.version)
- if self.hostUrl != package.hostUrl:
- xpatch.SetAttribute('host', self.hostUrl)
-
- self.file.storeXml(xpatch)
-
- xsource = TiXmlElement('source')
- self.sourceFile.storeMiniXml(xsource)
- xpatch.InsertEndChild(xsource)
-
- xtarget = TiXmlElement('target')
- self.targetFile.storeMiniXml(xtarget)
- xpatch.InsertEndChild(xtarget)
-
- return xpatch
-
- class Package:
- """ This is a particular package. This contains all of the
- information needed to reconstruct the package's desc file. """
-
- def __init__(self, packageDesc, patchMaker, xpackage = None):
- self.packageDir = Filename(patchMaker.installDir, packageDesc.getDirname())
- self.packageDesc = packageDesc
- self.patchMaker = patchMaker
- self.contentsDocPackage = xpackage
- self.patchVersion = 1
- self.currentPv = None
- self.basePv = None
- self.topPv = None
-
- self.packageName = None
- self.platform = None
- self.version = None
- self.hostUrl = None
- self.currentFile = None
- self.baseFile = None
-
- self.doc = None
- self.anyChanges = False
- self.patches = []
-
- def getCurrentKey(self):
- """ Returns the key to locate the current version of this
- package. """
-
- return (self.packageName, self.platform, self.version, self.hostUrl, self.currentFile)
-
- def getBaseKey(self):
- """ Returns the key to locate the "base" or oldest version
- of this package. """
-
- return (self.packageName, self.platform, self.version, self.hostUrl, self.baseFile)
-
- def getTopKey(self):
- """ Returns the key to locate the "top" or newest version
- of this package. """
-
- return (self.packageName, self.platform, self.version, self.hostUrl, self.topFile)
-
- def getGenericKey(self, fileSpec):
- """ Returns the key that has the indicated hash. """
- return (self.packageName, self.platform, self.version, self.hostUrl, fileSpec)
-
- def readDescFile(self, doProcessing = False):
- """ Reads the existing package.xml file and stores it in
- this class for later rewriting. if doProcessing is true,
- it may massage the file and the directory contents in
- preparation for building patches. Returns true on
- success, false on failure. """
-
- self.anyChanges = False
-
- packageDescFullpath = Filename(self.patchMaker.installDir, self.packageDesc)
- self.doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
- if not self.doc.LoadFile():
- print("Couldn't read %s" % (packageDescFullpath))
- return False
-
- xpackage = self.doc.FirstChildElement('package')
- if not xpackage:
- return False
- self.packageName = xpackage.Attribute('name')
- self.platform = xpackage.Attribute('platform')
- self.version = xpackage.Attribute('version')
-
- # All packages we defined in-line are assigned to the
- # "none" host. TODO: support patching from packages on
- # other hosts, which means we'll need to fill in a value
- # here for those hosts.
- self.hostUrl = None
-
- self.currentFile = None
- self.baseFile = None
- self.topFile = None
- self.compressedFilename = None
- compressedFile = None
-
- # Assume there are changes for this version, until we
- # discover that there aren't.
- isNewVersion = True
-
- # Get the actual current version.
- xarchive = xpackage.FirstChildElement('uncompressed_archive')
- if xarchive:
- self.currentFile = FileSpec()
- self.currentFile.loadXml(xarchive)
-
- # Get the top_version--the top (newest) of the patch
- # chain.
- xarchive = xpackage.FirstChildElement('top_version')
- if xarchive:
- self.topFile = FileSpec()
- self.topFile.loadXml(xarchive)
-
- if self.topFile.hash == self.currentFile.hash:
- # No new version this pass.
- isNewVersion = False
- else:
- # There's a new version this pass. Update it.
- self.anyChanges = True
-
- else:
- # If there isn't a top_version yet, we have to make
- # one, by duplicating the currentFile.
- self.topFile = copy.copy(self.currentFile)
- self.anyChanges = True
-
- # Get the current patch version. If we have a
- # patch_version attribute, it refers to this particular
- # instance of the file, and that is the current patch
- # version number. If we only have a last_patch_version
- # attribute, it means a patch has not yet been built for
- # this particular instance, and that number is the
- # previous version's patch version number.
- patchVersion = xpackage.Attribute('patch_version')
- if patchVersion:
- self.patchVersion = int(patchVersion)
- else:
- patchVersion = xpackage.Attribute('last_patch_version')
- if patchVersion:
- self.patchVersion = int(patchVersion)
- if isNewVersion:
- self.patchVersion += 1
- self.anyChanges = True
-
- # Put the patchVersion in the compressed filename, for
- # cache-busting. This means when the version changes, its
- # URL will also change, guaranteeing that users will
- # download the latest version, and not some stale cache
- # file.
- xcompressed = xpackage.FirstChildElement('compressed_archive')
- if xcompressed:
- compressedFile = FileSpec()
- compressedFile.loadXml(xcompressed)
-
- oldCompressedFilename = compressedFile.filename
- self.compressedFilename = oldCompressedFilename
-
- if doProcessing:
- newCompressedFilename = '%s.%s.pz' % (self.currentFile.filename, self.patchVersion)
- if newCompressedFilename != oldCompressedFilename:
- oldCompressedPathname = Filename(self.packageDir, oldCompressedFilename)
- newCompressedPathname = Filename(self.packageDir, newCompressedFilename)
- if oldCompressedPathname.renameTo(newCompressedPathname):
- compressedFile.fromFile(self.packageDir, newCompressedFilename)
- compressedFile.storeXml(xcompressed)
-
- self.compressedFilename = newCompressedFilename
- self.anyChanges = True
-
- # Get the base_version--the bottom (oldest) of the patch
- # chain.
- xarchive = xpackage.FirstChildElement('base_version')
- if xarchive:
- self.baseFile = FileSpec()
- self.baseFile.loadXml(xarchive)
- else:
- # If there isn't a base_version yet, we have to make
- # one, by duplicating the currentFile.
- self.baseFile = copy.copy(self.currentFile)
-
- # Note that the we only store the compressed version
- # of base_filename on disk, but we store the md5 of
- # the uncompressed version in the xml file. To
- # emphasize this, we name it without the .pz extension
- # in the xml file, even though the compressed file on
- # disk actually has a .pz extension.
- self.baseFile.filename += '.base'
-
- # Also duplicate the (compressed) file itself.
- if doProcessing and self.compressedFilename:
- fromPathname = Filename(self.packageDir, self.compressedFilename)
- toPathname = Filename(self.packageDir, self.baseFile.filename + '.pz')
- fromPathname.copyTo(toPathname)
- self.anyChanges = True
-
- self.patches = []
- xpatch = xpackage.FirstChildElement('patch')
- while xpatch:
- patchfile = PatchMaker.Patchfile(self)
- patchfile.loadXml(xpatch)
- self.patches.append(patchfile)
- xpatch = xpatch.NextSiblingElement('patch')
-
- return True
-
- def writeDescFile(self):
- """ Rewrites the desc file with the new patch
- information. """
-
- if not self.anyChanges:
- # No need to rewrite.
- return
-
- xpackage = self.doc.FirstChildElement('package')
- if not xpackage:
- return
-
- packageSeq = SeqValue()
- packageSeq.loadXml(xpackage, 'seq')
- packageSeq += 1
- packageSeq.storeXml(xpackage, 'seq')
-
- # Remove all of the old patch entries from the desc file
- # we read earlier.
- xremove = []
- for value in ['base_version', 'top_version', 'patch']:
- xpatch = xpackage.FirstChildElement(value)
- while xpatch:
- xremove.append(xpatch)
- xpatch = xpatch.NextSiblingElement(value)
-
- for xelement in xremove:
- xpackage.RemoveChild(xelement)
-
- xpackage.RemoveAttribute('last_patch_version')
-
- # Now replace them with the current patch information.
- xpackage.SetAttribute('patch_version', str(self.patchVersion))
-
- xarchive = TiXmlElement('base_version')
- self.baseFile.storeXml(xarchive)
- xpackage.InsertEndChild(xarchive)
-
- # The current version is now the top version.
- xarchive = TiXmlElement('top_version')
- self.currentFile.storeXml(xarchive)
- xpackage.InsertEndChild(xarchive)
-
- for patchfile in self.patches:
- xpatch = patchfile.makeXml(self)
- xpackage.InsertEndChild(xpatch)
-
- self.doc.SaveFile()
-
- # Also copy the seq to the import desc file, for
- # documentation purposes.
-
- importDescFilename = str(self.packageDesc)[:-3] + 'import.xml'
- importDescFullpath = Filename(self.patchMaker.installDir, importDescFilename)
- doc = TiXmlDocument(importDescFullpath.toOsSpecific())
- if doc.LoadFile():
- xpackage = doc.FirstChildElement('package')
- if xpackage:
- packageSeq.storeXml(xpackage, 'seq')
- doc.SaveFile()
- else:
- print("Couldn't read %s" % (importDescFullpath))
-
- if self.contentsDocPackage:
- # Now that we've rewritten the xml file, we have to
- # change the contents.xml file that references it to
- # indicate the new file hash.
- fileSpec = FileSpec()
- fileSpec.fromFile(self.patchMaker.installDir, self.packageDesc)
- fileSpec.storeXml(self.contentsDocPackage)
-
- # Also important to update the import.xml hash.
- ximport = self.contentsDocPackage.FirstChildElement('import')
- if ximport:
- fileSpec = FileSpec()
- fileSpec.fromFile(self.patchMaker.installDir, importDescFilename)
- fileSpec.storeXml(ximport)
-
- # Also copy the package seq value into the
- # contents.xml file, mainly for documentation purposes
- # (the authoritative seq value is within the desc
- # file).
- packageSeq.storeXml(self.contentsDocPackage, 'seq')
-
-
- # PatchMaker constructor.
- def __init__(self, installDir):
- self.installDir = installDir
- self.packageVersions = {}
- self.packages = []
-
- def buildPatches(self, packageNames = None):
- """ Makes the patches required in a particular directory
- structure on disk. If packageNames is None, this makes
- patches for all packages; otherwise, it should be a list of
- package name strings, limiting the set of packages that are
- processed. """
-
- if not self.readContentsFile():
- return False
- self.buildPatchChains()
- if packageNames is None:
- self.processAllPackages()
- else:
- self.processSomePackages(packageNames)
-
- self.writeContentsFile()
- self.cleanup()
- return True
-
- def cleanup(self):
- """ Should be called on exit to remove temporary files and
- such created during processing. """
-
- for pv in self.packageVersions.values():
- pv.cleanup()
-
- def getPatchChainToCurrent(self, descFilename, fileSpec):
- """ Reads the package defined in the indicated desc file, and
- constructs a patch chain from the version represented by
- fileSpec to the current version of this package, if possible.
- Returns the patch chain if successful, or None otherwise. """
-
- package = self.readPackageDescFile(descFilename)
- if not package:
- return None
-
- self.buildPatchChains()
- fromPv = self.getPackageVersion(package.getGenericKey(fileSpec))
- toPv = package.currentPv
-
- patchChain = None
- if toPv and fromPv:
- patchChain = toPv.getPatchChain(fromPv)
-
- return patchChain
-
- def readPackageDescFile(self, descFilename):
- """ Reads a desc file associated with a particular package,
- and adds the package to self.packages. Returns the Package
- object, or None on failure. """
-
- package = self.Package(Filename(descFilename), self)
- if not package.readDescFile(doProcessing = False):
- return None
-
- self.packages.append(package)
- return package
-
- def readContentsFile(self):
- """ Reads the contents.xml file at the beginning of
- processing. """
-
- contentsFilename = Filename(self.installDir, 'contents.xml')
- doc = TiXmlDocument(contentsFilename.toOsSpecific())
- if not doc.LoadFile():
- # Couldn't read file.
- print("couldn't read %s" % (contentsFilename))
- return False
-
- xcontents = doc.FirstChildElement('contents')
- if xcontents:
- contentsSeq = SeqValue()
- contentsSeq.loadXml(xcontents)
- contentsSeq += 1
- contentsSeq.storeXml(xcontents)
-
- xpackage = xcontents.FirstChildElement('package')
- while xpackage:
- solo = xpackage.Attribute('solo')
- solo = int(solo or '0')
- filename = xpackage.Attribute('filename')
- if filename and not solo:
- filename = Filename(filename)
- package = self.Package(filename, self, xpackage)
- package.readDescFile(doProcessing = True)
- self.packages.append(package)
-
- xpackage = xpackage.NextSiblingElement('package')
-
- self.contentsDoc = doc
-
- return True
-
- def writeContentsFile(self):
- """ Writes the contents.xml file at the end of processing. """
-
- # We also have to write the desc file for all packages that
- # might need it, because we might have changed some of them on
- # read.
- for package in self.packages:
- package.writeDescFile()
-
- # The above writeDescFile() call should also update each
- # package's element within the contents.xml document, so all
- # we have to do now is write out the document.
- self.contentsDoc.SaveFile()
-
- def getPackageVersion(self, key):
- """ Returns a shared PackageVersion object for the indicated
- key. """
-
- packageName, platform, version, hostUrl, file = key
-
- # We actually key on the hash, not the FileSpec itself.
- k = (packageName, platform, version, hostUrl, file.hash)
- pv = self.packageVersions.get(k, None)
- if not pv:
- pv = self.PackageVersion(*key)
- self.packageVersions[k] = pv
- return pv
-
- def buildPatchChains(self):
- """ Builds up the chains of PackageVersions and the patchfiles
- that connect them. """
-
- self.patchFilenames = {}
-
- for package in self.packages:
- if not package.baseFile:
- # This package doesn't have any versions yet.
- continue
-
- currentPv = self.getPackageVersion(package.getCurrentKey())
- package.currentPv = currentPv
- currentPv.packageCurrent = package
- currentPv.printName = package.currentFile.filename
-
- basePv = self.getPackageVersion(package.getBaseKey())
- package.basePv = basePv
- basePv.packageBase = package
- basePv.printName = package.baseFile.filename
-
- topPv = self.getPackageVersion(package.getTopKey())
- package.topPv = topPv
- topPv.packageTop = package
-
- for patchfile in package.patches:
- self.recordPatchfile(patchfile)
-
- def recordPatchfile(self, patchfile):
- """ Adds the indicated patchfile to the patch chains. """
- self.patchFilenames[patchfile.file.filename] = patchfile
-
- fromPv = self.getPackageVersion(patchfile.getSourceKey())
- patchfile.fromPv = fromPv
- fromPv.toPatches.append(patchfile)
-
- toPv = self.getPackageVersion(patchfile.getTargetKey())
- patchfile.toPv = toPv
- toPv.fromPatches.append(patchfile)
- toPv.printName = patchfile.file.filename
-
- def processSomePackages(self, packageNames):
- """ Builds missing patches only for the named packages. """
-
- remainingNames = packageNames[:]
- for package in self.packages:
- if package.packageName in packageNames:
- self.processPackage(package)
- if package.packageName in remainingNames:
- remainingNames.remove(package.packageName)
-
- if remainingNames:
- print("Unknown packages: %s" % (remainingNames,))
-
- def processAllPackages(self):
- """ Walks through the list of packages, and builds missing
- patches for each one. """
-
- for package in self.packages:
- self.processPackage(package)
-
- def processPackage(self, package):
- """ Builds missing patches for the indicated package. """
-
- if not package.baseFile:
- # No versions.
- return
-
- # What's the current version on the top of the tree?
- topPv = package.topPv
- currentPv = package.currentPv
-
- if topPv != currentPv:
- # They're different, so build a new patch.
- filename = Filename(package.currentFile.filename + '.%s.patch' % (package.patchVersion))
- assert filename not in self.patchFilenames
- if not self.buildPatch(topPv, currentPv, package, filename):
- raise Exception("Couldn't build patch.")
-
- def buildPatch(self, v1, v2, package, patchFilename):
- """ Builds a patch from PackageVersion v1 to PackageVersion
- v2, and stores it in patchFilename.pz. Returns true on
- success, false on failure."""
-
- pathname = Filename(package.packageDir, patchFilename)
- if not self.buildPatchFile(v1.getFile(), v2.getFile(), pathname,
- v1.printName, v2.printName):
- return False
-
- compressedPathname = Filename(pathname + '.pz')
- compressedPathname.unlink()
- if not compressFile(pathname, compressedPathname, 9):
- raise Exception("Couldn't compress patch.")
- pathname.unlink()
-
- patchfile = self.Patchfile(package)
- patchfile.fromFile(package.packageDir, patchFilename + '.pz',
- v1.file, v2.file)
- package.patches.append(patchfile)
- package.anyChanges = True
-
- self.recordPatchfile(patchfile)
-
- return True
-
- def buildPatchFile(self, origFilename, newFilename, patchFilename,
- printOrigName, printNewName):
- """ Creates a patch file from origFilename to newFilename,
- storing the result in patchFilename. Returns true on success,
- false on failure. """
-
- if not origFilename.exists():
- # No original version to patch from.
- return False
-
- print("Building patch from %s to %s" % (printOrigName, printNewName))
- patchFilename.unlink()
- p = Patchfile() # The C++ class
- if p.build(origFilename, newFilename, patchFilename):
- return True
-
- # Unable to build a patch for some reason.
- patchFilename.unlink()
- return False
diff --git a/direct/src/p3d/ScanDirectoryNode.py b/direct/src/p3d/ScanDirectoryNode.py
deleted file mode 100644
index 429b8f0fd2..0000000000
--- a/direct/src/p3d/ScanDirectoryNode.py
+++ /dev/null
@@ -1,95 +0,0 @@
-__all__ = ["ScanDirectoryNode"]
-
-from panda3d.core import VirtualFileSystem, VirtualFileMountSystem, Filename, TiXmlDocument
-vfs = VirtualFileSystem.getGlobalPtr()
-
-class ScanDirectoryNode:
- """ This class is used to scan a list of files on disk. """
-
- def __init__(self, pathname, ignoreUsageXml = False):
- self.pathname = pathname
- self.filenames = []
- self.fileSize = 0
- self.nested = []
- self.nestedSize = 0
-
- xusage = None
- if not ignoreUsageXml:
- # Look for a usage.xml file in this directory. If we find
- # one, we read it for the file size and then stop here, as
- # an optimization.
- usageFilename = Filename(pathname, 'usage.xml')
- doc = TiXmlDocument(usageFilename.toOsSpecific())
- if doc.LoadFile():
- xusage = doc.FirstChildElement('usage')
- if xusage:
- diskSpace = xusage.Attribute('disk_space')
- try:
- diskSpace = int(diskSpace or '')
- except ValueError:
- diskSpace = None
- if diskSpace is not None:
- self.fileSize = diskSpace
- return
-
- files = vfs.scanDirectory(self.pathname)
- if files is None:
- files = []
- for vfile in files:
- if hasattr(vfile, 'getMount'):
- if not isinstance(vfile.getMount(), VirtualFileMountSystem):
- # Not a real file; ignore it.
- continue
-
- if vfile.isDirectory():
- # A nested directory.
- subdir = ScanDirectoryNode(vfile.getFilename(), ignoreUsageXml = ignoreUsageXml)
- self.nested.append(subdir)
- self.nestedSize += subdir.getTotalSize()
-
- elif vfile.isRegularFile():
- # A nested file.
- self.filenames.append(vfile.getFilename())
- self.fileSize += vfile.getFileSize()
-
- else:
- # Some other wacky file thing.
- self.filenames.append(vfile.getFilename())
-
- if xusage:
- # Now update the usage.xml file with the newly-determined
- # disk space.
- xusage.SetAttribute('disk_space', str(self.getTotalSize()))
- tfile = Filename.temporary(str(pathname), '.xml')
- if doc.SaveFile(tfile.toOsSpecific()):
- tfile.renameTo(usageFilename)
-
- def getTotalSize(self):
- return self.nestedSize + self.fileSize
-
- def extractSubdir(self, pathname):
- """ Finds the ScanDirectoryNode within this node that
- corresponds to the indicated full pathname. If it is found,
- removes it from its parent, and returns it. If it is not
- found, returns None. """
-
- # We could be a little smarter here, but why bother. Just
- # recursively search all children.
- for subdir in self.nested:
- if subdir.pathname == pathname:
- self.nested.remove(subdir)
- self.nestedSize -= subdir.getTotalSize()
- return subdir
-
- result = subdir.extractSubdir(pathname)
- if result:
- self.nestedSize -= result.getTotalSize()
- if subdir.getTotalSize() == 0:
- # No other files in the subdirectory that contains
- # this package; remove it too.
- self.nested.remove(subdir)
- return result
-
- return None
-
-
diff --git a/direct/src/p3d/SeqValue.py b/direct/src/p3d/SeqValue.py
deleted file mode 100644
index 958132f74b..0000000000
--- a/direct/src/p3d/SeqValue.py
+++ /dev/null
@@ -1,90 +0,0 @@
-__all__ = ["SeqValue"]
-
-class SeqValue:
-
- """ This represents a sequence value read from a contents.xml
- file, either from the or the section. It's
- represented as series of dotted integers in the xml file, and
- stored internally as a tuple of integers.
-
- It may be incremented, which increments only the last integer in
- the series; or it may be compared with another SeqValue, which
- compares all of the integers componentwise. """
-
- def __init__(self, value = None):
- self.value = ()
- if value is not None:
- self.set(value)
-
- def set(self, value):
- """ Sets the seq from the indicated value of unspecified
- type. """
- if isinstance(value, tuple):
- self.setFromTuple(value)
- elif isinstance(value, str):
- self.setFromString(value)
- else:
- raise TypeError('Invalid sequence type: %s' % (value,))
-
- def setFromTuple(self, value):
- """ Sets the seq from the indicated tuple of integers. """
- assert isinstance(value, tuple)
- self.value = value
-
- def setFromString(self, value):
- """ Sets the seq from the indicated string of dot-separated
- integers. Raises ValueError on error. """
- assert isinstance(value, str)
-
- self.value = ()
- if value:
- value = value.split('.')
- value = map(int, value)
- self.value = tuple(value)
-
- def loadXml(self, xelement, attribute = 'seq'):
- """ Reads the seq from the indicated XML element. Returns
- true if loaded, false if not given or if there was an
- error. """
-
- self.value = ()
- value = xelement.Attribute(attribute)
- if value:
- try:
- self.setFromString(value)
- except ValueError:
- return False
- return True
-
- return False
-
-
- def storeXml(self, xelement, attribute = 'seq'):
- """ Adds the seq to the indicated XML element. """
- if self.value:
- value = '.'.join(map(str, self.value))
- xelement.SetAttribute(attribute, value)
-
- def __add__(self, inc):
- """ Increments the seq value, returning the new value. """
- if not self.value:
- value = (1,)
- else:
- value = self.value[:-1] + (self.value[-1] + inc,)
- return SeqValue(value)
-
- def __cmp__(self, other):
- """ Compares to another seq value. """
- return cmp(self.value, other.value)
-
- def __lt__(self, other):
- return self.value < other.value
-
- def __gt__(self, other):
- return self.value > other.value
-
- def __bool__(self):
- return bool(self.value)
-
- def __str__(self):
- return 'SeqValue%s' % (repr(self.value))
diff --git a/direct/src/p3d/__init__.py b/direct/src/p3d/__init__.py
deleted file mode 100644
index e6ab93b09d..0000000000
--- a/direct/src/p3d/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-"""
-This package provides the Python interface to functionality relating to
-the Panda3D Runtime environment.
-"""
diff --git a/direct/src/p3d/coreapi.pdef b/direct/src/p3d/coreapi.pdef
deleted file mode 100644
index 03612be605..0000000000
--- a/direct/src/p3d/coreapi.pdef
+++ /dev/null
@@ -1,124 +0,0 @@
-from panda3d.core import getModelPath, Filename, ConfigVariableFilename, DSearchPath, ExecutionEnvironment, PandaSystem
-
-# This file defines a number of standard "packages" that correspond to
-# a Panda3D distribution. These packages are built by passing this
-# file to the ppackage utility, either as a packaged application, or
-# as the module direct.p3d.ppackage.
-
-# The packages in this file define the "Core API". This is the second
-# installed piece of the three-part plugin system (the plugin, the
-# core API, Panda3D).
-
-# These packages are downloaded directly by the plugin, from the host
-# specified by the value of PANDA_PACKAGE_HOST_URL compiled into the
-# plugin. Thus, these packages are inextricably tied to the
-# particular plugin they have been built with. They do not have to be
-# present on a server that hosts a version of Panda3D for download,
-# just on the server that hosts the plugin itself. These packages do
-# not need to be updated with each new version of Panda3D.
-
-# Also see panda3d.pdef.
-
-class coreapi(solo):
- # The special "coreapi" package. As a "solo", this is just a
- # single .dll (or dylib, or whatever).
- setVer(PandaSystem.getP3dCoreapiVersionString())
- file('p3d_plugin.dll')
-
-class images(package):
- # The default startup images are stored as their own package.
- names = ['download', 'failed', 'play_click', 'play_ready', 'play_rollover',
- 'auth_click', 'auth_ready', 'auth_rollover']
- configDict = {}
-
- # Construct a search path to look for the images.
- search = DSearchPath()
-
- # First on the path: an explicit $PLUGIN_IMAGES env var.
- if ExecutionEnvironment.hasEnvironmentVariable('PLUGIN_IMAGES'):
- search.appendDirectory(Filename.expandFrom('$PLUGIN_IMAGES'))
-
- # Next on the path: the models/plugin_images directory within the
- # current directory.
- search.appendDirectory('models/plugin_images')
-
- # Finally on the path: models/plugin_images within the model
- # search path.
- for dir in getModelPath().getDirectories():
- search.appendDirectory(Filename(dir, 'plugin_images'))
-
- for name in names:
- # Look for a png image first.
- basename = '%s.png' % (name)
- filename = Filename(basename)
- found = filename.resolveFilename(search)
- if not found:
- # Then try a jpeg image.
- basename = '%s.jpg' % (name)
- filename = Filename(basename)
- found = filename.resolveFilename(search)
-
- if found:
- # Add the image file to the package
- file(filename, newName = basename, extract = True)
-
- # And set the config variable to reference it.
- token = '%s_img' % (name)
- configDict[token] = basename
- else:
- print("Could not locate %s" % (filename))
-
- # Also make a few special cases. We use the same default image
- # for many of the states.
- download = configDict.get('download_img', None)
- if download:
- configDict['ready_img'] = download
- configDict['unauth_img'] = download
- configDict['launch_img'] = download
- configDict['active_img'] = download
-
- config(**configDict)
-
-class certlist(package):
- # This package holds any certificates that should be considered
- # pre-approved by the plugin vendor. If you build and host your
- # own version of the Panda3D plugin, you can add your own
- # certificates to this list. The certificates in this package
- # will be assumed to have been approved by the user when he/she
- # installed the plugin.
-
- # They should be PEM-encoded and their filenames must end in the
- # ".pem" or ".crt" extension, and they should be added with the
- # extract = True flag so they will be extracted to disk.
- pass
-
-
-class p3dcert(package):
- # This special application, used to pop up a dialog to prompt the
- # user to accept or deny unknown applications, is its own package.
- config(display_name = "Authorization Dialog")
-
- if platform.startswith('osx'):
- # On Mac, we package up a P3DCert.app bundle. This includes
- # specifications in the plist file to avoid creating a dock
- # icon and stuff.
-
- # Find p3dcert.plist in the direct source tree.
- import direct
- plist = Filename(direct.__path__[0], 'plugin/p3dcert.plist')
- makeBundle('P3DCert.app', plist, executable = 'p3dcert')
-
- else:
- # Anywhere else, we just ship the executable file p3dcert.exe.
- file('p3dcert.exe', required = True)
-
-
-class p3dembed(package):
- # This contains the p3dembed executable, that is used by
- # pdeploy to generate a self-extracting .p3d executable.
-
- config(platform_specific = True)
- file('p3dembed.exe', required = True)
-
- if platform.startswith('win'):
- file('p3dembedw.exe', required = True)
diff --git a/direct/src/p3d/p3dWrapper.c b/direct/src/p3d/p3dWrapper.c
deleted file mode 100644
index ef96e036f9..0000000000
--- a/direct/src/p3d/p3dWrapper.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/* Filename: p3dWrapper.c
- * Created by: rdb (16Jan10)
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-/* p3dWrapper is a small wrapper executable that locates a .p3d file
- in the same directory as this executable file, with the same name
- (except .p3d instead of .exe of course). It is only meant to be
- used on Windows system, it is not needed on Unix-like systems. */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#define BUFFER_SIZE 1024
-
-/* It makes sense to use "App Paths\panda3d.exe". However, Microsoft
- decided in their infinite wisdom to disable Redirection for that
- key from Windows 7 onward, so we can't rely on it producing a
- result appropriate to the right architecture when both the 32-bit
- and 64-bit versions of the runtime are installed. Beh. */
-
-#define UNINST_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Panda3D Game Engine"
-
-int main (int argc, char* argv[]) {
- int i;
- char buffer[BUFFER_SIZE];
- char* p3dfile;
- char* runtime = NULL;
- DWORD size;
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- char *cmd;
- char *newcmd;
- HKEY hKey = 0;
- char buf[1024] = {0};
- DWORD dwType = REG_SZ;
- DWORD dwBufSize = sizeof(buf);
- size = GetModuleFileName(NULL, buffer, BUFFER_SIZE);
- assert (size > 0);
-
- /* Chop off the .exe and replace it by .p3d. */
- p3dfile = (char*) _alloca(size + 1);
- memcpy(p3dfile, buffer, size);
- p3dfile[size] = 0;
- memcpy(p3dfile + size - 3, "p3d", 3);
-
- /* Find the location of panda3d.exe using the registry path. */
-#ifdef _WIN64
- /* If we're on 64-bit Windows, try the 64-bit registry first. */
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, UNINST_KEY, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKey) == ERROR_SUCCESS) {
- if (RegQueryValueEx(hKey, "DisplayIcon", 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) {
- char *slash = strrchr(buf, '\\');
- if (slash != NULL) {
- strcpy(slash, "\\panda3d.exe");
- runtime = buf;
- }
- }
- RegCloseKey(hKey);
- }
-#endif
-
- /* On 32-bit Windows, or no 64-bit Runtime installed. Try 32-bit registry. */
- if (runtime == NULL) {
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, UNINST_KEY, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS) {
- if (RegQueryValueEx(hKey, "DisplayIcon", 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) {
- char *slash = strrchr(buf, '\\');
- if (slash != NULL) {
- strcpy(slash, "\\panda3d.exe");
- runtime = buf;
- }
- }
- RegCloseKey(hKey);
- }
- }
-
- /* Backward compatibility: Runtime 1.0.4 and below looked for the below key, even though the
- above keys should work fine, but let's just be certain.
- Find the Panda3D applet\DefaultIcon key and extract the path to the runtime from there. */
- if (runtime == NULL) {
- if (RegOpenKey(HKEY_CLASSES_ROOT, "Panda3D applet\\DefaultIcon", &hKey) == ERROR_SUCCESS) {
- if (RegQueryValueEx(hKey, 0, 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) {
- char *slash = strrchr(buf, '\\');
- if (slash != NULL) {
- strcpy(slash, "\\panda3d.exe");
- runtime = buf;
- }
- } else {
- fprintf(stderr, "Failed to read registry key. Try reinstalling the Panda3D Runtime.\n");
- return 1;
- }
- RegCloseKey(hKey);
- } else {
- fprintf(stderr, "The Panda3D Runtime does not appear to be installed!\n");
- return 1;
- }
- }
-
- if (runtime == NULL) {
- fprintf(stderr, "Failed to find panda3d.exe in registry. Try reinstalling the Panda3D Runtime.\n");
- return 1;
- }
-
- /* Build the command-line and run panda3d.exe. */
- cmd = GetCommandLine();
- newcmd = (char*) _alloca(strlen(runtime) + strlen(p3dfile) + strlen(cmd) - strlen (argv[0]) + 7);
- sprintf(newcmd, "\"%s\" \"%s\" %s", runtime, p3dfile, cmd + strlen(argv[0]));
- memset(&si, 0, sizeof(si));
- si.cb = sizeof(STARTUPINFO);
- if (CreateProcess(runtime, newcmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
- WaitForSingleObject(pi.hProcess, INFINITE);
- }
- return 0;
-}
diff --git a/direct/src/p3d/packp3d.py b/direct/src/p3d/packp3d.py
deleted file mode 100755
index dee4fa2656..0000000000
--- a/direct/src/p3d/packp3d.py
+++ /dev/null
@@ -1,241 +0,0 @@
-#! /usr/bin/env python
-
-usageText = """
-
-This command will pack a Panda application, consisting of a directory
-tree of .py files and models, into a p3d file for convenient
-distribution. The resulting p3d file can be run by the Panda3D
-runtime executable, or by the Panda3D web browser plugin.
-
-This command will build p3d files that reference Panda3D %s,
-from host %s .
-
-Also see ppackage, a more powerful (but more complex) tool that can
-also be used to build p3d applications, using a pdef description file.
-
-Usage:
-
- %s [opts] -o app.p3d
-
-Options:
-
- -o app.p3d
- Specify the name of the p3d file to generate. This is required.
-
- -d application_root
- Specify the root directory of the application source; this is a
- directory tree that contains all of your .py files and models.
- If this is omitted, the default is the current directory.
-
- -m main.py
- Names the Python file that begins the application. This should
- be a file within the root directory. If this is omitted, the
- default is a file named "main.py", or if there is only one Python
- file present, it is used. If this file contains a function
- called main(), that function will be called after importing it
- (this is preferable to having the module start itself immediately
- upon importing).
-
- -S file.crt[,chain.crt[,file.key[,\"password\"]]]
- Signs the resulting p3d with the indicated certificate. You may
- specify the signing certificate, the optional authorization
- chain, and the private key in three different files, or they may
- all be combined in the first file. If the private key is
- encrypted, the password will be required to decrypt it.
-
- -e ext
- Adds a new extension to be processed as a generic, compressible
- file type. Do not include the leading dot. Files matching this
- extension found within the root directory will be automatically
- added to the p3d file, in compressed form. This option may be
- repeated as necessary.
-
- -n ext
- Adds a new extension to be processed as a noncompressible file
- type. Files matching this extension will be added to the p3d
- file, in their original, uncompressed form. You should use this
- instead of -e for files that are uncompressible by their nature
- (e.g. mpg files). This option may be repeated as necessary.
-
- -x ext
- Marks files with the given extensions of needing to be physically
- extracted to disk before they can be loaded. This is used for
- file types that cannot be loaded via the virtual file system,
- such as .ico files on Windows.
- This option is currently only implemented when deploying the
- application with pdeploy.
-
- -p python_lib_dir
- Adds a directory to search for additional Python modules. You
- can use this to add your system's Python path, to allow packp3d
- to find any system modules not included in the standard Panda3D
- release, but your version of Python must match this one (%s).
- This option may be repeated to add multiple directories.
-
- -c config=value
- Sets the indicated config flag in the application. This option
- may be repeated as necessary.
-
- -r package[,version[,hostURL]]
- Names an additional package that this application requires at
- startup time. The default package is 'panda3d'; you may repeat
- this option to indicate dependencies on additional packages.
-
- -s search_dir
- Additional directories to search for previously-built packages.
- This option may be repeated as necessary. These directories may
- also be specified with the pdef-path Config.prc variable.
-
- -D
- Sets the allow_python_dev flag in the application. This enables
- additional runtime debug operations, particularly the -i option
- to the panda3d command, which enables a live Python prompt within
- the application's environment. Setting this flag may be useful
- to develop an application initially, but should not be set on an
- application intended for deployment.
-
-"""
-
-import sys
-import os
-import getopt
-import glob
-from direct.p3d import Packager
-from panda3d.core import *
-
-# Temp hack for debugging.
-#from direct.p3d.AppRunner import dummyAppRunner; dummyAppRunner()
-
-class ArgumentError(Exception):
- pass
-
-def makePackedApp(args):
- opts, args = getopt.getopt(args, 'o:d:m:S:e:n:x:p:c:r:s:Dh')
-
- packager = Packager.Packager()
-
- appFilename = None
- root = Filename('.')
- main = None
- configFlags = []
- requires = []
- allowPythonDev = False
-
- for option, value in opts:
- if option == '-o':
- appFilename = Filename.fromOsSpecific(value)
- elif option == '-d':
- root = Filename.fromOsSpecific(value)
- elif option == '-m':
- main = value
- elif option == '-S':
- tokens = value.split(',')
- while len(tokens) < 4:
- tokens.append('')
- certificate, chain, pkey, password = tokens[:4]
- packager.signParams.append((Filename.fromOsSpecific(certificate),
- Filename.fromOsSpecific(chain),
- Filename.fromOsSpecific(pkey),
- Filename.fromOsSpecific(password)))
- elif option == '-e':
- packager.binaryExtensions.append(value)
- elif option == '-n':
- packager.uncompressibleExtensions.append(value)
- elif option == '-x':
- packager.extractExtensions.append(value)
- elif option == '-p':
- sys.path.append(value)
- elif option == '-c':
- configFlags.append(value.split('=', 1))
- elif option == '-r':
- tokens = value.split(',')
- while len(tokens) < 3:
- tokens.append('')
- name, version, host = tokens[:3]
- requires.append((name, version, host))
- elif option == '-s':
- packager.installSearch.append(Filename.fromOsSpecific(value))
- elif option == '-D':
- allowPythonDev = True
- elif option == '-h':
- print(usageText % (
- PandaSystem.getPackageVersionString(),
- PandaSystem.getPackageHostUrl(),
- os.path.split(sys.argv[0])[1],
- '%s.%s' % (sys.version_info[0], sys.version_info[1])))
- sys.exit(0)
-
- if not appFilename:
- raise ArgumentError("No target app specified. Use:\n %s -o app.p3d\nUse -h to get more usage information." % (os.path.split(sys.argv[0])[1]))
-
- if args:
- raise ArgumentError("Extra arguments on command line.")
-
- if appFilename.getExtension() != 'p3d':
- raise ArgumentError('Application filename must end in ".p3d".')
-
- appDir = Filename(appFilename.getDirname())
- if not appDir:
- appDir = Filename('.')
- appBase = appFilename.getBasenameWoExtension()
-
- if main:
- main = Filename.fromOsSpecific(main)
- main.makeAbsolute(root)
- else:
- main = Filename(root, 'main.py')
- if not main.exists():
- main = glob.glob(os.path.join(root.toOsSpecific(), '*.py'))
- if len(main) == 0:
- raise ArgumentError('No Python files in root directory.')
- elif len(main) > 1:
- raise ArgumentError('Multiple Python files in root directory; specify the main application with -m "main".')
-
- main = Filename.fromOsSpecific(os.path.split(main[0])[1])
- main.makeAbsolute(root)
-
- packager.installDir = appDir
- packager.allowPythonDev = allowPythonDev
-
- # Put the root directory on the front of the model-path, so that
- # any texture references in egg or bam files that reference
- # textures from the top of the root directory will be properly
- # resolved.
- getModelPath().prependDirectory(root)
-
- try:
- packager.setup()
- packager.beginPackage(appBase, p3dApplication = True)
-
- # Pre-require panda3d, to give a less-confusing error message
- # if one of our requirements pulls in a wrong version of
- # panda3d.
- if 'panda3d' not in [t[0] for t in requires]:
- packager.do_require('panda3d')
-
- for name, version, host in requires:
- packager.do_require(name, version = version, host = host)
-
- if configFlags:
- packager.do_config(**dict(configFlags))
-
- packager.do_dir(root)
- packager.do_main(main)
- packager.endPackage()
- packager.close()
-
- except Packager.PackagerError:
- # Just print the error message and exit gracefully.
- inst = sys.exc_info()[1]
- print(inst.args[0])
- sys.exit(1)
-
-try:
- makePackedApp(sys.argv[1:])
-except ArgumentError as e:
- print(e.args[0])
- sys.exit(1)
-
-# An explicit call to exit() is required to exit the program, when
-# this module is packaged in a p3d file.
-sys.exit(0)
diff --git a/direct/src/p3d/panda3d.pdef b/direct/src/p3d/panda3d.pdef
deleted file mode 100644
index 78e7af2cf0..0000000000
--- a/direct/src/p3d/panda3d.pdef
+++ /dev/null
@@ -1,518 +0,0 @@
-import sys
-from panda3d.core import Filename, PandaSystem, getModelPath
-
-# This file defines a number of standard "packages" that correspond to
-# a Panda3D distribution. These packages are built by passing this
-# file to the ppackage utility, either as a packaged application, or
-# as the module direct.p3d.ppackage.
-
-# The packages in this file define Panda3D itself. This is the third
-# installed piece of the three-part plugin system (the plugin, the
-# core API, Panda3D).
-
-# When needed, these packages are downloaded by the core API, from the
-# host URL specified in a given p3d file, and not from any hardcoded
-# URL. Thus, any custom version of Panda3D may be hosted on any
-# server in the world, and any version of the plugin can download it.
-
-# Also see coreapi.pdef.
-
-class panda3d(package):
- # The main Panda3D package. Contains Python and most of the graphics
- # code in Panda3D.
-
- config(display_name = "Panda3D")
-
- # First, add the minimum startup files for a Panda3D package.
- # These are files that the Panda3D runtime will explicitly look
- # for by name in order to get itself bootstrapped.
- setupPanda3D()
-
- # These are Python modules that are needed by most Panda3D
- # applications. It doesn't matter too much if we miss one or two
- # here, since any module imported by any of this code will
- # automatically be included as well, and we end up with a pretty
- # complete list that way.
- module('direct.directbase.DirectStart',
- # Don't want to include all files in direct.p3d, because
- # that picks up the runtime scripts too, which are their
- # own p3d files below.
- 'direct.p3d.AppRunner',
- 'direct.p3d.DWBPackageInstaller',
- 'direct.actor.*',
- 'direct.controls.*',
- 'direct.directdevices.*',
- 'direct.directnotify.*',
- 'direct.directtools.*',
- 'direct.directutil.*',
- 'direct.distributed.*',
- 'direct.filter.*',
- 'direct.fsm.*',
- 'direct.gui.*',
- 'direct.interval.*',
- 'direct.particles.*',
- 'direct.showbase.*',
- 'direct.showutil.*',
- 'direct.stdpy.*',
- 'direct.task.*')
-
- # Keep these modules for backward compatibility.
- module('pandac.PandaModules', 'pandac.extension_native_helpers')
-
- module('panda3d.core',
- 'panda3d.direct',
- 'panda3d.fx',
- 'panda3d.physics')
-
- # Include various standard Python encodings. The rest is in morepy.
- module('encodings', 'encodings.aliases', 'encodings.undefined',
- 'encodings.utf_8', 'encodings.ascii', 'encodings.mbcs',
- 'encodings.latin_1', 'io')
- if sys.version_info < (3, 0):
- module('encodings.string_escape')
-
- # pandac.PandaModules pulls in other Panda3D libs automatically.
- # Exclude these Panda3D libs; they are big and many applications don't
- # use them. We define them as separate, optional packages, below,
- # except for skel - this is useless in a shipped game anyways.
- excludeModule('panda3d.egg')
- excludeModule('panda3d.ode')
- excludeModule('panda3d.bullet')
- excludeModule('panda3d.vision')
- excludeModule('panda3d.skel')
- excludeModule('panda3d.physx')
- excludeModule('panda3d.ai')
- excludeModule('panda3d.vrpn')
-
- excludeModule('libpandaegg')
- excludeModule('libpandaode')
- excludeModule('libpandabullet')
- excludeModule('libp3vision')
- excludeModule('libpandaskel')
- excludeModule('libpandaphysx')
- excludeModule('libpandaai')
- excludeModule('libp3vrpn')
-
- # Exclude these GUI toolkits; they're big, and many applications don't
- # use them. We define them as separate, optional packages, below.
- excludeModule('wx',
- 'direct.showbase.WxGlobal')
-
- excludeModule('Tkinter', 'tkinter', 'Pmw', 'tkinter.simpledialog',
- 'direct.showbase.TkGlobal',
- 'direct.tkpanels', 'direct.tkwidgets',
- 'tkCommonDialog', 'tkMessageBox', 'tkSimpleDialog')
-
- excludeModule('MySQLdb', 'MySQLdb.connections', 'MySQLdb.constants',
- 'MySQLdb.converters', '_mysql')
-
- # Some code in distributed conditionally imports these.
- excludeModule('otp.ai', 'otp.ai.AIZoneData')
-
- # Most of the core Panda3D DLL's will be included implicitly due to
- # being referenced by the above Python code. Here we name a few more
- # that are also needed, but aren't referenced by any code. Again,
- # note that the .dll extension is automatically replaced with the
- # platform-specific extension for an executable.
- file('libpandagl.dll', 'libp3tinydisplay.dll')
- if platform.startswith('win'):
- file('libpandadx9.dll')
-
- # A basic config file is needed to lay some some fundamental runtime
- # variables.
- if platform.startswith('win'):
- auxDisplays = """
-aux-display pandagl
-aux-display pandadx9
-aux-display p3tinydisplay
-"""
- else:
- auxDisplays = """
-aux-display pandagl
-aux-display p3tinydisplay
-"""
-
- file('Config.prc', extract = True, text = """
-plugin-path $PANDA3D_ROOT
-default-model-extension .bam
-cull-bin gui-popup 60 unsorted
-""" + auxDisplays)
-
-class morepy(package):
- # Additional Python modules provided by the standard Python
- # library. Include this package to get the full suite of standard
- # Python functionality.
-
- config(display_name = "Python standard library")
- require('panda3d')
-
- module('string', 're', 'struct', 'difflib',
- 'textwrap', 'codecs', 'unicodedata', 'stringprep', 'datetime',
- 'calendar', 'collections', 'heapq', 'bisect', 'array',
- 'sched', 'queue', 'weakref', 'types', 'copy', 'pprint',
- 'numbers', 'math', 'cmath', 'decimal', 'fractions',
- 'random', 'itertools', 'functools', 'operator', 'os.path',
- 'fileinput', 'filecmp', 'tempfile', 'glob', 'fnmatch', 'linecache',
- 'shutil', 'macpath', 'pickle', 'shelve', 'marshal', 'zlib',
- 'gzip', 'bz2', 'zipfile', 'tarfile', 'csv', 'netrc',
- 'xdrlib', 'plistlib', 'hashlib', 'hmac', 'os',
- 'time', 'optparse', 'getopt', 'logging', 'logging.*')
- module('getpass', 'curses', 'curses.textpad', 'curses.wrapper',
- 'curses.ascii', 'curses.panel', 'platform', 'errno',
- 'ctypes', 'select', 'threading', 'dummy_threading', 'dummy_thread',
- 'multiprocessing', 'mmap', 'readline', 'rlcompleter', 'subprocess',
- 'socket', 'ssl', 'signal', 'asyncore', 'asynchat', 'email', 'json',
- 'mailcap', 'mailbox', 'mimetypes', 'base64', 'binhex', 'binascii',
- 'quopri', 'uu', 'xml.parsers.expat', 'xml.dom', 'xml.dom.minidom',
- 'xml.dom.pulldom', 'xml.sax', 'xml.sax.handler',
- 'xml.sax.saxutils', 'xml.sax.xmlreader',
- 'xml.etree.ElementTree', 'webbrowser', 'cgi', 'cgitb')
- module('wsgiref', 'ftplib', 'poplib', 'imaplib', 'nntplib', 'smtplib',
- 'smtpd', 'telnetlib', 'uuid', 'audioop', 'aifc', 'sunau',
- 'wave', 'chunk', 'colorsys', 'imghdr', 'sndhdr',
- 'ossaudiodev', 'gettext', 'locale', 'cmd', 'shlex',
- 'pydoc', 'doctest', 'unittest', 'test',
- 'bdb', 'pdb', 'timeit', 'trace', 'sys')
- module('warnings', 'contextlib', 'abc',
- 'atexit', 'traceback', '__future__', 'gc', 'inspect',
- 'site', 'fpectl', 'code', 'codeop', 'zipimport', 'pkgutil',
- 'modulefinder', 'runpy', 'parser', 'symtable', 'symbol',
- 'token', 'keyword', 'tokenize', 'tabnanny', 'pyclbr',
- 'py_compile', 'compileall', 'dis', 'pickletools',
- 'distutils', 'msilib', 'msvcrt', 'winsound', 'posix', 'pwd', 'spwd',
- 'grp', 'crypt', 'termios', 'tty', 'pty', 'fcntl', 'pipes',
- 'resource', 'nis', 'syslog', 'ast')
-
- # Handle module name differences between Python 2 and Python 3 (see PEP3108)
- if sys.version_info[0] < 3:
- # Deprecated modules that were removed in Python 3
- module('posixfile', 'rfc822', 'mimetools','MimeWriter', 'mimify',
- 'multifile', 'sets', 'md5', 'sha', 'imp', 'formatter')
- # Mac-specific modules that were removed
- module('autoGIL', 'ColorPicker', 'EasyDialogs', 'findertools',
- 'FrameWork', 'ic', 'MacOS', 'macostools')
- # Removed because they were hardly used
- module('imputil', 'mutex', 'user', 'new')
- # Removed because they were obsolete
- module('Bastion', 'rexec', 'commands', 'dircache', 'dl', 'fpformat',
- 'htmllib', 'imageop', 'mhlib', 'popen2', 'sgmllib', 'stat',
- 'statvfs', 'thread', 'UserDict', 'UserList', 'UserString',
- 'future_builtins', 'hotshot', 'bsddb')
- # Renamed to fix PEP8 violations
- module('_winreg', 'ConfigParser', 'copy_reg', 'SocketServer')
- # C and Python implementations of the same interface were merged
- module('cPickle', 'cStringIO', 'StringIO')
- # Renamed because of poorly chosen names
- module('repr', 'test.test_support', '__builtins__')
- # Modules that got grouped under packages
- module('anydbm', 'whichdb', 'dbm', 'dumbdbm', 'gdbm', 'dbhash')
- module('HTMLParser', 'htmlentitydefs')
- module('BaseHTTPServer', 'SimpleHTTPServer', 'cookielib', 'Cookie',
- 'CGIHTTPServer', 'httplib')
- module('urllib', 'urllib2', 'urlparse', 'robotparser')
- module('xmlrpclib', 'SimpleXMLRPCServer', 'DocXMLRPCServer')
- else:
- # Renamed to fix PEP8 violations
- module('winreg', 'configparser', 'copyreg', 'socketserver')
- # Renamed because of poorly chosen names
- module('reprlib', 'test.support', 'builtins')
- # Modules that got grouped under packages
- module('dbm')
- module('html')
- module('http')
- module('urllib')
- module('xmlrpc')
-
- # To add the multitude of standard Python string encodings.
- module('encodings', 'encodings.*')
-
-class models(package):
- # The standard models package. This is the contents of the
- # "models" directory that ships with Panda; it includes a few
- # default fonts, and some silly little sample models like smiley
- # and teapot.
- config(display_name = "Standard models")
-
- # We assign it the same version as the panda3d package. This
- # would be assigned by default if we had a requirement on panda3d,
- # but there's no real reason to declare that requirement.
- config(version = PandaSystem.getPackageVersionString())
-
- # Look for cmss12.egg on the model-path. Wherever this is found,
- # we assume this is the models directory.
- pathname = getModelPath().findFile('cmss12.egg')
- if pathname:
- dir(pathname.getDirname(), newDir = 'models')
-
- # Some people are used to loading the models without models/ prefix.
- file('models.prc', extract = True, text = "model-path $MODELS_ROOT/models")
-
-
-class fmod(package):
- # This package includes the FMod audio library. This is
- # full-featured and robust, but it is closed-source and the
- # licensing is cost-free only for non-commercial products.
-
- config(display_name = "FMod audio library")
- require('panda3d')
-
- file('libp3fmod_audio.dll', required = True)
-
- file('fmod.prc', extract = True, text = """
-plugin-path $FMOD_ROOT
-audio-library-name p3fmod_audio
-""")
-
-class openal(package):
- # This package includes the OpenAL audio libraries. This is free
- # in both senses, but there are some technical issues, especially
- # on certain platforms.
-
- config(display_name = "OpenAL audio library")
- require('panda3d')
-
- file('libp3openal_audio.dll', required = True)
- file('OpenAL32.dll')
- file('soft_oal.dll')
- file('wrap_oal.dll')
-
- file('openal.prc', extract = True, text = """
-plugin-path $OPENAL_ROOT
-audio-library-name p3openal_audio
-""")
-
-class audio(package):
- # This package includes the best audio library for the given
- # platform, assuming a non-commercial application.
- require('panda3d')
-
- # OpenAL seems to work pretty well universally now.
- require('openal')
-
-class ffmpeg(package):
- # This package includes the ffmpeg libraries and integration.
- # It's a bit big, and is restricted by patents and the restrictive
- # GPLv3 license, which is why it is included as a separate package.
-
- config(display_name = "FFMpeg multimedia decoder")
- require('panda3d')
-
- file('libp3ffmpeg.dll', required = True)
-
- file('ffmpeg.prc', extract = True, text = """
-plugin-path $FFMPEG_ROOT
-load-audio-type * p3ffmpeg
-load-video-type * p3ffmpeg
-""")
-
-class egg(package):
- # This package contains the code for reading and operating on egg
- # files and other model files. Since the Packager automatically
- # converts egg files to bam files, this is not needed for most
- # Panda3D applications.
-
- config(display_name = "Panda3D egg loader")
- require('panda3d')
-
- module('panda3d.egg', required = True)
- file('libpandaegg.dll', required = True)
- file('libp3ptloader.dll', required = True)
-
- file('egg.prc', extract = True, text = """
-plugin-path $EGG_ROOT
-load-file-type egg pandaegg
-load-file-type p3ptloader
-
-# These are excerpted from the default Confauto.prc file.
-egg-object-type-portal portal { 1 }
-egg-object-type-polylight polylight { 1 }
-egg-object-type-seq24 { 1 } fps { 24 }
-egg-object-type-seq12 { 1 } fps { 12 }
-egg-object-type-indexed indexed { 1 }
-egg-object-type-seq10 { 1 } fps { 10 }
-egg-object-type-seq8 { 1 } fps { 8 }
-egg-object-type-seq6 { 1 } fps { 6 }
-egg-object-type-seq4 { 1 } fps { 4 }
-egg-object-type-seq2 { 1 } fps { 2 }
-
-egg-object-type-binary alpha { binary }
-egg-object-type-dual alpha { dual }
-egg-object-type-glass alpha { blend_no_occlude }
-
-egg-object-type-model { 1 }
-egg-object-type-dcs { 1 }
-egg-object-type-notouch { no_touch }
-
-egg-object-type-barrier { Polyset descend }
-egg-object-type-sphere { Sphere descend }
-egg-object-type-invsphere { InvSphere descend }
-egg-object-type-tube { Tube descend }
-egg-object-type-trigger { Polyset descend intangible }
-egg-object-type-trigger-sphere { Sphere descend intangible }
-egg-object-type-floor { Polyset descend level }
-egg-object-type-dupefloor { Polyset keep descend level }
-egg-object-type-bubble { Sphere keep descend }
-egg-object-type-ghost collide-mask { 0 }
-egg-object-type-glow blend { add }
-egg-object-type-direct-widget collide-mask { 0x80000000 } { Polyset descend }
-""")
-
-class ode(package):
- # This package contains the code for the ODE integration.
- # As not every application uses the ODE layers, and to cut down
- # the download size, it is provided as separate package.
-
- config(display_name = "Panda3D Open Dynamics Engine integration")
- require('panda3d')
-
- module('panda3d.ode', required = True)
- file('libpandaode.dll', required = True)
-
-class bullet(package):
- # This package contains the code for the Bullet integration.
- # As not every application uses the Bullet layers, and to cut down
- # the download size, it is provided as separate package.
-
- config(display_name = "Panda3D Bullet integration")
- require('panda3d')
-
- module('panda3d.bullet', required = True)
- file('libpandabullet.dll', required = True)
-
-class physx(package):
- # This package contains the code for the NVIDIA PhysX integration.
- # As not every application uses the NVIDIA PhysX layers, and to cut down
- # the download size, it is provided as separate package.
-
- config(display_name = "Panda3D PhysX integration")
- require('panda3d')
-
- module('panda3d.physx', required = True)
- file('libpandaphysx.dll', required = True)
- file('physxcudart_20.dll')
- file('PhysXDevice.dll')
-
-class ai(package):
- # This package contains the PandAI library for pathfinding
- # and seek/flee behaviors. As some games may not need this
- # functionality, it is provided as separate package to save
- # on package download size.
-
- config(display_name = "Panda3D AI modules")
- require('panda3d')
-
- module('panda3d.ai', required = True)
- file('libpandaai.dll', required = True)
-
-class vision(package):
- # This package contains the code for webcam support, augmented
- # reality and computer vision. As many games will not need this
- # functionality, it is provided as separate package to save
- # on package download size.
-
- config(display_name = "Panda3D vision modules")
- require('panda3d')
-
- module('panda3d.vision', required = True)
- file('libp3vision.dll', required = True)
-
-class rocket(package):
- # This package contains the code for libRocket integration.
- # As not every application uses libRocket GUI, and to cut down
- # the download size, it is provided as separate package.
-
- config(display_name = "Panda3D libRocket support")
- require('panda3d')
-
- file('rocket.py', extract = True, text = """
-from _rocketcore import *
-try:
- from _rocketcontrols import *
-except ImportError:
- pass
-""")
-
- module('panda3d.rocket', '_rocketcore', required = True)
- module('_rocketcontrols')
- file('libp3rocket.dll', required = True)
-
-class vrpn(package):
- # This package contains the code for VRPN integration.
- # Most applications don't use this, which is why it's separate.
-
- config(display_name = "Panda3D VRPN support")
- require('panda3d')
-
- module('panda3d.vrpn', required = True)
- file('libp3vrpn.dll', required = True)
-
-
-class packp3d(p3d):
- # This application is a command-line convenience for building a p3d
- # application out of a directory hierarchy on disk. We build it here
- # into its own p3d application, to allow end-users to easily build p3d
- # applications using the appropriate version of Python and Panda for
- # the targeted runtime.
-
- config(display_name = "Panda3D Application Packer",
- hidden = True, platform_specific = False,
- keep_user_env = True)
- require('panda3d', 'egg')
-
- mainModule('direct.p3d.packp3d')
-
- file('packp3d.prc', extract = True, text = "preload-textures false")
-
-
-class ppackage(p3d):
- # As above, a packaging utility. This is the fully-general ppackage
- # utility, which reads pdef files (like this one!) and creates one or
- # more packages or p3d applications.
-
- config(display_name = "Panda3D General Package Utility",
- hidden = True, platform_specific = False,
- keep_user_env = True)
- require('panda3d', 'egg')
-
- mainModule('direct.p3d.ppackage')
-
- file('ppackage.prc', extract = True, text = "preload-textures false")
-
-
-class ppatcher(p3d):
- # A handy utility to go along with ppackage. This builds
- # patchfiles as needed in the directory structure created by
- # ppackage.
-
- config(display_name = "Panda3D Patch Maker",
- hidden = True, platform_specific = False,
- keep_user_env = True)
- require('panda3d')
-
- mainModule('direct.p3d.ppatcher')
-
-
-class pmerge(p3d):
- # Another handy utility to go along with ppackage. This
- # merges multiple directory structures as created by
- # ppackage into a single one.
-
- config(display_name = "Panda3D Package Merger",
- hidden = True, platform_specific = False,
- keep_user_env = True)
- require('panda3d')
-
- mainModule('direct.p3d.pmerge')
-
-
-class pdeploy(p3d):
- # This utility can distribute a game in the form of
- # a standalone executable or a graphical installer.
-
- config(display_name = "Panda3D Deployment Tool",
- hidden = True, platform_specific = False,
- keep_user_env = True)
- require('panda3d', 'morepy')
-
- mainModule('direct.p3d.pdeploy')
-
diff --git a/direct/src/p3d/pdeploy.py b/direct/src/p3d/pdeploy.py
deleted file mode 100644
index f98d10b65d..0000000000
--- a/direct/src/p3d/pdeploy.py
+++ /dev/null
@@ -1,359 +0,0 @@
-#! /usr/bin/env python
-
-usageText = """
-
-This command will help you to distribute your Panda application,
-consisting of a .p3d package, into a standalone executable, graphical
-installer or an HTML webpage. It will attempt to create packages
-for every platform, if possible.
-Note that pdeploy requires an internet connection to run.
-
-When used with the 'installer' option, it is strongly advisable
-to specify most if not all of the descriptive information that can
-be passed on the command-line.
-
-Usage:
-
- %(prog)s [opts] app.p3d standalone|installer|html
-
-Modes:
-
- standalone
- A standalone executable will be created that embeds the given
- p3d file. The resulting executable will require an
- internet connection in order to run properly.
-
- installer
- In this mode, installable packages will be created for as many
- platforms as possible. To create Windows installers on
- non-Windows platforms, you need to have the "makensis" utility
- on your system PATH environment variable.
-
- html
- An HTML webpage will be generated that can be used to view
- the provided p3d file in a browser.
-
-Options:
-
- -n your_app
- Short, lowercase name of the application or game. May only
- contain alphanumeric characters, underscore or dash. This
- name will also define the output file(s) of the process.
- If omitted, the basename of the p3d file is used.
-
- -N "Your Application"
- Full name of the application or game. This is the
- name that will be displayed to the end-user.
- The 'display_name' config is used by default. If it
- is missing, the short name is used.
-
- -v version_number
- This should define the version number of your application
- or game. In some deploy modes, this argument is required.
- This should only contain alphanumeric characters, dots and
- dashes, as otherwise the result of the deployment may be
- invalid on some platforms.
-
- -o output_dir
- Indicates the directory where the output will be stored.
- Within this directory, subdirectories will be created
- for every platform, unless -t is provided.
- If omitted, the current working directory is assumed.
-
- -t token=value
- Defines a web token or parameter to pass to the application.
- Use this to configure how the application will be run.
- You can pass as many -t options as you need. Some examples of
- useful token names are width, height, log_basename, auto_start,
- hidden and console_environment.
-
- -P platform
- If this option is provided, it should specify a comma-
- separated list of platforms that the p3d package will be
- deployed for. If omitted, it will be built for all platforms.
- This option may be specified multiple times.
- Examples of valid platforms are win_i386, linux_amd64 and osx_ppc.
-
- -c
- If this option is provided, any -P options are ignored and
- the p3d package is only deployed for the current platform.
- Furthermore, no per-platform subdirectories will be created
- inside the output dirctory.
-
- -s
- This option only has effect in 'installer' mode. If it is
- provided, the resulting installers will be fully self-contained,
- will not require an internet connection to run, and start up
- much faster. Note that pdeploy will also take a very long time
- to finish when -s is provided.
- If it is omitted, pdeploy will finish much quicker, and the
- resulting installers will be smaller, but they will require
- an internet connection for the first run, and the load time
- will be considerably longer.
-
- -l "License Name"
- Specifies the name of the software license that the game
- or application is licensed under.
- Only relevant when generating a graphical installer.
-
- -L licensefile.txt
- This should point to a file that contains the full text
- describing the software license that the game or application
- is licensed under.
- Only relevant when generating a graphical installer.
-
- -O
- Specify this option when generating a graphical installer to omit
- the default checkboxes for "run this program" and "install a
- desktop shortcut" on completion.
-
- -a com.your_company
- Short identifier of the author of the application. The default
- is "org.panda3d", but you will most likely want to change
- it to your own name or that of your organization or company.
- Only relevant when generating a graphical installer.
-
- -A "Your Company"
- Full name of the author of the application. The default is
- determined from the GECOS information of the current user if
- available; if not, your username is used.
- Only relevant when generating a graphical installer.
-
- -e "you@your_company.com"
- E-mail address of the maintainer of the application. The default
- is username@hostname.
- Only relevant when generating a graphical installer.
-
- -i "path/icon32.png" -i "path/icon48.png" -i "path/icon128.png"
- This option should be repeated several times with different square
- image sizes. These images will then be combined to form an icon
- file that will be used to represent the installed application.
- To support all platforms, it is recommended to supply images of
- the sizes 16x16, 32x32, 48x48, 128x128, 256x256, and 512x512.
- The larger icon sizes can safely be omitted if you cannot
- provide images in that resolution.
- It is recommended to use .png images for correct transparency.
- If no images are provided, no icon will be generated.
- Only relevant when generating a graphical installer.
-
- -h
- Display this help
-
-"""
-
-DEPLOY_MODES = ["standalone", "installer", "html"]
-
-import sys
-import os
-import getopt
-from direct.p3d.DeploymentTools import Standalone, Installer, Icon
-from panda3d.core import Filename, PandaSystem
-
-def usage(code, msg = ''):
- if not msg:
- sys.stderr.write(usageText % {'prog' : os.path.split(sys.argv[0])[1]})
- sys.stderr.write(msg + '\n')
- sys.exit(code)
-
-shortname = ""
-fullname = ""
-version = ""
-outputDir = Filename("./")
-tokens = {}
-platforms = []
-currentPlatform = False
-licensename = ""
-licensefile = Filename()
-authorid = ""
-authorname = ""
-authoremail = ""
-iconFiles = []
-includeRequires = False
-omitDefaultCheckboxes = False
-
-try:
- opts, args = getopt.getopt(sys.argv[1:], 'n:N:v:o:t:P:csOl:L:a:A:e:i:h')
-except getopt.error as msg:
- usage(1, msg or 'Invalid option')
-
-for opt, arg in opts:
- if opt == '-n':
- shortname = arg.strip()
- elif opt == '-N':
- fullname = arg.strip()
- elif opt == '-v':
- version = arg.strip()
- elif opt == '-o':
- outputDir = Filename.fromOsSpecific(arg)
- elif opt == '-t':
- token = arg.strip().split("=", 1)
- tokens[token[0]] = token[1]
- elif opt == '-P':
- platforms.append(arg)
- elif opt == '-c':
- currentPlatform = True
- elif opt == '-s':
- includeRequires = True
- elif opt == '-O':
- omitDefaultCheckboxes = True
- elif opt == '-l':
- licensename = arg.strip()
- elif opt == '-L':
- licensefile = Filename.fromOsSpecific(arg)
- elif opt == '-a':
- authorid = arg.strip()
- elif opt == '-A':
- authorname = arg.strip()
- elif opt == '-e':
- authoremail = arg.strip()
- elif opt == '-i':
- iconFiles.append(Filename.fromOsSpecific(arg))
-
- elif opt == '-h':
- usage(0)
- else:
- msg = 'illegal option: ' + flag
- print(msg)
- sys.exit(1, msg)
-
-if not args or len(args) != 2:
- msg = 'Wrong number of arguments, received %s, expected 2' % (
- len(args or []))
- usage(1, msg)
-
-appFilename = Filename.fromOsSpecific(args[0])
-if appFilename.getExtension().lower() != 'p3d':
- print('Application filename must end in ".p3d".')
- sys.exit(1)
-deploy_mode = args[1].lower()
-
-if not appFilename.exists():
- print('Application filename does not exist!')
- sys.exit(1)
-
-if shortname == '':
- shortname = appFilename.getBasenameWoExtension()
-
-if shortname.lower() != shortname or ' ' in shortname:
- print('\nProvided short name should be lowercase, and may not contain spaces!\n')
-
-if version == '' and deploy_mode == 'installer':
- print('\nA version number is required in "installer" mode.\n')
- sys.exit(1)
-
-if not outputDir:
- print('\nYou must name the output directory with the -o parameter.\n')
- sys.exit(1)
-if not outputDir.exists():
- print('\nThe specified output directory does not exist!\n')
- sys.exit(1)
-elif not outputDir.isDirectory():
- print('\nThe specified output directory is a file!\n')
- sys.exit(1)
-
-if deploy_mode == 'standalone':
- s = Standalone(appFilename, tokens)
- s.basename = shortname
-
- if currentPlatform:
- platform = PandaSystem.getPlatform()
- if platform.startswith("win"):
- s.build(Filename(outputDir, shortname + ".exe"), platform)
- else:
- s.build(Filename(outputDir, shortname), platform)
- elif len(platforms) == 0:
- s.buildAll(outputDir)
- else:
- for platform in platforms:
- if platform.startswith("win"):
- s.build(Filename(outputDir, platform + "/" + shortname + ".exe"), platform)
- else:
- s.build(Filename(outputDir, platform + "/" + shortname), platform)
-
-elif deploy_mode == 'installer':
- if includeRequires:
- tokens["verify_contents"] = "never"
- i = Installer(appFilename, shortname, fullname, version, tokens = tokens)
- i.includeRequires = includeRequires
- if omitDefaultCheckboxes:
- i.offerRun = False
- i.offerDesktopShortcut = False
- i.licensename = licensename
- i.licensefile = licensefile
- if authorid:
- i.authorid = authorid
- if authorname:
- i.authorname = authorname
- if authoremail:
- i.authoremail = authoremail
- if not authorname or not authoremail or not authorid:
- print("Using author \"%s\" <%s> with ID %s" % \
- (i.authorname, i.authoremail, i.authorid))
-
- # Add the supplied icon images
- if len(iconFiles) > 0:
- failed = False
- i.icon = Icon()
- for iconFile in iconFiles:
- if not i.icon.addImage(iconFile):
- print('\nFailed to add icon image "%s"!\n' % iconFile)
- failed = True
- if failed:
- sys.exit(1)
-
- # Now build for the requested platforms.
- if currentPlatform:
- platform = PandaSystem.getPlatform()
- if platform.startswith("win"):
- i.build(outputDir, platform)
- else:
- i.build(outputDir, platform)
- elif len(platforms) == 0:
- i.buildAll(outputDir)
- else:
- for platform in platforms:
- output = Filename(outputDir, platform + "/")
- output.makeDir()
- i.build(output, platform)
-
- del i
-
-elif deploy_mode == 'html':
- w, h = tokens.get("width", 800), tokens.get("height", 600)
- if "data" not in tokens:
- tokens["data"] = appFilename.getBasename()
-
- print("Creating %s.html..." % shortname)
- html = open(Filename(outputDir, shortname + ".html").toOsSpecific(), "w")
- html.write("\n")
- html.write("\n")
- html.write(" \n")
- html.write(" %s \n" % fullname)
- html.write(" \n")
- if authorname:
- html.write(" \n" % authorname.replace('"', '\\"'))
- html.write(" \n")
- html.write(" \n")
- html.write(" ")
- html.write(" \n" % (w, h))
- for key, value in tokens.items():
- html.write(" \n" % (key, value.replace('"', '\\"')))
- html.write(" \n")
- html.write(" \n")
- html.write(" \n")
- html.write("\n")
- html.close()
-else:
- usage(1, 'Invalid deployment mode!')
-
-# An explicit call to exit() is required to exit the program, when
-# this module is packaged in a p3d file.
-sys.exit(0)
-
diff --git a/direct/src/p3d/pmerge.py b/direct/src/p3d/pmerge.py
deleted file mode 100755
index 99492df35b..0000000000
--- a/direct/src/p3d/pmerge.py
+++ /dev/null
@@ -1,106 +0,0 @@
-#! /usr/bin/env python
-
-usageText = """
-
-This script can be used to merge together the contents of two or more
-separately-built stage directories, built independently via ppackage,
-or via Packager.py. This script also verifies the hash, file size,
-and timestamp values in the stage directory as it runs, so it can be
-run on a single standalone directory just to perform this validation.
-
-This script is actually a wrapper around Panda's PackageMerger.py.
-
-Usage:
-
- %(prog)s [opts] [inputdir1 .. inputdirN]
-
-Parameters:
-
- inputdir1 .. inputdirN
- Specify the full path to the input directories you wish to merge.
- These are the directories specified by -i on the previous
- invocations of ppackage. The order is mostly unimportant.
-
-Options:
-
- -i install_dir
- The full path to the final install directory. This may also
- contain some pre-existing contents; if so, it is merged with all
- of the input directories as well. The contents of this directory
- are checked for self-consistency with regards to hashes and
- timestamps.
-
- -p packageName[,packageName...]
- Specifies one or more particular packages by name that are to be
- included from the input directories. Any packages not in this
- list are left unchanged in the install directory, even if there
- is a newer version in one of the input directories. If no
- packages are named, all packages are involved. This option may
- be repeated.
-
- -h
- Display this help
-
-"""
-
-import sys
-import getopt
-import os
-
-from direct.p3d import PackageMerger
-from panda3d.core import Filename
-
-def usage(code, msg = ''):
- sys.stderr.write(usageText % {'prog' : os.path.split(sys.argv[0])[1]})
- sys.stderr.write(msg + '\n')
- sys.exit(code)
-
-try:
- opts, args = getopt.getopt(sys.argv[1:], 'i:p:h')
-except getopt.error as msg:
- usage(1, msg)
-
-installDir = None
-packageNames = []
-for opt, arg in opts:
- if opt == '-i':
- installDir = Filename.fromOsSpecific(arg)
- elif opt == '-p':
- packageNames += arg.split(',')
-
- elif opt == '-h':
- usage(0)
- else:
- print('illegal option: ' + arg)
- sys.exit(1)
-
-if not packageNames:
- # No package names means allow all packages.
- packageNames = None
-
-inputDirs = []
-for arg in args:
- inputDirs.append(Filename.fromOsSpecific(arg))
-
-# It's now legal to have no input files if you only want to verify
-# timestamps and hashes.
-## if not inputDirs:
-## print "no input directories specified."
-## sys.exit(1)
-
-try:
- pm = PackageMerger.PackageMerger(installDir)
- for dir in inputDirs:
- pm.merge(dir, packageNames = packageNames)
- pm.close()
-
-except PackageMerger.PackageMergerError:
- # Just print the error message and exit gracefully.
- inst = sys.exc_info()[1]
- print(inst.args[0])
- sys.exit(1)
-
-
-# An explicit call to exit() is required to exit the program, when
-# this module is packaged in a p3d file.
-sys.exit(0)
diff --git a/direct/src/p3d/ppackage.py b/direct/src/p3d/ppackage.py
deleted file mode 100755
index cf42ea8403..0000000000
--- a/direct/src/p3d/ppackage.py
+++ /dev/null
@@ -1,267 +0,0 @@
-#! /usr/bin/env python
-
-usageText = """
-This script can be used to produce a Panda3D downloadable "package",
-which may contain arbitrary files--for instance, Python code, bam
-files, and/or compiled DLL's--and which may be downloaded by
-application code to extend an application at runtime.
-
-In addition to packages, this script can also be used to build
-standalone p3d applications, that is, packaged Python code in a p3d
-file, for execution by the Panda3D plugin or runtime. (But also see
-packp3d, which is designed to be a simpler interface for building
-applications.)
-
-This command will build p3d files that reference Panda3D %(version)s,
-from host %(host)s .
-
-This script is actually a wrapper around Panda's Packager.py, which
-can be invoked directly by Python code that requires a programmatic
-interface to building packages.
-
-Usage:
-
- %(prog)s [opts] package.pdef [packageName1 .. packageNameN]
-
-Parameters:
-
- package.pdef
- The config file that describes the contents of the package file(s)
- to be built, in excruciating detail. See the Panda3D manual for
- the syntax of this file.
-
- packageName1 .. packageNameN
- Specify the names of the package(s) you wish to build out of the
- package.pdef file. This allows you to build only a subset of the
- packages defined in this file. If you omit these parameters, all
- packages are built, and packages that cannot be built are silently
- ignored.
-
-Options:
-
- -i install_dir
- The full path to a local directory to copy the
- ready-to-be-published files into. This directory structure may
- contain multiple different packages from multiple different
- invocations of this script. It is the user's responsibility to
- copy this directory structure to a server, which will have the
- URL specified by the packager.setHost() call appearing within the
- pdef file.
-
- -p
- Automatically build patches against previous versions after
- generating the results. Patches are difference files that users
- can download when updating a package, in lieu of redownloading
- the whole package; this happens automatically if patches are
- present. You should generally build patches when you are
- committing to a final, public-facing release. Patches are
- usually a good idea, but generating a patch for each internal
- test build may needlessly generate a lot of small, inefficient
- patch files instead of a few larger ones. You can also generate
- patches after the fact, by running ppatcher on the install
- directory.
-
- -s search_dir
- Additional directories to search for previously-built packages.
- This option may be repeated as necessary. These directories may
- also be specified with the pdef-path Config.prc variable.
-
- -S file.crt[,chain.crt[,file.key[,\"password\"]]]
- Signs any resulting p3d file(s) with the indicated certificate.
- You may specify the signing certificate, the optional
- authorization chain, and the private key in three different
- files, or they may all be combined in the first file. If the
- private key is encrypted, the password will be required to
- decrypt it.
-
- -D
- Sets the allow_python_dev flag in any applications built with
- this command. This enables additional runtime debug operations,
- particularly the -i option to the panda3d command, which enables
- a live Python prompt within the application's environment.
- Setting this flag may be useful to develop an application
- initially, but should not be set on an application intended for
- deployment.
-
- -N
- If this option is set, Packager will not try to compile any Python
- files to .pyc or .pyo, instead storing the original source files.
-
- -u
- On the Mac OSX platform, this means that Panda was built with
- universal binaries, and the package should be built that way as
- well (that is, a version of the the package should be created for
- each supported architecture). On other platforms, this option
- does nothing. This is therefore safe to apply in all cases, if
- you wish to take advantage of universal binaries. This is
- equivalent to "-P osx_i386 -P osx_amd64" on Mac platforms.
-
- -P platform
- Specify the platform to masquerade as. The default is whatever
- platform Panda has been built for. You can use this on Mac OSX
- in order to build packages for an alternate architecture if you
- have built Panda with universal binaries; you may repeat this
- option for each architecture you wish to support. For other
- platforms, it is probably a mistake to set this. However, see
- the option -u.
-
- -R sysroot
- Specify the sysroot that these files were compiled against. This
- will shadow the system shared libraries, so that alternate
- versions are used instead of the system versions. If any program
- references a library, say /usr/lib/libfoo.so, and
- /sysroot/usr/lib/libfoo.so exists instead, that file will be used
- instead of the system library. This is particularly useful for
- cross-compilation. At the moment, this is supported only on OSX.
-
- -H
- Treats a packager.setHost() call in the pdef file as if it were
- merely a call to packager.addHost(). This allows producing a
- package for an alternate host than its normally configured host,
- which is sometimes useful in development.
-
- -a suffix
- Appends the given suffix to the p3d filename, before the extension.
- This is useful when the same packages are compiled several times
- but using different settings, and you want to mark those packages
- as such. This only applies to .p3d packages, not to other types
- of packages!
-
- -v
- Emit a warning for any file not recognized by the dir() command
- (indicating there may be a need for addExtensions(...)).
-
- -h
- Display this help
-"""
-
-import sys
-import getopt
-import os
-
-from direct.p3d import Packager
-from panda3d.core import *
-
-def usage(code, msg = ''):
- sys.stderr.write(usageText % {
- 'version' : PandaSystem.getPackageVersionString(),
- 'host' : PandaSystem.getPackageHostUrl(),
- 'prog' : os.path.split(sys.argv[0])[1]
- })
- sys.stderr.write(msg + '\n')
- sys.exit(code)
-
-installDir = None
-buildPatches = False
-installSearch = []
-signParams = []
-allowPythonDev = False
-storePythonSource = False
-universalBinaries = False
-systemRoot = None
-ignoreSetHost = False
-verbosePrint = False
-p3dSuffix = ''
-platforms = []
-
-try:
- opts, args = getopt.getopt(sys.argv[1:], 'i:ps:S:DNuP:R:Ha:hv')
-except getopt.error as msg:
- usage(1, msg)
-
-for opt, arg in opts:
- if opt == '-i':
- installDir = Filename.fromOsSpecific(arg)
- elif opt == '-p':
- buildPatches = True
- elif opt == '-s':
- installSearch.append(Filename.fromOsSpecific(arg))
- elif opt == '-S':
- tokens = arg.split(',')
- while len(tokens) < 4:
- tokens.append('')
- certificate, chain, pkey, password = tokens[:4]
- signParams.append((Filename.fromOsSpecific(certificate),
- Filename.fromOsSpecific(chain),
- Filename.fromOsSpecific(pkey),
- Filename.fromOsSpecific(password)))
- elif opt == '-D':
- allowPythonDev = True
- elif opt == '-N':
- storePythonSource = True
- elif opt == '-u':
- universalBinaries = True
- elif opt == '-P':
- platforms.append(arg)
- elif opt == '-R':
- systemRoot = arg
- elif opt == '-H':
- ignoreSetHost = True
- elif opt == '-a':
- p3dSuffix = arg
-
- elif opt == '-v':
- verbosePrint = True
-
- elif opt == '-h':
- usage(0)
- else:
- print('illegal option: ' + arg)
- sys.exit(1)
-
-if not args:
- usage(0)
-
-packageDef = Filename.fromOsSpecific(args[0])
-packageNames = None
-if len(args) > 1:
- packageNames = args[1:]
-
-# Add the directory containing the pdef file itself to sys.path, to
-# help the Packager locate modules where a pathname isn't specified.
-dirname = packageDef.getDirname()
-if dirname:
- sys.path.append(Filename(dirname).toOsSpecific())
-else:
- sys.path.append('.')
-
-if universalBinaries:
- if platforms:
- print('\nYou may not specify both -u and -P.\n')
- sys.exit(1)
- if PandaSystem.getPlatform().startswith('osx_'):
- platforms = ['osx_i386', 'osx_amd64']
-
-if not platforms:
- platforms = [PandaSystem.getPlatform()]
-
-for platform in platforms:
- packager = Packager.Packager(platform = platform)
- packager.installDir = installDir
- packager.installSearch = installSearch + packager.installSearch
- if installDir is not None:
- packager.installSearch = [installDir] + packager.installSearch
- packager.signParams = signParams
- packager.allowPythonDev = allowPythonDev
- packager.storePythonSource = storePythonSource
- packager.systemRoot = systemRoot
- packager.ignoreSetHost = ignoreSetHost
- packager.verbosePrint = verbosePrint
- packager.p3dSuffix = p3dSuffix
-
- try:
- packager.setup()
- packages = packager.readPackageDef(packageDef, packageNames = packageNames)
- packager.close()
- if buildPatches:
- packager.buildPatches(packages)
-
- except Packager.PackagerError:
- # Just print the error message and exit gracefully.
- inst = sys.exc_info()[1]
- print(inst.args[0])
- sys.exit(1)
-
-# An explicit call to exit() is required to exit the program, when
-# this module is packaged in a p3d file.
-sys.exit(0)
diff --git a/direct/src/p3d/ppatcher.py b/direct/src/p3d/ppatcher.py
deleted file mode 100755
index 69e9689622..0000000000
--- a/direct/src/p3d/ppatcher.py
+++ /dev/null
@@ -1,101 +0,0 @@
-#! /usr/bin/env python
-
-usageText = """
-
-This script generates the patches required to support incremental
-download of Panda3D packages. It can be run as a post-process on a
-directory hierarchy created by ppackage; it will examine the directory
-hierarchy, and create any patches that appear to be missing.
-
-You may run ppackage on the same directory hierarchy as many times as
-you like, without creating patches. You may then download and test
-the resulting files--users connecting to the tree without fresh
-patches will be forced to download the entire file, instead of making
-an incremental download, but the entire process will work otherwise.
-When you are satisfied that all of the files are ready to be released,
-you may run ppackage on the directory hierarchy to generate the
-required patches.
-
-Generating the patches just before final release is a good idea to
-limit the number of trivially small patches that are created. Each
-time this script is run, a patch is created from the previous version,
-and these patches daisy-chain together to define a complete update
-sequence. If you run this script on internal releases, you will
-generate a long chain of small patches that your users must download;
-this is pointless if there is no possibility of anyone having
-downloaded one of the intervening versions.
-
-You can also generate patches with the -p option to ppackage, but that
-only generates patches for the specific packages built by that
-invocation of ppackage. If you use the ppatcher script instead, it
-will generate patches for all packages (or the set of packages that
-you name specifically).
-
-This script is actually a wrapper around Panda's PatchMaker.py.
-
-Usage:
-
- %(prog)s [opts] [packageName1 .. packageNameN]
-
-Parameters:
-
- packageName1 .. packageNameN
- Specify the names of the package(s) you wish to generate patches
- for. This allows you to build patches for only a subset of the
- packages found in the tree. If you omit these parameters, patches
- are built for all packages that require them.
-
-Options:
-
- -i install_dir
- The full path to the install directory. This should be the same
- directory named by the -i parameter to ppackage.
-
- -h
- Display this help
-
-"""
-
-import sys
-import getopt
-import os
-
-from direct.p3d.PatchMaker import PatchMaker
-from panda3d.core import Filename
-
-def usage(code, msg = ''):
- sys.stderr.write(usageText % {'prog' : os.path.split(sys.argv[0])[1]})
- sys.stderr.write(msg + '\n')
- sys.exit(code)
-
-try:
- opts, args = getopt.getopt(sys.argv[1:], 'i:h')
-except getopt.error as msg:
- usage(1, msg)
-
-installDir = None
-for opt, arg in opts:
- if opt == '-i':
- installDir = Filename.fromOsSpecific(arg)
-
- elif opt == '-h':
- usage(0)
- else:
- print('illegal option: ' + arg)
- sys.exit(1)
-
-packageNames = args
-
-if not installDir:
- installDir = Filename('install')
-
-if not packageNames:
- # "None" means all packages.
- packageNames = None
-
-pm = PatchMaker(installDir)
-pm.buildPatches(packageNames = packageNames)
-
-# An explicit call to exit() is required to exit the program, when
-# this module is packaged in a p3d file.
-sys.exit(0)
diff --git a/direct/src/p3d/runp3d.py b/direct/src/p3d/runp3d.py
deleted file mode 100644
index 2ab457bc3c..0000000000
--- a/direct/src/p3d/runp3d.py
+++ /dev/null
@@ -1,79 +0,0 @@
-#! /usr/bin/env python
-
-"""
-
-This tool will invoke the AppRunner to execute a packaged p3d
-application. It requires that that the current Panda3D and Python
-versions match the version expected by the application.
-
-Normally, you do not need to use this tool; instead, use the provided
-standalone panda3d executable to invoke any p3d application. Using
-panda3d will guarantee that the correct versions of Panda3D and Python
-are used to run the application. However, there may be occasions when
-it is useful to use this tool to run the application with the current
-build instead of with its advertised version requirements.
-
-Usage:
-
- runp3d.py app.p3d [args]
-
-The command-line arguments following the application name are passed
-into the application unchanged.
-
-See pack3d.p3d for an application that generates these p3d files.
-
-"""
-
-import sys
-import getopt
-from .AppRunner import AppRunner, ArgumentError
-from direct.task.TaskManagerGlobal import taskMgr
-from panda3d.core import Filename
-
-def parseSysArgs():
- """ Handles sys.argv, if there are any local arguments, and
- returns a new argv suitable for passing into the
- application. """
-
- # We prefix a "+" sign, following the GNU convention, to tell
- # getopt not to parse options following the first non-option
- # parameter.
- opts, args = getopt.getopt(sys.argv[1:], '+h')
-
- for option, value in opts:
- if option == '-h':
- print(__doc__)
- sys.exit(1)
-
- if not args or not args[0]:
- raise ArgumentError("No Panda app specified. Use:\nrunp3d.py app.p3d")
-
- arg0 = args[0]
- p3dFilename = Filename.fromOsSpecific(arg0)
- if p3dFilename.exists():
- p3dFilename.makeAbsolute()
- arg0 = p3dFilename.toOsSpecific()
-
- return [arg0] + args[1:]
-
-def runPackedApp(pathname):
- runner = AppRunner()
- runner.gotWindow = True
- try:
- runner.setP3DFilename(pathname, tokens = [], argv = [],
- instanceId = 0, interactiveConsole = False)
- except ArgumentError as e:
- print(e.args[0])
- sys.exit(1)
-
-if __name__ == '__main__':
- runner = AppRunner()
- runner.gotWindow = True
- try:
- argv = parseSysArgs()
- runner.setP3DFilename(argv[0], tokens = [], argv = argv,
- instanceId = 0, interactiveConsole = False)
- except ArgumentError as e:
- print(e.args[0])
- sys.exit(1)
- taskMgr.run()
diff --git a/direct/src/p3d/thirdparty.pdef b/direct/src/p3d/thirdparty.pdef
deleted file mode 100644
index bc001e427d..0000000000
--- a/direct/src/p3d/thirdparty.pdef
+++ /dev/null
@@ -1,167 +0,0 @@
-# This file defines a number of standard "packages" that correspond to
-# a Panda3D distribution. These packages are built by passing this
-# file to the ppackage utility, either as a packaged application, or
-# as the module direct.p3d.ppackage.
-
-# The packages in this file define the thirdparty
-# packages that could be used by a .p3d archive.
-
-# When needed, these packages are downloaded by the core API, from the
-# host URL specified in a given p3d file, and not from any hardcoded
-# URL. Thus, any custom version of Panda3D may be hosted on any
-# server in the world, and any version of the plugin can download it.
-
-# Also see panda3d.pdef.
-
-from panda3d.core import Filename
-import sys
-
-class wx(package):
- config(display_name = "wxPython GUI Toolkit")
- #config(gui_app = True)
- require('panda3d')
-
- module('direct.showbase.WxGlobal')
- module('direct.wxwidgets')
- module('direct.wxwidgets.*')
- module('wx', required = True)
- module('wx.*')
-
-class tk(package):
- config(display_name = "Tk GUI Toolkit")
- #config(gui_app = True)
- require('panda3d')
-
- if sys.version_info >= (3, 0):
- module('tkinter', '_tkinter', required = True)
- module('tkinter.colorchooser', 'tkinter.commondialog',
- 'tkinter.constants', 'tkinter.dialog', 'tkinter.dnd',
- 'tkinter.filedialog', 'tkinter.font', 'tkinter.messagebox',
- 'tkinter.scrolledtext', 'tkinter.simpledialog', 'tkinter.tix',
- 'tkinter.ttk')
- else:
- module('Tkinter', '_tkinter', required = True)
- module('Tkconstants', 'Tkdnd', 'Tix', 'ScrolledText', 'turtle',
- 'tkColorChooser', 'tkCommonDialog', 'tkFileDialog',
- 'tkFont', 'tkMessageBox', 'tkSimpleDialog')
-
- module('direct.showbase.TkGlobal',
- 'direct.tkpanels',
- 'direct.tkwidgets')
-
- try:
- if sys.version_info >= (3, 0):
- from tkinter import Tcl
- else:
- from Tkinter import Tcl
- tcl = Tcl()
- dir = Filename.fromOsSpecific(tcl.eval("info library"))
- ver = tcl.eval("info tclversion")
-
- file(Filename(dir, "*.tcl"), newDir = 'lib/tcl' + ver, extract = True)
- except ImportError:
- pass
-
- try:
- import Pmw
- except ImportError:
- Pmw = None
-
- if Pmw is not None:
- Pmw = sys.modules['_Pmw']
- sys.modules['Pmw'] = Pmw
- sys.modules['Pmw'].__name__ = 'Pmw'
- module('Pmw', 'Pmw.Pmw_1_3.lib.*')
- path = Filename.fromOsSpecific(Pmw.__path__[0])
- file(Filename(path, 'Pmw_1_3/lib/Pmw.def'), newDir = 'Pmw/Pmw_1_3/lib')
-
-class sqlite(package):
- config(display_name = "SQLite Interface")
- require('panda3d')
-
- module('sqlite', 'sqlite3')
-
-class numpy(package):
- config(display_name = "NumPy")
- require('panda3d')
-
- module('numpy', required = True)
-
-class pygame(package):
- config(display_name = "PyGame")
- require('panda3d', 'numpy')
-
- module('pygame', required = True)
- module('pygame.*', 'pygame._view')
-
-class twisted(package):
- config(display_name = "Twisted")
- require('panda3d')
-
- module('twisted', 'twisted._version', required = True)
- module('twisted.application', 'twisted.conch', 'twisted.cred',
- 'twisted.enterprise', 'twisted.internet', 'twisted.lore',
- 'twisted.mail', 'twisted.manhole', 'twisted.names',
- 'twisted.news', 'twisted.pair', 'twisted.persisted',
- 'twisted.plugin', 'twisted.plugins', 'twisted.python',
- 'twisted.runner', 'twisted.scripts', 'twisted.spread',
- 'twisted.tap', 'twisted.trial', 'twisted.vfs',
- 'twisted.web', 'twisted.web2', 'twisted.words')
-
- module('twisted.*', 'twisted.*.*')
-
-class pil(package):
- config(display_name = "Python Imaging Library")
- require('panda3d')
-
- module('Image', required = True)
- module('PIL', 'ArgImagePlugin', 'BdfFontFile',
- 'BmpImagePlugin', 'BufrStubImagePlugin', 'ContainerIO',
- 'CurImagePlugin', 'DcxImagePlugin', 'EpsImagePlugin',
- 'ExifTags', 'FitsStubImagePlugin', 'FliImagePlugin',
- 'FontFile', 'FpxImagePlugin', 'GbrImagePlugin', 'GdImageFile',
- 'GifImagePlugin', 'GimpGradientFile', 'GimpPaletteFile',
- 'GribStubImagePlugin', 'Hdf5StubImagePlugin',
- 'IcnsImagePlugin', 'IcoImagePlugin', 'ImImagePlugin',
- 'ImageChops', 'ImageCms', 'ImageColor', 'ImageDraw',
- 'ImageDraw2', 'ImageEnhance', 'ImageFile', 'ImageFileIO',
- 'ImageFilter', 'ImageFont', 'ImageGL', 'ImageGrab',
- 'ImageMath', 'ImageMode', 'ImageOps', 'ImagePalette',
- 'ImagePath', 'ImageQt', 'ImageSequence', 'ImageShow',
- 'ImageStat', 'ImageTk', 'ImageTransform', 'ImageWin',
- 'ImtImagePlugin', 'IptcImagePlugin', 'JpegImagePlugin',
- 'McIdasImagePlugin', 'MicImagePlugin', 'MpegImagePlugin',
- 'MspImagePlugin', 'OleFileIO', 'PSDraw', 'PaletteFile',
- 'PalmImagePlugin', 'PcdImagePlugin', 'PcfFontFile',
- 'PcxImagePlugin', 'PdfImagePlugin', 'PixarImagePlugin',
- 'PngImagePlugin', 'PpmImagePlugin', 'PsdImagePlugin',
- 'SgiImagePlugin', 'SpiderImagePlugin', 'SunImagePlugin',
- 'TarIO', 'TgaImagePlugin', 'TiffImagePlugin', 'TiffTags',
- 'WalImageFile', 'WmfImagePlugin', 'XVThumbImagePlugin',
- 'XbmImagePlugin', 'XpmImagePlugin', '_imaging',
- '_imagingmath', '_imagingtk')
-
-class pyopengl(package):
- config(display_name = "PyOpenGL")
- require('panda3d', 'numpy')
-
- module('OpenGL', 'OpenGL.GL', required = True)
- module('OpenGL.GLU', 'OpenGL.GLUT', 'OpenGL.GLE', 'OpenGL.GLX')
-
-class httplib2(package):
- config(display_name = "httplib2", platform_specific = False)
- require('panda3d')
-
- module('httplib2', required = True)
-
-class box2d(package):
- config(display_name = "Box2D")
- require('panda3d')
-
- module('Box2D', required = True)
-
-class pyglet(package):
- config(display_name = "pyglet", platform_specific = False)
- require('panda3d', 'morepy')
-
- module('pyglet', required = True)
diff --git a/direct/src/particles/ParticleEffect.py b/direct/src/particles/ParticleEffect.py
index c58b7b4d46..06b701b388 100644
--- a/direct/src/particles/ParticleEffect.py
+++ b/direct/src/particles/ParticleEffect.py
@@ -7,6 +7,12 @@ from . import Particles
from . import ForceGroup
from direct.directnotify import DirectNotifyGlobal
+import sys
+
+
+if sys.version_info < (3, 0):
+ FileNotFoundError = IOError
+
class ParticleEffect(NodePath):
notify = DirectNotifyGlobal.directNotify.newCategory('ParticleEffect')
@@ -163,49 +169,51 @@ class ParticleEffect(NodePath):
def saveConfig(self, filename):
filename = Filename(filename)
- f = open(filename.toOsSpecific(), 'wb')
- # Add a blank line
- f.write('\n')
+ with open(filename.toOsSpecific(), 'w') as f:
+ # Add a blank line
+ f.write('\n')
- # Make sure we start with a clean slate
- f.write('self.reset()\n')
+ # Make sure we start with a clean slate
+ f.write('self.reset()\n')
- pos = self.getPos()
- hpr = self.getHpr()
- scale = self.getScale()
- f.write('self.setPos(%0.3f, %0.3f, %0.3f)\n' %
- (pos[0], pos[1], pos[2]))
- f.write('self.setHpr(%0.3f, %0.3f, %0.3f)\n' %
- (hpr[0], hpr[1], hpr[2]))
- f.write('self.setScale(%0.3f, %0.3f, %0.3f)\n' %
- (scale[0], scale[1], scale[2]))
+ pos = self.getPos()
+ hpr = self.getHpr()
+ scale = self.getScale()
+ f.write('self.setPos(%0.3f, %0.3f, %0.3f)\n' %
+ (pos[0], pos[1], pos[2]))
+ f.write('self.setHpr(%0.3f, %0.3f, %0.3f)\n' %
+ (hpr[0], hpr[1], hpr[2]))
+ f.write('self.setScale(%0.3f, %0.3f, %0.3f)\n' %
+ (scale[0], scale[1], scale[2]))
- # Save all the particles to file
- num = 0
- for p in list(self.particlesDict.values()):
- target = 'p%d' % num
- num = num + 1
- f.write(target + ' = Particles.Particles(\'%s\')\n' % p.getName())
- p.printParams(f, target)
- f.write('self.addParticles(%s)\n' % target)
+ # Save all the particles to file
+ num = 0
+ for p in list(self.particlesDict.values()):
+ target = 'p%d' % num
+ num = num + 1
+ f.write(target + ' = Particles.Particles(\'%s\')\n' % p.getName())
+ p.printParams(f, target)
+ f.write('self.addParticles(%s)\n' % target)
- # Save all the forces to file
- num = 0
- for fg in list(self.forceGroupDict.values()):
- target = 'f%d' % num
- num = num + 1
- f.write(target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \
- fg.getName())
- fg.printParams(f, target)
- f.write('self.addForceGroup(%s)\n' % target)
-
- # Close the file
- f.close()
+ # Save all the forces to file
+ num = 0
+ for fg in list(self.forceGroupDict.values()):
+ target = 'f%d' % num
+ num = num + 1
+ f.write(target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \
+ fg.getName())
+ fg.printParams(f, target)
+ f.write('self.addForceGroup(%s)\n' % target)
def loadConfig(self, filename):
- data = vfs.readFile(filename, 1)
- data = data.replace(b'\r', b'')
+ fn = Filename(filename)
+ vfs = VirtualFileSystem.getGlobalPtr()
try:
+ if not vfs.resolveFilename(fn, getModelPath().value) and not fn.isRegularFile():
+ raise FileNotFoundError("could not find particle file: %s" % (filename))
+
+ data = vfs.readFile(fn, True)
+ data = data.replace(b'\r', b'')
exec(data)
except:
self.notify.warning('loadConfig: failed to load particle file: '+ repr(filename))
diff --git a/direct/src/particles/Particles.py b/direct/src/particles/Particles.py
index 6ed19af896..d05700c3b1 100644
--- a/direct/src/particles/Particles.py
+++ b/direct/src/particles/Particles.py
@@ -22,6 +22,7 @@ from panda3d.physics import RingEmitter
from panda3d.physics import SphereSurfaceEmitter
from panda3d.physics import SphereVolumeEmitter
from panda3d.physics import TangentRingEmitter
+from panda3d.physics import SpriteAnim
from . import SpriteParticleRendererExt
diff --git a/direct/src/plugin/binaryXml.cxx b/direct/src/plugin/binaryXml.cxx
deleted file mode 100644
index 7dc4a60268..0000000000
--- a/direct/src/plugin/binaryXml.cxx
+++ /dev/null
@@ -1,351 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file binaryXml.cxx
- * @author drose
- * @date 2009-07-13
- */
-
-#include "binaryXml.h"
-#include "p3d_lock.h"
-#include
-
-using std::istream;
-using std::ostream;
-using std::ostringstream;
-using std::string;
-
-static const bool debug_xml_output = false;
-
-static LOCK xml_lock;
-static bool xml_lock_initialized = false;
-
-#define DO_BINARY_XML 1
-
-enum NodeType {
- NT_unknown,
- NT_document,
- NT_element,
- NT_text,
-};
-
-// This typedef defines a 32-bit unsigned integer. It's used for passing
-// values through the binary XML stream.
-typedef unsigned int xml_uint32;
-
-// These are both prime numbers, though I don't know if that really matters.
-// Mainly, they're big random numbers.
-static const xml_uint32 length_nonce1 = 812311453;
-static const xml_uint32 length_nonce2 = 612811373;
-
-/**
- * Should be called before spawning any threads to ensure the lock is
- * initialized.
- */
-void
-init_xml() {
- if (!xml_lock_initialized) {
- INIT_LOCK(xml_lock);
- xml_lock_initialized = true;
- }
-}
-
-/**
- * Recursively writes a node and all of its children to the given stream.
- */
-static void
-write_xml_node(ostream &out, TiXmlNode *xnode) {
- const string &value = xnode->ValueStr();
- xml_uint32 value_length = value.length();
- xml_uint32 value_proof = (value_length + length_nonce1) * length_nonce2;
-
- // We write out not only value_length, but the same value again hashed by
- // length_nonce1 and 2 (and truncated back to xml_uint32), just to prove to
- // the reader that we're still on the same page. We do this only on the top
- // node; we don't bother for the nested nodes.
- out.write((char *)&value_length, sizeof(value_length));
- out.write((char *)&value_proof, sizeof(value_proof));
- out.write(value.data(), value_length);
-
- // Now write out the node type.
- NodeType type = NT_element;
- if (xnode->ToDocument() != nullptr) {
- type = NT_document;
- } else if (xnode->ToElement() != nullptr) {
- type = NT_element;
- } else if (xnode->ToText() != nullptr) {
- type = NT_text;
- } else {
- type = NT_unknown;
- }
-
- out.put((char)type);
- // We don't bother to write any further data for the unknown types.
- if (type == NT_unknown) {
- return;
- }
-
- if (type == NT_element) {
- // Write the element attributes.
- TiXmlElement *xelement = xnode->ToElement();
- assert(xelement != nullptr);
- const TiXmlAttribute *xattrib = xelement->FirstAttribute();
-
- while (xattrib != nullptr) {
- // We have an attribute.
- out.put((char)true);
-
- string name = xattrib->Name();
- xml_uint32 name_length = name.length();
- out.write((char *)&name_length, sizeof(name_length));
- out.write(name.data(), name_length);
-
- const string &value = xattrib->ValueStr();
- xml_uint32 value_length = value.length();
- out.write((char *)&value_length, sizeof(value_length));
- out.write(value.data(), value_length);
-
- xattrib = xattrib->Next();
- }
-
- // The end of the attributes list.
- out.put((char)false);
- }
-
- // Now write all of the children.
- TiXmlNode *xchild = xnode->FirstChild();
- while (xchild != nullptr) {
- // We have a child.
- out.put((char)true);
- write_xml_node(out, xchild);
- xchild = xchild->NextSibling();
- }
-
- // The end of the children list.
- out.put((char)false);
-}
-
-/**
- * Recursively reads a node and all of its children to the given stream.
- * Returns the newly-allocated node. The caller is responsible for eventually
- * deleting the return value. Returns NULL on error.
- */
-static TiXmlNode *
-read_xml_node(istream &in, char *&buffer, xml_uint32 &buffer_length,
- ostream &logfile) {
- xml_uint32 value_length;
- in.read((char *)&value_length, sizeof(value_length));
- if (in.gcount() != sizeof(value_length)) {
- return nullptr;
- }
- xml_uint32 value_proof_expect = (value_length + length_nonce1) * length_nonce2;
- xml_uint32 value_proof;
- in.read((char *)&value_proof, sizeof(value_proof));
- if (in.gcount() != sizeof(value_proof)) {
- return nullptr;
- }
- if (value_proof != value_proof_expect) {
- // Hey, we ran into garbage: the proof value didn't match our expected
- // proof value.
- logfile << "Garbage on XML stream!\n";
-
- // Print out the garbage; maybe it will help the developer figure out
- // where it came from.
- logfile << "Begin garbage:\n";
- ostringstream strm;
- strm.write((char *)&value_length, sizeof(value_length));
- strm.write((char *)&value_proof, sizeof(value_proof));
- logfile << strm.str();
- for (size_t i = 0; i < 100; ++i) {
- int ch = in.get();
- if (ch != EOF) {
- logfile.put(ch);
- }
- }
- logfile << "\n";
- logfile << "End garbage.\n";
- return nullptr;
- }
-
- if (value_length > buffer_length) {
- delete[] buffer;
- buffer_length = value_length;
- buffer = new char[buffer_length];
- }
-
- in.read(buffer, value_length);
- string value(buffer, value_length);
-
- // Read the node type.
- NodeType type = (NodeType)in.get();
- if (type == NT_unknown) {
- return nullptr;
- }
-
- TiXmlNode *xnode = nullptr;
- if (type == NT_element) {
- xnode = new TiXmlElement(value);
- } else if (type == NT_document) {
- xnode = new TiXmlDocument;
- } else if (type == NT_text) {
- xnode = new TiXmlText(value);
- } else {
- assert(false);
- }
-
- if (type == NT_element) {
- // Read the element attributes.
- TiXmlElement *xelement = xnode->ToElement();
- assert(xelement != nullptr);
- bool got_attrib = (bool)(in.get() != 0);
-
- while (got_attrib && in && !in.eof()) {
- // We have an attribute.
- xml_uint32 name_length;
- in.read((char *)&name_length, sizeof(name_length));
- if (in.gcount() != sizeof(name_length)) {
- delete xnode;
- return nullptr;
- }
-
- if (name_length > buffer_length) {
- delete[] buffer;
- buffer_length = name_length;
- buffer = new char[buffer_length];
- }
-
- in.read(buffer, name_length);
- string name(buffer, name_length);
-
- xml_uint32 value_length;
- in.read((char *)&value_length, sizeof(value_length));
- if (in.gcount() != sizeof(value_length)) {
- delete xnode;
- return nullptr;
- }
-
- if (value_length > buffer_length) {
- delete[] buffer;
- buffer_length = value_length;
- buffer = new char[buffer_length];
- }
-
- in.read(buffer, value_length);
- string value(buffer, value_length);
-
- xelement->SetAttribute(name, value);
-
- got_attrib = (bool)(in.get() != 0);
- }
- }
-
- // Now read all of the children.
- bool got_child = (bool)(in.get() != 0);
-
- while (got_child && in && !in.eof()) {
- // We have a child.
- TiXmlNode *xchild = read_xml_node(in, buffer, buffer_length, logfile);
- if (xchild != nullptr) {
- xnode->LinkEndChild(xchild);
- }
-
- got_child = (bool)(in.get() != 0);
- }
-
- return xnode;
-}
-
-
-
-/**
- * Writes the indicated TinyXml document to the given stream.
- */
-void
-write_xml(ostream &out, TiXmlDocument *doc, ostream &logfile) {
- assert(xml_lock_initialized);
- ACQUIRE_LOCK(xml_lock);
-
-#ifdef DO_BINARY_XML
- // Binary write.
- write_xml_node(out, doc);
-
-#else
- // Formatted ASCII write.
-
- // We need a declaration to write it safely.
- TiXmlDeclaration decl("1.0", "utf-8", "");
- doc->InsertBeforeChild(doc->FirstChild(), decl);
-
- out << *doc;
-#endif
-
- out << flush;
-
- if (debug_xml_output) {
- // Write via ostringstream, so it all goes in one operation, to help out
- // the interleaving from multiple threads.
- ostringstream logout;
- logout << "sent: " << *doc << "\n";
- logfile << logout.str() << flush;
- }
-
- RELEASE_LOCK(xml_lock);
-}
-
-/**
- * Reads a TinyXml document from the given stream, and returns it. If the
- * document is not yet available, blocks until it is, or until there is an
- * error condition on the input.
- *
- * The return value is NULL if there is an error, or the newly-allocated
- * document if it is successfully read. If not NULL, the document has been
- * allocated with new, and should be eventually freed by the caller with
- * delete.
- */
-TiXmlDocument *
-read_xml(istream &in, ostream &logfile) {
- // We don't acquire xml_lock while reading. We can't, because our XML
- // readers are all designed to block until data is available, and they can't
- // block while holding the lock.
-
- // Fortunately, there should be only one reader at a time, so a lock isn't
- // really needed here.
-
-#if DO_BINARY_XML
- // binary read.
- xml_uint32 buffer_length = 128;
- char *buffer = new char[buffer_length];
- TiXmlNode *xnode = read_xml_node(in, buffer, buffer_length, logfile);
- delete[] buffer;
- if (xnode == nullptr) {
- return nullptr;
- }
-
- TiXmlDocument *doc = xnode->ToDocument();
- assert(doc != nullptr);
-
-#else
- // standard ASCII read.
- TiXmlDocument *doc = new TiXmlDocument;
- in >> *doc;
- if (in.fail() || in.eof()) {
- delete doc;
- return nullptr;
- }
-#endif
-
- if (debug_xml_output) {
- // Write via ostringstream, so it all goes in one operation, to help out
- // the interleaving from multiple threads.
- ostringstream logout;
- logout << "received: " << *doc << "\n";
- logfile << logout.str() << flush;
- }
-
- return doc;
-}
diff --git a/direct/src/plugin/binaryXml.h b/direct/src/plugin/binaryXml.h
deleted file mode 100644
index 477c51e83f..0000000000
--- a/direct/src/plugin/binaryXml.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file binaryXml.h
- * @author drose
- * @date 2009-07-13
- */
-
-#ifndef BINARYXML_H
-#define BINARYXML_H
-
-#include "get_tinyxml.h"
-#include "handleStream.h"
-#include
-
-// A pair of functions to input and output the TinyXml constructs on the
-// indicated streams. We could, of course, use the TinyXml output operators,
-// but this is a smidge more efficient and gives us more control.
-
-void init_xml();
-void write_xml(std::ostream &out, TiXmlDocument *doc, std::ostream &logfile);
-TiXmlDocument *read_xml(std::istream &in, std::ostream &logfile);
-
-#endif
diff --git a/direct/src/plugin/failed.pgm b/direct/src/plugin/failed.pgm
deleted file mode 100644
index c470d8fb6f..0000000000
Binary files a/direct/src/plugin/failed.pgm and /dev/null differ
diff --git a/direct/src/plugin/failed.xcf b/direct/src/plugin/failed.xcf
deleted file mode 100644
index 4f3a406f00..0000000000
Binary files a/direct/src/plugin/failed.xcf and /dev/null differ
diff --git a/direct/src/plugin/fhandle.h b/direct/src/plugin/fhandle.h
deleted file mode 100644
index 90459efbe1..0000000000
--- a/direct/src/plugin/fhandle.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file fhandle.h
- * @author drose
- * @date 2009-08-29
- */
-
-#ifndef FHANDLE_H
-#define FHANDLE_H
-
-// This header file simply defines the FHandle type, which is used to pass
-// around a handle to an open file object.
-
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include
-typedef HANDLE FHandle;
-static const HANDLE invalid_fhandle = INVALID_HANDLE_VALUE;
-#else
-// On POSIX, we use a file descriptor as a "handle".
-typedef int FHandle;
-static const int invalid_fhandle = -1;
-#endif
-
-#endif
diff --git a/direct/src/plugin/fileSpec.I b/direct/src/plugin/fileSpec.I
deleted file mode 100644
index 22e6f26f81..0000000000
--- a/direct/src/plugin/fileSpec.I
+++ /dev/null
@@ -1,102 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file fileSpec.I
- * @author drose
- * @date 2009-06-29
- */
-
-/**
- * Returns the relative path to this file on disk, within the package root
- * directory.
- */
-inline const std::string &FileSpec::
-get_filename() const {
- return _filename;
-}
-
-/**
- * Changes the relative path to this file on disk, within the package root
- * directory.
- */
-inline void FileSpec::
-set_filename(const std::string &filename) {
- _filename = filename;
-}
-
-/**
- * Returns the full path to this file on disk.
- */
-inline std::string FileSpec::
-get_pathname(const std::string &package_dir) const {
- return package_dir + "/" + _filename;
-}
-
-/**
- * Returns the expected size of this file on disk, in bytes.
- */
-inline size_t FileSpec::
-get_size() const {
- return _size;
-}
-
-/**
- * Returns the expected last-modify timestamp of this file on disk.
- */
-inline time_t FileSpec::
-get_timestamp() const {
- return _timestamp;
-}
-
-/**
- * Returns true if we have successfully read a hash value, false otherwise.
- */
-inline bool FileSpec::
-has_hash() const {
- return _got_hash;
-}
-
-/**
- * After a call to quick_verify() or full_verify(), this method *may* return a
- * pointer to a FileSpec that represents the actual data read on disk, or it
- * may return NULL. If this returns a non-NULL value, you may use it to
- * extract the md5 hash of the existing file, thus saving the effort of
- * performing the hash twice.
- */
-inline const FileSpec *FileSpec::
-get_actual_file() const {
- return _actual_file;
-}
-
-/**
- * Returns the integer value corresponding to the indicated hex digit.
- * Returns -1 if it is not a hex digit.
- */
-inline int FileSpec::
-decode_hexdigit(char c) {
- if (isdigit(c)) {
- return c - '0';
- }
- c = tolower(c);
- if (c >= 'a' && c <= 'f') {
- return c - 'a' + 10;
- }
-
- return -1;
-}
-
-/**
- * Returns the hex digit corresponding to the indicated integer value.
- */
-inline char FileSpec::
-encode_hexdigit(int c) {
- if (c >= 10) {
- return c - 10 + 'a';
- }
- return c + '0';
-}
diff --git a/direct/src/plugin/fileSpec.cxx b/direct/src/plugin/fileSpec.cxx
deleted file mode 100644
index fd7169c4d1..0000000000
--- a/direct/src/plugin/fileSpec.cxx
+++ /dev/null
@@ -1,486 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file fileSpec.cxx
- * @author drose
- * @date 2009-06-29
- */
-
-#include "fileSpec.h"
-#include "wstring_encode.h"
-#include
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#ifdef _WIN32
-#include
-#include
-#define utimbuf _utimbuf
-
-#else
-#include
-
-#endif
-
-using std::istream;
-using std::ostream;
-using std::string;
-using std::wstring;
-
-/**
- *
- */
-FileSpec::
-FileSpec() {
- _size = 0;
- _timestamp = 0;
- memset(_hash, 0, hash_size);
- _got_hash = false;
- _actual_file = nullptr;
-}
-
-/**
- *
- */
-FileSpec::
-FileSpec(const FileSpec ©) :
- _filename(copy._filename),
- _size(copy._size),
- _timestamp(copy._timestamp),
- _got_hash(copy._got_hash)
-{
- memcpy(_hash, copy._hash, hash_size);
- _actual_file = nullptr;
-}
-
-/**
- *
- */
-void FileSpec::
-operator = (const FileSpec ©) {
- _filename = copy._filename;
- _size = copy._size;
- _timestamp = copy._timestamp;
- memcpy(_hash, copy._hash, hash_size);
- _got_hash = copy._got_hash;
-}
-
-/**
- *
- */
-FileSpec::
-~FileSpec() {
- if (_actual_file != nullptr) {
- delete _actual_file;
- }
-}
-
-/**
- * Reads the data from the indicated XML file.
- */
-void FileSpec::
-load_xml(TiXmlElement *xelement) {
- const char *filename = xelement->Attribute("filename");
- if (filename != nullptr) {
- _filename = filename;
- }
-
- const char *size = xelement->Attribute("size");
- if (size != nullptr) {
- char *endptr;
- _size = strtoul(size, &endptr, 10);
- }
-
- const char *timestamp = xelement->Attribute("timestamp");
- if (timestamp != nullptr) {
- char *endptr;
- _timestamp = strtoul(timestamp, &endptr, 10);
- }
-
- _got_hash = false;
- const char *hash = xelement->Attribute("hash");
- if (hash != nullptr && strlen(hash) == (hash_size * 2)) {
- // Decode the hex hash string.
- _got_hash = decode_hex(_hash, hash, hash_size);
- }
-}
-
-/**
- * Stores the data to the indicated XML file.
- */
-void FileSpec::
-store_xml(TiXmlElement *xelement) {
- if (!_filename.empty()) {
- xelement->SetAttribute("filename", _filename);
- }
- if (_size != 0) {
- xelement->SetAttribute("size", _size);
- }
- if (_timestamp != 0) {
- xelement->SetAttribute("timestamp", (int)_timestamp);
- }
- if (_got_hash) {
- char hash[hash_size * 2 + 1];
- encode_hex(hash, _hash, hash_size);
- hash[hash_size * 2] = '\0';
- xelement->SetAttribute("hash", hash);
- }
-}
-
-/**
- * Performs a quick test to ensure the file has not been modified. This test
- * is vulnerable to people maliciously attempting to fool the program (by
- * setting datestamps etc.).
- *
- * Returns true if it is intact, false if it needs to be redownloaded.
- */
-bool FileSpec::
-quick_verify(const string &package_dir) {
- return quick_verify_pathname(get_pathname(package_dir));
-}
-
-/**
- * Works like quick_verify(), above, with an explicit pathname. Useful for
- * verifying the copy of a file in a temporary location.
- */
-bool FileSpec::
-quick_verify_pathname(const string &pathname) {
- if (_actual_file != nullptr) {
- delete _actual_file;
- _actual_file = nullptr;
- }
-
- int result = 1;
-#ifdef _WIN32
- struct _stat st;
- wstring pathname_w;
- if (string_to_wstring(pathname_w, pathname)) {
- result = _wstat(pathname_w.c_str(), &st);
- }
-#else // _WIN32
- struct stat st;
- result = stat(pathname.c_str(), &st);
-#endif // _WIN32
-
- if (result != 0) {
- // cerr << "file not found: " << _filename << "\n";
- return false;
- }
-
- if (st.st_size != _size) {
- // If the size is wrong, the file fails. cerr << "size wrong: " <<
- // _filename << "\n";
- return false;
- }
-
- if (st.st_mtime == _timestamp) {
- // If the size is right and the timestamp is right, the file passes. cerr
- // << "file ok: " << _filename << "\n";
- return true;
- }
-
- // cerr << "modification time wrong: " << _filename << "\n";
-
- // If the size is right but the timestamp is wrong, the file soft-fails. We
- // follow this up with a hash check.
- if (!priv_check_hash(pathname, &st)) {
- // Hard fail, the hash is wrong. cerr << "hash check wrong: " <<
- // _filename << "\n";
- return false;
- }
-
- // cerr << "hash check ok: " << _filename << "\n";
-
- // The hash is OK after all. Change the file's timestamp back to what we
- // expect it to be, so we can quick-verify it successfully next time.
- utimbuf utb;
- utb.actime = st.st_atime;
- utb.modtime = _timestamp;
-
-#ifdef _WIN32
- _wutime(pathname_w.c_str(), &utb);
-#else // _WIN32
- utime(pathname.c_str(), &utb);
-#endif // _WIN32
-
- return true;
-}
-
-/**
- * Performs a more thorough test to ensure the file has not been modified.
- * This test is less vulnerable to malicious attacks, since it reads and
- * verifies the entire file.
- *
- * Returns true if it is intact, false if it needs to be redownloaded.
- */
-bool FileSpec::
-full_verify(const string &package_dir) {
- if (_actual_file != nullptr) {
- delete _actual_file;
- _actual_file = nullptr;
- }
-
- string pathname = get_pathname(package_dir);
- int result = 1;
-#ifdef _WIN32
- struct _stat st;
- wstring pathname_w;
- if (string_to_wstring(pathname_w, pathname)) {
- result = _wstat(pathname_w.c_str(), &st);
- }
-#else // _WIN32
- struct stat st;
- result = stat(pathname.c_str(), &st);
-#endif // _WIN32
-
- if (result != 0) {
- // cerr << "file not found: " << _filename << "\n";
- return false;
- }
-
- if (st.st_size != _size) {
- // If the size is wrong, the file fails. cerr << "size wrong: " <<
- // _filename << "\n";
- return false;
- }
-
- if (!priv_check_hash(pathname, &st)) {
- // Hard fail, the hash is wrong. cerr << "hash check wrong: " <<
- // _filename << "\n";
- return false;
- }
-
- // cerr << "hash check ok: " << _filename << "\n";
-
- // The hash is OK. If the timestamp is wrong, change it back to what we
- // expect it to be, so we can quick-verify it successfully next time.
-
- if (st.st_mtime != _timestamp) {
- utimbuf utb;
- utb.actime = st.st_atime;
- utb.modtime = _timestamp;
-#ifdef _WIN32
- _wutime(pathname_w.c_str(), &utb);
-#else // _WIN32
- utime(pathname.c_str(), &utb);
-#endif // _WIN32
- }
-
- return true;
-}
-
-/**
- * Returns a FileSpec that represents the actual data read on disk. This will
- * read the disk to determine the data if necessary.
- */
-const FileSpec *FileSpec::
-force_get_actual_file(const string &pathname) {
- if (_actual_file == nullptr) {
-#ifdef _WIN32
- struct _stat st;
- wstring pathname_w;
- if (string_to_wstring(pathname_w, pathname)) {
- _wstat(pathname_w.c_str(), &st);
- }
-#else // _WIN32
- struct stat st;
- stat(pathname.c_str(), &st);
-#endif // _WIN32
-
- priv_check_hash(pathname, &st);
- }
-
- return _actual_file;
-}
-
-/**
- * Returns true if the file has the expected md5 hash, false otherwise.
- */
-bool FileSpec::
-check_hash(const string &pathname) const {
- FileSpec other;
- if (!other.read_hash(pathname)) {
- return false;
- }
-
- return (memcmp(_hash, other._hash, hash_size) == 0);
-}
-
-/**
- * Computes the hash from the indicated pathname and stores it within the
- * FileSpec.
- */
-bool FileSpec::
-read_hash(const string &pathname) {
- memset(_hash, 0, hash_size);
- _got_hash = false;
-
- ifstream stream;
-#ifdef _WIN32
- wstring pathname_w;
- if (string_to_wstring(pathname_w, pathname)) {
- stream.open(pathname_w.c_str(), std::ios::in | std::ios::binary);
- }
-#else // _WIN32
- stream.open(pathname.c_str(), std::ios::in | std::ios::binary);
-#endif // _WIN32
-
- if (!stream) {
- // cerr << "unable to read " << pathname << "\n";
- return false;
- }
-
- MD5_CTX ctx;
- MD5_Init(&ctx);
-
- static const int buffer_size = 4096;
- char buffer[buffer_size];
-
- stream.read(buffer, buffer_size);
- size_t count = stream.gcount();
- while (count != 0) {
- MD5_Update(&ctx, buffer, count);
- stream.read(buffer, buffer_size);
- count = stream.gcount();
- }
-
- MD5_Final(_hash, &ctx);
- _got_hash = true;
-
- return true;
-}
-
-/**
- * Reads the hash from the next 16 bytes on the indicated istream, in the same
- * unusual order observed by Panda's HashVal::read_stream() method.
- */
-bool FileSpec::
-read_hash_stream(istream &in) {
- for (int i = 0; i < hash_size; i += 4) {
- unsigned int a = in.get();
- unsigned int b = in.get();
- unsigned int c = in.get();
- unsigned int d = in.get();
- _hash[i + 0] = d;
- _hash[i + 1] = c;
- _hash[i + 2] = b;
- _hash[i + 3] = a;
- }
-
- return !in.fail();
-}
-
-/**
- * Returns < 0 if this hash sorts before the other hash, > 0 if it sorts
- * after, 0 if they are the same.
- */
-int FileSpec::
-compare_hash(const FileSpec &other) const {
- return memcmp(_hash, other._hash, hash_size);
-}
-
-/**
- * Describes the data in the FileSpec.
- */
-void FileSpec::
-write(ostream &out) const {
- out << "filename: " << _filename << ", " << _size << " bytes, "
- << asctime(localtime(&_timestamp));
- // asctime includes a newline.
- out << "hash: ";
- stream_hex(out, _hash, hash_size);
- out << "\n";
-}
-
-/**
- * Writes just the hash code.
- */
-void FileSpec::
-output_hash(ostream &out) const {
- stream_hex(out, _hash, hash_size);
-}
-
-/**
- * Returns true if the file has the expected md5 hash, false otherwise.
- * Updates _actual_file with the data read from disk, including the hash, for
- * future reference.
- *
- * The parameter stp is a pointer to a stat structure. It's declared as a
- * void * to get around issues with the nonstandard declaration of this
- * structure in Windows.
- */
-bool FileSpec::
-priv_check_hash(const string &pathname, void *stp) {
- const struct stat &st = *(const struct stat *)stp;
- assert(_actual_file == nullptr);
- _actual_file = new FileSpec;
- _actual_file->_filename = pathname;
- _actual_file->_size = st.st_size;
- _actual_file->_timestamp = st.st_mtime;
-
- if (!_actual_file->read_hash(pathname)) {
- return false;
- }
-
- return (memcmp(_hash, _actual_file->_hash, hash_size) == 0);
-}
-
-/**
- * Decodes the hex string in source into the character array in dest. dest
- * must have has least size bytes; source must have size * 2 bytes.
- *
- * Returns true on success, false if there was a non-hex digit in the string.
- */
-bool FileSpec::
-decode_hex(unsigned char *dest, const char *source, size_t size) {
- for (size_t i = 0; i < size; ++i) {
- int high = decode_hexdigit(source[i * 2]);
- int low = decode_hexdigit(source[i * 2 + 1]);
- if (high < 0 || low < 0) {
- return false;
- }
- dest[i] = (high << 4) | low;
- }
-
- return true;
-}
-
-/**
- * Encodes a character array into a hex string for output. dest must have at
- * least size * 2 bytes; source must have size bytes. The result is not null-
- * terminated.
- */
-void FileSpec::
-encode_hex(char *dest, const unsigned char *source, size_t size) {
- for (size_t i = 0; i < size; ++i) {
- int high = (source[i] >> 4) & 0xf;
- int low = source[i] & 0xf;
- dest[2 * i] = encode_hexdigit(high);
- dest[2 * i + 1] = encode_hexdigit(low);
- }
-}
-
-/**
- * Writes the indicated buffer as a string of hex characters to the given
- * ostream.
- */
-void FileSpec::
-stream_hex(ostream &out, const unsigned char *source, size_t size) {
- for (size_t i = 0; i < size; ++i) {
- int high = (source[i] >> 4) & 0xf;
- int low = source[i] & 0xf;
- out.put(encode_hexdigit(high));
- out.put(encode_hexdigit(low));
- }
-}
diff --git a/direct/src/plugin/fileSpec.h b/direct/src/plugin/fileSpec.h
deleted file mode 100644
index 3a661ab603..0000000000
--- a/direct/src/plugin/fileSpec.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file fileSpec.h
- * @author drose
- * @date 2009-06-29
- */
-
-#ifndef FILESPEC_H
-#define FILESPEC_H
-
-#include "get_tinyxml.h"
-#include
-
-/**
- * This simple class is used both within the core API in this module, as well
- * as within the plugin_npapi plugin implementation, to represent a file on
- * disk that may need to be verified or (re)downloaded.
- */
-class FileSpec {
-public:
- FileSpec();
- FileSpec(const FileSpec ©);
- void operator = (const FileSpec ©);
- ~FileSpec();
-
- void load_xml(TiXmlElement *xelement);
- void store_xml(TiXmlElement *xelement);
-
- inline const std::string &get_filename() const;
- inline void set_filename(const std::string &filename);
- inline std::string get_pathname(const std::string &package_dir) const;
- inline size_t get_size() const;
- inline time_t get_timestamp() const;
- inline bool has_hash() const;
-
- bool quick_verify(const std::string &package_dir);
- bool quick_verify_pathname(const std::string &pathname);
- bool full_verify(const std::string &package_dir);
- inline const FileSpec *get_actual_file() const;
- const FileSpec *force_get_actual_file(const std::string &pathname);
-
- bool check_hash(const std::string &pathname) const;
- bool read_hash(const std::string &pathname);
- bool read_hash_stream(std::istream &in);
- int compare_hash(const FileSpec &other) const;
-
- void write(std::ostream &out) const;
- void output_hash(std::ostream &out) const;
-
-private:
- bool priv_check_hash(const std::string &pathname, void *stp);
- static inline int decode_hexdigit(char c);
- static inline char encode_hexdigit(int c);
-
- static bool decode_hex(unsigned char *dest, const char *source, size_t size);
- static void encode_hex(char *dest, const unsigned char *source, size_t size);
- static void stream_hex(std::ostream &out, const unsigned char *source, size_t size);
-
- enum { hash_size = 16 };
-
- std::string _filename;
- size_t _size;
- time_t _timestamp;
- unsigned char _hash[hash_size];
- bool _got_hash;
-
- FileSpec *_actual_file;
-};
-
-#include "fileSpec.I"
-
-#endif
diff --git a/direct/src/plugin/find_root_dir.cxx b/direct/src/plugin/find_root_dir.cxx
deleted file mode 100644
index 7d20461aa9..0000000000
--- a/direct/src/plugin/find_root_dir.cxx
+++ /dev/null
@@ -1,311 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file find_root_dir.cxx
- * @author drose
- * @date 2009-06-29
- */
-
-#include "find_root_dir.h"
-#include "mkdir_complete.h"
-#include "get_tinyxml.h"
-#include "wstring_encode.h"
-
-#ifdef _WIN32
-#include
-#include
-#else
-#include
-#include
-#include
-#include
-#include
-#endif
-
-using std::cerr;
-using std::string;
-using std::wstring;
-
-#ifdef _WIN32
-// From KnownFolders.h (part of Vista SDK):
-#define DEFINE_KNOWN_FOLDER(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
- const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
-DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppData, 0xF1B32785, 0x6FBA, 0x4FCF, 0x9D, 0x55, 0x7B, 0x8E, 0x7F, 0x15, 0x70, 0x91);
-DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppDataLow, 0xA520A1A4, 0x1780, 0x4FF6, 0xBD, 0x18, 0x16, 0x73, 0x43, 0xC5, 0xAF, 0x16);
-DEFINE_KNOWN_FOLDER(FOLDERID_InternetCache, 0x352481E8, 0x33BE, 0x4251, 0xBA, 0x85, 0x60, 0x07, 0xCA, 0xED, 0xCF, 0x9D);
-#endif // _WIN32
-
-
-#ifdef _WIN32
-/**
- * A wrapper around SHGetSpecialFolderPath(), to return the Panda3D directory
- * under the indicated CSIDL folder.
- */
-static wstring
-get_csidl_dir_w(int csidl) {
- static const int buffer_size = MAX_PATH;
- wchar_t buffer[buffer_size];
- if (SHGetSpecialFolderPathW(nullptr, buffer, csidl, true)) {
- wstring root = buffer;
- root += wstring(L"/Panda3D");
-
- if (mkdir_complete_w(root, cerr)) {
- return root;
- }
- }
-
- // Something went wrong.
- return wstring();
-}
-#endif // _WIN32
-
-#ifdef _WIN32
-/**
- * Wide-character implementation of find_root_dir_default(), only needed for
- * Windows.
- */
-static wstring
-find_root_dir_default_w() {
- // First, use IEIsProtectedModeProcess() to determine if we are running in
- // IE's "protected mode" under Vista.
-
- wstring root;
- bool is_protected = false;
- HMODULE ieframe = LoadLibrary("ieframe.dll");
- if (ieframe != nullptr) {
- typedef HRESULT STDAPICALLTYPE IEIsProtectedModeProcess(BOOL *pbResult);
- IEIsProtectedModeProcess *func = (IEIsProtectedModeProcess *)GetProcAddress(ieframe, "IEIsProtectedModeProcess");
- if (func != nullptr) {
- BOOL result = false;
- HRESULT hr = (*func)(&result);
- if (hr == S_OK) {
- is_protected = (result != 0);
- }
- // Any other return value means some error, especially E_NOTIMPL, which
- // means we're not running under Vista. In this case we can assume
- // we're not running in protected mode.
- }
-
- if (is_protected) {
- // If we *are* running in protected mode, we need to use
- // FOLDERID_LocalAppDataLow.
-
- // We should be able to use IEGetWriteableFolderPath() to query this
- // folder, but for some reason, that function returns E_ACCESSDENIED on
- // FOLDERID_LocalAppDataLow, even though this is certainly a folder we
- // have write access to.
-
- // Well, SHGetKnownFolderPath() does work. This function only exists on
- // Vista and above, though, so we still have to pull it out of the DLL
- // instead of hard-linking it.
-
- HMODULE shell32 = LoadLibrary("shell32.dll");
- if (shell32 != nullptr) {
- typedef HRESULT STDAPICALLTYPE SHGetKnownFolderPath(REFGUID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
- SHGetKnownFolderPath *func = (SHGetKnownFolderPath *)GetProcAddress(shell32, "SHGetKnownFolderPath");
- if (func != nullptr) {
- LPWSTR cache_path = nullptr;
- HRESULT hr = (*func)(FOLDERID_LocalAppDataLow, 0, nullptr, &cache_path);
-
- if (SUCCEEDED(hr)) {
- root = cache_path;
- CoTaskMemFree(cache_path);
-
- root += wstring(L"/Panda3D");
- if (mkdir_complete_w(root, cerr)) {
- FreeLibrary(shell32);
- FreeLibrary(ieframe);
- return root;
- }
- }
- }
- FreeLibrary(shell32);
- }
-
- // Couldn't get FOLDERID_LocalAppDataLow for some reason. We're in
- // fallback mode now. Use IEGetWriteableFolderPath to get the standard
- // cache folder.
- typedef HRESULT STDAPICALLTYPE IEGetWriteableFolderPath(REFGUID clsidFolderID, LPWSTR* lppwstrPath);
- IEGetWriteableFolderPath *func = (IEGetWriteableFolderPath *)GetProcAddress(ieframe, "IEGetWriteableFolderPath");
- if (func != nullptr) {
- LPWSTR cache_path = nullptr;
-
- // Since we're here, we'll start by asking for LocalAppDataLow, even
- // though I know it doesn't work.
- HRESULT hr = (*func)(FOLDERID_LocalAppDataLow, &cache_path);
- if (FAILED(hr)) {
- // This one should work.
- hr = (*func)(FOLDERID_InternetCache, &cache_path);
- }
-
- if (SUCCEEDED(hr)) {
- root = cache_path;
- CoTaskMemFree(cache_path);
- root += wstring(L"/Panda3D");
- if (mkdir_complete_w(root, cerr)) {
- FreeLibrary(ieframe);
- return root;
- }
- }
- }
- }
-
- FreeLibrary(ieframe);
- }
-
- // All right, here we are in the normal, unprotected mode. This is also the
- // normal XP codepath.
-
- // e.g., c:Documents and SettingsLocal SettingsApplication
- // DataPanda3D
- root = get_csidl_dir_w(CSIDL_LOCAL_APPDATA);
- if (!root.empty()) {
- return root;
- }
-
- // For some crazy reason, we can't get CSIDL_LOCAL_APPDATA. Fall back to
- // the cache folder.
-
- // e.g. c:Documents and SettingsLocal SettingsTemporary Internet
- // FilesPanda3D
- root = get_csidl_dir_w(CSIDL_INTERNET_CACHE);
- if (!root.empty()) {
- return root;
- }
-
- // If we couldn't get any of those folders, huh. Punt and try for the old
- // standby GetTempPath, for lack of anything better.
- static const int buffer_size = MAX_PATH;
- wchar_t buffer[buffer_size];
- if (GetTempPathW(buffer_size, buffer) != 0) {
- root = buffer;
- root += wstring(L"Panda3D");
- if (mkdir_complete_w(root, cerr)) {
- return root;
- }
- }
-
- return wstring();
-}
-#endif // _WIN32
-
-
-/**
- * Returns the path to the system-default for the root directory. This is
- * where we look first.
- */
-static string
-find_root_dir_default() {
-#ifdef _WIN32
- wstring root_w = find_root_dir_default_w();
- if (!root_w.empty()) {
- string root;
- if (wstring_to_string(root, root_w)) {
- return root;
- }
- }
-
-#elif defined(__APPLE__)
- // e.g., UsersLibraryCachesPanda3D
- string root = find_osx_root_dir();
- if (!root.empty()) {
- return root;
- }
-
-#else // The Linux/*BSD case
- // e.g., home.panda3d
-
- string root;
- const passwd *pwdata = getpwuid(getuid());
- if (pwdata == nullptr) {
- char *home = getenv("HOME");
- if (home == nullptr) {
- // Beh. Let's hope it never gets to this point.
- return ".";
- } else {
- root = home;
- }
- } else {
- root = pwdata->pw_dir;
- }
-
- root += "/.panda3d";
- if (mkdir(root.c_str(), 0700) == 0 || errno == EEXIST) {
- return root;
- }
-
-#endif
-
- // Couldn't find a directory. Punt.
- return ".";
-}
-
-
-/**
- * Returns the path to the installable Panda3D directory on the user's
- * machine.
- */
-static string
-find_root_dir_actual() {
- string root = find_root_dir_default();
-
- // Now look for a config.xml file in that directory, which might redirect us
- // elsewhere.
- string config_filename = root + "/config.xml";
- TiXmlDocument doc(config_filename);
- if (!doc.LoadFile()) {
- // No config.xml found, or not valid xml.
- return root;
- }
-
- TiXmlElement *xconfig = doc.FirstChildElement("config");
- if (xconfig == nullptr) {
- // No element within config.xml.
- return root;
- }
-
- const char *new_root = xconfig->Attribute("root_dir");
- if (new_root == nullptr || *new_root == '\0') {
- // No root_dir specified.
- return root;
- }
-
- if (!mkdir_complete(new_root, cerr)) {
- // The specified root_dir wasn't valid.
- return root;
- }
-
- // We've been redirected to another location. Respect that.
- return new_root;
-}
-
-/**
- * This is the public interface to the above functions.
- */
-string
-find_root_dir() {
- string root = find_root_dir_actual();
-
-#ifdef _WIN32
- // Now map that (possibly utf-8) filename into its 8.3 equivalent, so we can
- // safely pass it around to Python and other tools that might not understand
- // Unicode filenames. Silly Windows, creating an entirely new and
- // incompatible kind of filename.
- wstring root_w;
- string_to_wstring(root_w, root);
-
- DWORD length = GetShortPathNameW(root_w.c_str(), nullptr, 0);
- wchar_t *short_name = new wchar_t[length];
- GetShortPathNameW(root_w.c_str(), short_name, length);
-
- wstring_to_string(root, short_name);
- delete[] short_name;
-#endif // _WIN32
-
- return root;
-}
diff --git a/direct/src/plugin/find_root_dir.h b/direct/src/plugin/find_root_dir.h
deleted file mode 100644
index e76be59af3..0000000000
--- a/direct/src/plugin/find_root_dir.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file find_root_dir.h
- * @author drose
- * @date 2009-06-29
- */
-
-#ifndef FIND_ROOT_DIR_H
-#define FIND_ROOT_DIR_H
-
-#include
-#include
-
-std::string find_root_dir();
-
-#ifdef __APPLE__
-std::string find_osx_root_dir();
-#endif // __APPLE__
-
-#endif
diff --git a/direct/src/plugin/find_root_dir_assist.mm b/direct/src/plugin/find_root_dir_assist.mm
deleted file mode 100644
index f27693d147..0000000000
--- a/direct/src/plugin/find_root_dir_assist.mm
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file find_root_dir_assist.mm
- * @author drose
- * @date 2009-04-13
- */
-
-#include "find_root_dir.h"
-
-#ifdef __APPLE__
-
-#include
-#include
-
-/**
- * Copy the Objective-C string to a C++ string.
- */
-static string
-NSString_to_cpp_string(NSString *str) {
- size_t length = [str length];
- string result;
- for (size_t i = 0; i < length; ++i) {
- result += (char)[str characterAtIndex: i];
- }
-
- return result;
-}
-
-/**
- *
- */
-static string
-call_NSSearchPathForDirectories(NSSearchPathDirectory dirkey, NSSearchPathDomainMask domain) {
- // Ensure that Carbon has been initialized, and that we have an auto-release
- // pool.
- NSApplicationLoad();
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- NSArray *paths = NSSearchPathForDirectoriesInDomains(dirkey, domain, YES);
- string result;
- if ([paths count] != 0) {
- result = NSString_to_cpp_string([paths objectAtIndex:0]);
- }
- [pool release];
-
- return result;
-}
-
-/**
- *
- */
-static string
-get_osx_home_directory() {
- NSApplicationLoad();
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- NSString *dir = NSHomeDirectory();
- string result = NSString_to_cpp_string(dir);
- [pool release];
-
- return result;
-}
-
-/**
- *
- */
-std::string
-find_osx_root_dir() {
- string result = call_NSSearchPathForDirectories(NSCachesDirectory, NSUserDomainMask);
- if (!result.empty()) {
- return result + "/Panda3D";
- }
- result = get_osx_home_directory();
- if (!result.empty()) {
- return result + "/Panda3D";
- }
-
- return string();
-}
-
-#endif // __APPLE__
diff --git a/direct/src/plugin/get_tinyxml.h b/direct/src/plugin/get_tinyxml.h
deleted file mode 100644
index 12f12f054d..0000000000
--- a/direct/src/plugin/get_tinyxml.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file get_tinyxml.h
- * @author drose
- * @date 2009-07-01
- */
-
-#ifndef GET_TINYXML_H
-#define GET_TINYXML_H
-
-// This header file exists just to include tinyxml.h safely. We need this
-// since tinyxml.h requires having the symbol TIXML_USE_STL already defined
-// before you include it.
-
-#ifndef TIXML_USE_STL
-#define TIXML_USE_STL
-#endif
-
-#include
-
-#endif
diff --git a/direct/src/plugin/get_twirl_data.cxx b/direct/src/plugin/get_twirl_data.cxx
deleted file mode 100644
index 8d1bbd73fd..0000000000
--- a/direct/src/plugin/get_twirl_data.cxx
+++ /dev/null
@@ -1,963 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file get_twirl_data.cxx
- * @author drose
- * @date 2011-08-24
- */
-
-#include "get_twirl_data.h"
-#include
-
-struct twirl_flip {
- int _index;
- bool _flip_x;
- bool _flip_y;
- bool _exchange;
-};
-
-static twirl_flip twirl_flip_table[twirl_num_steps + 1] = {
- { 0, false, false, false }, // 0
- { 1, false, false, false }, // 30
- { 2, false, false, false }, // 60
- { 0, false, true, true }, // 90
- { 1, false, true, true }, // 120
- { 2, false, true, true }, // 150
- { 0, true, true, false }, // 180
- { 1, true, true, false }, // 210
- { 2, true, true, false }, // 240
- { 0, true, false, true }, // 270
- { 1, true, false, true }, // 300
- { 2, true, false, true }, // 330
- { 3, false, false, false } // the "failed" icon
-};
-
-// These tables are the raw data for the twirl icon data. They're extracted
-// with bin2c from the three layers of twirl.xcf, in this directory.
-static const unsigned char twirl_0_data[] = {
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xe4, 0xe4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xff, 0xfe, 0xfd, 0xff, 0xa5, 0x1f, 0x1f, 0xa5, 0xff, 0xfd, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xd7, 0x16, 0x00, 0x00,
- 0x16, 0xd7, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xff, 0xeb, 0xba, 0xe6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0x91, 0x00, 0x05, 0x05, 0x00, 0x91, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xff, 0xfd, 0xfa, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xd7, 0x2f, 0x06, 0x23, 0xa6, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x66, 0x00, 0x04, 0x04, 0x00, 0x66, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xf8, 0xec, 0xe8, 0xed, 0xfb, 0xff, 0xff,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x80, 0x00,
- 0x0a, 0x04, 0x0c, 0xb0, 0xff, 0xfe, 0xff, 0xfb, 0x5c, 0x00, 0x03,
- 0x03, 0x00, 0x5c, 0xfb, 0xff, 0xfe, 0xff, 0xf9, 0xe9, 0xe8, 0xe8,
- 0xe8, 0xf3, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0x7c, 0x08, 0x11, 0x14, 0x03, 0x30, 0xe6, 0xff, 0xff,
- 0xfb, 0x5c, 0x00, 0x03, 0x03, 0x00, 0x5c, 0xfb, 0xff, 0xff, 0xfd,
- 0xed, 0xe8, 0xe9, 0xe9, 0xe8, 0xf3, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xab, 0x0a, 0x0f, 0x15, 0x13,
- 0x07, 0x79, 0xff, 0xff, 0xfd, 0x62, 0x00, 0x03, 0x03, 0x00, 0x61,
- 0xfd, 0xff, 0xff, 0xf4, 0xe8, 0xe9, 0xea, 0xe9, 0xe9, 0xf7, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf1,
- 0x2c, 0x0a, 0x14, 0x15, 0x0d, 0x1e, 0xcb, 0xff, 0xff, 0x79, 0x00,
- 0x04, 0x04, 0x00, 0x79, 0xff, 0xff, 0xfb, 0xeb, 0xe9, 0xea, 0xe9,
- 0xe9, 0xeb, 0xfd, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0x97, 0x04, 0x14, 0x15, 0x18, 0x00, 0x82,
- 0xff, 0xff, 0x99, 0x00, 0x03, 0x03, 0x00, 0x99, 0xff, 0xff, 0xf4,
- 0xe7, 0xea, 0xea, 0xe9, 0xe8, 0xf5, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf2, 0x42, 0x03,
- 0x17, 0x16, 0x0b, 0x39, 0xe9, 0xff, 0xb5, 0x01, 0x00, 0x00, 0x01,
- 0xb6, 0xff, 0xfd, 0xee, 0xe9, 0xea, 0xea, 0xe8, 0xed, 0xfd, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xdd, 0x8b, 0x84, 0xad, 0xee, 0xff, 0xff,
- 0xff, 0xff, 0xc2, 0x18, 0x0b, 0x15, 0x13, 0x09, 0xbf, 0xff, 0xda,
- 0x25, 0x00, 0x00, 0x25, 0xd9, 0xff, 0xf9, 0xe9, 0xe9, 0xea, 0xe9,
- 0xe9, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf0, 0xe7, 0xe8, 0xf8,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x48, 0x17, 0x1f,
- 0x20, 0x3b, 0x98, 0xee, 0xff, 0xff, 0xff, 0xa2, 0x0f, 0x0e, 0x15,
- 0x05, 0x7a, 0xff, 0xff, 0x54, 0x00, 0x00, 0x53, 0xff, 0xff, 0xf3,
- 0xe8, 0xea, 0xe9, 0xe9, 0xf5, 0xff, 0xfe, 0xff, 0xfc, 0xec, 0xd9,
- 0xd3, 0xd2, 0xd1, 0xd9, 0xfb, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xcc, 0x1f, 0x20, 0x28, 0x26, 0x22, 0x1a, 0x4c, 0xbf, 0xff, 0xff,
- 0xff, 0x86, 0x02, 0x0e, 0x09, 0x41, 0xfa, 0xff, 0x8d, 0x00, 0x00,
- 0x8d, 0xff, 0xfa, 0xee, 0xe8, 0xe9, 0xe8, 0xf3, 0xff, 0xff, 0xff,
- 0xf4, 0xdd, 0xd2, 0xd3, 0xd3, 0xd4, 0xd3, 0xd2, 0xf2, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xf0, 0x3f, 0x1a, 0x2a, 0x2b, 0x2a, 0x2a,
- 0x1c, 0x28, 0xa1, 0xff, 0xff, 0xff, 0x76, 0x02, 0x04, 0x1e, 0xee,
- 0xff, 0xd7, 0x07, 0x07, 0xd7, 0xff, 0xfc, 0xeb, 0xe8, 0xe8, 0xf1,
- 0xff, 0xff, 0xff, 0xee, 0xd5, 0xd1, 0xd4, 0xd4, 0xd5, 0xd4, 0xd1,
- 0xd7, 0xfa, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xba, 0x28,
- 0x1a, 0x28, 0x2b, 0x2a, 0x2d, 0x24, 0x23, 0x87, 0xff, 0xff, 0xff,
- 0x80, 0x01, 0x0b, 0xc5, 0xff, 0xff, 0x24, 0x24, 0xff, 0xff, 0xfa,
- 0xe9, 0xe7, 0xf2, 0xfe, 0xff, 0xff, 0xe9, 0xd4, 0xd3, 0xd5, 0xd5,
- 0xd5, 0xd4, 0xd1, 0xd3, 0xef, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe,
- 0xfe, 0xff, 0xff, 0xc0, 0x4a, 0x1f, 0x22, 0x2e, 0x2c, 0x2b, 0x25,
- 0x18, 0x79, 0xfb, 0xff, 0xff, 0x97, 0x0b, 0x7b, 0xff, 0xff, 0x9b,
- 0x9b, 0xff, 0xff, 0xf4, 0xe8, 0xf4, 0xff, 0xff, 0xff, 0xe6, 0xd1,
- 0xd3, 0xd5, 0xd5, 0xd5, 0xd3, 0xd2, 0xda, 0xf1, 0xff, 0xff, 0xfe,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xef, 0x8e, 0x38,
- 0x17, 0x21, 0x29, 0x2b, 0x26, 0x17, 0x81, 0xff, 0xff, 0xff, 0xa0,
- 0x94, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf5, 0xf5, 0xff, 0xff,
- 0xff, 0xe8, 0xd1, 0xd3, 0xd5, 0xd4, 0xd3, 0xd1, 0xd7, 0xe7, 0xfa,
- 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff,
- 0xfe, 0xff, 0xff, 0xd6, 0x95, 0x53, 0x22, 0x1c, 0x20, 0x1c, 0x15,
- 0x96, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xff, 0xff, 0xec, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xdb,
- 0xe9, 0xf6, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xee, 0xca,
- 0x8d, 0x58, 0x35, 0x24, 0x1e, 0x9f, 0xff, 0xfb, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xed, 0xd3, 0xd3, 0xd6,
- 0xdc, 0xe7, 0xf3, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf3, 0xd1, 0x8f, 0x9c, 0xfc,
- 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff,
- 0xec, 0xe7, 0xf5, 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xe1, 0xac,
- 0x8d, 0x85, 0x85, 0x89, 0x9b, 0xb3, 0xc8, 0xe3, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5,
- 0xec, 0xe5, 0xdd, 0xd7, 0xd6, 0xd6, 0xd9, 0xe3, 0xf5, 0xff, 0xff,
- 0xff, 0xbb, 0x50, 0x30, 0x2f, 0x33, 0x33, 0x32, 0x2f, 0x30, 0x41,
- 0x5b, 0x7e, 0xaa, 0xe1, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xf5, 0xe3, 0xd4, 0xc8, 0xbf, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xb9,
- 0xba, 0xc4, 0xe8, 0xff, 0xeb, 0x57, 0x31, 0x44, 0x42, 0x42, 0x42,
- 0x42, 0x43, 0x42, 0x3e, 0x38, 0x30, 0x36, 0x45, 0x5c, 0xb4, 0xff,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xe6, 0xc8, 0xc0, 0xbb, 0xb9, 0xbc, 0xbe, 0xbf, 0xc0,
- 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xb9, 0xc7, 0xf8, 0xeb, 0x57, 0x31,
- 0x44, 0x42, 0x42, 0x42, 0x42, 0x43, 0x42, 0x3e, 0x38, 0x30, 0x36,
- 0x46, 0x5c, 0xb4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xe6, 0xc8, 0xc0, 0xbb, 0xb9,
- 0xbc, 0xbe, 0xbf, 0xc0, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xb9, 0xc7,
- 0xf8, 0xff, 0xbb, 0x50, 0x30, 0x2f, 0x33, 0x33, 0x32, 0x2f, 0x30,
- 0x41, 0x5b, 0x7e, 0xaa, 0xe1, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xff, 0xf5, 0xe3, 0xd4, 0xc8, 0xbf, 0xb9, 0xb9, 0xba, 0xba, 0xba,
- 0xb9, 0xba, 0xc4, 0xe8, 0xff, 0xff, 0xff, 0xe1, 0xac, 0x8d, 0x85,
- 0x85, 0x89, 0x9b, 0xb3, 0xc8, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xec, 0xe5,
- 0xdd, 0xd7, 0xd6, 0xd6, 0xd9, 0xe3, 0xf5, 0xff, 0xff, 0xfe, 0xfd,
- 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xfb, 0xf2, 0xd5, 0x9f, 0xb2, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xd7, 0xd2, 0xed, 0xfa, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xef, 0xd0, 0x9f, 0x74, 0x5b, 0x4e, 0x4d, 0xba, 0xff, 0xfc,
- 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xd9,
- 0xa5, 0xa7, 0xae, 0xbc, 0xd1, 0xea, 0xf8, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff,
- 0xfe, 0xff, 0xff, 0xda, 0xa4, 0x6f, 0x4c, 0x49, 0x4c, 0x48, 0x46,
- 0xb3, 0xff, 0xff, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xd5, 0xa1, 0xa4, 0xa5, 0xa4, 0xa6, 0xb9,
- 0xd4, 0xef, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xed, 0x9e, 0x5c, 0x44, 0x4e, 0x53,
- 0x55, 0x50, 0x47, 0xa3, 0xff, 0xff, 0xff, 0xbc, 0xba, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xcf, 0xd4, 0xff, 0xff, 0xff, 0xcd, 0xa2,
- 0xa8, 0xaa, 0xa9, 0xa6, 0xa1, 0xaf, 0xd2, 0xf8, 0xff, 0xff, 0xfe,
- 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xc5, 0x69, 0x4b,
- 0x4f, 0x57, 0x56, 0x55, 0x50, 0x47, 0x9c, 0xff, 0xff, 0xff, 0xb5,
- 0x61, 0xb0, 0xff, 0xff, 0xcd, 0xcd, 0xff, 0xff, 0xc3, 0x90, 0xd0,
- 0xff, 0xff, 0xfd, 0xc9, 0xa2, 0xa7, 0xaa, 0xaa, 0xab, 0xa6, 0xa5,
- 0xb6, 0xe5, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xbe, 0x4f, 0x48, 0x53, 0x55, 0x55, 0x56, 0x4e, 0x50, 0xa7, 0xff,
- 0xff, 0xfc, 0xa6, 0x5b, 0x65, 0xde, 0xff, 0xff, 0x92, 0x92, 0xff,
- 0xff, 0xe5, 0x90, 0x8c, 0xc6, 0xff, 0xff, 0xff, 0xcf, 0xa6, 0xa7,
- 0xaa, 0xa9, 0xaa, 0xa9, 0xa3, 0xa9, 0xe3, 0xff, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xff, 0xed, 0x5f, 0x48, 0x54, 0x55, 0x54, 0x54, 0x47,
- 0x57, 0xbb, 0xff, 0xff, 0xff, 0xa1, 0x5c, 0x5f, 0x72, 0xf7, 0xff,
- 0xeb, 0x83, 0x83, 0xeb, 0xff, 0xf7, 0x99, 0x8d, 0x8c, 0xc1, 0xff,
- 0xff, 0xff, 0xd9, 0xa9, 0xa4, 0xa9, 0xa9, 0xaa, 0xa9, 0xa3, 0xb2,
- 0xf9, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xcd, 0x4a, 0x4d, 0x52,
- 0x50, 0x4d, 0x49, 0x76, 0xd3, 0xff, 0xff, 0xff, 0xab, 0x5c, 0x66,
- 0x62, 0x8a, 0xfd, 0xff, 0xc7, 0x79, 0x79, 0xc7, 0xff, 0xfd, 0xa8,
- 0x8f, 0x92, 0x8c, 0xc8, 0xff, 0xff, 0xff, 0xe6, 0xb7, 0xa3, 0xa6,
- 0xa7, 0xa8, 0xa5, 0xa5, 0xeb, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xf0, 0x67, 0x45, 0x4b, 0x4d, 0x65, 0xb3, 0xf5, 0xff, 0xff, 0xff,
- 0xbd, 0x64, 0x66, 0x6a, 0x5f, 0xaf, 0xff, 0xff, 0xa9, 0x75, 0x75,
- 0xa9, 0xff, 0xff, 0xc3, 0x8d, 0x95, 0x91, 0x92, 0xd5, 0xff, 0xff,
- 0xff, 0xf8, 0xd5, 0xb0, 0xa5, 0xa5, 0xa2, 0xb5, 0xfa, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xe2, 0xa3, 0xa0, 0xc2, 0xf5, 0xff,
- 0xff, 0xff, 0xff, 0xd2, 0x68, 0x65, 0x6a, 0x68, 0x64, 0xd9, 0xff,
- 0xed, 0x92, 0x7a, 0x7a, 0x92, 0xed, 0xff, 0xe2, 0x90, 0x94, 0x95,
- 0x90, 0x96, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xde, 0xcd, 0xd0,
- 0xf1, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf3, 0x81, 0x5f, 0x6b, 0x6b,
- 0x63, 0x86, 0xf3, 0xff, 0xda, 0x81, 0x7f, 0x7f, 0x81, 0xda, 0xff,
- 0xf5, 0xa5, 0x90, 0x95, 0x96, 0x8c, 0xa9, 0xf9, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xb7,
- 0x5e, 0x69, 0x69, 0x6c, 0x5b, 0xb4, 0xff, 0xff, 0xcc, 0x75, 0x81,
- 0x81, 0x75, 0xcc, 0xff, 0xff, 0xc6, 0x8a, 0x96, 0x95, 0x94, 0x8d,
- 0xcf, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xf3, 0x75, 0x63, 0x69, 0x6a, 0x64, 0x73, 0xe2, 0xff,
- 0xff, 0xbc, 0x75, 0x81, 0x81, 0x75, 0xbc, 0xff, 0xff, 0xe7, 0x99,
- 0x91, 0x95, 0x94, 0x90, 0x9f, 0xf8, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xc5, 0x62, 0x66, 0x6a, 0x68,
- 0x62, 0xb0, 0xff, 0xff, 0xfe, 0xb0, 0x77, 0x81, 0x81, 0x77, 0xb0,
- 0xfe, 0xff, 0xff, 0xc2, 0x8e, 0x94, 0x95, 0x92, 0x90, 0xd9, 0xff,
- 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xa9,
- 0x61, 0x68, 0x69, 0x5e, 0x80, 0xf4, 0xff, 0xff, 0xfd, 0xae, 0x77,
- 0x81, 0x81, 0x77, 0xae, 0xfd, 0xff, 0xff, 0xf4, 0xa1, 0x8d, 0x94,
- 0x93, 0x8f, 0xc4, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xad, 0x5c, 0x62, 0x5e, 0x68, 0xd3, 0xff, 0xfe,
- 0xff, 0xfd, 0xae, 0x77, 0x81, 0x81, 0x77, 0xae, 0xfd, 0xff, 0xfe,
- 0xff, 0xdb, 0x91, 0x8d, 0x90, 0x8b, 0xc5, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xe7, 0x7f, 0x62, 0x78,
- 0xce, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb3, 0x75, 0x81, 0x81, 0x75,
- 0xb3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd6, 0x9b, 0x8e, 0xa0, 0xed,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
- 0xff, 0xf6, 0xdc, 0xf4, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xc8,
- 0x75, 0x82, 0x82, 0x75, 0xc8, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xf4, 0xe0, 0xf6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xfe, 0xff, 0xeb, 0x8a, 0x76, 0x76, 0x8a, 0xeb, 0xff, 0xfe,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xd2, 0x8f, 0x8f,
- 0xd2, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xf1, 0xf1, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff
-};
-
-static const unsigned char twirl_30_data[] = {
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xe7, 0xe7, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xff, 0xfe, 0xfd, 0xff, 0xac, 0x31, 0x31, 0xac, 0xff, 0xfd, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xda, 0x28, 0x02, 0x02,
- 0x28, 0xda, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xff, 0xed, 0xc1, 0xe9, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0x9a, 0x02, 0x1a, 0x1a, 0x02, 0x9a, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xff, 0xed, 0xc2, 0xef, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xdb, 0x42, 0x1d, 0x37, 0xae, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x73, 0x01, 0x18, 0x18, 0x01, 0x73, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xab, 0x18, 0x00, 0x23, 0xd7, 0xff, 0xff,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x8c, 0x18,
- 0x21, 0x1b, 0x23, 0xb7, 0xff, 0xfe, 0xff, 0xfb, 0x6a, 0x05, 0x17,
- 0x17, 0x05, 0x6a, 0xfb, 0xff, 0xfe, 0xff, 0xb4, 0x00, 0x00, 0x00,
- 0x00, 0x73, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0x88, 0x1f, 0x28, 0x2a, 0x1b, 0x44, 0xe8, 0xff, 0xff,
- 0xfb, 0x6a, 0x05, 0x17, 0x17, 0x05, 0x6a, 0xfb, 0xff, 0xff, 0xec,
- 0x25, 0x00, 0x00, 0x00, 0x00, 0x6b, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xb4, 0x21, 0x25, 0x2b, 0x29,
- 0x1e, 0x86, 0xff, 0xff, 0xfd, 0x6e, 0x04, 0x17, 0x17, 0x04, 0x6e,
- 0xfd, 0xff, 0xff, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf3,
- 0x3f, 0x21, 0x2a, 0x2b, 0x24, 0x34, 0xd0, 0xff, 0xff, 0x85, 0x00,
- 0x18, 0x18, 0x00, 0x84, 0xff, 0xff, 0xce, 0x10, 0x00, 0x00, 0x00,
- 0x00, 0x13, 0xea, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xa1, 0x1b, 0x2a, 0x2b, 0x2e, 0x16, 0x8e,
- 0xff, 0xff, 0xa2, 0x01, 0x18, 0x18, 0x01, 0xa2, 0xff, 0xff, 0x7f,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x83, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf3, 0x54, 0x1b,
- 0x2d, 0x2c, 0x22, 0x4b, 0xeb, 0xff, 0xbc, 0x16, 0x13, 0x13, 0x16,
- 0xbc, 0xff, 0xea, 0x2f, 0x00, 0x02, 0x02, 0x00, 0x28, 0xeb, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xe0, 0x96, 0x91, 0xb5, 0xf0, 0xff, 0xff,
- 0xff, 0xff, 0xc8, 0x2e, 0x22, 0x2b, 0x29, 0x20, 0xc5, 0xff, 0xdd,
- 0x37, 0x0b, 0x0b, 0x36, 0xdd, 0xff, 0xc0, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0xb2, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xf7, 0xf3, 0xf3, 0xfb,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x5a, 0x2e, 0x35,
- 0x36, 0x4e, 0xa2, 0xf0, 0xff, 0xff, 0xff, 0xab, 0x26, 0x24, 0x2b,
- 0x1c, 0x87, 0xff, 0xff, 0x62, 0x02, 0x02, 0x61, 0xff, 0xff, 0x76,
- 0x00, 0x00, 0x00, 0x00, 0x8d, 0xff, 0xff, 0xff, 0xfd, 0xf6, 0xec,
- 0xe8, 0xe8, 0xe8, 0xec, 0xfd, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xd1, 0x35, 0x36, 0x3d, 0x3b, 0x38, 0x31, 0x5d, 0xc5, 0xff, 0xff,
- 0xff, 0x91, 0x1a, 0x25, 0x20, 0x52, 0xfb, 0xff, 0x97, 0x09, 0x09,
- 0x97, 0xff, 0xfc, 0x36, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff,
- 0xf9, 0xee, 0xe8, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xf9, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xf2, 0x52, 0x31, 0x3f, 0x40, 0x3f, 0x3f,
- 0x32, 0x3d, 0xaa, 0xff, 0xff, 0xff, 0x83, 0x19, 0x1b, 0x33, 0xef,
- 0xff, 0xdb, 0x1c, 0x1c, 0xdb, 0xff, 0xf1, 0x0d, 0x00, 0x00, 0x5e,
- 0xff, 0xff, 0xff, 0xf7, 0xea, 0xe8, 0xe9, 0xe9, 0xea, 0xe9, 0xe8,
- 0xeb, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xc0, 0x3d,
- 0x30, 0x3d, 0x40, 0x3f, 0x41, 0x39, 0x38, 0x93, 0xff, 0xff, 0xff,
- 0x8c, 0x19, 0x22, 0xcb, 0xff, 0xff, 0x36, 0x37, 0xff, 0xff, 0xc8,
- 0x00, 0x00, 0x68, 0xfb, 0xff, 0xff, 0xf4, 0xe9, 0xe9, 0xea, 0xea,
- 0xea, 0xe9, 0xe8, 0xe9, 0xf7, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe,
- 0xfe, 0xff, 0xff, 0xc7, 0x5c, 0x35, 0x38, 0x42, 0x41, 0x40, 0x3b,
- 0x2f, 0x86, 0xfb, 0xff, 0xff, 0xa1, 0x22, 0x88, 0xff, 0xff, 0xa3,
- 0xa3, 0xff, 0xff, 0x77, 0x00, 0x81, 0xff, 0xff, 0xfe, 0xf2, 0xe8,
- 0xe9, 0xea, 0xea, 0xea, 0xe9, 0xe8, 0xec, 0xf8, 0xff, 0xff, 0xfe,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xf0, 0x99, 0x4c,
- 0x2e, 0x37, 0x3e, 0x40, 0x3b, 0x2e, 0x8e, 0xff, 0xff, 0xff, 0xa9,
- 0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x89, 0x8b, 0xff, 0xff,
- 0xff, 0xf3, 0xe8, 0xe9, 0xea, 0xe9, 0xe9, 0xe8, 0xeb, 0xf3, 0xfd,
- 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff,
- 0xfe, 0xff, 0xff, 0xda, 0x9f, 0x63, 0x38, 0x32, 0x36, 0x32, 0x2c,
- 0xa0, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
- 0xfc, 0xff, 0xff, 0xff, 0xf5, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xed,
- 0xf4, 0xfa, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xcf,
- 0x99, 0x69, 0x4a, 0x3a, 0x34, 0xa9, 0xff, 0xfb, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfc, 0xff, 0xf6, 0xe9, 0xe9, 0xea,
- 0xee, 0xf3, 0xf9, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xf4, 0xd5, 0x9a, 0xa6, 0xfc,
- 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff,
- 0xf5, 0xf3, 0xf9, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xe4, 0xb5,
- 0x99, 0x93, 0x92, 0x96, 0xa6, 0xbb, 0xce, 0xe6, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
- 0xf2, 0xee, 0xe8, 0xe5, 0xe4, 0xe4, 0xe6, 0xed, 0xf8, 0xff, 0xff,
- 0xff, 0xc3, 0x63, 0x47, 0x46, 0x49, 0x49, 0x49, 0x46, 0x46, 0x56,
- 0x6d, 0x8d, 0xb3, 0xe4, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xf8, 0xec, 0xe2, 0xdb, 0xd5, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd1,
- 0xd1, 0xd8, 0xf0, 0xff, 0xed, 0x6a, 0x47, 0x58, 0x57, 0x57, 0x57,
- 0x57, 0x57, 0x57, 0x53, 0x4e, 0x47, 0x4c, 0x5a, 0x6d, 0xbc, 0xff,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xee, 0xdb, 0xd6, 0xd2, 0xd1, 0xd3, 0xd4, 0xd5, 0xd5,
- 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd1, 0xda, 0xfa, 0xed, 0x69, 0x47,
- 0x58, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x53, 0x4e, 0x47, 0x4c,
- 0x5a, 0x6d, 0xbc, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xee, 0xdb, 0xd6, 0xd2, 0xd1,
- 0xd3, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd1, 0xda,
- 0xfa, 0xff, 0xc3, 0x63, 0x47, 0x46, 0x49, 0x49, 0x49, 0x46, 0x46,
- 0x56, 0x6d, 0x8d, 0xb3, 0xe4, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xff, 0xf8, 0xec, 0xe2, 0xdb, 0xd5, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2,
- 0xd1, 0xd1, 0xd8, 0xf0, 0xff, 0xff, 0xff, 0xe4, 0xb5, 0x99, 0x93,
- 0x92, 0x96, 0xa6, 0xbb, 0xce, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf2, 0xee,
- 0xe8, 0xe5, 0xe4, 0xe4, 0xe6, 0xed, 0xf8, 0xff, 0xff, 0xfe, 0xfd,
- 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xfc, 0xf4, 0xda, 0xab, 0xbb, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xe1, 0xdd, 0xf1, 0xfb, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xf1, 0xd6, 0xaa, 0x86, 0x6f, 0x63, 0x63, 0xc2, 0xff, 0xfc,
- 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xe2,
- 0xbb, 0xbd, 0xc2, 0xcd, 0xdd, 0xef, 0xf9, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff,
- 0xfe, 0xff, 0xff, 0xde, 0xb0, 0x80, 0x62, 0x60, 0x62, 0x5f, 0x5d,
- 0xbd, 0xff, 0xff, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xdf, 0xb8, 0xba, 0xbb, 0xba, 0xbc, 0xca,
- 0xdf, 0xf3, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xef, 0xaa, 0x70, 0x5b, 0x64, 0x69,
- 0x6a, 0x66, 0x5e, 0xae, 0xff, 0xff, 0xff, 0xc5, 0xc4, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xd8, 0xdc, 0xff, 0xff, 0xff, 0xd9, 0xb9,
- 0xbd, 0xbf, 0xbe, 0xbc, 0xb8, 0xc3, 0xdd, 0xfa, 0xff, 0xff, 0xfe,
- 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xcd, 0x7b, 0x61,
- 0x65, 0x6c, 0x6b, 0x6a, 0x65, 0x5e, 0xa8, 0xff, 0xff, 0xff, 0xc0,
- 0x78, 0xbc, 0xff, 0xff, 0xd5, 0xd5, 0xff, 0xff, 0xcf, 0xa6, 0xd9,
- 0xff, 0xff, 0xfd, 0xd6, 0xb9, 0xbd, 0xbf, 0xbf, 0xc0, 0xbc, 0xbb,
- 0xc8, 0xec, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xc6, 0x64, 0x5e, 0x68, 0x6a, 0x6a, 0x6b, 0x64, 0x66, 0xb2, 0xff,
- 0xff, 0xfc, 0xb3, 0x73, 0x7c, 0xe4, 0xff, 0xff, 0xa4, 0xa4, 0xff,
- 0xff, 0xea, 0xa6, 0xa2, 0xd1, 0xff, 0xff, 0xff, 0xdb, 0xbc, 0xbc,
- 0xbf, 0xbf, 0xbf, 0xbe, 0xb9, 0xbe, 0xe9, 0xff, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xff, 0xf0, 0x72, 0x5f, 0x69, 0x6a, 0x69, 0x69, 0x5e,
- 0x6c, 0xc4, 0xff, 0xff, 0xff, 0xae, 0x74, 0x77, 0x86, 0xf8, 0xff,
- 0xee, 0x98, 0x98, 0xee, 0xff, 0xf9, 0xad, 0xa3, 0xa3, 0xce, 0xff,
- 0xff, 0xff, 0xe2, 0xbe, 0xba, 0xbe, 0xbe, 0xbf, 0xbe, 0xba, 0xc5,
- 0xfa, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xd3, 0x60, 0x63, 0x67,
- 0x66, 0x63, 0x5f, 0x87, 0xd8, 0xff, 0xff, 0xff, 0xb7, 0x74, 0x7d,
- 0x79, 0x9b, 0xfd, 0xff, 0xd0, 0x8f, 0x8f, 0xd0, 0xff, 0xfd, 0xba,
- 0xa5, 0xa7, 0xa3, 0xd3, 0xff, 0xff, 0xff, 0xec, 0xc9, 0xba, 0xbc,
- 0xbd, 0xbe, 0xbb, 0xbb, 0xef, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xf2, 0x7a, 0x5c, 0x62, 0x63, 0x78, 0xbd, 0xf7, 0xff, 0xff, 0xff,
- 0xc6, 0x7b, 0x7d, 0x80, 0x77, 0xbb, 0xff, 0xff, 0xb7, 0x8c, 0x8c,
- 0xb7, 0xff, 0xff, 0xcf, 0xa4, 0xaa, 0xa7, 0xa7, 0xdd, 0xff, 0xff,
- 0xff, 0xfa, 0xe0, 0xc4, 0xbb, 0xbb, 0xb9, 0xc8, 0xfb, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xe6, 0xae, 0xab, 0xca, 0xf6, 0xff,
- 0xff, 0xff, 0xff, 0xd8, 0x7e, 0x7b, 0x80, 0x7f, 0x7b, 0xdf, 0xff,
- 0xef, 0xa4, 0x90, 0x90, 0xa4, 0xef, 0xff, 0xe8, 0xa6, 0xa9, 0xaa,
- 0xa6, 0xab, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xe6, 0xda, 0xdb,
- 0xf5, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf5, 0x94, 0x77, 0x81, 0x81,
- 0x7a, 0x97, 0xf5, 0xff, 0xe0, 0x95, 0x94, 0x94, 0x95, 0xe0, 0xff,
- 0xf7, 0xb7, 0xa6, 0xaa, 0xaa, 0xa3, 0xba, 0xfa, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xc1,
- 0x76, 0x7f, 0x7f, 0x81, 0x74, 0xbf, 0xff, 0xff, 0xd5, 0x8c, 0x96,
- 0x96, 0x8c, 0xd5, 0xff, 0xff, 0xd1, 0xa1, 0xab, 0xaa, 0xa9, 0xa4,
- 0xd9, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xf5, 0x89, 0x7a, 0x7f, 0x80, 0x7b, 0x88, 0xe6, 0xff,
- 0xff, 0xc7, 0x8b, 0x96, 0x96, 0x8b, 0xc7, 0xff, 0xff, 0xec, 0xad,
- 0xa7, 0xaa, 0xa9, 0xa6, 0xb2, 0xfa, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xce, 0x79, 0x7d, 0x80, 0x7e,
- 0x79, 0xbb, 0xff, 0xff, 0xfe, 0xbd, 0x8d, 0x96, 0x96, 0x8d, 0xbd,
- 0xfe, 0xff, 0xff, 0xce, 0xa5, 0xa9, 0xaa, 0xa7, 0xa6, 0xe1, 0xff,
- 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xb5,
- 0x78, 0x7e, 0x7f, 0x75, 0x92, 0xf5, 0xff, 0xff, 0xfd, 0xbb, 0x8d,
- 0x96, 0x96, 0x8d, 0xbb, 0xfd, 0xff, 0xff, 0xf6, 0xb4, 0xa3, 0xa9,
- 0xa8, 0xa5, 0xcf, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xb9, 0x74, 0x79, 0x76, 0x7e, 0xda, 0xff, 0xfe,
- 0xff, 0xfd, 0xbb, 0x8d, 0x96, 0x96, 0x8d, 0xbb, 0xfd, 0xff, 0xfe,
- 0xff, 0xe2, 0xa7, 0xa3, 0xa6, 0xa2, 0xd1, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xeb, 0x91, 0x78, 0x8c,
- 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x8b, 0x96, 0x96, 0x8b,
- 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xaf, 0xa4, 0xb3, 0xf0,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
- 0xff, 0xf7, 0xe0, 0xf6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xd1,
- 0x8c, 0x97, 0x97, 0x8c, 0xd1, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xf6, 0xe6, 0xf8, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xfe, 0xff, 0xee, 0x9e, 0x8c, 0x8c, 0x9e, 0xee, 0xff, 0xfe,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xd9, 0xa2, 0xa2,
- 0xd9, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xf4, 0xf4, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff
-};
-
-static const unsigned char twirl_60_data[] = {
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xe9, 0xe9, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xff, 0xfe, 0xfd, 0xff, 0xb4, 0x45, 0x45, 0xb4, 0xff, 0xfd, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xde, 0x3d, 0x1a, 0x1a,
- 0x3d, 0xde, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xff, 0xef, 0xc7, 0xeb, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xa3, 0x1a, 0x2f, 0x2f, 0x1a, 0xa3, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xff, 0xee, 0xc7, 0xf0, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xdf, 0x55, 0x34, 0x4b, 0xb6, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x80, 0x18, 0x2e, 0x2e, 0x18, 0x80, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xb2, 0x2c, 0x07, 0x35, 0xda, 0xff, 0xff,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x97, 0x2e,
- 0x37, 0x32, 0x39, 0xbe, 0xff, 0xfe, 0xff, 0xfc, 0x78, 0x1c, 0x2d,
- 0x2d, 0x1c, 0x78, 0xfc, 0xff, 0xfe, 0xff, 0xba, 0x12, 0x03, 0x09,
- 0x00, 0x7e, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0x94, 0x35, 0x3d, 0x3f, 0x31, 0x57, 0xeb, 0xff, 0xff,
- 0xfb, 0x77, 0x1c, 0x2d, 0x2d, 0x1c, 0x77, 0xfb, 0xff, 0xff, 0xed,
- 0x37, 0x02, 0x14, 0x12, 0x07, 0x78, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xbb, 0x37, 0x3b, 0x40, 0x3e,
- 0x34, 0x92, 0xff, 0xff, 0xfd, 0x7c, 0x1c, 0x2d, 0x2d, 0x1c, 0x7c,
- 0xfd, 0xff, 0xff, 0x83, 0x08, 0x12, 0x15, 0x0f, 0x09, 0xa4, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf4,
- 0x52, 0x37, 0x3f, 0x40, 0x39, 0x47, 0xd5, 0xff, 0xff, 0x90, 0x18,
- 0x2e, 0x2e, 0x18, 0x90, 0xff, 0xff, 0xd2, 0x24, 0x0b, 0x15, 0x14,
- 0x0b, 0x26, 0xec, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xa9, 0x32, 0x3f, 0x40, 0x43, 0x2d, 0x9a,
- 0xff, 0xff, 0xab, 0x19, 0x2e, 0x2e, 0x19, 0xab, 0xff, 0xff, 0x8a,
- 0x00, 0x18, 0x14, 0x14, 0x03, 0x8d, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf4, 0x65, 0x31,
- 0x41, 0x41, 0x38, 0x5d, 0xed, 0xff, 0xc2, 0x2c, 0x29, 0x29, 0x2c,
- 0xc2, 0xff, 0xec, 0x40, 0x0a, 0x17, 0x17, 0x04, 0x39, 0xed, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xe4, 0xa2, 0x9c, 0xbd, 0xf1, 0xff, 0xff,
- 0xff, 0xff, 0xcd, 0x42, 0x38, 0x40, 0x3e, 0x36, 0xca, 0xff, 0xe0,
- 0x49, 0x22, 0x22, 0x49, 0xe0, 0xff, 0xc4, 0x0b, 0x13, 0x16, 0x0d,
- 0x12, 0xb8, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xa4, 0x70, 0x74, 0xd4,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x6d, 0x45, 0x4b,
- 0x4c, 0x61, 0xac, 0xf1, 0xff, 0xff, 0xff, 0xb3, 0x3b, 0x3a, 0x40,
- 0x33, 0x93, 0xff, 0xff, 0x70, 0x1a, 0x1a, 0x70, 0xff, 0xff, 0x81,
- 0x04, 0x15, 0x0f, 0x0c, 0x97, 0xff, 0xff, 0xff, 0xf1, 0x8e, 0x19,
- 0x00, 0x00, 0x00, 0x1c, 0xe9, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xd6, 0x4b, 0x4c, 0x52, 0x50, 0x4e, 0x47, 0x6f, 0xcc, 0xff, 0xff,
- 0xff, 0x9c, 0x31, 0x3b, 0x36, 0x64, 0xfb, 0xff, 0xa0, 0x20, 0x20,
- 0xa0, 0xff, 0xfc, 0x47, 0x09, 0x10, 0x00, 0x7b, 0xff, 0xff, 0xff,
- 0xbc, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xf3, 0x65, 0x47, 0x54, 0x55, 0x54, 0x54,
- 0x49, 0x53, 0xb3, 0xff, 0xff, 0xff, 0x90, 0x30, 0x32, 0x47, 0xf1,
- 0xff, 0xde, 0x31, 0x31, 0xde, 0xff, 0xf2, 0x21, 0x05, 0x00, 0x6b,
- 0xff, 0xff, 0xff, 0x99, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0f, 0xe5, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xc7, 0x53,
- 0x47, 0x53, 0x55, 0x54, 0x56, 0x4f, 0x4e, 0x9f, 0xff, 0xff, 0xff,
- 0x97, 0x30, 0x38, 0xd0, 0xff, 0xff, 0x4a, 0x4a, 0xff, 0xff, 0xcc,
- 0x0e, 0x00, 0x74, 0xfa, 0xff, 0xff, 0x7b, 0x00, 0x00, 0x02, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x9e, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe,
- 0xfe, 0xff, 0xff, 0xcd, 0x6e, 0x4b, 0x4e, 0x57, 0x56, 0x55, 0x50,
- 0x46, 0x93, 0xfc, 0xff, 0xff, 0xaa, 0x38, 0x93, 0xff, 0xff, 0xac,
- 0xac, 0xff, 0xff, 0x83, 0x06, 0x8b, 0xff, 0xff, 0xff, 0x6a, 0x00,
- 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x1e, 0xa8, 0xff, 0xff, 0xfe,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xf2, 0xa4, 0x60,
- 0x44, 0x4d, 0x53, 0x55, 0x51, 0x45, 0x9a, 0xff, 0xff, 0xff, 0xb1,
- 0xa8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x92, 0x95, 0xff, 0xff,
- 0xff, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0xe4,
- 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff,
- 0xfe, 0xff, 0xff, 0xde, 0xa9, 0x74, 0x4e, 0x49, 0x4c, 0x49, 0x43,
- 0xab, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
- 0xfc, 0xff, 0xff, 0xff, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27,
- 0x77, 0xc7, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xd4,
- 0xa4, 0x79, 0x5d, 0x4f, 0x4b, 0xb2, 0xff, 0xfc, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfa, 0xff, 0x98, 0x00, 0x00, 0x0a,
- 0x2f, 0x6e, 0xb9, 0xe7, 0xff, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xf5, 0xda, 0xa5, 0xb0, 0xfd,
- 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff,
- 0x8b, 0x70, 0xc0, 0xee, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xe8, 0xbe,
- 0xa5, 0xa0, 0xa0, 0xa3, 0xb1, 0xc4, 0xd4, 0xe9, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd,
- 0xf9, 0xf6, 0xf4, 0xf2, 0xf1, 0xf1, 0xf2, 0xf6, 0xfb, 0xff, 0xff,
- 0xff, 0xca, 0x76, 0x5e, 0x5d, 0x5f, 0x60, 0x5f, 0x5d, 0x5d, 0x6b,
- 0x7f, 0x9b, 0xbd, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfc, 0xf6, 0xf1, 0xed, 0xea, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
- 0xe8, 0xeb, 0xf7, 0xff, 0xef, 0x7c, 0x5e, 0x6d, 0x6c, 0x6b, 0x6b,
- 0x6b, 0x6c, 0x6c, 0x69, 0x63, 0x5e, 0x62, 0x6e, 0x7f, 0xc4, 0xff,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xf6, 0xed, 0xea, 0xe9, 0xe8, 0xe9, 0xe9, 0xea, 0xea,
- 0xea, 0xea, 0xea, 0xea, 0xea, 0xe8, 0xec, 0xfd, 0xef, 0x7c, 0x5e,
- 0x6d, 0x6c, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x69, 0x63, 0x5e, 0x62,
- 0x6e, 0x7f, 0xc4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf6, 0xed, 0xea, 0xe9, 0xe8,
- 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xe8, 0xec,
- 0xfd, 0xff, 0xca, 0x76, 0x5e, 0x5d, 0x5f, 0x60, 0x5f, 0x5d, 0x5d,
- 0x6b, 0x7f, 0x9b, 0xbd, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xff, 0xfc, 0xf6, 0xf1, 0xed, 0xea, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
- 0xe8, 0xe8, 0xeb, 0xf7, 0xff, 0xff, 0xff, 0xe8, 0xbe, 0xa5, 0xa0,
- 0xa0, 0xa3, 0xb1, 0xc4, 0xd4, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf9, 0xf6,
- 0xf4, 0xf2, 0xf1, 0xf1, 0xf2, 0xf6, 0xfb, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xfc, 0xf6, 0xdf, 0xb7, 0xc5, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xeb, 0xe8, 0xf6, 0xfd, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xf3, 0xdc, 0xb7, 0x98, 0x85, 0x7a, 0x7a, 0xcb, 0xff, 0xfd,
- 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xec,
- 0xd2, 0xd3, 0xd7, 0xde, 0xe8, 0xf4, 0xfb, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff,
- 0xfe, 0xff, 0xff, 0xe3, 0xbc, 0x94, 0x7a, 0x77, 0x79, 0x76, 0x75,
- 0xc7, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xea, 0xd0, 0xd2, 0xd2, 0xd2, 0xd3, 0xdc,
- 0xea, 0xf7, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xf1, 0xb6, 0x85, 0x73, 0x7a, 0x7f,
- 0x80, 0x7c, 0x75, 0xba, 0xff, 0xff, 0xff, 0xcf, 0xcd, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xe2, 0xe5, 0xff, 0xff, 0xff, 0xe6, 0xd1,
- 0xd4, 0xd5, 0xd4, 0xd3, 0xd1, 0xd8, 0xe8, 0xfb, 0xff, 0xff, 0xfe,
- 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xd4, 0x8f, 0x78,
- 0x7b, 0x82, 0x80, 0x80, 0x7c, 0x76, 0xb5, 0xff, 0xff, 0xff, 0xcb,
- 0x8e, 0xc7, 0xff, 0xff, 0xde, 0xde, 0xff, 0xff, 0xda, 0xbc, 0xe3,
- 0xff, 0xff, 0xfe, 0xe4, 0xd1, 0xd3, 0xd5, 0xd5, 0xd5, 0xd3, 0xd2,
- 0xdb, 0xf2, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xcf, 0x7b, 0x76, 0x7e, 0x80, 0x80, 0x81, 0x7b, 0x7d, 0xbd, 0xff,
- 0xff, 0xfd, 0xc0, 0x8a, 0x91, 0xe8, 0xff, 0xff, 0xb6, 0xb6, 0xff,
- 0xff, 0xef, 0xbc, 0xb9, 0xdc, 0xff, 0xff, 0xff, 0xe7, 0xd3, 0xd3,
- 0xd5, 0xd4, 0xd5, 0xd4, 0xd1, 0xd4, 0xf1, 0xff, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xff, 0xf2, 0x87, 0x76, 0x7f, 0x80, 0x7f, 0x7f, 0x76,
- 0x81, 0xcc, 0xff, 0xff, 0xff, 0xbc, 0x8b, 0x8d, 0x9a, 0xf9, 0xff,
- 0xf1, 0xac, 0xac, 0xf1, 0xff, 0xfa, 0xc1, 0xba, 0xb9, 0xda, 0xff,
- 0xff, 0xff, 0xec, 0xd4, 0xd2, 0xd4, 0xd4, 0xd5, 0xd4, 0xd1, 0xd9,
- 0xfc, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xda, 0x78, 0x7a, 0x7e,
- 0x7c, 0x7a, 0x76, 0x98, 0xdd, 0xff, 0xff, 0xff, 0xc3, 0x8b, 0x92,
- 0x8f, 0xab, 0xfd, 0xff, 0xd9, 0xa5, 0xa5, 0xd9, 0xff, 0xfd, 0xcb,
- 0xbb, 0xbd, 0xba, 0xde, 0xff, 0xff, 0xff, 0xf2, 0xdb, 0xd1, 0xd3,
- 0xd4, 0xd4, 0xd2, 0xd2, 0xf5, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xf4, 0x8e, 0x74, 0x79, 0x7a, 0x8c, 0xc7, 0xf8, 0xff, 0xff, 0xff,
- 0xcf, 0x90, 0x92, 0x95, 0x8d, 0xc6, 0xff, 0xff, 0xc6, 0xa3, 0xa3,
- 0xc6, 0xff, 0xff, 0xda, 0xba, 0xbf, 0xbd, 0xbd, 0xe5, 0xff, 0xff,
- 0xff, 0xfb, 0xea, 0xd8, 0xd2, 0xd2, 0xd1, 0xda, 0xfc, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xea, 0xba, 0xb8, 0xd1, 0xf7, 0xff,
- 0xff, 0xff, 0xff, 0xdf, 0x93, 0x91, 0x95, 0x94, 0x91, 0xe5, 0xff,
- 0xf3, 0xb6, 0xa6, 0xa6, 0xb6, 0xf3, 0xff, 0xed, 0xbb, 0xbe, 0xbf,
- 0xbc, 0xbf, 0xee, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xee, 0xe6, 0xe8,
- 0xf8, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf6, 0xa5, 0x8d, 0x96, 0x95,
- 0x90, 0xa8, 0xf6, 0xff, 0xe6, 0xaa, 0xa9, 0xa9, 0xaa, 0xe6, 0xff,
- 0xf9, 0xc9, 0xbc, 0xbf, 0xbf, 0xba, 0xcb, 0xfb, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xcc,
- 0x8d, 0x94, 0x94, 0x96, 0x8a, 0xc9, 0xff, 0xff, 0xdd, 0xa2, 0xab,
- 0xab, 0xa2, 0xdd, 0xff, 0xff, 0xdd, 0xb9, 0xc0, 0xbf, 0xbe, 0xba,
- 0xe2, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xf6, 0x9c, 0x90, 0x94, 0x95, 0x90, 0x9b, 0xea, 0xff,
- 0xff, 0xd3, 0xa2, 0xab, 0xab, 0xa2, 0xd3, 0xff, 0xff, 0xf1, 0xc1,
- 0xbc, 0xbf, 0xbe, 0xbc, 0xc5, 0xfb, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xd6, 0x8f, 0x92, 0x95, 0x93,
- 0x8f, 0xc6, 0xff, 0xff, 0xfe, 0xca, 0xa4, 0xab, 0xab, 0xa4, 0xca,
- 0xfe, 0xff, 0xff, 0xda, 0xbb, 0xbe, 0xbf, 0xbd, 0xbc, 0xe8, 0xff,
- 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xc1,
- 0x8f, 0x93, 0x94, 0x8c, 0xa4, 0xf7, 0xff, 0xff, 0xfd, 0xc8, 0xa4,
- 0xaa, 0xaa, 0xa4, 0xc8, 0xfd, 0xff, 0xff, 0xf8, 0xc6, 0xba, 0xbe,
- 0xbe, 0xbb, 0xdb, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xc5, 0x8b, 0x8f, 0x8c, 0x93, 0xe0, 0xff, 0xfe,
- 0xff, 0xfd, 0xc9, 0xa4, 0xab, 0xab, 0xa4, 0xc9, 0xfd, 0xff, 0xfe,
- 0xff, 0xe9, 0xbc, 0xba, 0xbb, 0xb9, 0xdc, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xee, 0xa3, 0x8f, 0x9f,
- 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xa2, 0xab, 0xab, 0xa2,
- 0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xbb, 0xc6, 0xf4,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff,
- 0xff, 0xf8, 0xe5, 0xf7, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xda,
- 0xa3, 0xab, 0xab, 0xa3, 0xda, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xf8, 0xec, 0xf9, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
- 0xff, 0xfe, 0xff, 0xf1, 0xb1, 0xa3, 0xa3, 0xb1, 0xf1, 0xff, 0xfe,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xe1, 0xb4, 0xb4,
- 0xe1, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xf6, 0xf6, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff
-};
-
-// Also, the "failed" icon.
-const unsigned char failed_data[] = {
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfb, 0xfb, 0xfe, 0xff, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfd, 0xff, 0xfa, 0x91, 0x93, 0xfd, 0xff, 0xfd, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xfe, 0xfc, 0xff, 0x8c, 0x00, 0x00, 0x8f, 0xff,
- 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xcf, 0x0f,
- 0x00, 0x00, 0x0f, 0xcf, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xff, 0x51, 0x00, 0x01, 0x01, 0x00, 0x51, 0xff, 0xff, 0xfe,
- 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xa4, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xef, 0x1e, 0x00,
- 0x00, 0x55, 0x54, 0x00, 0x00, 0x1f, 0xf0, 0xff, 0xfe, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0x6a, 0x00, 0x00, 0x1c, 0xbf, 0xbe, 0x1c, 0x00, 0x00, 0x68,
- 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xfe, 0xfb, 0xff, 0xbb, 0x03, 0x00, 0x00, 0x7d, 0xd2, 0xd2,
- 0x7e, 0x00, 0x00, 0x03, 0xbc, 0xff, 0xfb, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xf7, 0x3c, 0x00, 0x00,
- 0x3f, 0xc6, 0xc4, 0xc4, 0xc6, 0x3e, 0x00, 0x00, 0x3d, 0xf8, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff,
- 0x8b, 0x00, 0x00, 0x0c, 0xa1, 0xcd, 0xc0, 0xc0, 0xcd, 0xa1, 0x0c,
- 0x00, 0x00, 0x89, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xcf, 0x09, 0x00, 0x00, 0x6c, 0xd4, 0xc2, 0xc0,
- 0xc0, 0xc2, 0xd4, 0x6d, 0x00, 0x00, 0x09, 0xd0, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
- 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x4a, 0x00, 0x00, 0x29,
- 0xc3, 0xca, 0xc7, 0xd0, 0xd0, 0xc7, 0xca, 0xc3, 0x29, 0x00, 0x00,
- 0x4a, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xa5,
- 0x00, 0x00, 0x00, 0x90, 0xd1, 0xc7, 0xb2, 0x52, 0x52, 0xb2, 0xc7,
- 0xd1, 0x91, 0x00, 0x00, 0x00, 0xa6, 0xff, 0xfe, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfc, 0xff, 0xeb, 0x25, 0x00, 0x00, 0x53, 0xc9, 0xc4, 0xd2, 0x6b,
- 0x00, 0x00, 0x6b, 0xd2, 0xc4, 0xc9, 0x53, 0x00, 0x00, 0x25, 0xec,
- 0xff, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0x69, 0x00, 0x00, 0x20, 0xb3,
- 0xcb, 0xbe, 0xd5, 0x58, 0x00, 0x00, 0x58, 0xd5, 0xbe, 0xcb, 0xb3,
- 0x21, 0x00, 0x00, 0x69, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xb7, 0x01,
- 0x00, 0x00, 0x7e, 0xd3, 0xc0, 0xbf, 0xd4, 0x5d, 0x00, 0x00, 0x5d,
- 0xd4, 0xbf, 0xc0, 0xd3, 0x7f, 0x00, 0x00, 0x01, 0xb8, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xf5, 0x36, 0x00, 0x00, 0x3f, 0xc9, 0xc5, 0xc2, 0xbf, 0xd4,
- 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf, 0xc2, 0xc5, 0xca, 0x3f, 0x00,
- 0x00, 0x37, 0xf6, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0x87, 0x00, 0x00, 0x07, 0xa6, 0xcd,
- 0xc2, 0xc3, 0xbf, 0xd4, 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf, 0xc3,
- 0xc2, 0xcd, 0xa6, 0x07, 0x00, 0x00, 0x85, 0xff, 0xff, 0xfe, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xd4, 0x06, 0x00,
- 0x00, 0x6c, 0xd3, 0xc5, 0xc2, 0xc3, 0xbf, 0xd4, 0x5d, 0x00, 0x00,
- 0x5d, 0xd4, 0xbf, 0xc2, 0xc2, 0xc5, 0xd3, 0x6d, 0x00, 0x00, 0x06,
- 0xd5, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0x4e, 0x00, 0x00, 0x2e, 0xbe, 0xcd, 0xc2, 0xc2, 0xc2, 0xbf,
- 0xd4, 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf, 0xc3, 0xc2, 0xc2, 0xcd,
- 0xbe, 0x2e, 0x00, 0x00, 0x4f, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xfe, 0xfb, 0xff, 0xa2, 0x00, 0x00, 0x02, 0x8f, 0xcf, 0xc3,
- 0xc2, 0xc2, 0xc2, 0xbf, 0xd4, 0x5d, 0x00, 0x00, 0x5d, 0xd4, 0xbf,
- 0xc3, 0xc2, 0xc2, 0xc3, 0xcf, 0x8f, 0x02, 0x00, 0x00, 0xa1, 0xff,
- 0xfb, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xe6, 0x27, 0x00, 0x00,
- 0x53, 0xcb, 0xc2, 0xc2, 0xc3, 0xc2, 0xc3, 0xbf, 0xd4, 0x5c, 0x00,
- 0x00, 0x5c, 0xd4, 0xbf, 0xc3, 0xc2, 0xc3, 0xc2, 0xc1, 0xcc, 0x54,
- 0x00, 0x00, 0x27, 0xe8, 0xff, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0x66, 0x00, 0x00, 0x20, 0xb7, 0xcc, 0xc1, 0xc3, 0xc2, 0xc2, 0xc3,
- 0xbf, 0xd4, 0x59, 0x00, 0x00, 0x59, 0xd4, 0xbf, 0xc3, 0xc2, 0xc2,
- 0xc3, 0xc1, 0xcc, 0xb7, 0x20, 0x00, 0x00, 0x66, 0xff, 0xff, 0xfe,
- 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xfe, 0xff, 0xff, 0xb7, 0x00, 0x00, 0x00, 0x83, 0xd3, 0xc2, 0xc3,
- 0xc3, 0xc2, 0xc2, 0xc2, 0xc0, 0xce, 0x86, 0x01, 0x01, 0x86, 0xce,
- 0xc0, 0xc3, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2, 0xd3, 0x84, 0x00, 0x00,
- 0x00, 0xb8, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0x30, 0x00, 0x00, 0x3e,
- 0xca, 0xc8, 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
- 0x90, 0x90, 0xc3, 0xc3, 0xc2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2,
- 0xc8, 0xca, 0x3d, 0x00, 0x00, 0x31, 0xfd, 0xff, 0xfe, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8a,
- 0x00, 0x00, 0x0a, 0xa2, 0xcf, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2,
- 0xc3, 0xc3, 0xc2, 0xc3, 0xd3, 0xd3, 0xc3, 0xc2, 0xc3, 0xc3, 0xc2,
- 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xcf, 0xa2, 0x0a, 0x00, 0x00, 0x8a,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff,
- 0xfc, 0xff, 0xcf, 0x0e, 0x00, 0x00, 0x6b, 0xd0, 0xc5, 0xc2, 0xc2,
- 0xc3, 0xc3, 0xc2, 0xc2, 0xc3, 0xc3, 0xc1, 0xc9, 0xc5, 0xc5, 0xc9,
- 0xc1, 0xc3, 0xc3, 0xc2, 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc5, 0xd1,
- 0x6e, 0x00, 0x00, 0x0e, 0xcf, 0xff, 0xfb, 0xff, 0xfe, 0xfe, 0xfe,
- 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x4e, 0x00, 0x00, 0x31, 0xbf,
- 0xc8, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc9,
- 0xab, 0x52, 0x52, 0xab, 0xc9, 0xc1, 0xc3, 0xc2, 0xc2, 0xc2, 0xc3,
- 0xc2, 0xc2, 0xc2, 0xc8, 0xc0, 0x30, 0x00, 0x00, 0x4f, 0xff, 0xff,
- 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xa0, 0x00,
- 0x00, 0x04, 0x93, 0xd1, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc2,
- 0xc2, 0xc2, 0xc0, 0xcf, 0x4c, 0x00, 0x00, 0x4c, 0xcf, 0xc0, 0xc3,
- 0xc2, 0xc2, 0xc2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc0, 0xd1, 0x92, 0x04,
- 0x00, 0x00, 0x9e, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xe5, 0x1e, 0x00, 0x00, 0x54, 0xcd, 0xc3, 0xc3, 0xc3, 0xc2,
- 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc3, 0xc0, 0xd0, 0x4e, 0x00, 0x00,
- 0x4e, 0xd0, 0xc0, 0xc3, 0xc2, 0xc2, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2,
- 0xc3, 0xc3, 0xce, 0x56, 0x00, 0x00, 0x20, 0xe6, 0xff, 0xfe, 0xfe,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x1b, 0xba, 0xcc,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1,
- 0xc7, 0xae, 0x4c, 0x4c, 0xae, 0xc7, 0xc1, 0xc2, 0xc2, 0xc2, 0xc2,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xcc, 0xbb, 0x19, 0x00, 0x00,
- 0x61, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xbd, 0x00, 0x00,
- 0x00, 0x85, 0xd6, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xcb, 0xd2, 0xd2, 0xcb, 0xc5, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
- 0xd6, 0x84, 0x00, 0x00, 0x00, 0xbb, 0xff, 0xfe, 0xff, 0xfd, 0xff,
- 0xf7, 0x38, 0x00, 0x00, 0x42, 0xbf, 0xc6, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf,
- 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xc6, 0xc0, 0x42, 0x00, 0x00, 0x38, 0xf9,
- 0xff, 0xfd, 0xfc, 0xff, 0x83, 0x00, 0x00, 0x00, 0x16, 0x24, 0x20,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x24, 0x16,
- 0x00, 0x00, 0x00, 0x82, 0xff, 0xfd, 0xff, 0xfa, 0x34, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xf7, 0xff, 0xfe,
- 0xff, 0x86, 0x12, 0x11, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x12, 0x18,
- 0x8a, 0xff, 0xfe, 0xfe, 0xff, 0xfd, 0xd2, 0xc4, 0xc8, 0xc8, 0xc8,
- 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
- 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
- 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
- 0xc8, 0xc8, 0xc5, 0xd8, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe,
- 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe,
- 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff,
- 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
- 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
- 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
- 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
- 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
- 0xff, 0xfe, 0xff, 0xff, 0xff
-};
-
-static const unsigned char * const twirl_data[4] = {
- twirl_0_data,
- twirl_30_data,
- twirl_60_data,
- failed_data
-};
-
-
-/**
- * Fills the indicated data array with a string of twirl_width * twirl_height
- * * 3 bytes, representing the RGB pixel values of the twirl_width x
- * twirl_height image at frame number step of twirl_num_steps frames. The
- * specified fg and bg colors are applied to the array appropriately.
- *
- * Returns true on success, false on failure. On false, the array is
- * initialized with zero.
- *
- * You must pass data_length = twirl_width * twirl_height * 3; this value is
- * passed as a sanity check on array size. You should pass step so that 0 <=
- * step < twirl_num_steps + 1. (The special value of step == twirl_num_steps
- * returns the "failed" icon.)
- */
-bool
-get_twirl_data(unsigned char data[], size_t data_length, int step,
- int fg_r, int fg_g, int fg_b,
- int bg_r, int bg_g, int bg_b) {
-
- if (step < 0 || step >= twirl_num_steps + 1) {
- memset(data, 0, data_length);
- return false;
- }
- if (data_length != twirl_width * twirl_height) {
- memset(data, 0, data_length);
- return false;
- }
-
- twirl_flip &flip = twirl_flip_table[step];
- const unsigned char *in_data = twirl_data[flip._index];
-
- for (int yi = 0; yi < twirl_height; ++yi) {
- const unsigned char *sp = &in_data[yi * twirl_width];
- for (int xi = 0; xi < twirl_width; ++xi) {
- int xo = xi;
- int yo = yi;
- if (flip._flip_x) {
- xo = twirl_width - 1 - xo;
- }
- if (flip._flip_y) {
- yo = twirl_width - 1 - yo;
- }
- if (flip._exchange) {
- int t = xo;
- xo = yo;
- yo = t;
- }
- unsigned char *dp = &data[yo * twirl_width * 3];
- dp += 3 * xo;
-
- double t = (double)(*sp) / 255.0;
- ++sp;
-
- dp[0] = (unsigned char)(fg_r + t * (bg_r - fg_r));
- dp[1] = (unsigned char)(fg_g + t * (bg_g - fg_g));
- dp[2] = (unsigned char)(fg_b + t * (bg_b - fg_b));
- }
- }
-
- return true;
-}
diff --git a/direct/src/plugin/get_twirl_data.h b/direct/src/plugin/get_twirl_data.h
deleted file mode 100644
index 3b021a01e4..0000000000
--- a/direct/src/plugin/get_twirl_data.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file get_twirl_data.h
- * @author drose
- * @date 2011-08-24
- */
-
-#ifndef GET_TWIRL_DATA_H
-#define GET_TWIRL_DATA_H
-
-#include
-
-static const int twirl_width = 48;
-static const int twirl_height = 48;
-static const int twirl_num_steps = 12;
-
-bool get_twirl_data(unsigned char data[], size_t data_length, int step,
- int fg_r, int fg_g, int fg_b,
- int bg_r, int bg_g, int bg_b);
-
-#endif
diff --git a/direct/src/plugin/handleStream.I b/direct/src/plugin/handleStream.I
deleted file mode 100644
index 60021cee96..0000000000
--- a/direct/src/plugin/handleStream.I
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStream.I
- * @author drose
- * @date 2009-06-05
- */
-
-/**
- *
- */
-inline HandleStream::
-HandleStream() : std::iostream(&_buf) {
-}
-
-/**
- *
- */
-inline HandleStream::
-~HandleStream() {
- close();
-}
-
-/**
- * Attempts to open the given handle for input. The stream may not be
- * simultaneously open for input and output.
- */
-inline void HandleStream::
-open_read(FHandle handle) {
- clear((std::ios::iostatetate)0);
- _buf.open_read(handle);
- if (!_buf.is_open_read()) {
- clear(std::ios::failbit);
- }
-}
-
-/**
- * Attempts to open the given handle for output. The stream may not be
- * simultaneously open for input and output.
- */
-inline void HandleStream::
-open_write(FHandle handle) {
- clear((std::ios::iostatetate)0);
- _buf.open_write(handle);
- if (!_buf.is_open_write()) {
- clear(std::ios::failbit);
- }
-}
-
-/**
- *
- */
-inline void HandleStream::
-close() {
- _buf.close();
-}
-
-/**
- * Closes the underlying handle, *without* attempting to flush the stream.
- */
-inline void HandleStream::
-close_handle() {
- _buf.close_handle();
-}
-
-/**
- * Returns the handle that was passed to open_read() or open_write().
- */
-inline FHandle HandleStream::
-get_handle() const {
- return _buf.get_handle();
-}
-
-/**
- * Returns true if there is data in the stream's "get" buffer, meaning that at
- * least one character can be extracted from the stream without making an OS
- * read() call. Returns false if the get buffer is empty, meaning the next
- * read call will hit the OS.
- */
-inline bool HandleStream::
-has_gdata() const {
- return _buf.has_gdata();
-}
diff --git a/direct/src/plugin/handleStream.cxx b/direct/src/plugin/handleStream.cxx
deleted file mode 100644
index 5d6580f493..0000000000
--- a/direct/src/plugin/handleStream.cxx
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStream.cxx
- * @author drose
- * @date 2009-06-05
- */
-
-#include "handleStream.h"
diff --git a/direct/src/plugin/handleStream.h b/direct/src/plugin/handleStream.h
deleted file mode 100644
index b28dec5f6a..0000000000
--- a/direct/src/plugin/handleStream.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStream.h
- * @author drose
- * @date 2009-06-05
- */
-
-#ifndef HANDLESTREAM_H
-#define HANDLESTREAM_H
-
-#include "handleStreamBuf.h"
-
-/**
- * Implements a C++ stream object suitable for reading from and writing to
- * Windows' HANDLE objects, or Posix file descriptors. This is necessary to
- * map low-level pipes into an iostream for tinyxml.
- */
-class HandleStream : public std::iostream {
-public:
- inline HandleStream();
- inline ~HandleStream();
-
- inline void open_read(FHandle handle);
- inline void open_write(FHandle handle);
- inline void close();
- inline void close_handle();
-
- inline FHandle get_handle() const;
- inline bool has_gdata() const;
-
-private:
- HandleStreamBuf _buf;
-};
-
-#include "handleStream.I"
-
-#endif
diff --git a/direct/src/plugin/handleStreamBuf.I b/direct/src/plugin/handleStreamBuf.I
deleted file mode 100644
index 24036ee9d3..0000000000
--- a/direct/src/plugin/handleStreamBuf.I
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStreamBuf.I
- * @author drose
- * @date 2009-06-05
- */
-
-/**
- * Returns the handle that was passed to open_read() or open_write().
- */
-inline FHandle HandleStreamBuf::
-get_handle() const {
- return _handle;
-}
-
-/**
- * Returns true if there is data in the stream's "get" buffer, meaning that at
- * least one character can be extracted from the stream without making an OS
- * read() call. Returns false if the get buffer is empty, meaning the next
- * read call will hit the OS.
- */
-inline bool HandleStreamBuf::
-has_gdata() const {
- return (egptr() != gptr());
-}
diff --git a/direct/src/plugin/handleStreamBuf.cxx b/direct/src/plugin/handleStreamBuf.cxx
deleted file mode 100644
index 55e74977b5..0000000000
--- a/direct/src/plugin/handleStreamBuf.cxx
+++ /dev/null
@@ -1,342 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStreamBuf.cxx
- * @author drose
- * @date 2009-06-05
- */
-
-#include "handleStreamBuf.h"
-
-#include
-#include
-
-#ifndef _WIN32
-#include
-#include
-#include
-#include
-#include
-#endif // _WIN32
-
-#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__FreeBSD__)
-#include
-#endif // !_WIN32 && !__APPLE__ && !__FreeBSD__
-
-using std::cerr;
-using std::dec;
-using std::hex;
-
-static const size_t handle_buffer_size = 4096;
-
-/**
- *
- */
-HandleStreamBuf::
-HandleStreamBuf() {
- _is_open_read = false;
- _is_open_write = false;
-
- _handle = invalid_fhandle;
-
- INIT_LOCK(_lock);
-
- _buffer = new char[handle_buffer_size];
- char *ebuf = _buffer + handle_buffer_size;
- setg(_buffer, ebuf, ebuf);
- setp(_buffer, ebuf);
-}
-
-/**
- *
- */
-HandleStreamBuf::
-~HandleStreamBuf() {
- close();
-
- delete[] _buffer;
-
- DESTROY_LOCK(_lock);
-}
-
-/**
- * Attempts to open the given handle for input. The stream may not be
- * simultaneously open for input and output.
- */
-void HandleStreamBuf::
-open_read(FHandle handle) {
- close();
-
- _handle = handle;
- _is_open_read = true;
-}
-
-/**
- * Attempts to open the given handle for output. The stream may not be
- * simultaneously open for input and output.
- */
-void HandleStreamBuf::
-open_write(FHandle handle) {
- close();
-
- _handle = handle;
- _is_open_write = true;
-}
-
-/**
- * Returns true if the file is open for input, false otherwise.
- */
-bool HandleStreamBuf::
-is_open_read() const {
- return _is_open_read;
-}
-
-/**
- * Returns true if the file is open for output, false otherwise.
- */
-bool HandleStreamBuf::
-is_open_write() const {
- return _is_open_write;
-}
-
-/**
- * Empties the buffer and closes the file.
- */
-void HandleStreamBuf::
-close() {
- // Make sure the write buffer is flushed.
- sync();
-
- close_handle();
-
- pbump(pbase() - pptr());
- gbump(egptr() - gptr());
-}
-
-/**
- * Closes the underlying handle, *without* attempting to flush the stream.
- */
-void HandleStreamBuf::
-close_handle() {
-#ifdef _WIN32
- if (_handle != nullptr) {
- CloseHandle(_handle);
- }
- _handle = nullptr;
-#else
- if (_handle != -1) {
- ::close(_handle);
- }
- _handle = -1;
-#endif // _WIN32
-
- _is_open_read = false;
- _is_open_write = false;
-}
-
-/**
- * Called by the system ostream implementation when its internal buffer is
- * filled, plus one character.
- */
-int HandleStreamBuf::
-overflow(int ch) {
- ACQUIRE_LOCK(_lock);
-
- bool okflag = true;
-
- assert(pptr() >= pbase());
- size_t n = pptr() - pbase();
- if (n != 0) {
- size_t wrote = write_chars(pbase(), n);
- assert(wrote <= n);
- pbump(-(int)wrote);
- if (wrote != n) {
- okflag = false;
- }
- }
-
- if (okflag && ch != EOF) {
- if (pptr() != epptr()) {
- // Store the extra character back in the buffer.
- *(pptr()) = ch;
- pbump(1);
- } else {
- // No room to store ch.
- okflag = false;
- }
- }
-
- RELEASE_LOCK(_lock);
-
- if (!okflag) {
- return EOF;
- }
- return 0;
-}
-
-/**
- * Called by the system iostream implementation to implement a flush
- * operation.
- */
-int HandleStreamBuf::
-sync() {
- ACQUIRE_LOCK(_lock);
- assert(pptr() >= pbase());
- size_t n = pptr() - pbase();
-
- size_t wrote = write_chars(pbase(), n);
- assert(wrote <= n);
- pbump(-(int)wrote);
-
- RELEASE_LOCK(_lock);
-
- if (n != wrote) {
- return EOF;
- }
- return 0;
-}
-
-/**
- * Called by the system istream implementation when its internal buffer needs
- * more characters.
- */
-int HandleStreamBuf::
-underflow() {
- ACQUIRE_LOCK(_lock);
- // Sometimes underflow() is called even if the buffer is not empty.
- if (gptr() >= egptr()) {
- // Mark the buffer filled (with buffer_size bytes).
- size_t buffer_size = egptr() - eback();
- gbump(-(int)buffer_size);
-
- size_t num_bytes = buffer_size;
- size_t read_count = read_chars(gptr(), buffer_size);
-
- if (read_count != num_bytes) {
- // Oops, we didn't read what we thought we would.
- if (read_count == 0) {
- gbump(num_bytes);
- RELEASE_LOCK(_lock);
- return EOF;
- }
-
- // Slide what we did read to the top of the buffer.
- assert(read_count < num_bytes);
- size_t delta = num_bytes - read_count;
- memmove(gptr() + delta, gptr(), read_count);
- gbump(delta);
- }
- }
-
- unsigned char next = *gptr();
- RELEASE_LOCK(_lock);
-
- return next;
-}
-
-/**
- * Attempts to extract the indicated number of characters from the current
- * file position. Returns the number of characters extracted.
- */
-size_t HandleStreamBuf::
-read_chars(char *start, size_t length) {
- if (length == 0 || !_is_open_read) {
- // Trivial no-op.
- return 0;
- }
-
- // Make sure the write buffer is flushed.
- sync();
-
- if (length == 0) {
- return 0;
- }
-
-#ifdef _WIN32
- // Windows case.
- DWORD bytes_read = 0;
- BOOL success = ReadFile(_handle, start, length, &bytes_read, nullptr);
- if (!success) {
- DWORD error = GetLastError();
- if (error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE) {
- cerr << "Error reading " << length
- << " bytes, windows error code 0x" << hex
- << error << dec << ".\n";
- return 0;
- }
- }
-
- length = bytes_read;
-
-#else
- // Posix case.
- ssize_t result = ::read(_handle, start, length);
- if (result < 0) {
- cerr << "Error reading " << length << " bytes\n";
- return 0;
- }
-
- length = result;
-#endif // _WIN32
-
- return length;
-}
-
-/**
- * Outputs the indicated stream of characters to the current file position.
- */
-size_t HandleStreamBuf::
-write_chars(const char *start, size_t length) {
- if (length == 0) {
- // Trivial no-op.
- return 0;
- }
-
- // Make sure the read buffer is flushed.
- size_t n = egptr() - gptr();
- gbump(n);
-
- if (length == 0 || !_is_open_write) {
- return 0;
- }
-
-#ifdef _WIN32
- // Windows case.
- DWORD bytes_written = 0;
- BOOL success = WriteFile(_handle, start, length, &bytes_written, nullptr);
- if (!success) {
- assert(bytes_written <= length);
- DWORD error = GetLastError();
- if (error != ERROR_NO_DATA && error != ERROR_BROKEN_PIPE) {
- cerr << "Error writing " << length
- << " bytes, windows error code 0x" << hex
- << error << dec << ".\n";
- }
- return bytes_written;
- }
- assert(bytes_written == length);
-
-#else
- // Posix case.
- size_t remaining = length;
- while (remaining > 0) {
- ssize_t result = ::write(_handle, start, remaining);
- if (result < 0) {
- if (errno != EPIPE) {
- cerr << "Error writing " << remaining << " bytes\n";
- }
- return length - remaining;
- }
-
- start += result;
- remaining -= result;
- }
-#endif // _WIN32
-
- return length;
-}
diff --git a/direct/src/plugin/handleStreamBuf.h b/direct/src/plugin/handleStreamBuf.h
deleted file mode 100644
index da3448d4d3..0000000000
--- a/direct/src/plugin/handleStreamBuf.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file handleStreamBuf.h
- * @author drose
- * @date 2009-06-05
- */
-
-#ifndef HANDLESTREAMBUF_H
-#define HANDLESTREAMBUF_H
-
-#include "fhandle.h"
-#include "p3d_lock.h"
-#include
-
-/**
- *
- */
-class HandleStreamBuf : public std::streambuf {
-public:
- HandleStreamBuf();
- virtual ~HandleStreamBuf();
-
- void open_read(FHandle handle);
- void open_write(FHandle handle);
- bool is_open_read() const;
- bool is_open_write() const;
- void close();
- void close_handle();
-
- inline FHandle get_handle() const;
- inline bool has_gdata() const;
-
-protected:
- virtual int overflow(int c);
- virtual int sync();
- virtual int underflow();
-
-private:
- size_t read_chars(char *start, size_t length);
- size_t write_chars(const char *start, size_t length);
-
-private:
- bool _is_open_read;
- bool _is_open_write;
-
- FHandle _handle;
- LOCK _lock;
-
- char *_buffer;
-};
-
-#include "handleStreamBuf.I"
-
-#endif
diff --git a/direct/src/plugin/is_pathsep.I b/direct/src/plugin/is_pathsep.I
deleted file mode 100644
index 3dd35068b8..0000000000
--- a/direct/src/plugin/is_pathsep.I
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file is_pathsep.I
- * @author drose
- * @date 2009-07-07
- */
-
-/**
- * Returns true if the indicated character is a path separator character (e.g.
- * slash or backslash), false otherwise.
- */
-inline bool
-is_pathsep(int ch) {
- if (ch == '/') {
- return true;
- }
-#ifdef _WIN32
- if (ch == '\\') {
- return true;
- }
-#endif
- return false;
-}
diff --git a/direct/src/plugin/is_pathsep.h b/direct/src/plugin/is_pathsep.h
deleted file mode 100644
index d1e326ee2d..0000000000
--- a/direct/src/plugin/is_pathsep.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file is_pathsep.h
- * @author drose
- * @date 2009-07-07
- */
-
-#ifndef IS_PATHSEP_H
-#define IS_PATHSEP_H
-
-inline bool
-is_pathsep(int ch);
-
-#include "is_pathsep.I"
-
-#endif
diff --git a/direct/src/plugin/load_plugin.cxx b/direct/src/plugin/load_plugin.cxx
deleted file mode 100644
index a1575ff087..0000000000
--- a/direct/src/plugin/load_plugin.cxx
+++ /dev/null
@@ -1,476 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file load_plugin.cxx
- * @author drose
- * @date 2009-06-19
- */
-
-#include "load_plugin.h"
-#include "p3d_plugin_config.h"
-#include "is_pathsep.h"
-#include "wstring_encode.h"
-
-#include
-
-#include
-
-#ifndef _WIN32
-#include
-#endif
-
-using std::string;
-
-#ifdef _WIN32
-static const string dll_ext = ".dll";
-#elif defined(__APPLE__)
-static const string dll_ext = ".dylib";
-#else
-static const string dll_ext = ".so";
-#endif
-
-static const string default_plugin_filename = "p3d_plugin";
-
-P3D_initialize_func *P3D_initialize_ptr;
-P3D_finalize_func *P3D_finalize_ptr;
-P3D_set_plugin_version_func *P3D_set_plugin_version_ptr;
-P3D_set_super_mirror_func *P3D_set_super_mirror_ptr;
-P3D_new_instance_func *P3D_new_instance_ptr;
-P3D_instance_start_func *P3D_instance_start_ptr;
-P3D_instance_start_stream_func *P3D_instance_start_stream_ptr;
-P3D_instance_finish_func *P3D_instance_finish_ptr;
-P3D_instance_setup_window_func *P3D_instance_setup_window_ptr;
-
-P3D_object_get_type_func *P3D_object_get_type_ptr;
-P3D_object_get_bool_func *P3D_object_get_bool_ptr;
-P3D_object_get_int_func *P3D_object_get_int_ptr;
-P3D_object_get_float_func *P3D_object_get_float_ptr;
-P3D_object_get_string_func *P3D_object_get_string_ptr;
-P3D_object_get_repr_func *P3D_object_get_repr_ptr;
-P3D_object_get_property_func *P3D_object_get_property_ptr;
-P3D_object_set_property_func *P3D_object_set_property_ptr;
-P3D_object_has_method_func *P3D_object_has_method_ptr;
-P3D_object_call_func *P3D_object_call_ptr;
-P3D_object_eval_func *P3D_object_eval_ptr;
-P3D_object_incref_func *P3D_object_incref_ptr;
-P3D_object_decref_func *P3D_object_decref_ptr;
-
-P3D_make_class_definition_func *P3D_make_class_definition_ptr;
-P3D_new_undefined_object_func *P3D_new_undefined_object_ptr;
-P3D_new_none_object_func *P3D_new_none_object_ptr;
-P3D_new_bool_object_func *P3D_new_bool_object_ptr;
-P3D_new_int_object_func *P3D_new_int_object_ptr;
-P3D_new_float_object_func *P3D_new_float_object_ptr;
-P3D_new_string_object_func *P3D_new_string_object_ptr;
-P3D_instance_get_panda_script_object_func *P3D_instance_get_panda_script_object_ptr;
-P3D_instance_set_browser_script_object_func *P3D_instance_set_browser_script_object_ptr;
-
-P3D_instance_get_request_func *P3D_instance_get_request_ptr;
-P3D_check_request_func *P3D_check_request_ptr;
-P3D_request_finish_func *P3D_request_finish_ptr;
-P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream_ptr;
-P3D_instance_handle_event_func *P3D_instance_handle_event_ptr;
-
-#ifdef _WIN32
-static HMODULE module = nullptr;
-#else
-static void *module = nullptr;
-#endif
-
-static bool plugin_loaded = false;
-static bool dso_needs_unload = false;
-
-
-/**
- * Returns the default plugin filename, without any directory path (but
- * including the extension appropriate to this platform).
- */
-string
-get_plugin_basename() {
- return default_plugin_filename + dll_ext;
-}
-
-/**
- * Returns the position in the string of the dot before the filename
- * extension; that is, the position of the rightmost dot that is right of the
- * rightmost slash (or backslash, on Windows). Returns string::npos if there
- * is no extension.
- */
-static size_t
-find_extension_dot(const string &filename) {
- size_t p = filename.length();
- while (p > 0 && !is_pathsep(filename[p - 1])) {
- --p;
- if (filename[p] == '.') {
- return p;
- }
- }
-
- return string::npos;
-}
-
-// Forward reference for function defined below.
-static void unload_dso();
-
-/**
- * Loads the plugin and assigns all of the function pointers. Returns true on
- * success, false on failure. If load_plugin() has already been called
- * successfully, this returns true immediately, without parsing any
- * parameters.
- *
- * If p3d_plugin_filename is empty, the module is assumed to be already loaded
- * (or statically linked in), and the symbols are located within the current
- * address space.
- */
-bool
-load_plugin(const string &p3d_plugin_filename,
- const string &contents_filename, const string &host_url,
- P3D_verify_contents verify_contents, const string &platform,
- const string &log_directory, const string &log_basename,
- bool trusted_environment, bool console_environment,
- const string &root_dir, const string &host_dir,
- const string &start_dir, std::ostream &logfile) {
- if (plugin_loaded) {
- return true;
- }
- string filename = p3d_plugin_filename;
-
-#ifdef _WIN32
- assert(module == nullptr);
-
- if (filename.empty()) {
- // If no filename is supplied, look within our existing address space.
- module = GetModuleHandle(nullptr);
- dso_needs_unload = false;
-
- } else {
- // If a filename is supplied, attempt to load it as a dynamic library.
-
- // On Windows, the filename passed to LoadLibrary() must have an
- // extension, or a default ".DLL" will be implicitly added. If the file
- // actually has no extension, we must add "." to avoid this.
-
- // Check whether the filename has an extension.
- size_t extension_dot = find_extension_dot(filename);
- if (extension_dot == string::npos) {
- // No extension.
- filename += ".";
- }
-
- SetErrorMode(0);
- std::wstring filename_w;
- if (string_to_wstring(filename_w, filename)) {
- module = LoadLibraryW(filename_w.c_str());
- }
- dso_needs_unload = true;
- }
-
- if (module == nullptr) {
- // Couldn't load the DLL.
- logfile
- << "Couldn't load " << filename << ", error = "
- << GetLastError() << "\n";
- return false;
- }
-
- #define get_func GetProcAddress
-
-#else // _WIN32
- // Posix case.
- assert(module == nullptr);
- if (filename.empty()) {
- module = dlopen(nullptr, RTLD_LAZY | RTLD_LOCAL);
- } else {
- module = dlopen(filename.c_str(), RTLD_LAZY | RTLD_LOCAL);
- }
- if (module == nullptr) {
- // Couldn't load the .so.
- const char *message = dlerror();
- if (message == nullptr) {
- message = "No error";
- }
- logfile << "Couldn't load " << filename << ": " << message << "\n";
-
- return false;
- }
- dso_needs_unload = true;
-
- #define get_func dlsym
-
-#endif // _WIN32
-
- // Now get all of the function pointers.
- P3D_initialize_ptr = (P3D_initialize_func *)get_func(module, "P3D_initialize");
- P3D_finalize_ptr = (P3D_finalize_func *)get_func(module, "P3D_finalize");
- P3D_set_plugin_version_ptr = (P3D_set_plugin_version_func *)get_func(module, "P3D_set_plugin_version");
- P3D_set_super_mirror_ptr = (P3D_set_super_mirror_func *)get_func(module, "P3D_set_super_mirror");
- P3D_new_instance_ptr = (P3D_new_instance_func *)get_func(module, "P3D_new_instance");
- P3D_instance_start_ptr = (P3D_instance_start_func *)get_func(module, "P3D_instance_start");
- P3D_instance_start_stream_ptr = (P3D_instance_start_stream_func *)get_func(module, "P3D_instance_start_stream");
- P3D_instance_finish_ptr = (P3D_instance_finish_func *)get_func(module, "P3D_instance_finish");
- P3D_instance_setup_window_ptr = (P3D_instance_setup_window_func *)get_func(module, "P3D_instance_setup_window");
-
- P3D_object_get_type_ptr = (P3D_object_get_type_func *)get_func(module, "P3D_object_get_type");
- P3D_object_get_bool_ptr = (P3D_object_get_bool_func *)get_func(module, "P3D_object_get_bool");
- P3D_object_get_int_ptr = (P3D_object_get_int_func *)get_func(module, "P3D_object_get_int");
- P3D_object_get_float_ptr = (P3D_object_get_float_func *)get_func(module, "P3D_object_get_float");
- P3D_object_get_string_ptr = (P3D_object_get_string_func *)get_func(module, "P3D_object_get_string");
- P3D_object_get_repr_ptr = (P3D_object_get_repr_func *)get_func(module, "P3D_object_get_repr");
- P3D_object_get_property_ptr = (P3D_object_get_property_func *)get_func(module, "P3D_object_get_property");
- P3D_object_set_property_ptr = (P3D_object_set_property_func *)get_func(module, "P3D_object_set_property");
- P3D_object_has_method_ptr = (P3D_object_has_method_func *)get_func(module, "P3D_object_has_method");
- P3D_object_call_ptr = (P3D_object_call_func *)get_func(module, "P3D_object_call");
- P3D_object_eval_ptr = (P3D_object_eval_func *)get_func(module, "P3D_object_eval");
- P3D_object_incref_ptr = (P3D_object_incref_func *)get_func(module, "P3D_object_incref");
- P3D_object_decref_ptr = (P3D_object_decref_func *)get_func(module, "P3D_object_decref");
- P3D_make_class_definition_ptr = (P3D_make_class_definition_func *)get_func(module, "P3D_make_class_definition");
- P3D_new_undefined_object_ptr = (P3D_new_undefined_object_func *)get_func(module, "P3D_new_undefined_object");
- P3D_new_none_object_ptr = (P3D_new_none_object_func *)get_func(module, "P3D_new_none_object");
- P3D_new_bool_object_ptr = (P3D_new_bool_object_func *)get_func(module, "P3D_new_bool_object");
- P3D_new_int_object_ptr = (P3D_new_int_object_func *)get_func(module, "P3D_new_int_object");
- P3D_new_float_object_ptr = (P3D_new_float_object_func *)get_func(module, "P3D_new_float_object");
- P3D_new_string_object_ptr = (P3D_new_string_object_func *)get_func(module, "P3D_new_string_object");
- P3D_instance_get_panda_script_object_ptr = (P3D_instance_get_panda_script_object_func *)get_func(module, "P3D_instance_get_panda_script_object");
- P3D_instance_set_browser_script_object_ptr = (P3D_instance_set_browser_script_object_func *)get_func(module, "P3D_instance_set_browser_script_object");
-
- P3D_instance_get_request_ptr = (P3D_instance_get_request_func *)get_func(module, "P3D_instance_get_request");
- P3D_check_request_ptr = (P3D_check_request_func *)get_func(module, "P3D_check_request");
- P3D_request_finish_ptr = (P3D_request_finish_func *)get_func(module, "P3D_request_finish");
- P3D_instance_feed_url_stream_ptr = (P3D_instance_feed_url_stream_func *)get_func(module, "P3D_instance_feed_url_stream");
- P3D_instance_handle_event_ptr = (P3D_instance_handle_event_func *)get_func(module, "P3D_instance_handle_event");
-
- #undef get_func
-
- // Successfully loaded.
- plugin_loaded = true;
-
- if (!init_plugin(contents_filename, host_url,
- verify_contents, platform,
- log_directory, log_basename,
- trusted_environment, console_environment,
- root_dir, host_dir, start_dir, logfile)) {
- unload_dso();
- return false;
- }
-
- return true;
-}
-
-/**
- * Ensures all the required function pointers have been set, and then calls
- * P3D_initialize() on the recently-loaded plugin. Returns true on success,
- * false on failure.
- *
- * It is not necessary to call this after calling load_plugin(); it is called
- * implicitly.
- */
-bool
-init_plugin(const string &contents_filename, const string &host_url,
- P3D_verify_contents verify_contents, const string &platform,
- const string &log_directory, const string &log_basename,
- bool trusted_environment, bool console_environment,
- const string &root_dir, const string &host_dir,
- const string &start_dir, std::ostream &logfile) {
-
- // Ensure that all of the function pointers have been found.
- if (P3D_initialize_ptr == nullptr ||
- P3D_finalize_ptr == nullptr ||
- P3D_set_plugin_version_ptr == nullptr ||
- P3D_set_super_mirror_ptr == nullptr ||
- P3D_new_instance_ptr == nullptr ||
- P3D_instance_start_ptr == nullptr ||
- P3D_instance_start_stream_ptr == nullptr ||
- P3D_instance_finish_ptr == nullptr ||
- P3D_instance_setup_window_ptr == nullptr ||
-
- P3D_object_get_type_ptr == nullptr ||
- P3D_object_get_bool_ptr == nullptr ||
- P3D_object_get_int_ptr == nullptr ||
- P3D_object_get_float_ptr == nullptr ||
- P3D_object_get_string_ptr == nullptr ||
- P3D_object_get_repr_ptr == nullptr ||
- P3D_object_get_property_ptr == nullptr ||
- P3D_object_set_property_ptr == nullptr ||
- P3D_object_has_method_ptr == nullptr ||
- P3D_object_call_ptr == nullptr ||
- P3D_object_eval_ptr == nullptr ||
- P3D_object_incref_ptr == nullptr ||
- P3D_object_decref_ptr == nullptr ||
-
- P3D_make_class_definition_ptr == nullptr ||
- P3D_new_undefined_object_ptr == nullptr ||
- P3D_new_none_object_ptr == nullptr ||
- P3D_new_bool_object_ptr == nullptr ||
- P3D_new_int_object_ptr == nullptr ||
- P3D_new_float_object_ptr == nullptr ||
- P3D_new_string_object_ptr == nullptr ||
- P3D_instance_get_panda_script_object_ptr == nullptr ||
- P3D_instance_set_browser_script_object_ptr == nullptr ||
-
- P3D_instance_get_request_ptr == nullptr ||
- P3D_check_request_ptr == nullptr ||
- P3D_request_finish_ptr == nullptr ||
- P3D_instance_feed_url_stream_ptr == nullptr ||
- P3D_instance_handle_event_ptr == nullptr) {
-
- logfile
- << "Some function pointers not found:"
- << "\nP3D_initialize_ptr = " << P3D_initialize_ptr
- << "\nP3D_finalize_ptr = " << P3D_finalize_ptr
- << "\nP3D_set_plugin_version_ptr = " << P3D_set_plugin_version_ptr
- << "\nP3D_set_super_mirror_ptr = " << P3D_set_super_mirror_ptr
- << "\nP3D_new_instance_ptr = " << P3D_new_instance_ptr
- << "\nP3D_instance_start_ptr = " << P3D_instance_start_ptr
- << "\nP3D_instance_start_stream_ptr = " << P3D_instance_start_stream_ptr
- << "\nP3D_instance_finish_ptr = " << P3D_instance_finish_ptr
- << "\nP3D_instance_setup_window_ptr = " << P3D_instance_setup_window_ptr
-
- << "\nP3D_object_get_type_ptr = " << P3D_object_get_type_ptr
- << "\nP3D_object_get_bool_ptr = " << P3D_object_get_bool_ptr
- << "\nP3D_object_get_int_ptr = " << P3D_object_get_int_ptr
- << "\nP3D_object_get_float_ptr = " << P3D_object_get_float_ptr
- << "\nP3D_object_get_string_ptr = " << P3D_object_get_string_ptr
- << "\nP3D_object_get_repr_ptr = " << P3D_object_get_repr_ptr
- << "\nP3D_object_get_property_ptr = " << P3D_object_get_property_ptr
- << "\nP3D_object_set_property_ptr = " << P3D_object_set_property_ptr
- << "\nP3D_object_has_method_ptr = " << P3D_object_has_method_ptr
- << "\nP3D_object_call_ptr = " << P3D_object_call_ptr
- << "\nP3D_object_eval_ptr = " << P3D_object_eval_ptr
- << "\nP3D_object_incref_ptr = " << P3D_object_incref_ptr
- << "\nP3D_object_decref_ptr = " << P3D_object_decref_ptr
-
- << "\nP3D_make_class_definition_ptr = " << P3D_make_class_definition_ptr
- << "\nP3D_new_undefined_object_ptr = " << P3D_new_undefined_object_ptr
- << "\nP3D_new_none_object_ptr = " << P3D_new_none_object_ptr
- << "\nP3D_new_bool_object_ptr = " << P3D_new_bool_object_ptr
- << "\nP3D_new_int_object_ptr = " << P3D_new_int_object_ptr
- << "\nP3D_new_float_object_ptr = " << P3D_new_float_object_ptr
- << "\nP3D_new_string_object_ptr = " << P3D_new_string_object_ptr
- << "\nP3D_instance_get_panda_script_object_ptr = " << P3D_instance_get_panda_script_object_ptr
- << "\nP3D_instance_set_browser_script_object_ptr = " << P3D_instance_set_browser_script_object_ptr
-
- << "\nP3D_instance_get_request_ptr = " << P3D_instance_get_request_ptr
- << "\nP3D_check_request_ptr = " << P3D_check_request_ptr
- << "\nP3D_request_finish_ptr = " << P3D_request_finish_ptr
- << "\nP3D_instance_feed_url_stream_ptr = " << P3D_instance_feed_url_stream_ptr
- << "\nP3D_instance_handle_event_ptr = " << P3D_instance_handle_event_ptr
- << "\n";
- return false;
- }
-
- // A bit of extra hand-hacked compatibility for using newer plug-ins with an
- // older version of the core API.
- int api_version = P3D_API_VERSION;
- if (api_version == 17 && start_dir.empty()) {
- api_version = 16;
- if (host_dir.empty()) {
- api_version = 15;
- }
- }
-
- if (!P3D_initialize_ptr(api_version, contents_filename.c_str(),
- host_url.c_str(), verify_contents, platform.c_str(),
- log_directory.c_str(), log_basename.c_str(),
- trusted_environment, console_environment,
- root_dir.c_str(), host_dir.c_str(),
- start_dir.c_str())) {
- // Oops, failure to initialize.
- logfile
- << "Failed to initialize plugin (passed API version "
- << api_version << ")\n";
- return false;
- }
-
- return true;
-}
-
-/**
- * Calls finalize, then removes the plugin from memory space and clears all of
- * the pointers.
- */
-void
-unload_plugin(std::ostream &logfile) {
- if (!plugin_loaded) {
- return;
- }
-
- P3D_finalize_ptr();
- unload_dso();
-}
-
-/**
- * Removes the plugin from memory space and clears all of the pointers. This
- * is only intended to be called by load_plugin(), above, in the specific case
- * that the plugin loaded but could not successfully initialize itself. All
- * user code should call unload_plugin(), above, which first calls
- * P3D_finalize().
- */
-static void
-unload_dso() {
- if (dso_needs_unload) {
- assert(module != nullptr);
-#ifdef _WIN32
- FreeLibrary(module);
-#else
- dlclose(module);
-#endif
- module = nullptr;
- dso_needs_unload = false;
- }
-
- P3D_initialize_ptr = nullptr;
- P3D_finalize_ptr = nullptr;
- P3D_set_plugin_version_ptr = nullptr;
- P3D_set_super_mirror_ptr = nullptr;
- P3D_new_instance_ptr = nullptr;
- P3D_instance_start_ptr = nullptr;
- P3D_instance_start_stream_ptr = nullptr;
- P3D_instance_finish_ptr = nullptr;
- P3D_instance_setup_window_ptr = nullptr;
-
- P3D_object_get_type_ptr = nullptr;
- P3D_object_get_bool_ptr = nullptr;
- P3D_object_get_int_ptr = nullptr;
- P3D_object_get_float_ptr = nullptr;
- P3D_object_get_string_ptr = nullptr;
- P3D_object_get_repr_ptr = nullptr;
- P3D_object_get_property_ptr = nullptr;
- P3D_object_set_property_ptr = nullptr;
- P3D_object_has_method_ptr = nullptr;
- P3D_object_call_ptr = nullptr;
- P3D_object_eval_ptr = nullptr;
- P3D_object_incref_ptr = nullptr;
- P3D_object_decref_ptr = nullptr;
-
- P3D_make_class_definition_ptr = nullptr;
- P3D_new_undefined_object_ptr = nullptr;
- P3D_new_none_object_ptr = nullptr;
- P3D_new_bool_object_ptr = nullptr;
- P3D_new_int_object_ptr = nullptr;
- P3D_new_float_object_ptr = nullptr;
- P3D_new_string_object_ptr = nullptr;
- P3D_instance_get_panda_script_object_ptr = nullptr;
- P3D_instance_set_browser_script_object_ptr = nullptr;
-
- P3D_instance_get_request_ptr = nullptr;
- P3D_check_request_ptr = nullptr;
- P3D_request_finish_ptr = nullptr;
- P3D_instance_feed_url_stream_ptr = nullptr;
- P3D_instance_handle_event_ptr = nullptr;
-
- plugin_loaded = false;
-}
-
-/**
- * Returns true if the plugin has been loaded successfully by a previous call
- * to load_plugin(), false otherwise.
- */
-bool
-is_plugin_loaded() {
- return plugin_loaded;
-}
diff --git a/direct/src/plugin/load_plugin.h b/direct/src/plugin/load_plugin.h
deleted file mode 100644
index 2e462ed9cd..0000000000
--- a/direct/src/plugin/load_plugin.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file load_plugin.h
- * @author drose
- * @date 2009-06-19
- */
-
-#ifndef LOAD_PLUGIN_H
-#define LOAD_PLUGIN_H
-
-#include "p3d_plugin.h"
-
-#include
-
-extern P3D_initialize_func *P3D_initialize_ptr;
-extern P3D_finalize_func *P3D_finalize_ptr;
-extern P3D_set_plugin_version_func *P3D_set_plugin_version_ptr;
-extern P3D_set_super_mirror_func *P3D_set_super_mirror_ptr;
-extern P3D_new_instance_func *P3D_new_instance_ptr;
-extern P3D_instance_start_func *P3D_instance_start_ptr;
-extern P3D_instance_start_stream_func *P3D_instance_start_stream_ptr;
-extern P3D_instance_finish_func *P3D_instance_finish_ptr;
-extern P3D_instance_setup_window_func *P3D_instance_setup_window_ptr;
-
-extern P3D_object_get_type_func *P3D_object_get_type_ptr;
-extern P3D_object_get_bool_func *P3D_object_get_bool_ptr;
-extern P3D_object_get_int_func *P3D_object_get_int_ptr;
-extern P3D_object_get_float_func *P3D_object_get_float_ptr;
-extern P3D_object_get_string_func *P3D_object_get_string_ptr;
-extern P3D_object_get_repr_func *P3D_object_get_repr_ptr;
-extern P3D_object_get_property_func *P3D_object_get_property_ptr;
-extern P3D_object_set_property_func *P3D_object_set_property_ptr;
-extern P3D_object_has_method_func *P3D_object_has_method_ptr;
-extern P3D_object_call_func *P3D_object_call_ptr;
-extern P3D_object_eval_func *P3D_object_eval_ptr;
-extern P3D_object_incref_func *P3D_object_incref_ptr;
-extern P3D_object_decref_func *P3D_object_decref_ptr;
-
-extern P3D_make_class_definition_func *P3D_make_class_definition_ptr;
-extern P3D_new_undefined_object_func *P3D_new_undefined_object_ptr;
-extern P3D_new_none_object_func *P3D_new_none_object_ptr;
-extern P3D_new_bool_object_func *P3D_new_bool_object_ptr;
-extern P3D_new_int_object_func *P3D_new_int_object_ptr;
-extern P3D_new_float_object_func *P3D_new_float_object_ptr;
-extern P3D_new_string_object_func *P3D_new_string_object_ptr;
-extern P3D_instance_get_panda_script_object_func *P3D_instance_get_panda_script_object_ptr;
-extern P3D_instance_set_browser_script_object_func *P3D_instance_set_browser_script_object_ptr;
-
-extern P3D_instance_get_request_func *P3D_instance_get_request_ptr;
-extern P3D_check_request_func *P3D_check_request_ptr;
-extern P3D_request_finish_func *P3D_request_finish_ptr;
-extern P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream_ptr;
-extern P3D_instance_handle_event_func *P3D_instance_handle_event_ptr;
-
-std::string get_plugin_basename();
-bool
-load_plugin(const std::string &p3d_plugin_filename,
- const std::string &contents_filename, const std::string &host_url,
- P3D_verify_contents verify_contents, const std::string &platform,
- const std::string &log_directory, const std::string &log_basename,
- bool trusted_environment, bool console_environment,
- const std::string &root_dir, const std::string &host_dir,
- const std::string &start_dir, std::ostream &logfile);
-bool
-init_plugin(const std::string &contents_filename, const std::string &host_url,
- P3D_verify_contents verify_contents, const std::string &platform,
- const std::string &log_directory, const std::string &log_basename,
- bool trusted_environment, bool console_environment,
- const std::string &root_dir, const std::string &host_dir,
- const std::string &start_dir, std::ostream &logfile);
-
-void unload_plugin(std::ostream &logfile);
-bool is_plugin_loaded();
-
-#endif
diff --git a/direct/src/plugin/mkdir_complete.cxx b/direct/src/plugin/mkdir_complete.cxx
deleted file mode 100644
index 6689584198..0000000000
--- a/direct/src/plugin/mkdir_complete.cxx
+++ /dev/null
@@ -1,224 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file mkdir_complete.cxx
- * @author drose
- * @date 2009-06-29
- */
-
-#include "mkdir_complete.h"
-#include "is_pathsep.h"
-#include "wstring_encode.h"
-
-#ifdef _WIN32
-#include
-#include // chmod()
-#else
-#include
-#include // for mkdir()
-#include
-#include // strerror()
-#include
-#endif
-
-using std::ostream;
-using std::string;
-using std::wstring;
-
-/**
- * Returns the directory component of the indicated pathname, or the empty
- * string if there is no directory prefix.
- */
-static string
-get_dirname(const string &filename) {
- size_t p = filename.length();
- while (p > 0) {
- --p;
- if (is_pathsep(filename[p])) {
- return filename.substr(0, p);
- }
- }
-
- return string();
-}
-
-#ifdef _WIN32
-/**
- * The wide-character implementation of get_dirname(). Only implemented (and
- * needed) on Windows.
- */
-static wstring
-get_dirname_w(const wstring &filename) {
- size_t p = filename.length();
- while (p > 0) {
- --p;
- if (is_pathsep(filename[p])) {
- return filename.substr(0, p);
- }
- }
-
- return wstring();
-}
-#endif // _WIN32
-
-
-
-/**
- * Creates a new directory, with normal access privileges. Returns true on
- * success, false on failure. Will create intervening directories if
- * necessary.
- */
-bool
-mkdir_complete(const string &dirname, ostream &logfile) {
-#ifdef _WIN32
- wstring dirname_w;
- if (!string_to_wstring(dirname_w, dirname)) {
- return false;
- }
- return mkdir_complete_w(dirname_w, logfile);
-
-#else //_WIN32
- if (mkdir(dirname.c_str(), 0755) == 0) {
- // Success!
- return true;
- }
-
- // Failed.
- if (errno == EEXIST) {
- // Not really an error: the directory is already there.
- return true;
- }
-
- if (errno == ENOENT || errno == EACCES) {
- // We need to make the parent directory first.
- string parent = get_dirname(dirname);
- if (!parent.empty() && mkdir_complete(parent, logfile)) {
- // Parent successfully created. Try again to make the child.
- if (mkdir(dirname.c_str(), 0755) == 0) {
- // Got it!
- return true;
- }
- // Couldn't create the directory. :(
- logfile
- << "Couldn't create " << dirname << ": " << strerror(errno) << "\n";
- }
- }
- return false;
-
-#endif // _WIN32
-}
-
-/**
- * Creates a new file with normal access priviledges. Returns true on
- * success, false on failure. This will create intervening directories if
- * needed.
- */
-bool
-mkfile_complete(const string &filename, ostream &logfile) {
-#ifdef _WIN32
- wstring filename_w;
- if (!string_to_wstring(filename_w, filename)) {
- return false;
- }
- return mkfile_complete_w(filename_w, logfile);
-#else // _WIN32
- // Make sure we delete any previously-existing file first.
- unlink(filename.c_str());
-
- int fd = creat(filename.c_str(), 0755);
- if (fd == -1) {
- // Try to make the parent directory first.
- string parent = get_dirname(filename);
- if (!parent.empty() && mkdir_complete(parent, logfile)) {
- // Parent successfully created. Try again to make the file.
- fd = creat(filename.c_str(), 0755);
- }
- if (fd == -1) {
- logfile
- << "Couldn't create " << filename << ": " << strerror(errno) << "\n";
- return false;
- }
- }
- close(fd);
- return true;
-
-#endif // _WIN32
-}
-
-
-#ifdef _WIN32
-/**
- * The wide-character implementation of mkdir_complete(). Only implemented
- * (and needed) on Windows.
- */
-bool
-mkdir_complete_w(const wstring &dirname, ostream &logfile) {
- if (CreateDirectoryW(dirname.c_str(), nullptr) != 0) {
- // Success!
- return true;
- }
-
- // Failed.
- DWORD last_error = GetLastError();
- if (last_error == ERROR_ALREADY_EXISTS) {
- // Not really an error: the directory is already there.
- return true;
- }
-
- if (last_error == ERROR_PATH_NOT_FOUND) {
- // We need to make the parent directory first.
- wstring parent = get_dirname_w(dirname);
- if (!parent.empty() && mkdir_complete_w(parent, logfile)) {
- // Parent successfully created. Try again to make the child.
- if (CreateDirectoryW(dirname.c_str(), nullptr) != 0) {
- // Got it!
- return true;
- }
- logfile
- << "Couldn't create " << dirname << "\n";
- }
- }
- return false;
-}
-#endif // _WIN32
-
-#ifdef _WIN32
-/**
- * The wide-character implementation of mkfile_complete(). Only implemented
- * (and needed) on Windows.
- */
-bool
-mkfile_complete_w(const wstring &filename, ostream &logfile) {
- // Make sure we delete any previously-existing file first.
-
- // Windows can't delete a file if it's read-only. Weird.
- _wchmod(filename.c_str(), 0644);
- _wunlink(filename.c_str());
-
- HANDLE file = CreateFileW(filename.c_str(), GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
- if (file == INVALID_HANDLE_VALUE) {
- // Try to make the parent directory first.
- wstring parent = get_dirname_w(filename);
- if (!parent.empty() && mkdir_complete_w(parent, logfile)) {
- // Parent successfully created. Try again to make the file.
- file = CreateFileW(filename.c_str(), GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
- }
- if (file == INVALID_HANDLE_VALUE) {
- logfile
- << "Couldn't create " << filename << "\n";
- return false;
- }
- }
- CloseHandle(file);
- return true;
-}
-#endif // _WIN32
diff --git a/direct/src/plugin/mkdir_complete.h b/direct/src/plugin/mkdir_complete.h
deleted file mode 100644
index be79cf7e8e..0000000000
--- a/direct/src/plugin/mkdir_complete.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file mkdir_complete.h
- * @author drose
- * @date 2009-06-29
- */
-
-#ifndef MKDIR_COMPLETE_H
-#define MKDIR_COMPLETE_H
-
-#include
-#include
-
-bool mkdir_complete(const std::string &dirname, std::ostream &logfile);
-bool mkfile_complete(const std::string &dirname, std::ostream &logfile);
-
-#ifdef _WIN32
-bool mkdir_complete_w(const std::wstring &dirname, std::ostream &logfile);
-bool mkfile_complete_w(const std::wstring &dirname, std::ostream &logfile);
-#endif // _WIN32
-
-#endif
diff --git a/direct/src/plugin/p3dAuthSession.I b/direct/src/plugin/p3dAuthSession.I
deleted file mode 100644
index 0e451a8f87..0000000000
--- a/direct/src/plugin/p3dAuthSession.I
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dAuthSession.I
- * @author drose
- * @date 2009-09-17
- */
diff --git a/direct/src/plugin/p3dAuthSession.cxx b/direct/src/plugin/p3dAuthSession.cxx
deleted file mode 100644
index e76bbe0a07..0000000000
--- a/direct/src/plugin/p3dAuthSession.cxx
+++ /dev/null
@@ -1,456 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dAuthSession.cxx
- * @author drose
- * @date 2009-09-17
- */
-
-#include "p3dAuthSession.h"
-#include "p3dInstance.h"
-#include "p3dInstanceManager.h"
-#include "p3dMultifileReader.h"
-#include "p3d_plugin_config.h"
-#include "mkdir_complete.h"
-#include "wstring_encode.h"
-
-#include
-
-#ifndef _WIN32
-#include
-#include
-#include
-#include
-#include
-#include
-#endif
-
-using std::string;
-
-/**
- *
- */
-P3DAuthSession::
-P3DAuthSession(P3DInstance *inst) :
- _inst(inst)
-{
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
-#ifdef _WIN32
- _p3dcert_handle = INVALID_HANDLE_VALUE;
-#else
- _p3dcert_pid = -1;
-#endif
- _p3dcert_started = false;
- _p3dcert_running = false;
- _started_wait_thread = false;
- INIT_THREAD(_wait_thread);
-
- // Write the cert chain to a temporary file.
- _cert_filename = new P3DTemporaryFile(".crt");
- string filename = _cert_filename->get_filename();
- FILE *fp = fopen(filename.c_str(), "w");
- if (fp == nullptr) {
- nout << "Couldn't write temporary file\n";
- return;
- }
-
- if (inst->_mf_reader.get_num_signatures() > 0) {
- const P3DMultifileReader::CertChain &cert_chain =
- inst->_mf_reader.get_signature(0);
-
- if (cert_chain.size() > 0) {
- // Save the cert_dir, this is where the p3dcert program will need to
- // write the cert when it is approved.
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- _cert_dir = inst_mgr->get_cert_dir(cert_chain[0]._cert);
- }
-
- P3DMultifileReader::CertChain::const_iterator ci;
- for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) {
- X509 *c = (*ci)._cert;
- PEM_write_X509(fp, c);
- }
- }
- fclose(fp);
-
- start_p3dcert();
-}
-
-
-/**
- *
- */
-P3DAuthSession::
-~P3DAuthSession() {
- shutdown(false);
-
- if (_cert_filename != nullptr) {
- delete _cert_filename;
- }
-}
-
-/**
- * Terminates the session by killing the subprocess.
- */
-void P3DAuthSession::
-shutdown(bool send_message) {
- if (!send_message) {
- // If we're not to send the instance the shutdown message as a result of
- // this, then clear the _inst pointer now.
- _inst = nullptr;
- }
-
- if (_p3dcert_running) {
- nout << "Killing p3dcert process\n";
-#ifdef _WIN32
- TerminateProcess(_p3dcert_handle, 2);
- CloseHandle(_p3dcert_handle);
-
-#else // _WIN32
- kill(_p3dcert_pid, SIGKILL);
-
- // Wait a few milliseconds for the process to exit, and then get its
- // return status to clean up the zombie status. If we don't wait long
- // enough, don't sweat it.
- struct timeval tv;
- tv.tv_sec = 0;
- tv.tv_usec = 100000;
- select(0, nullptr, nullptr, nullptr, &tv);
- int status;
- waitpid(_p3dcert_pid, &status, WNOHANG);
-#endif // _WIN32
-
- _p3dcert_running = false;
- }
- _p3dcert_started = false;
-
- // Now that the process has stopped, the thread should stop itself quickly
- // too.
- join_wait_thread();
-
- // We're no longer bound to any particular instance.
- _inst = nullptr;
-}
-
-/**
- * Starts the p3dcert program running in a child process.
- */
-void P3DAuthSession::
-start_p3dcert() {
- if (_p3dcert_started) {
- // Already started.
- return;
- }
-
- if (_inst->_p3dcert_package == nullptr) {
- nout << "Couldn't start Python: no p3dcert package.\n";
- return;
- }
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
- _start_dir = inst_mgr->get_start_dir();
-
- string root_dir = _inst->_p3dcert_package->get_package_dir();
- mkdir_complete(_start_dir, nout);
-
- _p3dcert_exe = root_dir + "/p3dcert";
-#ifdef _WIN32
- _p3dcert_exe += ".exe";
-#endif
-#ifdef __APPLE__
- // On OSX, run from the packaged bundle.
- _p3dcert_exe = root_dir + "/P3DCert.app/Contents/MacOS/p3dcert";
-#endif
-
- // Populate the new process' environment.
-
-#ifdef _WIN32
- // These are the enviroment variables we forward from the current
- // environment, if they are set.
- const wchar_t *keep[] = {
- L"TMP", L"TEMP", L"HOME", L"USER",
- L"SYSTEMROOT", L"USERPROFILE", L"COMSPEC",
- nullptr
- };
-
- std::wstring env_w;
-
- for (int ki = 0; keep[ki] != nullptr; ++ki) {
- wchar_t *value = _wgetenv(keep[ki]);
- if (value != nullptr) {
- env_w += keep[ki];
- env_w += L"=";
- env_w += value;
- env_w += (wchar_t)'\0';
- }
- }
-
- wstring_to_string(_env, env_w);
-
-#else // _WIN32
-
- _env = string();
-
- // These are the enviroment variables we forward from the current
- // environment, if they are set.
- const char *keep[] = {
- "TMP", "TEMP", "HOME", "USER",
- "LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG",
-#ifdef HAVE_X11
- "DISPLAY", "XAUTHORITY",
-#endif
- nullptr
- };
- for (int ki = 0; keep[ki] != nullptr; ++ki) {
- char *value = getenv(keep[ki]);
- if (value != nullptr) {
- _env += keep[ki];
- _env += "=";
- _env += value;
- _env += '\0';
- }
- }
-#endif // _WIN32
-
- // Define some new environment variables.
- _env += "PATH=";
- _env += root_dir;
- _env += '\0';
-
- _env += "LD_LIBRARY_PATH=";
- _env += root_dir;
- _env += '\0';
-
- _env += "DYLD_LIBRARY_PATH=";
- _env += root_dir;
- _env += '\0';
-
- _env += "P3DCERT_ROOT=";
- _env += root_dir;
- _env += '\0';
-
- nout << "Setting environment:\n";
- write_env();
-
- nout << "Attempting to start p3dcert from " << _p3dcert_exe << "\n";
-
- bool started_p3dcert = false;
-#ifdef _WIN32
- _p3dcert_handle = win_create_process();
- started_p3dcert = (_p3dcert_handle != INVALID_HANDLE_VALUE);
-#else
- _p3dcert_pid = posix_create_process();
- started_p3dcert = (_p3dcert_pid > 0);
-#endif
- if (!started_p3dcert) {
- nout << "Failed to create process.\n";
- return;
- }
-
- _p3dcert_started = true;
- _p3dcert_running = true;
-
- spawn_wait_thread();
-}
-
-/**
- * Starts the wait thread. This thread is responsible for waiting for the
- * process to finish, and notifying the instance when it does.
- */
-void P3DAuthSession::
-spawn_wait_thread() {
- SPAWN_THREAD(_wait_thread, wt_thread_run, this);
- _started_wait_thread = true;
-}
-
-/**
- * Waits for the wait thread to stop.
- */
-void P3DAuthSession::
-join_wait_thread() {
- if (!_started_wait_thread) {
- return;
- }
-
- JOIN_THREAD(_wait_thread);
- _started_wait_thread = false;
-}
-
-/**
- * Writes _env, which is formatted as a string containing zero-byte-terminated
- * environment defintions, to the nout stream, one definition per line.
- */
-void P3DAuthSession::
-write_env() const {
- size_t p = 0;
- size_t zero = _env.find('\0', p);
- while (zero != string::npos) {
- nout << " ";
- nout.write(_env.data() + p, zero - p);
- nout << "\n";
- p = zero + 1;
- zero = _env.find('\0', p);
- }
-}
-
-/**
- * The main function for the wait thread.
- */
-void P3DAuthSession::
-wt_thread_run() {
- // All we do here is wait for the process to terminate.
- nout << "wt_thread_run in " << this << " beginning\n";
-#ifdef _WIN32
- DWORD result = WaitForSingleObject(_p3dcert_handle, INFINITE);
- if (result != 0) {
- nout << "Wait for process failed: " << GetLastError() << "\n";
- }
- CloseHandle(_p3dcert_handle);
- _p3dcert_handle = INVALID_HANDLE_VALUE;
- _p3dcert_running = false;
- nout << "p3dcert process has successfully stopped.\n";
-#else
- int status;
- pid_t result = waitpid(_p3dcert_pid, &status, 0);
- if (result == -1) {
- perror("waitpid");
- }
- _p3dcert_pid = -1;
- _p3dcert_running = false;
-
- nout << "p3dcert process has successfully stopped.\n";
- if (WIFEXITED(status)) {
- nout << " exited normally, status = "
- << WEXITSTATUS(status) << "\n";
- } else if (WIFSIGNALED(status)) {
- nout << " signalled by " << WTERMSIG(status) << ", core = "
- << WCOREDUMP(status) << "\n";
- } else if (WIFSTOPPED(status)) {
- nout << " stopped by " << WSTOPSIG(status) << "\n";
- }
-#endif // _WIN32
-
- // Notify the instance that we're done.
- P3DInstance *inst = _inst;
- if (inst != nullptr) {
- inst->auth_finished_sub_thread();
- }
-
- nout << "Exiting wt_thread_run in " << this << "\n";
-}
-
-#ifdef _WIN32
-/**
- * Creates a sub-process to run _p3dcert_exe, with the appropriate command-
- * line arguments, and the environment string defined in _env.
- *
- * Returns the handle to the created process on success, or
- * INVALID_HANDLE_VALUE on falure.
- */
-HANDLE P3DAuthSession::
-win_create_process() {
- // Make sure we see an error dialog if there is a missing DLL.
- SetErrorMode(0);
-
- STARTUPINFO startup_info;
- ZeroMemory(&startup_info, sizeof(startup_info));
- startup_info.cb = sizeof(startup_info);
-
- // Make sure the initial window is *shown* for this graphical app.
- startup_info.wShowWindow = SW_SHOW;
- startup_info.dwFlags |= STARTF_USESHOWWINDOW;
-
- const char *start_dir_cstr = _start_dir.c_str();
-
- // Construct the command-line string, containing the quoted command-line
- // arguments.
- std::ostringstream stream;
- stream << "\"" << _p3dcert_exe << "\" \""
- << _cert_filename->get_filename() << "\" \"" << _cert_dir << "\"";
-
- // I'm not sure why CreateProcess wants a non-const char pointer for its
- // command-line string, but I'm not taking chances. It gets a non-const
- // char array that it can modify.
- string command_line_str = stream.str();
- char *command_line = new char[command_line_str.size() + 1];
- memcpy(command_line, command_line_str.c_str(), command_line_str.size() + 1);
-
- nout << "Command line: " << command_line_str << "\n";
-
- // Something about p3dCert_wx tends to become crashy when we call it from
- // CreateProcessW(). Something about the way wx parses the command-line
- // parameters? Well, whatever, we don't really need the Unicode form
- // anyway.
- PROCESS_INFORMATION process_info;
- BOOL result = CreateProcess
- (_p3dcert_exe.c_str(), command_line, nullptr, nullptr, TRUE,
- 0, (void *)_env.c_str(),
- start_dir_cstr, &startup_info, &process_info);
- bool started_program = (result != 0);
-
- delete[] command_line;
-
- if (!started_program) {
- nout << "CreateProcess failed, error: " << GetLastError() << "\n";
- return INVALID_HANDLE_VALUE;
- }
-
- CloseHandle(process_info.hThread);
- return process_info.hProcess;
-}
-#endif // _WIN32
-
-
-#ifndef _WIN32
-/**
- * Creates a sub-process to run _p3dcert_exe, with the appropriate command-
- * line arguments, and the environment string defined in _env.
- *
- * Returns the pid of the created process on success, or -1 on falure.
- */
-int P3DAuthSession::
-posix_create_process() {
- // Fork and exec.
- pid_t child = fork();
- if (child < 0) {
- perror("fork");
- return -1;
- }
-
- if (child == 0) {
- // Here we are in the child process.
-
- if (chdir(_start_dir.c_str()) < 0) {
- nout << "Could not chdir to " << _start_dir << "\n";
- // This is a warning, not an error. We don't actually care that much
- // about the starting directory.
- }
-
- // build up an array of char strings for the environment.
- std::vector ptrs;
- size_t p = 0;
- size_t zero = _env.find('\0', p);
- while (zero != string::npos) {
- ptrs.push_back(_env.data() + p);
- p = zero + 1;
- zero = _env.find('\0', p);
- }
- ptrs.push_back(nullptr);
-
- execle(_p3dcert_exe.c_str(), _p3dcert_exe.c_str(),
- _cert_filename->get_filename().c_str(), _cert_dir.c_str(),
- (char *)0, &ptrs[0]);
- nout << "Failed to exec " << _p3dcert_exe << "\n";
- _exit(1);
- }
-
- return child;
-}
-#endif // _WIN32
diff --git a/direct/src/plugin/p3dAuthSession.h b/direct/src/plugin/p3dAuthSession.h
deleted file mode 100644
index f37f24d9e3..0000000000
--- a/direct/src/plugin/p3dAuthSession.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dAuthSession.h
- * @author drose
- * @date 2009-09-17
- */
-
-#ifndef P3DAUTHSESSION_H
-#define P3DAUTHSESSION_H
-
-#include "p3d_plugin_common.h"
-#include "p3dPackage.h"
-#include "get_tinyxml.h"
-#include "p3dTemporaryFile.h"
-#include "p3dReferenceCount.h"
-
-class P3DInstance;
-
-/**
- * This is an instance of a p3dcert program running in a subprocess. There's
- * no communication with the process, or none of that complicated stuff the
- * P3DSession has to do; all we do here is fire off the process, then wait for
- * it to exit.
- */
-class P3DAuthSession : public P3DReferenceCount {
-public:
- P3DAuthSession(P3DInstance *inst);
- ~P3DAuthSession();
-
- void shutdown(bool send_message);
-
-private:
- void start_p3dcert();
-
- void spawn_wait_thread();
- void join_wait_thread();
-
- void write_env() const;
-
-private:
- // These methods run only within the read thread.
- THREAD_CALLBACK_DECLARATION(P3DAuthSession, wt_thread_run);
- void wt_thread_run();
-
-#ifdef _WIN32
- HANDLE win_create_process();
-#else
- int posix_create_process();
-#endif
-
-private:
- P3DInstance *_inst;
- std::string _start_dir;
-
- // This information is passed to create_process().
- P3DTemporaryFile *_cert_filename;
- std::string _cert_dir;
- std::string _p3dcert_exe;
- std::string _env;
-
-#ifdef _WIN32
- HANDLE _p3dcert_handle;
-#else
- int _p3dcert_pid;
-#endif
- bool _p3dcert_started;
- bool _p3dcert_running;
-
- // The remaining members are manipulated by or for the read thread.
- bool _started_wait_thread;
- THREAD _wait_thread;
-};
-
-#include "p3dAuthSession.I"
-
-#endif
diff --git a/direct/src/plugin/p3dBoolObject.cxx b/direct/src/plugin/p3dBoolObject.cxx
deleted file mode 100644
index 7740c2f669..0000000000
--- a/direct/src/plugin/p3dBoolObject.cxx
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dBoolObject.cxx
- * @author drose
- * @date 2009-06-30
- */
-
-#include "p3dBoolObject.h"
-
-/**
- *
- */
-P3DBoolObject::
-P3DBoolObject(bool value) : _value(value) {
-}
-
-/**
- *
- */
-P3DBoolObject::
-P3DBoolObject(const P3DBoolObject ©) :
- P3DObject(copy),
- _value(copy._value)
-{
-}
-
-/**
- * Returns the fundamental type of this kind of object.
- */
-P3D_object_type P3DBoolObject::
-get_type() {
- return P3D_OT_bool;
-}
-
-/**
- * Returns the object value coerced to a boolean, if possible.
- */
-bool P3DBoolObject::
-get_bool() {
- return _value;
-}
-
-/**
- * Returns the object value coerced to an integer, if possible.
- */
-int P3DBoolObject::
-get_int() {
- return _value;
-}
-
-/**
- * Fills the indicated C++ string object with the value of this object coerced
- * to a string.
- */
-void P3DBoolObject::
-make_string(std::string &value) {
- if (_value) {
- value = "True";
- } else {
- value = "False";
- }
-}
diff --git a/direct/src/plugin/p3dBoolObject.h b/direct/src/plugin/p3dBoolObject.h
deleted file mode 100644
index 85eb0a127c..0000000000
--- a/direct/src/plugin/p3dBoolObject.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dBoolObject.h
- * @author drose
- * @date 2009-06-30
- */
-
-#ifndef P3DBOOLOBJECT_H
-#define P3DBOOLOBJECT_H
-
-#include "p3d_plugin_common.h"
-#include "p3dObject.h"
-
-/**
- * An object type that contains a boolean value.
- */
-class P3DBoolObject : public P3DObject {
-public:
- P3DBoolObject(bool value);
- P3DBoolObject(const P3DBoolObject ©);
-
-public:
- virtual P3D_object_type get_type();
- virtual bool get_bool();
- virtual int get_int();
- virtual void make_string(std::string &value);
-
-private:
- bool _value;
-};
-
-#endif
diff --git a/direct/src/plugin/p3dCInstance.I b/direct/src/plugin/p3dCInstance.I
deleted file mode 100644
index 34edfd6b62..0000000000
--- a/direct/src/plugin/p3dCInstance.I
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCInstance.I
- * @author drose
- * @date 2009-06-08
- */
-
-/**
- * Returns a unique integer for each instance in the system.
- */
-inline int P3DCInstance::
-get_instance_id() const {
- return _instance_id;
-}
diff --git a/direct/src/plugin/p3dCInstance.cxx b/direct/src/plugin/p3dCInstance.cxx
deleted file mode 100644
index 0f77925b22..0000000000
--- a/direct/src/plugin/p3dCInstance.cxx
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCInstance.cxx
- * @author drose
- * @date 2009-06-08
- */
-
-#include "p3dCInstance.h"
-
-
-/**
- * Constructs a new Instance from an XML description.
- */
-P3DCInstance::
-P3DCInstance(TiXmlElement *xinstance) :
- _func(nullptr)
-{
- xinstance->Attribute("instance_id", &_instance_id);
-}
-
-/**
- *
- */
-P3DCInstance::
-~P3DCInstance() {
-}
diff --git a/direct/src/plugin/p3dCInstance.h b/direct/src/plugin/p3dCInstance.h
deleted file mode 100644
index 8d0ebc29c2..0000000000
--- a/direct/src/plugin/p3dCInstance.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCInstance.h
- * @author drose
- * @date 2009-06-08
- */
-
-#ifndef P3DCINSTANCE_H
-#define P3DCINSTANCE_H
-
-#include "pandabase.h"
-
-#include "p3d_plugin.h"
-#include "pvector.h"
-#include "get_tinyxml.h"
-#include "windowHandle.h"
-
-class P3DSession;
-
-/**
- * This is an instance of a Panda3D window, as seen in the child-level
- * process.
- */
-class P3DCInstance : public P3D_instance {
-public:
- P3DCInstance(TiXmlElement *xinstance);
- ~P3DCInstance();
-
- inline int get_instance_id() const;
-
-public:
- PT(WindowHandle) _parent_window_handle;
-
-private:
- P3D_request_ready_func *_func;
-
- int _instance_id;
-
- friend class P3DPythonRun;
-};
-
-#include "p3dCInstance.I"
-
-#endif
diff --git a/direct/src/plugin/p3dCert.cxx b/direct/src/plugin/p3dCert.cxx
deleted file mode 100644
index e6868bdb80..0000000000
--- a/direct/src/plugin/p3dCert.cxx
+++ /dev/null
@@ -1,742 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert.cxx
- * @author rdb
- * @date 2011-03-08
- */
-
-#include "p3dCert.h"
-#include "p3dCert_strings.h"
-#include "wstring_encode.h"
-#include "mkdir_complete.h"
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#define BUTTON_WIDTH 180 // fit the Russian text
-#define BUTTON_SPACE 10
-
-#include "ca_bundle_data_src.c"
-
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include
-#include
-#include
-
-#define snprintf sprintf_s
-#endif
-
-#ifdef __APPLE__
-#include
-#endif
-
-using std::cerr;
-using std::string;
-using std::wstring;
-
-static LanguageIndex li = LI_default;
-
-#if defined(_WIN32)
-static LanguageIndex detect_language() {
- // This function was introduced in Windows Vista; it may not be available on
- // older systems.
- typedef BOOL (*GUPL)(DWORD, PULONG, PZZWSTR, PULONG);
- GUPL pGetUserPreferredUILanguages = (GUPL)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
- TEXT("GetUserPreferredUILanguages"));
- if (pGetUserPreferredUILanguages != nullptr) {
- ULONG num_langs = 0;
- ULONG buffer_size = 0;
- pGetUserPreferredUILanguages(8, &num_langs, nullptr, &buffer_size);
- PZZWSTR buffer = (PZZWSTR)_alloca(buffer_size);
- if (pGetUserPreferredUILanguages(8, &num_langs, buffer, &buffer_size)) {
- for (ULONG i = 0; i < num_langs; ++i) {
- size_t len = wcslen(buffer);
- if (len >= 2 && (buffer[2] == 0 || buffer[2] == L'-')) {
- // It may be a two-letter code; match it in our list.
- for (int j = 0; j < LI_COUNT; ++j) {
- const char *lang_code = language_codes[j];
- if (lang_code != nullptr && lang_code[0] == buffer[0] &&
- lang_code[1] == buffer[1]) {
- return (LanguageIndex)j;
- }
- }
- }
- buffer += len + 1;
- }
- }
- }
-
- // Fall back to the old Windows XP function.
- LANGID lang = GetUserDefaultUILanguage() & 0x3ff;
- if (lang == 0) {
- return LI_default;
- }
-
- for (int i = 0; i < LI_COUNT; ++i) {
- if (language_ids[i] != 0 && language_ids[i] == lang) {
- return (LanguageIndex)i;
- }
- }
- return LI_default;
-}
-
-#elif defined(__APPLE__)
-static LanguageIndex detect_language() {
- // Get and iterate through the list of preferred languages.
- CFArrayRef langs = CFLocaleCopyPreferredLanguages();
- CFIndex num_langs = CFArrayGetCount(langs);
-
- for (long i = 0; i < num_langs; ++i) {
- CFStringRef lang = (CFStringRef)CFArrayGetValueAtIndex(langs, i);
-
- CFIndex length = CFStringGetLength(lang);
- if (length < 2) {
- continue;
- }
-
- CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
- char *buffer = (char *)alloca(max_size);
- if (!CFStringGetCString(lang, buffer, max_size, kCFStringEncodingUTF8)) {
- continue;
- }
-
- if (isalnum(buffer[2])) {
- // It's not a two-letter code.
- continue;
- }
-
- // See if we support this language.
- for (int j = 0; j < LI_COUNT; ++j) {
- const char *lang_code = language_codes[j];
- if (lang_code != nullptr && strncasecmp(buffer, lang_code, 2) == 0) {
- CFRelease(langs);
- return (LanguageIndex)j;
- }
- }
- }
-
- CFRelease(langs);
- return LI_default;
-}
-
-#else
-static LanguageIndex detect_language() {
- // First consult the LANGUAGE variable, which is a GNU extension that can
- // contain multiple languages in order of preference.
- const char *lang = getenv("LANGUAGE");
- while (lang != nullptr && lang[0] != 0) {
- size_t len;
- const char *next = strchr(lang, ':');
- if (next == nullptr) {
- len = strlen(lang);
- } else {
- len = (next - lang);
- ++next;
- }
-
- if (len >= 2 && !isalnum(lang[2])) {
- // It may be a two-letter language code; match it in our list.
- for (int i = 0; i < LI_COUNT; ++i) {
- const char *lang_code = language_codes[i];
- if (lang_code != nullptr && strncasecmp(lang, lang_code, 2) == 0) {
- return (LanguageIndex)i;
- }
- }
- }
-
- lang = next;
- }
-
- // Fall back to the C locale functions.
- setlocale(LC_ALL, "");
- lang = setlocale(LC_MESSAGES, nullptr);
-
- if (lang == nullptr || lang[0] == 0 || strcmp(lang, "C") == 0) {
- // Try the LANG variable.
- lang = getenv("LANG");
- }
-
- if (lang == nullptr || strlen(lang) < 2 || isalnum(lang[2])) {
- // Couldn't extract a meaningful two-letter code.
- return LI_default;
- }
-
- // It may be a two-letter language code; match it in our list.
- for (int i = 0; i < LI_COUNT; ++i) {
- const char *lang_code = language_codes[i];
- if (lang_code != nullptr && strncasecmp(lang, lang_code, 2) == 0) {
- return (LanguageIndex)i;
- }
- }
- return LI_default;
-}
-#endif
-
-#ifdef _WIN32
-int WINAPI
-wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
- OpenSSL_add_all_algorithms();
-
- LPWSTR *argv;
- int argc;
- argv = CommandLineToArgvW(pCmdLine, &argc);
- if (argv == nullptr || argc != 2) {
- cerr << "usage: p3dcert cert_filename cert_dir\n";
- return 1;
- }
-
- li = detect_language();
-
- wstring cert_filename (argv[0]);
- wstring cert_dir (argv[1]);
- LocalFree(argv);
-
- AuthDialog *dialog = new AuthDialog(cert_filename, cert_dir);
- dialog->show();
-
- return Fl::run();
-}
-
-#else // _WIN32
-int main(int argc, char **argv) {
- OpenSSL_add_all_algorithms();
-
- if (argc != 3) {
- cerr << "usage: p3dcert cert_filename cert_dir\n";
- return 1;
- }
-
- li = detect_language();
-
- string cert_filename (argv[1]);
- string cert_dir (argv[2]);
-
- AuthDialog *dialog = new AuthDialog(cert_filename, cert_dir);
- dialog->show(1, argv);
-
- return Fl::run();
-}
-#endif // _WIN32
-
-/**
- *
- */
-#ifdef _WIN32
-AuthDialog::
-AuthDialog(const wstring &cert_filename, const wstring &cert_dir) :
-#else
-AuthDialog::
-AuthDialog(const string &cert_filename, const string &cert_dir) :
-#endif
- Fl_Window(435, 242, new_application_title[li]),
- _cert_dir(cert_dir)
-{
- _view_cert_dialog = nullptr;
-
- _cert = nullptr;
- _stack = nullptr;
- _verify_result = -1;
-
- // Center the window on the screen.
- position((Fl::w() - w()) / 2, (Fl::h() - h()) / 2);
- set_modal();
-
- read_cert_file(cert_filename);
- get_friendly_name();
- verify_cert();
- layout();
-}
-
-/**
- *
- */
-AuthDialog::
-~AuthDialog() {
- if (_view_cert_dialog != nullptr) {
- _view_cert_dialog->hide();
- }
-
- if (_cert != nullptr) {
- X509_free(_cert);
- _cert = nullptr;
- }
- if (_stack != nullptr) {
- sk_X509_free(_stack);
- _stack = nullptr;
- }
-}
-
-/**
- * The user clicks the "Run" button.
- */
-void AuthDialog::
-run_clicked(Fl_Widget *w, void *data) {
- AuthDialog *dlg = (AuthDialog *) data;
- dlg->approve_cert();
-}
-
-/**
- * The user clicks the "View Certificate" button.
- */
-void AuthDialog::
-view_cert_clicked(Fl_Widget *w, void *data) {
- AuthDialog *dlg = (AuthDialog *) data;
-
- if (dlg->_view_cert_dialog != nullptr) {
- dlg->_view_cert_dialog->hide();
- }
- dlg->hide();
- dlg->_view_cert_dialog = new ViewCertDialog(dlg, dlg->_cert);
- dlg->_view_cert_dialog->show();
-}
-
-/**
- * The user clicks the "Cancel" button.
- */
-void AuthDialog::
-cancel_clicked(Fl_Widget *w, void *data) {
- AuthDialog *dlg = (AuthDialog *) data;
- dlg->hide();
-}
-
-/**
- * Writes the certificate into the _cert_dir, so that it will be found by the
- * P3DInstanceManager and known to be approved.
- */
-void AuthDialog::
-approve_cert() {
- assert(_cert != nullptr);
-
- // Make sure the directory exists.
-#ifdef _WIN32
- mkdir_complete_w(_cert_dir, cerr);
-#else
- mkdir_complete(_cert_dir, cerr);
-#endif
-
- // Look for an unused filename.
- int i = 1;
- size_t buf_length = _cert_dir.length() + 100;
-
- // Sure, there's a slight race condition right now: another process might
- // attempt to create the same filename. So what.
- FILE *fp = nullptr;
-
-#ifdef _WIN32
- wchar_t *buf = new wchar_t[buf_length];
-
- while (true) {
- swprintf(buf, L"%s/p%d.crt", _cert_dir.c_str(), i);
- assert(wcslen(buf) < buf_length);
-
- // Check if it already exists. If not, take it.
- if (GetFileAttributesW(buf) == -1) {
- break;
- }
- ++i;
- }
- fp = _wfopen(buf, L"w");
-
-#else // _WIN32
- char *buf = new char[buf_length];
-
- while (true) {
- sprintf(buf, "%s/p%d.crt", _cert_dir.c_str(), i);
- assert(strlen(buf) < buf_length);
-
- // Check if it already exists. If not, take it.
- struct stat statbuf;
- if (stat(buf, &statbuf) != 0) {
- break;
- }
- ++i;
- }
- fp = fopen(buf, "w");
-#endif // _WIN32
-
- if (fp != nullptr) {
- PEM_write_X509(fp, _cert);
- fclose(fp);
- }
-
- hide();
-}
-
-/**
- * Reads the list of certificates in the pem filename passed on the command
- * line into _cert and _stack.
- */
-#ifdef _WIN32
-void AuthDialog::
-read_cert_file(const wstring &cert_filename) {
-#else
-void AuthDialog::
-read_cert_file(const string &cert_filename) {
-#endif
-
- FILE *fp = nullptr;
-#ifdef _WIN32
- fp = _wfopen(cert_filename.c_str(), L"r");
-#else // _WIN32
- fp = fopen(cert_filename.c_str(), "r");
-#endif // _WIN32
-
- if (fp == nullptr) {
-#ifdef _WIN32
- wcerr << L"Couldn't read " << cert_filename.c_str() << L"\n";
-#else
- cerr << "Couldn't read " << cert_filename.c_str() << "\n";
-#endif
- return;
- }
- _cert = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
- if (_cert == nullptr) {
-#ifdef _WIN32
- wcerr << L"Could not read certificate in " << cert_filename.c_str() << L".\n";
-#else
- cerr << "Could not read certificate in " << cert_filename.c_str() << ".\n";
-#endif
- fclose(fp);
- return;
- }
-
- // Build up a STACK of the remaining certificates in the file.
- _stack = sk_X509_new(nullptr);
- X509 *c = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
- while (c != nullptr) {
- sk_X509_push(_stack, c);
- c = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
- }
-
- fclose(fp);
-}
-
-/**
- * Extracts the "friendly name" from the certificate: the common name or email
- * name.
- */
-void AuthDialog::
-get_friendly_name() {
- if (_cert == nullptr) {
- _friendly_name.clear();
- return;
- }
-
-
- static const int nid_choices[] = {
- NID_pkcs9_emailAddress,
- NID_commonName,
- -1,
- };
-
- // Choose the first NID that exists on the cert.
- for (int ni = 0; nid_choices[ni] != -1; ++ni) {
- int nid = nid_choices[ni];
-
- // A complex OpenSSL interface to extract out the name in utf-8.
- X509_NAME *xname = X509_get_subject_name(_cert);
- if (xname != nullptr) {
- int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
- if (pos != -1) {
- // We just get the first common name. I guess it's possible to have
- // more than one; not sure what that means in this context.
- X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
- if (xentry != nullptr) {
- ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
- if (data != nullptr) {
- // We use "print" to dump the output to a memory BIO. Is there an
- // easier way to decode the ASN1_STRING? Curse these incomplete
- // docs.
- BIO *mbio = BIO_new(BIO_s_mem());
- ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
-
- char *pp;
- long pp_size = BIO_get_mem_data(mbio, &pp);
- _friendly_name = string(pp, pp_size);
- BIO_free(mbio);
- return;
- }
- }
- }
- }
- }
-}
-
-/**
- * Checks whether the certificate is valid by the chain and initializes
- * _verify_status accordingly.
- */
-void AuthDialog::
-verify_cert() {
- if (_cert == nullptr) {
- _verify_result = -1;
- return;
- }
-
- // Create a new X509_STORE.
- X509_STORE *store = X509_STORE_new();
- X509_STORE_set_default_paths(store);
-
- // Add in the well-known certificate authorities.
- load_certificates_from_der_ram(store, (const char *)ca_bundle_data, ca_bundle_data_len);
-
- // Create the X509_STORE_CTX for verifying the cert and chain.
- X509_STORE_CTX *ctx = X509_STORE_CTX_new();
- X509_STORE_CTX_init(ctx, store, _cert, _stack);
- X509_STORE_CTX_set_cert(ctx, _cert);
-
- if (X509_verify_cert(ctx)) {
- _verify_result = 0;
- } else {
- _verify_result = X509_STORE_CTX_get_error(ctx);
- }
-
- X509_STORE_CTX_free(ctx);
-
- X509_STORE_free(store);
-
- cerr << "Got certificate from " << _friendly_name.c_str()
- << ", verify_result = " << _verify_result << "\n";
-}
-
-/**
- * Reads a chain of trusted certificates from the indicated data buffer and
- * adds them to the X509_STORE object. The data buffer should be DER-
- * formatted. Returns the number of certificates read on success, or 0 on
- * failure.
- *
- * You should call this only with trusted, locally-stored certificates; not
- * with certificates received from an untrusted source.
- */
-int AuthDialog::
-load_certificates_from_der_ram(X509_STORE *store,
- const char *data, size_t data_size) {
- int count = 0;
-
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
- // Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **.
- const unsigned char *bp, *bp_end;
-#else
- // Prior to 0.9.8, d2i_X509() accepted an unsigned char **.
- unsigned char *bp, *bp_end;
-#endif
-
- bp = (unsigned char *)data;
- bp_end = bp + data_size;
- X509 *x509 = d2i_X509(nullptr, &bp, bp_end - bp);
- while (x509 != nullptr) {
- X509_STORE_add_cert(store, x509);
- ++count;
- x509 = d2i_X509(nullptr, &bp, bp_end - bp);
- }
-
- return count;
-}
-
-/**
- * Arranges the text and controls within the dialog.
- */
-void AuthDialog::
-layout() {
- get_text(_header, sizeof _header, _text, sizeof _text);
-
- // Now replicate out any @ signs in the text to avoid FlTk's escape
- // sequences.
- int j = 0;
- for (int i = 0; _text[i] != '\0'; ++i) {
- _text_clean[j++] = _text[i];
- if (_text[i] == '@') {
- _text_clean[j++] = _text[i];
- }
- }
- _text_clean[j] = '\0';
- assert(strlen(_text_clean) < sizeof(_text_clean));
-
- int next_y = 35;
- if (strlen(_header) > 0) {
- Fl_Box *text0 = new Fl_Box(w() / 2, next_y, 0, 25, _header);
- text0->align(FL_ALIGN_TOP | FL_ALIGN_CENTER);
- text0->labelfont(FL_BOLD);
- text0->labelsize(text0->labelsize() * 1.5);
- next_y += 25;
- }
-
- Fl_Box *text1 = new Fl_Box(17, next_y, 400, 180, _text_clean);
- text1->align(FL_ALIGN_TOP | FL_ALIGN_INSIDE | FL_ALIGN_WRAP);
- next_y += 180;
-
- short nbuttons = 1;
- if (_cert != nullptr) {
- nbuttons++;
- if (_verify_result == 0) {
- nbuttons++;
- }
- }
- short bx = (w() - nbuttons * BUTTON_WIDTH - (nbuttons - 1) * BUTTON_SPACE) / 2;
-
- if (_verify_result == 0 && _cert != nullptr) {
- Fl_Return_Button *run_button = new Fl_Return_Button(bx, next_y, BUTTON_WIDTH, 25, run_title[li]);
- run_button->callback(this->run_clicked, this);
- bx += BUTTON_WIDTH + BUTTON_SPACE;
- }
-
- if (_cert != nullptr) {
- Fl_Button *view_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, show_cert_title[li]);
- view_button->callback(this->view_cert_clicked, this);
- bx += BUTTON_WIDTH + BUTTON_SPACE;
- }
-
- Fl_Button *cancel_button;
- cancel_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, cancel_title[li]);
- cancel_button->callback(this->cancel_clicked, this);
-
- next_y += 42;
- size(435, next_y);
-
- end();
- set_modal();
-}
-
-/**
- * Fills in the text appropriate to display in the dialog box, based on the
- * certificate read so far.
- */
-void AuthDialog::
-get_text(char *header, size_t hlen, char *text, size_t tlen) {
- switch (_verify_result) {
- case -1:
- strncpy(header, no_cert_title[li], hlen);
- strncpy(text, no_cert_text[li], tlen);
- break;
-
- case 0:
- snprintf(text, tlen, verified_cert_text[li], _friendly_name.c_str(),
- _friendly_name.c_str(), _friendly_name.c_str());
- break;
-
- case X509_V_ERR_CERT_NOT_YET_VALID:
- case X509_V_ERR_CERT_HAS_EXPIRED:
- case X509_V_ERR_CRL_NOT_YET_VALID:
- case X509_V_ERR_CRL_HAS_EXPIRED:
- strncpy(header, expired_cert_title[li], hlen);
- snprintf(text, tlen, expired_cert_text[li], _friendly_name.c_str());
- break;
-
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
- strncpy(header, unverified_cert_title[li], hlen);
- strncpy(text, unknown_auth_cert_text[li], tlen);
- break;
-
- case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
- case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
- strncpy(header, unverified_cert_title[li], hlen);
- strncpy(text, self_signed_cert_text[li], tlen);
- break;
-
- default:
- strncpy(header, unverified_cert_title[li], hlen);
- snprintf(text, tlen, generic_error_cert_text[li], _verify_result);
- }
-}
-
-/**
- *
- */
-ViewCertDialog::
-ViewCertDialog(AuthDialog *auth_dialog, X509 *cert) :
- Fl_Window(600, 400, show_cert_title[li]),
- _auth_dialog(auth_dialog),
- _cert(cert)
-{
- // Center the window on the screen.
- position((Fl::w() - w()) / 2, (Fl::h() - h()) / 2);
- set_modal();
-
- layout();
-}
-
-/**
- *
- */
-ViewCertDialog::
-~ViewCertDialog() {
- if (_auth_dialog != nullptr) {
- _auth_dialog->_view_cert_dialog = nullptr;
- }
-}
-
-/**
- * The user clicks the "Run" button.
- */
-void ViewCertDialog::
-run_clicked(Fl_Widget *w, void *data) {
- ViewCertDialog *dlg = (ViewCertDialog *) data;
- if (dlg->_auth_dialog != nullptr){
- dlg->_auth_dialog->approve_cert();
- }
- dlg->hide();
-}
-
-/**
- * The user clicks the "Cancel" button.
- */
-void ViewCertDialog::
-cancel_clicked(Fl_Widget *w, void *data) {
- ViewCertDialog *dlg = (ViewCertDialog *) data;
- if (dlg->_auth_dialog != nullptr){
- dlg->_auth_dialog->hide();
- }
- dlg->hide();
-}
-
-/**
- * Arranges the text and controls within the dialog.
- */
-void ViewCertDialog::
-layout() {
- // Format the certificate text for display in the dialog.
- assert(_cert != nullptr);
-
- BIO *mbio = BIO_new(BIO_s_mem());
- X509_print(mbio, _cert);
-
- char *pp;
- long pp_size = BIO_get_mem_data(mbio, &pp);
- string cert_body(pp, pp_size);
- BIO_free(mbio);
-
- Fl_Text_Buffer *buffer = new Fl_Text_Buffer;
- buffer->append(cert_body.c_str());
-
- Fl_Text_Display *text = new Fl_Text_Display(20, 20, 565, 320);
- text->buffer(buffer);
-
- short bx = (w() - BUTTON_WIDTH * 2 - BUTTON_SPACE) / 2;
-
- Fl_Return_Button *run_button = new Fl_Return_Button(bx, 360, BUTTON_WIDTH, 25, run_title[li]);
- run_button->callback(this->run_clicked, this);
-
- bx += BUTTON_WIDTH + BUTTON_SPACE;
-
- Fl_Button *cancel_button = new Fl_Button(bx, 360, BUTTON_WIDTH, 25, cancel_title[li]);
- cancel_button->callback(this->cancel_clicked, this);
-
- end();
- set_modal();
-}
diff --git a/direct/src/plugin/p3dCert.h b/direct/src/plugin/p3dCert.h
deleted file mode 100644
index c17c8129e8..0000000000
--- a/direct/src/plugin/p3dCert.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert.h
- * @author rdb
- * @date 2011-03-08
- */
-
-#ifndef P3DCERT_H
-#define P3DCERT_H
-
-#include
-#include
-
-#define OPENSSL_NO_KRB5
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-class ViewCertDialog;
-
-#ifndef STACK_OF
- // At some point, presumably in 1.0.0, openssl went to the STACK_OF() macro
- // system to typedef the contents of a stack. Unfortunately, that new API
- // is different. We define some macros here here for backward compatiblity.
- #define STACK_OF(type) STACK
- #define sk_X509_push(stack, item) sk_push((stack), (char *)(item))
- #define sk_X509_free(stack) sk_free(stack)
- #define sk_X509_new(cmp) sk_new(cmp)
-#endif
-
-/**
- * This is the primary dialog of this application.
- *
- * This dialog is presented to the user when he/she clicks on the red
- * authorization button on the splash window. It tells the user the status of
- * the application's signature, and invites the user to approve the signature
- * or cancel.
- */
-class AuthDialog : public Fl_Window {
-public:
-#ifdef _WIN32
- AuthDialog(const std::wstring &cert_filename, const std::wstring &cert_dir);
-#else
- AuthDialog(const std::string &cert_filename, const std::string &cert_dir);
-#endif
- virtual ~AuthDialog();
-
- static void run_clicked(Fl_Widget *w, void *data);
- static void view_cert_clicked(Fl_Widget *w, void *data);
- static void cancel_clicked(Fl_Widget *w, void *data);
-
- void approve_cert();
-
-private:
-#ifdef _WIN32
- void read_cert_file(const std::wstring &cert_filename);
-#else
- void read_cert_file(const std::string &cert_filename);
-#endif
- void get_friendly_name();
- void verify_cert();
- int load_certificates_from_der_ram(X509_STORE *store,
- const char *data, size_t data_size);
-
- void layout();
- void get_text(char *header, size_t hlen, char *text, size_t tlen);
-
-public:
- ViewCertDialog *_view_cert_dialog;
-
-private:
-#ifdef _WIN32
- std::wstring _cert_dir;
-#else
- std::string _cert_dir;
-#endif
- X509 *_cert;
- STACK_OF(X509) *_stack;
-
- char _header[64];
- char _text[1024];
- char _text_clean[2048];
-
- std::string _friendly_name;
- int _verify_result;
-};
-
-/**
- * This is the detailed view of the particular certificate.
- */
-class ViewCertDialog : public Fl_Window {
-public:
- ViewCertDialog(AuthDialog *auth_dialog, X509 *cert);
- virtual ~ViewCertDialog();
-
- static void run_clicked(Fl_Widget *w, void *data);
- static void cancel_clicked(Fl_Widget *w, void *data);
-
-private:
- void layout();
-
-private:
- AuthDialog *_auth_dialog;
- X509 *_cert;
-};
-
-#endif
diff --git a/direct/src/plugin/p3dCert_strings.cxx b/direct/src/plugin/p3dCert_strings.cxx
deleted file mode 100644
index 575dee34b0..0000000000
--- a/direct/src/plugin/p3dCert_strings.cxx
+++ /dev/null
@@ -1,567 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert_strings.cxx
- * @author rdb
- * @date 2015-03-25
- */
-
-#include "p3dCert_strings.h"
-
-// Translations kindly provided by: eng: drwr nld: rdb deu: Sebastian Hoffmann
-// spa: Imanol Celaya ita: Flavio Clava rus: montreal
-
-const char *language_codes[LI_COUNT] =
- {"en", "nl", "de", "es", "it", "eo", "ru"};
-
-// https:msdn.microsoft.comen-
-// uslibrarywindowsdesktopdd318693%28v=vs.85%29.aspx
-const unsigned char language_ids[LI_COUNT] =
- {0x09, 0x13, 0x07, 0x0A, 0x10, 0x8F, 0x19};
-
-const char *
-run_title[LI_COUNT] = {
- "Run",
- "Uitvoeren",
- "Starten",
- "Ejecutar",
- "Lancia",
- "Lan\304\211i",
- "\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214",
-};
-
-const char *
-cancel_title[LI_COUNT] = {
- "Cancel",
- "Annuleren",
- "Abbrechen",
- "Cancelar",
- "Cancella",
- "Nuligi",
- "\320\236\321\202\320\274\320\265\320\275\320\260",
-};
-
-const char *
-show_cert_title[LI_COUNT] = {
- "Show Certificate",
- "Toon Certificaat",
- "Zertifikat anzeigen",
- "Mostrar certificado",
- "Mostra certificato",
- "Montri Ateston",
- "\320\237\320\276\320\272\320\260\320\267\320\260\321\202\321\214 \321\201"
- "\320\265\321\200\321\202\320\270\321\204\320\270\320\272\320\260\321\202",
-};
-
-const char *
-new_application_title[LI_COUNT] = {
- "New Panda3D Application",
- "Nieuwe Panda3D Applicatie",
- "Neue Panda3D-Anwendung",
- "Nueva aplicaci\303\263n de Panda3D",
- "Nuova applicazione Panda3D",
- "Nova aplika\304\265o de Panda3D",
- "\320\235\320\276\320\262\320\276\320\265 Panda3D-\320\277\321\200\320\270"
- "\320\273\320\276\320\266\320\265\320\275\320\270\320\265",
-};
-
-const char *
-no_cert_title[LI_COUNT] = {
- "No signature!",
- "Geen handtekening!",
- "Keine Signatur!",
- "Sin firma!",
- "Nessuna firma!",
- "Neniu subskribo!",
- "\320\235\320\265\321\202 \320\277\320\276\320\264\320\277\320\270\321\201"
- "\320\270!",
-};
-
-const char *
-unverified_cert_title[LI_COUNT] = {
- "Unverified signature!",
- "Ongeverifieerde handtekening!",
- "Unbest\303\244tigte Signatur!",
- "Firma sin verificar!",
- "Firma non verificata!",
- "Nekontrolita subskribo!",
- "\320\235\320\265\320\277\321\200\320\276\320\262\320\265\321\200\320\265"
- "\320\275\320\275\320\260\321\217 \320\277\320\276\320\264\320\277\320\270"
- "\321\201\321\214!",
-};
-
-const char *
-expired_cert_title[LI_COUNT] = {
- "Expired signature!",
- "Verlopen handtekening!",
- "Abgelaufene Signatur!",
- "Firma caducada!",
- "Firma scaduta!",
- "Eksvalidi\304\235inta subskribo!",
- "\320\241\321\200\320\276\320\272 \320\264\320\265\320\271\321\201\321\202"
- "\320\262\320\270\321\217 \320\277\320\276\320\264\320\277\320\270\321\201"
- "\320\270 \320\270\321\201\321\202\321\221\320\272!",
-};
-
-
-const char *
-self_signed_cert_text[LI_COUNT] = {
- // eng
- "This Panda3D application uses a self-signed certificate. "
- "This means the author's name can't be verified, and you have "
- "no way of knowing for sure who wrote it.\n"
- "\n"
- "We recommend you click Cancel to avoid running this application.",
-
- // nld
- "Deze Panda3D applicatie gebruikt een zelf-getekend certificaat. "
- "Dit betekent dat de auteursnaam niet kan worden geverifieerd, en het "
- "niet zeker is of de applicatie te vertrouwen is.\n"
- "\n"
- "Het is aanbevolen om op Annuleren te klikken om de applicatie af te "
- "sluiten.",
-
- // deu
- "Diese Panda3D-Anwendung benutzt ein selbst-signiertes Zertifikat. Dies "
- "bedeutet, dass weder der Name des Autors \303\274berpr\303\274ft werden "
- "kann, noch dass garantiert werden kann, dass tats\303\244chlich die "
- "angegebene Person diese Anwendung geschrieben hat.\n"
- "\n"
- "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht "
- "auszuf\303\274hren.",
-
- // spa
- "Esta aplicaci\303\263n de Panda3D usa un certificado autofirmado. "
- "Esto significa que el nombre del autor no puede ser verificado y no se "
- "puede conocer seguro quien la ha escrito.\n"
- "\n"
- "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.",
-
- // ita
- "Questa applicazione Panda3D usa un certificato autofirmato. Ci\303\262 "
- "significa che il nome dell'autore non pu\303\262 essere verificato, e "
- "che non hai modo di assicurarti circa chi la abbia scritta.\n"
- "\n"
- "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa "
- "applicazione.",
-
- // epo
- "\304\210i tiu aplika\304\265o de Panda3D uzas memsubskribitan ateston. "
- "Tio signifas ke la nomo de la verkanto ne povas esti kontrolita, kaj vi "
- "ne havas certan scimanieron pri la vera verkanto de la aplika\304\265o.\n"
- "\n"
- "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on de "
- "\304\211i tiu aplika\304\265o.",
-
- // rus
- "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266"
- "\320\265\320\275\320\270\320\265 \320\270\321\201\320\277\320\276\320\273"
- "\321\214\320\267\321\203\320\265\321\202 \321\201\320\260\320\274\320\276"
- "\320\267\320\260\320\262\320\265\321\200\320\265\320\275\320\275\321\213"
- "\320\271 \321\201\320\265\321\200\321\202\320\270\321\204\320\270\320\272"
- "\320\260\321\202. \320\255\321\202\320\276 \320\276\320\267\320\275"
- "\320\260\321\207\320\260\320\265\321\202, \321\207\321\202\320\276 "
- "\320\270\320\274\321\217 \320\260\320\262\321\202\320\276\321\200\320\260 "
- "\320\275\320\265 \320\274\320\276\320\266\320\265\321\202 \320\261\321\213"
- "\321\202\321\214 \320\277\320\276\320\264\321\202\320\262\320\265\320\266"
- "\320\264\320\265\320\275\320\276, \320\270 \320\262\321\213 \320\275"
- "\320\265 \320\274\320\276\320\266\320\265\321\202\320\265 \320\267\320\275"
- "\320\260\321\202\321\214, \320\272\321\202\320\276 \320\275\320\260"
- "\320\277\320\270\321\201\320\260\320\273 \321\215\321\202\320\276 \320\277"
- "\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265.\n"
- "\n"
- "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275"
- "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260"
- "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265"
- "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
- "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
- "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
- "\320\265.",
-};
-
-
-const char *
-unknown_auth_cert_text[LI_COUNT] = {
- "This Panda3D application has been signed, but we don't recognize "
- "the authority that verifies the signature. This means the author's "
- "name can't be verified, and you have no way of knowing "
- "for sure who wrote it.\n"
- "\n"
- "We recommend you click Cancel to avoid running this application.",
-
- // nld
- "Deze Panda3D applicatie is ondertekend, maar de certificaatautoriteit "
- "die het certificaat heeft uitgegeven wordt niet herkend. Dit betekent "
- "dat de auteursnaam niet te vertrouwen is, en het niet zeker is wie de "
- "applicatie gemaakt heeft.\n"
- "\n"
- "Het is aanbevolen om op Annuleren te klikken om de applicatie af te "
- "sluiten.",
-
- // deu
- "Diese Panda3D-Anwendung wurde signiert, aber die "
- "\303\234berpr\303\274fungsstelle wurde nicht anerkannt. Dies bedeutet, "
- "dass weder der Name des Autors \303\274berpr\303\274ft werden kann, noch "
- "dass garantiert werden kann, dass tats\303\244chlich die angegebene "
- "Person diese Anwendung geschrieben hat.\n"
- "\n"
- "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht "
- "auszuf\303\274hren.",
-
- // spa
- "Esta aplicaci\303\263n de Panda3D esta firmada, pero no reconocemos la "
- "autoridad que la verifica. Esto significa que el nombre del autor no "
- "puede ser verificado y no se puede conocer seguro quien la ha escrito.\n"
- "\n"
- "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.",
-
- // ita
- "Questa applicazione Panda3D \303\250 stata firmata, ma non riconosciamo "
- "l'autorit\303\240 che verifica la firma. Ci\303\262 significa che il "
- "nome dell'autore non pu\303\262 essere verificato, e che non hai modo "
- "di assicurarti circa chi la abbia scritta.\n"
- "\n"
- "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa "
- "applicazione.",
-
- // epo
- "\304\210i tiu aplika\304\265o estas subskribita, sed ni ne rekonas "
- "la a\305\255toritaton, kiu kontrolas la subskribon. Tio signifas ke la "
- "nomo de la verkanto ne povas esti konfidata, kaj vi ne havas certan "
- "scimanieron pri la vera verkanto de la aplika\304\265o.\n"
- "\n"
- "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on "
- "de \304\211i tiu aplika\304\265o.",
-
- // rus
- "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270"
- "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\265\321\201"
- "\321\202\321\214 \320\277\320\276\320\264\320\277\320\270\321\201\321\214,"
- " \320\277\320\276 \320\272\320\276\321\202\320\276\321\200\320\276\320\271"
- " \320\274\321\213 \320\275\320\265 \320\274\320\276\320\266\320\265"
- "\320\274 \321\200\320\260\321\201\320\277\320\276\320\267\320\275\320\260"
- "\321\202\321\214 \320\262\320\273\320\260\320\264\320\265\320\273\321\214"
- "\321\206\320\260. \320\255\321\202\320\276 \320\276\320\267\320\275"
- "\320\260\321\207\320\260\320\265\321\202, \321\207\321\202\320\276 "
- "\320\270\320\274\321\217 \320\260\320\262\321\202\320\276\321\200\320\260 "
- "\320\275\320\265 \320\274\320\276\320\266\320\265\321\202 \320\261\321\213"
- "\321\202\321\214 \320\276\320\277\321\200\320\265\320\264\320\265\320\273"
- "\320\265\320\275\320\276, \320\270 \320\275\320\265\320\262\320\276"
- "\320\267\320\274\320\276\320\266\320\275\320\276 \321\202\320\276\321\207"
- "\320\275\320\276 \321\203\320\267\320\275\320\260\321\202\321\214, "
- "\320\272\321\202\320\276 \320\275\320\260\320\277\320\270\321\201\320\260"
- "\320\273 \321\215\321\202\320\276 \320\277\321\200\320\270\320\273\320\276"
- "\320\266\320\265\320\275\320\270\320\265.\n"
- "\n"
- "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275"
- "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260"
- "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265"
- "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
- "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
- "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
- "\320\265.",
-};
-
-
-const char *
-verified_cert_text[LI_COUNT] = {
- // eng
- "This Panda3D application has been signed by %s. "
- "If you trust %s, then click the Run button below "
- "to run this application on your computer. This will also "
- "automatically approve this and any other applications signed by "
- "%s in the future.\n"
- "\n"
- "If you are unsure about this application, "
- "you should click Cancel instead.",
-
- // nld
- "Deze Panda3D applicatie is ondertekend door %s. "
- "Als u %s vertrouwt, klik dan de onderstaande knop Uitvoeren om de applicatie "
- "op uw computer uit te voeren. Dit zal ook deze en andere door %s getekende "
- "applicaties in het vervolg toestaan.\n"
- "\n"
- "Als u niet zeker bent over deze applicatie is het aanbevolen om op "
- "Annuleren te klikken.",
-
- // deu
- "Diese Panda3D-Anwendung wurde von %s signiert. Falls Sie %s vertrauen, "
- "dr\303\274cken Sie den Starten-Knopf, um diese Anwendung auszuf\303\274hren. "
- "Zudem werden in der Zukunft diese und alle anderen von %s signierten "
- "Anwendungen automatisch als vertrauensw\303\274rdig anerkannt.\n"
- "\n"
- "Falls Sie sich unsicher \303\274ber diese Anwendung sind, sollten Sie "
- "Abbrechen dr\303\274cken.",
-
- // spa
- "Esta aplicaci\303\263n de Panda3D ha sido firmada por %s. Si se considera %s"
- "de confianza el bot\303\263n inferior ejecutar\303\241 la aplicaci\303\263n."
- "Esto validara esta y cualquier otra applizacion firmada por %s en el futuro.\n"
- "\n"
- "Si se duda de la aplicaci\303\263nse recomienda cancelar."
-
- // ita
- "Questa applicazione Panda3D \303\250 stata firmata da %s. Se %s \303\250 "
- "un'entit\303\240 fidata, allora clicca il bottone Lancia sottostante per "
- "lanciare questa applicazione sul tuo computer. Inoltre, ci\303\262 "
- "approver\303\240 automaticamente questa e ogni altra applicazione "
- "firmata da %s in futuro.\n"
- "\n"
- "Se non sei sicuro circa questa applicazione, dovresti invece cliccare "
- "su Cancella.",
-
- // epo
- "\304\210i tiu aplika\304\265o estas subskribita de %s. "
- "Se %s estas fidinda la\305\255 vi, premu la butonon 'Lan\304\211i' sube por "
- "lan\304\211i \304\211i tiun aplika\304\265on per via komputilo. Anka\305\255 "
- "tio a\305\255tomate estonece aprobos \304\211i tiun kaj alian ajn "
- "aplika\304\265on, kiu estas subskribita de %s.\n"
- "\n"
- "Se vi ne estas certa pri \304\211i tiu aplika\304\265o, "
- "vi anstata\305\255e povus premi la butonon 'Nuligi'.",
-
- // rus
- "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266"
- "\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\277\320\270"
- "\321\201\320\260\320\275\320\276 %s. "
- "\320\225\321\201\320\273\320\270 \320\262\321\213 \320\264\320\276\320\262"
- "\320\265\321\200\321\217\320\265\321\202\320\265 %s, \320\275\320\260"
- "\320\266\320\274\320\270\321\202\320\265 \320\272\320\275\320\276\320\277"
- "\320\272\321\203 \"\320\227\320\260\320\277\321\203\321\201\321\202"
- "\320\270\321\202\321\214\" \320\262\320\275\320\270\320\267\321\203, "
- "\321\207\321\202\320\276\320\261\321\213 \320\267\320\260\320\277\321\203"
- "\321\201\321\202\320\270\321\202\321\214 \321\215\321\202\320\276 \320\277"
- "\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265 "
- "\320\275\320\260 \320\262\320\260\321\210\320\265\320\274 \320\272\320\276"
- "\320\274\320\277\321\214\321\216\321\202\320\265\321\200\320\265. \320\255"
- "\321\202\320\276 \321\202\320\260\320\272\320\266\320\265 \320\260\320\262"
- "\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201"
- "\320\272\320\270 \320\277\320\276\320\264\321\202\320\262\320\265\321\200"
- "\320\264\320\270\321\202 \321\215\321\202\320\276 \320\270 \320\264"
- "\321\200\321\203\320\263\320\270\320\265 \320\277\321\200\320\270\320\273"
- "\320\276\320\266\320\265\320\275\320\270\321\217, \320\275\320\260\320\277"
- "\320\270\321\201\320\260\320\275\320\275\321\213\320\265 %s \320\262 "
- "\320\261\321\203\320\264\321\203\321\211\320\265\320\274.\n"
- "\n"
- "\320\225\321\201\320\273\320\270 \320\262\321\213 \320\275\320\265 "
- "\321\203\320\262\320\265\321\200\320\265\320\275\321\213 \320\262 \321\215"
- "\321\202\320\276\320\274 \320\277\321\200\320\270\320\273\320\276\320\266"
- "\320\265\320\275\320\270\320\270, \320\275\320\260\320\266\320\274\320\270"
- "\321\202\320\265 \320\272\320\275\320\276\320\277\320\272\321\203 \""
- "\320\236\321\202\320\274\320\265\320\275\320\260\".",
-};
-
-
-const char *
-expired_cert_text[LI_COUNT] = {
- // eng
- "This Panda3D application has been signed by %s, "
- "but the certificate has expired.\n"
- "\n"
- "You should check the current date set on your computer's clock "
- "to make sure it is correct.\n"
- "\n"
- "If your computer's date is correct, we recommend "
- "you click Cancel to avoid running this application.",
-
- // nld
- "Deze Panda3D applicatie is ondertekend door %s, maar de geldigheidsdatum "
- "van het certificaat is verstreken.\n"
- "\n"
- "Controleer de datum op uw computerklok om te zorgen dat deze juist is "
- "ingesteld.\n"
- "\n"
- "Als de datum op uw computer juist is, is het aanbevolen om op Annuleren te "
- "klikken om de applicatie af te sluiten.",
-
- // deu
- "Diese Anwendung wurde von %s signiert, aber das Zertifikat ist abgelaufen.\n"
- "\n"
- "Sie sollten die aktuelle Uhrzeit auf Ihrem Computer "
- "\303\274berpr\303\274fen, um sicherzugehen, dass sie korrekt ist.\n"
- "\n"
- "Falls die Uhrzeit auf Ihrem Computer korrekt ist, empfehlen wir Ihnen "
- "Abbrechen zu dr\303\274cken.",
-
- // spa
- "Esta aplicaci\303\263n Panda3D ha sido firmada por %s pero el certificado ha"
- "caducado.\n"
- "\n"
- "Se recomienda comprobar la fecha del reloj.\n"
- "\n"
- "Si la fecha del reloj es correcta se recomienda cancelar.",
-
- // ita
- "Questa applicazione Panda3D \303\250 stata firmata da %s, ma il "
- "certificato \303\250 scaduto.\n"
- "\n"
- "Dovresti controllare la data attuale impostata nell'orologio del tuo "
- "computer per assicurarti che sia corretta.\n"
- "\n"
- "Se la data del tuo computer \303\250 corretta, raccomandiamo di cliccare "
- "Cancella per evitare di lanciare questa applicazione."
-
- // epo
- "\304\210i tiu aplika\304\265o de Panda3D estas subskribita de %s, "
- "sed la atesto eksvalidi\304\235is.\n"
- "\n"
- "Vi povus kontroli la aktualan daton, kiu estas agordata sur la horlo\304\235o de "
- "via komputilo por certigi ke \304\235i estas korekta.\n"
- "\n"
- "Se la dato de via komputilo estas korekta, ni rekomendas ke vi premas la "
- "butonon 'Nuligi' por eviti lan\304\211on de \304\211i tiu aplika\304\265o.",
-
- // rus
- "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266"
- "\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\277\320\270"
- "\321\201\320\260\320\275\320\276 %s, \320\276\320\264\320\275\320\260"
- "\320\272\320\276 \321\201\321\200\320\276\320\272 \320\264\320\265\320\271"
- "\321\201\321\202\320\262\320\270\321\217 \321\201\320\265\321\200\321\202"
- "\320\270\321\204\320\270\320\272\320\260\321\202\320\260 \320\270\321\201"
- "\321\202\321\221\320\272.\n"
- "\n"
- "\320\237\321\200\320\276\320\262\320\265\321\200\321\214\321\202\320\265, "
- "\320\277\320\276\320\266\320\260\320\273\321\203\320\271\321\201\321\202"
- "\320\260, \320\264\320\260\321\202\321\203 \320\275\320\260 \320\262"
- "\320\260\321\210\320\265\320\274 \320\272\320\276\320\274\320\277\321\214"
- "\321\216\321\202\320\265\321\200\320\265.\n"
- "\n"
- "\320\225\321\201\320\273\320\270 \320\275\320\260 \320\262\320\260\321\210"
- "\320\265\320\274 \320\272\320\276\320\274\320\277\321\214\321\216\321\202"
- "\320\265\321\200\320\265 \321\203\321\201\321\202\320\260\320\275\320\276"
- "\320\262\320\273\320\265\320\275\320\276 \320\277\321\200\320\260\320\262"
- "\320\270\320\273\321\214\320\275\320\276\320\265 \320\262\321\200\320\265"
- "\320\274\321\217, \320\274\321\213 \321\200\320\265\320\272\320\276"
- "\320\274\320\265\320\275\320\264\321\203\320\265\320\274 \320\262\320\260"
- "\320\274 \320\275\320\260\320\266\320\260\321\202\321\214 \320\272\320\275"
- "\320\276\320\277\320\272\321\203 \"\320\236\321\202\320\274\320\265"
- "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
- "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
- "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
- "\320\265.",
-};
-
-
-const char *
-generic_error_cert_text[LI_COUNT] = {
- // eng
- "This Panda3D application has been signed, but there is a problem "
- "with the certificate (OpenSSL error code %d).\n"
- "\n"
- "We recommend you click Cancel to avoid running this application.",
-
- // nld
- "Deze Panda3D applicatie is ondertekend, maar er is een probleem "
- "met het certificaat opgetreden (OpenSSL foutcode %d).\n"
- "\n"
- "Het is aanbevolen om op Annuleren te klikken om de applicatie af te "
- "sluiten.",
-
- // deu
- "Diese Panda3D-Anwendung wurde signiert, aber es gibt ein Problem mit "
- "dem Zertifikat (OpenSSL Fehlercode %d).\n"
- "\n"
- "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht "
- "auszuf\303\274hren.",
-
- // spa
- "Esta aplicaci\303\263n de Panda3D esta firmada pero hay un problema con el "
- "certificado (Error de OpenSSL %d).\n"
- "\n"
- "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.",
-
- // ita
- "Questa applicazione Panda3D \303\250 stata firmata, ma c'\303\250 un "
- "problema col certificato (codice di errore OpenSSL %s).\n"
- "\n"
- "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa "
- "applicazione.",
-
- // epo
- "\304\210i tiu aplika\304\265o de Panda3D estas subskribita, sed la "
- "atesto havas problemon (OpenSSL erarkodo %d).\n"
- "\n"
- "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on "
- "de \304\211i tiu aplika\304\265o.",
-
- // rus
- "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270"
- "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\265\321\201"
- "\321\202\321\214 \320\277\320\276\320\264\320\277\320\270\321\201\321\214,"
- " \320\275\320\276 \320\262\320\276\320\267\320\275\320\270\320\272\320\273"
- "\320\260 \320\277\321\200\320\276\320\261\320\273\320\265\320\274\320\260 "
- "\321\201 \321\201\320\265\321\200\321\202\320\270\321\204\320\270\320\272"
- "\320\260\321\202\320\276\320\274 (\320\232\320\276\320\264 \320\276"
- "\321\210\320\270\320\261\320\272\320\270 OpenSSL %d).\n"
- "\n"
- "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275"
- "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260"
- "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265"
- "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
- "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
- "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
- "\320\265.",
-};
-
-
-const char *
-no_cert_text[LI_COUNT] = {
- "This Panda3D application has not been signed. This means you have "
- "no way of knowing for sure who wrote it.\n"
- "\n"
- "Click Cancel to avoid running this application.",
-
- // nld
- "Deze Panda3D applicatie is niet ondertekend. Dit betekent dat het niet "
- "mogelijk is om de auteur te verifi\303\253ren.\n"
- "\n"
- "Klik op Annuleren om de applicatie af te sluiten.",
-
- // deu
- "Diese Panda3D-Anwendung wurde nicht signiert. Es gibt keine "
- "M\303\266glichkeit festzustellen, wer diese entwickelt hat.\n"
- "\n"
- "Dr\303\274cken Sie Abbrechen, um diese Anwendung nicht auszuf\303\274hren.",
-
- // spa
- "Esta aplicaci\303\263n de Panda3D no esta firmada, no hay forma de conocer "
- "quien la ha escrito.\n"
- "\n"
- "Cancelar para evitar ejecutar la aplicaci\303\263n.",
-
- // ita
- "Questa applicazione Panda3D non \303\250 stata firmata. Ci\303\262 "
- "significa che non hai modo di assicurarti circa chi la abbia scritta.\n"
- "\n"
- "Clicca Cancella per evitare di lanciare questa applicazione.",
-
- // epo
- "\304\210i tiu aplika\304\265o de Panda3D ne estas subskribita. Tio "
- "signifas ke vi ne havas certan scimanieron pri la vera verkanto de "
- "la aplika\304\265o.\n"
- "\n"
- "Premu la butonon 'Nuligi' por eviti lan\304\211on de \304\211i tiu "
- "aplika\304\265o.",
-
- // rus
- "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270"
- "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\275\320\265"
- "\321\202 \320\277\320\276\320\264\320\277\320\270\321\201\320\270. "
- "\320\255\321\202\320\276 \320\276\320\267\320\275\320\260\321\207\320\260"
- "\320\265\321\202, \321\207\321\202\320\276 \320\262\321\213 \320\275"
- "\320\265 \320\274\320\276\320\266\320\265\321\202\320\265 \320\267\320\275"
- "\320\260\321\202\321\214, \320\272\321\202\320\276 \320\265\320\263"
- "\320\276 \320\275\320\260\320\277\320\270\321\201\320\260\320\273.\n"
- "\n"
- "\320\235\320\260\320\266\320\274\320\270\321\202\320\265 \"\320\236"
- "\321\202\320\274\320\265\320\275\320\260\", \321\207\321\202\320\276"
- "\320\261\321\213 \320\275\320\265 \320\267\320\260\320\277\321\203\321\201"
- "\320\272\320\260\321\202\321\214 \320\277\321\200\320\270\320\273\320\276"
- "\320\266\320\265\320\275\320\270\320\265.",
-};
diff --git a/direct/src/plugin/p3dCert_strings.h b/direct/src/plugin/p3dCert_strings.h
deleted file mode 100644
index 7e6d391e2a..0000000000
--- a/direct/src/plugin/p3dCert_strings.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert_strings.h
- * @author rdb
- * @date 2015-03-25
- */
-
-enum LanguageIndex {
- LI_en, // English
- LI_nl, // Dutch
- LI_de, // German
- LI_es, // Spanish
- LI_it, // Italian
- LI_eo, // Esperanto
- LI_ru, // Russian
- LI_COUNT,
-
- LI_default = LI_en
-};
-
-extern const char *language_codes[LI_COUNT];
-extern const unsigned char language_ids[LI_COUNT];
-
-extern const char *run_title[LI_COUNT];
-extern const char *cancel_title[LI_COUNT];
-extern const char *show_cert_title[LI_COUNT];
-
-extern const char *new_application_title[LI_COUNT];
-extern const char *no_cert_title[LI_COUNT];
-extern const char *unverified_cert_title[LI_COUNT];
-extern const char *expired_cert_title[LI_COUNT];
-
-extern const char *self_signed_cert_text[LI_COUNT];
-extern const char *unknown_auth_cert_text[LI_COUNT];
-extern const char *verified_cert_text[LI_COUNT];
-extern const char *expired_cert_text[LI_COUNT];
-extern const char *generic_error_cert_text[LI_COUNT];
-extern const char *no_cert_text[LI_COUNT];
diff --git a/direct/src/plugin/p3dCert_wx.cxx b/direct/src/plugin/p3dCert_wx.cxx
deleted file mode 100644
index e7002db46a..0000000000
--- a/direct/src/plugin/p3dCert_wx.cxx
+++ /dev/null
@@ -1,618 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert_wx.cxx
- * @author drose
- * @date 2009-09-11
- */
-
-#include "p3dCert_wx.h"
-#include "wstring_encode.h"
-#include "mkdir_complete.h"
-
-#include
-#include
-
-#include "ca_bundle_data_src.c"
-
-using std::cerr;
-
-static const wxString
-self_signed_cert_text =
- _T("This Panda3D application uses a self-signed certificate. ")
- _T("This means the author's name can't be verified, and you have ")
- _T("no way of knowing for sure who wrote it.\n\n")
-
- _T("We recommend you click Cancel to avoid running this application.");
-
-static const wxString
-unknown_auth_cert_text =
- _T("This Panda3D application has been signed, but we don't recognize ")
- _T("the authority that verifies the signature. This means the author's ")
- _T("name can't be trusted, and you have no way of knowing ")
- _T("for sure who wrote it.\n\n")
-
- _T("We recommend you click Cancel to avoid running this application.");
-
-static const wxString
-verified_cert_text =
- _T("This Panda3D application has been signed by %s. ")
- _T("If you trust %s, then click the Run button below ")
- _T("to run this application on your computer. This will also ")
- _T("automatically approve this and any other applications signed by ")
- _T("%s in the future.\n\n")
-
- _T("If you are unsure about this application, ")
- _T("you should click Cancel instead.");
-
-static const wxString
-expired_cert_text =
- _T("This Panda3D application has been signed by %s, ")
- _T("but the certificate has expired.\n\n")
-
- _T("You should check the current date set on your computer's clock ")
- _T("to make sure it is correct.\n\n")
-
- _T("If your computer's date is correct, we recommend ")
- _T("you click Cancel to avoid running this application.");
-
-static const wxString
-generic_error_cert_text =
- _T("This Panda3D application has been signed, but there is a problem ")
- _T("with the certificate (OpenSSL error code %d).\n\n")
-
- _T("We recommend you click Cancel to avoid running this application.");
-
-static const wxString
-no_cert_text =
- _T("This Panda3D application has not been signed. This means you have ")
- _T("no way of knowing for sure who wrote it.\n\n")
-
- _T("Click Cancel to avoid running this application.");
-
-// wxWidgets boilerplate macro to define main() and start up the application.
-IMPLEMENT_APP(P3DCertApp)
-
-/**
- * The "main" of a wx application. This is the first entry point.
- */
-bool P3DCertApp::
-OnInit() {
- // call the base class initialization method, currently it only parses a few
- // common command-line options but it could be do more in the future
- if (!wxApp::OnInit()) {
- return false;
- }
-
- OpenSSL_add_all_algorithms();
-
- AuthDialog *dialog = new AuthDialog(_cert_filename, _cert_dir);
- SetTopWindow(dialog);
- dialog->Show(true);
- dialog->SetFocus();
- dialog->Raise();
-
- // Return true to enter the main loop and wait for user input.
- return true;
-}
-
-/**
- * A callback to initialize the parser with the command line options.
- */
-void P3DCertApp::
-OnInitCmdLine(wxCmdLineParser &parser) {
- parser.AddParam();
- parser.AddParam();
-}
-
-/**
- * A callback after the successful parsing of the command line.
- */
-bool P3DCertApp::
-OnCmdLineParsed(wxCmdLineParser &parser) {
- _cert_filename = (const char *)parser.GetParam(0).mb_str();
- _cert_dir = (const char *)parser.GetParam(1).mb_str();
-
- return true;
-}
-
-
-// The event table for AuthDialog.
-#define VIEW_CERT_BUTTON (wxID_HIGHEST + 1)
-BEGIN_EVENT_TABLE(AuthDialog, wxDialog)
- EVT_BUTTON(wxID_OK, AuthDialog::run_clicked)
- EVT_BUTTON(VIEW_CERT_BUTTON, AuthDialog::view_cert_clicked)
- EVT_BUTTON(wxID_CANCEL, AuthDialog::cancel_clicked)
-END_EVENT_TABLE()
-
-/**
- *
- */
-AuthDialog::
-AuthDialog(const std::string &cert_filename, const std::string &cert_dir) :
- // I hate stay-on-top dialogs, but if we don't set this flag, it doesn't
- // come to the foreground on OSX, and might be lost behind the browser
- // window.
- wxDialog(nullptr, wxID_ANY, _T("New Panda3D Application"), wxDefaultPosition,
- wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxSTAY_ON_TOP),
- _cert_dir(cert_dir)
-{
- _view_cert_dialog = nullptr;
-
- _cert = nullptr;
- _stack = nullptr;
- _verify_result = -1;
-
- read_cert_file(cert_filename);
- get_friendly_name();
- verify_cert();
- layout();
-}
-
-/**
- *
- */
-AuthDialog::
-~AuthDialog() {
- if (_view_cert_dialog != nullptr) {
- _view_cert_dialog->Destroy();
- }
-
- if (_cert != nullptr) {
- X509_free(_cert);
- _cert = nullptr;
- }
- if (_stack != nullptr) {
- sk_X509_free(_stack);
- _stack = nullptr;
- }
-}
-
-/**
- * The user clicks the "Run" button.
- */
-void AuthDialog::
-run_clicked(wxCommandEvent &event) {
- approve_cert();
-}
-
-/**
- * The user clicks the "View Certificate" button.
- */
-void AuthDialog::
-view_cert_clicked(wxCommandEvent &event) {
- if (_view_cert_dialog != nullptr) {
- _view_cert_dialog->Destroy();
- }
- Hide();
- _view_cert_dialog = new ViewCertDialog(this, _cert);
- _view_cert_dialog->Show();
-}
-
-/**
- * The user clicks the "Cancel" button.
- */
-void AuthDialog::
-cancel_clicked(wxCommandEvent &event) {
- Destroy();
-}
-
-/**
- * Writes the certificate into the _cert_dir, so that it will be found by the
- * P3DInstanceManager and known to be approved.
- */
-void AuthDialog::
-approve_cert() {
- assert(_cert != nullptr);
-
- // Make sure the directory exists.
- mkdir_complete(_cert_dir, cerr);
-
- // Look for an unused filename.
- int i = 1;
- size_t buf_length = _cert_dir.length() + 100;
- char *buf = new char[buf_length];
-#ifdef _WIN32
- std::wstring buf_w;
-#endif // _WIN32
-
- while (true) {
- sprintf(buf, "%s/p%d.crt", _cert_dir.c_str(), i);
- assert(strlen(buf) < buf_length);
-
- // Check if it already exists. If not, take it.
-#ifdef _WIN32
- DWORD results = 0;
- if (string_to_wstring(buf_w, buf)) {
- results = GetFileAttributesW(buf_w.c_str());
- }
- if (results == -1) {
- break;
- }
-#else
- struct stat statbuf;
- if (stat(buf, &statbuf) != 0) {
- break;
- }
-#endif
- ++i;
- }
-
- // Sure, there's a slight race condition right now: another process might
- // attempt to create the same filename. So what.
- FILE *fp = nullptr;
-#ifdef _WIN32
- fp = _wfopen(buf_w.c_str(), L"w");
-#else // _WIN32
- fp = fopen(buf, "w");
-#endif // _WIN32
- if (fp != nullptr) {
- PEM_write_X509(fp, _cert);
- fclose(fp);
- }
-
- Destroy();
-}
-
-/**
- * Reads the list of certificates in the pem filename passed on the command
- * line into _cert and _stack.
- */
-void AuthDialog::
-read_cert_file(const std::string &cert_filename) {
- FILE *fp = nullptr;
-#ifdef _WIN32
- std::wstring cert_filename_w;
- if (string_to_wstring(cert_filename_w, cert_filename)) {
- fp = _wfopen(cert_filename_w.c_str(), L"r");
- }
-#else // _WIN32
- fp = fopen(cert_filename.c_str(), "r");
-#endif // _WIN32
-
- if (fp == nullptr) {
- cerr << "Couldn't read " << cert_filename << "\n";
- return;
- }
- _cert = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
- if (_cert == nullptr) {
- cerr << "Could not read certificate in " << cert_filename << ".\n";
- fclose(fp);
- return;
- }
-
- // Build up a STACK of the remaining certificates in the file.
- _stack = sk_X509_new(nullptr);
- X509 *c = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
- while (c != nullptr) {
- sk_X509_push(_stack, c);
- c = PEM_read_X509(fp, nullptr, nullptr, (void *)"");
- }
-
- fclose(fp);
-}
-
-/**
- * Extracts the "friendly name" from the certificate: the common name or email
- * name.
- */
-void AuthDialog::
-get_friendly_name() {
- if (_cert == nullptr) {
- _friendly_name.clear();
- return;
- }
-
-
- static const int nid_choices[] = {
- NID_pkcs9_emailAddress,
- NID_commonName,
- -1,
- };
-
- // Choose the first NID that exists on the cert.
- for (int ni = 0; nid_choices[ni] != -1; ++ni) {
- int nid = nid_choices[ni];
-
- // A complex OpenSSL interface to extract out the name in utf-8.
- X509_NAME *xname = X509_get_subject_name(_cert);
- if (xname != nullptr) {
- int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
- if (pos != -1) {
- // We just get the first common name. I guess it's possible to have
- // more than one; not sure what that means in this context.
- X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
- if (xentry != nullptr) {
- ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
- if (data != nullptr) {
- // We use "print" to dump the output to a memory BIO. Is there an
- // easier way to decode the ASN1_STRING? Curse these incomplete
- // docs.
- BIO *mbio = BIO_new(BIO_s_mem());
- ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
-
- char *pp;
- long pp_size = BIO_get_mem_data(mbio, &pp);
- _friendly_name = wxString(pp, wxConvUTF8, pp_size);
- BIO_free(mbio);
- return;
- }
- }
- }
- }
- }
-}
-
-/**
- * Checks whether the certificate is valid by the chain and initializes
- * _verify_status accordingly.
- */
-void AuthDialog::
-verify_cert() {
- if (_cert == nullptr) {
- _verify_result = -1;
- return;
- }
-
- // Create a new X509_STORE.
- X509_STORE *store = X509_STORE_new();
- X509_STORE_set_default_paths(store);
-
- // Add in the well-known certificate authorities.
- load_certificates_from_der_ram(store, (const char *)ca_bundle_data, ca_bundle_data_len);
-
- // Create the X509_STORE_CTX for verifying the cert and chain.
- X509_STORE_CTX *ctx = X509_STORE_CTX_new();
- X509_STORE_CTX_init(ctx, store, _cert, _stack);
- X509_STORE_CTX_set_cert(ctx, _cert);
-
- if (X509_verify_cert(ctx)) {
- _verify_result = 0;
- } else {
- _verify_result = X509_STORE_CTX_get_error(ctx);
- }
-
- X509_STORE_CTX_free(ctx);
-
- X509_STORE_free(store);
-
- cerr << "Got certificate from " << _friendly_name.mb_str()
- << ", verify_result = " << _verify_result << "\n";
-}
-
-/**
- * Reads a chain of trusted certificates from the indicated data buffer and
- * adds them to the X509_STORE object. The data buffer should be DER-
- * formatted. Returns the number of certificates read on success, or 0 on
- * failure.
- *
- * You should call this only with trusted, locally-stored certificates; not
- * with certificates received from an untrusted source.
- */
-int AuthDialog::
-load_certificates_from_der_ram(X509_STORE *store,
- const char *data, size_t data_size) {
- int count = 0;
-
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
- // Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **.
- const unsigned char *bp, *bp_end;
-#else
- // Prior to 0.9.8, d2i_X509() accepted an unsigned char **.
- unsigned char *bp, *bp_end;
-#endif
-
- bp = (unsigned char *)data;
- bp_end = bp + data_size;
- X509 *x509 = d2i_X509(nullptr, &bp, bp_end - bp);
- while (x509 != nullptr) {
- X509_STORE_add_cert(store, x509);
- ++count;
- x509 = d2i_X509(nullptr, &bp, bp_end - bp);
- }
-
- return count;
-}
-
-/**
- * Arranges the text and controls within the dialog.
- */
-void AuthDialog::
-layout() {
- wxString header, text;
- get_text(header, text);
-
- wxPanel *panel = new wxPanel(this);
- wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
-
- wxFont font = panel->GetFont();
- wxFont *bold_font = wxTheFontList->FindOrCreateFont
- ((int)(font.GetPointSize() * 1.5),
- font.GetFamily(), font.GetStyle(), wxFONTWEIGHT_BOLD);
-
- if (!header.IsEmpty()) {
- wxStaticText *text0 = new wxStaticText
- (panel, wxID_ANY, header, wxDefaultPosition, wxDefaultSize,
- wxALIGN_CENTER);
- text0->SetFont(*bold_font);
- vsizer->Add(text0, 0, wxCENTER | wxALL, 10);
- }
-
- wxStaticText *text1 = new wxStaticText
- (panel, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
- text1->Wrap(400);
- vsizer->Add(text1, 0, wxCENTER | wxALL, 10);
-
- // Create the run cancel buttons.
- wxBoxSizer *bsizer = new wxBoxSizer(wxHORIZONTAL);
-
- if (_verify_result == 0 && _cert != nullptr) {
- wxButton *run_button = new wxButton(panel, wxID_OK, _T("Run"));
- bsizer->Add(run_button, 0, wxALIGN_CENTER | wxALL, 5);
- }
-
- if (_cert != nullptr) {
- wxButton *view_button = new wxButton(panel, VIEW_CERT_BUTTON, _T("View Certificate"));
- bsizer->Add(view_button, 0, wxALIGN_CENTER | wxALL, 5);
- }
-
- wxButton *cancel_button = new wxButton(panel, wxID_CANCEL, _T("Cancel"));
- bsizer->Add(cancel_button, 0, wxALIGN_CENTER | wxALL, 5);
-
- vsizer->Add(bsizer, 0, wxALIGN_CENTER | wxALL, 5);
-
- panel->SetSizer(vsizer);
- panel->SetAutoLayout(true);
- vsizer->Fit(this);
-}
-
-/**
- * Fills in the text appropriate to display in the dialog box, based on the
- * certificate read so far.
- */
-void AuthDialog::
-get_text(wxString &header, wxString &text) {
- switch (_verify_result) {
- case -1:
- header = _T("No signature!");
- text = no_cert_text;
- break;
-
- case 0:
- text.Printf(verified_cert_text, _friendly_name.c_str(), _friendly_name.c_str(), _friendly_name.c_str());
- break;
-
- case X509_V_ERR_CERT_NOT_YET_VALID:
- case X509_V_ERR_CERT_HAS_EXPIRED:
- case X509_V_ERR_CRL_NOT_YET_VALID:
- case X509_V_ERR_CRL_HAS_EXPIRED:
- header = _T("Expired signature!");
- text.Printf(expired_cert_text, _friendly_name.c_str());
- break;
-
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
- header = _T("Unverified signature!");
- text.Printf(unknown_auth_cert_text, _friendly_name.c_str());
- break;
-
- case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
- case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
- header = _T("Unverified signature!");
- text = self_signed_cert_text;
- break;
-
- default:
- header = _T("Unverified signature!");
- text.Printf(generic_error_cert_text, _verify_result);
- }
-}
-
-
-// The event table for ViewCertDialog.
-BEGIN_EVENT_TABLE(ViewCertDialog, wxDialog)
- EVT_BUTTON(wxID_OK, ViewCertDialog::run_clicked)
- EVT_BUTTON(wxID_CANCEL, ViewCertDialog::cancel_clicked)
-END_EVENT_TABLE()
-
-/**
- *
- */
-ViewCertDialog::
-ViewCertDialog(AuthDialog *auth_dialog, X509 *cert) :
-wxDialog(nullptr, wxID_ANY, _T("View Certificate"), wxDefaultPosition,
- wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
- _auth_dialog(auth_dialog),
- _cert(cert)
-{
- layout();
-}
-
-/**
- *
- */
-ViewCertDialog::
-~ViewCertDialog() {
- if (_auth_dialog != nullptr) {
- _auth_dialog->_view_cert_dialog = nullptr;
- }
-}
-
-/**
- * The user clicks the "Run" button.
- */
-void ViewCertDialog::
-run_clicked(wxCommandEvent &event) {
- if (_auth_dialog != nullptr){
- _auth_dialog->approve_cert();
- }
- Destroy();
-}
-
-/**
- * The user clicks the "Cancel" button.
- */
-void ViewCertDialog::
-cancel_clicked(wxCommandEvent &event) {
- if (_auth_dialog != nullptr){
- _auth_dialog->Destroy();
- }
- Destroy();
-}
-
-/**
- * Arranges the text and controls within the dialog.
- */
-void ViewCertDialog::
-layout() {
- // Format the certificate text for display in the dialog.
- assert(_cert != nullptr);
-
- BIO *mbio = BIO_new(BIO_s_mem());
- X509_print(mbio, _cert);
-
- char *pp;
- long pp_size = BIO_get_mem_data(mbio, &pp);
- wxString cert_body(pp, wxConvUTF8, pp_size);
- BIO_free(mbio);
-
- wxPanel *panel = new wxPanel(this);
- wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
-
- wxScrolledWindow *slwin = new wxScrolledWindow
- (panel, -1, wxDefaultPosition, wxDefaultSize, wxVSCROLL | wxHSCROLL | wxBORDER_SUNKEN);
- slwin->SetScrollRate(20, 20);
-
- wxBoxSizer *slsizer = new wxBoxSizer(wxVERTICAL);
-
- wxStaticText *text1 = new wxStaticText
- (slwin, wxID_ANY, cert_body, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
- slsizer->Add(text1, 0, wxEXPAND, 0);
- slwin->SetSizer(slsizer);
-
- vsizer->Add(slwin, 1, wxEXPAND | wxALL, 10);
-
- // Create the run cancel buttons.
- wxBoxSizer *bsizer = new wxBoxSizer(wxHORIZONTAL);
-
- wxButton *run_button = new wxButton(panel, wxID_OK, _T("Run"));
- bsizer->Add(run_button, 0, wxALIGN_CENTER | wxALL, 5);
-
- wxButton *cancel_button = new wxButton(panel, wxID_CANCEL, _T("Cancel"));
- bsizer->Add(cancel_button, 0, wxALIGN_CENTER | wxALL, 5);
-
- vsizer->Add(bsizer, 0, wxALIGN_CENTER | wxALL, 5);
-
- panel->SetSizer(vsizer);
- panel->SetAutoLayout(true);
- vsizer->Fit(this);
-
- // Make sure the resulting window is at least a certain size.
- int width, height;
- GetSize(&width, &height);
- SetSize(std::max(width, 600), std::max(height, 400));
-}
diff --git a/direct/src/plugin/p3dCert_wx.h b/direct/src/plugin/p3dCert_wx.h
deleted file mode 100644
index b0b3dda12e..0000000000
--- a/direct/src/plugin/p3dCert_wx.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dCert_wx.h
- * @author drose
- * @date 2009-09-11
- */
-
-#ifndef P3DCERT_WX_H
-#define P3DCERT_WX_H
-
-#include
-
-#define OPENSSL_NO_KRB5
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-class ViewCertDialog;
-
-#ifndef STACK_OF
- // At some point, presumably in 1.0.0, openssl went to the STACK_OF() macro
- // system to typedef the contents of a stack. Unfortunately, that new API
- // is different. We define some macros here here for backward compatiblity.
- #define STACK_OF(type) STACK
- #define sk_X509_push(stack, item) sk_push((stack), (char *)(item))
- #define sk_X509_free(stack) sk_free(stack)
- #define sk_X509_new(cmp) sk_new(cmp)
-#endif
-
-/**
- * This is the wxApp that drives this application.
- */
-class P3DCertApp : public wxApp {
-public:
- virtual bool OnInit();
- virtual void OnInitCmdLine(wxCmdLineParser &parser);
- virtual bool OnCmdLineParsed(wxCmdLineParser &parser);
-
-private:
- std::string _cert_filename;
- std::string _cert_dir;
-};
-
-/**
- * This is the primary dialog of this application.
- *
- * This dialog is presented to the user when he/she clicks on the red
- * authorization button on the splash window. It tells the user the status of
- * the application's signature, and invites the user to approve the signature
- * or cancel.
- */
-class AuthDialog : public wxDialog {
-public:
- AuthDialog(const std::string &cert_filename, const std::string &cert_dir);
- virtual ~AuthDialog();
-
- void run_clicked(wxCommandEvent &event);
- void view_cert_clicked(wxCommandEvent &event);
- void cancel_clicked(wxCommandEvent &event);
-
- void approve_cert();
-
-private:
- void read_cert_file(const std::string &cert_filename);
- void get_friendly_name();
- void verify_cert();
- int load_certificates_from_der_ram(X509_STORE *store,
- const char *data, size_t data_size);
-
- void layout();
- void get_text(wxString &header, wxString &text);
-
-public:
- ViewCertDialog *_view_cert_dialog;
-
-private:
- // any class wishing to process wxWidgets events must use this macro
- DECLARE_EVENT_TABLE()
-
- std::string _cert_dir;
- X509 *_cert;
- STACK_OF(X509) *_stack;
-
- wxString _friendly_name;
- int _verify_result;
-};
-
-/**
- * This is the detailed view of the particular certificate.
- */
-class ViewCertDialog : public wxDialog {
-public:
- ViewCertDialog(AuthDialog *auth_dialog, X509 *cert);
- virtual ~ViewCertDialog();
-
- void run_clicked(wxCommandEvent &event);
- void cancel_clicked(wxCommandEvent &event);
-
-private:
- void layout();
-
-private:
- DECLARE_EVENT_TABLE()
-
- AuthDialog *_auth_dialog;
- X509 *_cert;
-};
-
-#endif
diff --git a/direct/src/plugin/p3dConcreteSequence.cxx b/direct/src/plugin/p3dConcreteSequence.cxx
deleted file mode 100644
index ec4291cb7b..0000000000
--- a/direct/src/plugin/p3dConcreteSequence.cxx
+++ /dev/null
@@ -1,213 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConcreteSequence.cxx
- * @author drose
- * @date 2009-06-30
- */
-
-#include "p3dConcreteSequence.h"
-#include "p3dSession.h"
-
-/**
- *
- */
-P3DConcreteSequence::
-P3DConcreteSequence() {
-}
-
-/**
- *
- */
-P3DConcreteSequence::
-~P3DConcreteSequence() {
- Elements::iterator ei;
- for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
- P3D_OBJECT_DECREF(*ei);
- }
-}
-
-/**
- * Returns true if this is actually an instance of a P3DConcreteSequence,
- * false otherwise.
- */
-bool P3DConcreteSequence::
-is_sequence_object() {
- return true;
-}
-
-/**
- * Returns the fundamental type of this kind of object.
- */
-P3D_object_type P3DConcreteSequence::
-get_type() {
- return P3D_OT_object;
-}
-
-/**
- * Returns the object value coerced to a boolean, if possible.
- */
-bool P3DConcreteSequence::
-get_bool() {
- return !_elements.empty();
-}
-
-/**
- * Fills the indicated C++ string object with the value of this object coerced
- * to a string.
- */
-void P3DConcreteSequence::
-make_string(std::string &value) {
- std::ostringstream strm;
- strm << "[";
- if (!_elements.empty()) {
- strm << *_elements[0];
- for (size_t i = 1; i < _elements.size(); ++i) {
- strm << ", " << *_elements[i];
- }
- }
- strm << "]";
-
- value = strm.str();
-}
-
-/**
- * Returns the named property element in the object. The return value is a
- * new-reference P3D_object, or NULL on error.
- */
-P3D_object *P3DConcreteSequence::
-get_property(const std::string &property) {
- // We only understand integer "property" names.
- char *endptr;
- int index = strtoul(property.c_str(), &endptr, 10);
- if (*endptr != '\0') {
- return nullptr;
- }
-
- return get_element(index);
-}
-
-/**
- * Modifies (or deletes, if value is NULL) the named property element in the
- * object. Returns true on success, false on failure.
- */
-bool P3DConcreteSequence::
-set_property(const std::string &property, P3D_object *value) {
- // We only understand integer "property" names.
- char *endptr;
- int index = strtoul(property.c_str(), &endptr, 10);
- if (*endptr != '\0') {
- return false;
- }
-
- return set_element(index, value);
-}
-
-/**
- * If this object has a valid XML representation for the indicated session
- * (that hasn't already been implemented by the generic code in P3DSession),
- * this method will apply it to the indicated "value" element and return true.
- * Otherwise, this method will leave the element unchanged and return false.
- */
-bool P3DConcreteSequence::
-fill_xml(TiXmlElement *xvalue, P3DSession *session) {
- xvalue->SetAttribute("type", "concrete_sequence");
- Elements::const_iterator ei;
- for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
- xvalue->LinkEndChild(session->p3dobj_to_xml(*ei));
- }
-
- return true;
-}
-
-/**
- * Returns a pointer to the array of objects represented by this object. Most
- * objects represent only themselves, but a P3DConcreteSequence represents its
- * list.
- */
-P3D_object **P3DConcreteSequence::
-get_object_array() {
- if (_elements.empty()) {
- return nullptr;
- }
- return &_elements[0];
-}
-
-/**
- * Returns the number of elements in the array returned by get_object_array().
- */
-int P3DConcreteSequence::
-get_object_array_size() {
- return _elements.size();
-}
-
-/**
- * Returns the number of items in the sequence.
- */
-int P3DConcreteSequence::
-get_length() const {
- return _elements.size();
-}
-
-/**
- * Returns the nth item in the sequence. The return value is a new-reference
- * P3DObject object, or NULL on error.
- */
-P3D_object *P3DConcreteSequence::
-get_element(int n) const {
- if (n >= 0 && n < (int)_elements.size()) {
- P3D_OBJECT_INCREF(_elements[n]);
- return _elements[n];
- }
-
- return nullptr;
-}
-
-/**
- * Modifies (or deletes, if value is NULL) the nth item in the sequence.
- * Returns true on success, false on failure.
- */
-bool P3DConcreteSequence::
-set_element(int n, P3D_object *value) {
- if (value == nullptr) {
- // Delete an element.
- if (n < 0 || n >= (int)_elements.size()) {
- // Invalid index.
- return false;
- }
- P3D_OBJECT_DECREF(_elements[n]);
- _elements.erase(_elements.begin() + n);
- return true;
-
- } else if (n == _elements.size()) {
- // Append an element.
- append(value);
- return true;
-
- } else {
- // Replace an element.
- if (n < 0 || n >= (int)_elements.size()) {
- // Invalid index.
- return false;
- }
-
- P3D_OBJECT_INCREF(value);
- P3D_OBJECT_DECREF(_elements[n]);
- _elements[n] = value;
- return true;
- }
-}
-
-/**
- * Adds a new element to the end of the list.
- */
-void P3DConcreteSequence::
-append(P3D_object *value) {
- _elements.push_back(value);
- P3D_OBJECT_INCREF(value);
-}
diff --git a/direct/src/plugin/p3dConcreteSequence.h b/direct/src/plugin/p3dConcreteSequence.h
deleted file mode 100644
index 09b489c5ba..0000000000
--- a/direct/src/plugin/p3dConcreteSequence.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConcreteSequence.h
- * @author drose
- * @date 2009-06-30
- */
-
-#ifndef P3DCONCRETESEQUENCE_H
-#define P3DCONCRETESEQUENCE_H
-
-#include "p3d_plugin_common.h"
-#include "p3dObject.h"
-
-/**
- * An object type that contains a sequence of objects, which is passed by
- * value between Python and JavaScript, so may be more optimal for small lists
- * that are accessed repeatedly.
- *
- * This is converted from a Python "tuple" object.
- */
-class P3DConcreteSequence : public P3DObject {
-public:
- P3DConcreteSequence();
- virtual ~P3DConcreteSequence();
-
- virtual bool is_sequence_object();
-
- virtual P3D_object_type get_type();
- virtual bool get_bool();
-
- virtual void make_string(std::string &value);
-
- virtual P3D_object *get_property(const std::string &property);
- virtual bool set_property(const std::string &property, P3D_object *value);
-
- virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session);
- virtual P3D_object **get_object_array();
- virtual int get_object_array_size();
-
- int get_length() const;
- P3D_object *get_element(int n) const;
- bool set_element(int n, P3D_object *value);
- void append(P3D_object *value);
-
-private:
- typedef std::vector Elements;
- Elements _elements;
-};
-
-#endif
diff --git a/direct/src/plugin/p3dConcreteStruct.cxx b/direct/src/plugin/p3dConcreteStruct.cxx
deleted file mode 100644
index fa9917d955..0000000000
--- a/direct/src/plugin/p3dConcreteStruct.cxx
+++ /dev/null
@@ -1,178 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConcreteStruct.cxx
- * @author drose
- * @date 2009-07-14
- */
-
-#include "p3dConcreteStruct.h"
-
-using std::string;
-
-/**
- *
- */
-P3DConcreteStruct::
-P3DConcreteStruct() {
-}
-
-/**
- *
- */
-P3DConcreteStruct::
-~P3DConcreteStruct() {
- Elements::iterator ei;
- for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
- P3D_OBJECT_DECREF((*ei).second);
- }
-}
-
-/**
- * Returns the fundamental type of this kind of object.
- */
-P3D_object_type P3DConcreteStruct::
-get_type() {
- return P3D_OT_object;
-}
-
-/**
- * Returns the object value coerced to a boolean, if possible.
- */
-bool P3DConcreteStruct::
-get_bool() {
- return !_elements.empty();
-}
-
-/**
- * Fills the indicated C++ string object with the value of this object coerced
- * to a string.
- */
-void P3DConcreteStruct::
-make_string(string &value) {
- std::ostringstream strm;
- strm << "{";
- if (!_elements.empty()) {
- Elements::iterator ei;
- ei = _elements.begin();
- strm << (*ei).first << ": " << *(*ei).second;
- ++ei;
- while (ei != _elements.end()) {
- strm << ", " << (*ei).first << ": " << *(*ei).second;
- ++ei;
- }
- }
- strm << "}";
-
- value = strm.str();
-}
-
-/**
- * Returns the named property element in the object. The return value is a
- * new-reference P3D_object, or NULL on error.
- */
-P3D_object *P3DConcreteStruct::
-get_property(const string &property) {
- Elements::const_iterator ei = _elements.find(property);
- if (ei != _elements.end()) {
- P3D_OBJECT_INCREF((*ei).second);
- return (*ei).second;
- }
-
- return nullptr;
-}
-
-/**
- * Modifies (or deletes, if value is NULL) the named property element in the
- * object. Returns true on success, false on failure.
- */
-bool P3DConcreteStruct::
-set_property(const string &property, P3D_object *value) {
- if (value == nullptr) {
- // Delete an element.
- Elements::iterator ei = _elements.find(property);
- if (ei == _elements.end()) {
- // Invalid property.
- return false;
- }
- P3D_OBJECT_DECREF((*ei).second);
- _elements.erase(ei);
- return true;
-
- } else {
- // Replace or insert an element.
- P3D_OBJECT_INCREF(value);
- std::pair result = _elements.insert(Elements::value_type(property, value));
- if (!result.second) {
- // Replacing an element.
- Elements::iterator ei = result.first;
- P3D_OBJECT_DECREF((*ei).second);
- (*ei).second = value;
- }
- return true;
- }
-}
-
-/**
- * Returns true if the named method exists on this object, false otherwise.
- */
-bool P3DConcreteStruct::
-has_method(const string &method_name) {
- if (method_name == "toString") {
- return true;
- }
-
- return false;
-}
-
-/**
- * Invokes the named method on the object, passing the indicated parameters.
- * If the method name is empty, invokes the object itself.
- *
- * If needs_response is true, the return value is a new-reference P3D_object
- * on success, or NULL on failure. If needs_response is false, the return
- * value is always NULL, and there is no way to determine success or failure.
- */
-P3D_object *P3DConcreteStruct::
-call(const string &method_name, bool needs_response,
- P3D_object *params[], int num_params) {
- P3D_object *result = nullptr;
-
- if (method_name == "toString") {
- string value;
- make_string(value);
- result = P3D_new_string_object(value.data(), value.length());
- }
-
- if (result != nullptr && !needs_response) {
- P3D_OBJECT_DECREF(result);
- result = nullptr;
- }
-
- return result;
-}
-
-
-/**
- * If this object has a valid XML representation for the indicated session
- * (that hasn't already been implemented by the generic code in P3DSession),
- * this method will apply it to the indicated "value" element and return true.
- * Otherwise, this method will leave the element unchanged and return false.
- */
-bool P3DConcreteStruct::
-fill_xml(TiXmlElement *xvalue, P3DSession *session) {
- xvalue->SetAttribute("type", "concrete_struct");
- Elements::const_iterator ei;
- for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
- TiXmlElement *xitem = session->p3dobj_to_xml((*ei).second);
- xitem->SetAttribute("key", (*ei).first);
- xvalue->LinkEndChild(xitem);
- }
-
- return true;
-}
diff --git a/direct/src/plugin/p3dConcreteStruct.h b/direct/src/plugin/p3dConcreteStruct.h
deleted file mode 100644
index cccd35bbeb..0000000000
--- a/direct/src/plugin/p3dConcreteStruct.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConcreteStruct.h
- * @author drose
- * @date 2009-07-14
- */
-
-#ifndef P3DCONCRETESTRUCT_H
-#define P3DCONCRETESTRUCT_H
-
-#include "p3d_plugin_common.h"
-#include "p3dObject.h"
-
-/**
- * A simple object that contains a standard mapping of string -> element. It
- * is passed by value between Python and Javascript, so it may be more optimal
- * for relatively small objects.
- *
- * Methods are not supported, other than built-in methods like toString().
- */
-class P3DConcreteStruct : public P3DObject {
-public:
- P3DConcreteStruct();
- virtual ~P3DConcreteStruct();
-
- virtual P3D_object_type get_type();
- virtual bool get_bool();
-
- virtual void make_string(std::string &value);
-
- virtual P3D_object *get_property(const std::string &property);
- virtual bool set_property(const std::string &property, P3D_object *value);
-
- virtual bool has_method(const std::string &method_name);
- virtual P3D_object *call(const std::string &method_name, bool needs_response,
- P3D_object *params[], int num_params);
-
- virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session);
-
-private:
- typedef std::map Elements;
- Elements _elements;
-};
-
-#endif
diff --git a/direct/src/plugin/p3dConditionVar.I b/direct/src/plugin/p3dConditionVar.I
deleted file mode 100644
index 08ae4ffe7e..0000000000
--- a/direct/src/plugin/p3dConditionVar.I
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConditionVar.I
- * @author drose
- * @date 2009-07-02
- */
diff --git a/direct/src/plugin/p3dConditionVar.cxx b/direct/src/plugin/p3dConditionVar.cxx
deleted file mode 100644
index 22133c0781..0000000000
--- a/direct/src/plugin/p3dConditionVar.cxx
+++ /dev/null
@@ -1,171 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConditionVar.cxx
- * @author drose
- * @date 2009-07-02
- */
-
-#include "p3dConditionVar.h"
-
-#ifndef _WIN32
-#include
-#include
-#endif
-#include
-
-/**
- *
- */
-P3DConditionVar::
-P3DConditionVar() {
-#ifdef _WIN32
- InitializeCriticalSection(&_lock);
-
- // Create an auto-reset event.
- _event_signal = CreateEvent(nullptr, false, false, nullptr);
-
-#else // _WIN32
- pthread_mutexattr_t attr;
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
- int result = pthread_mutex_init(&_lock, &attr);
- pthread_mutexattr_destroy(&attr);
- assert(result == 0);
-
- result = pthread_cond_init(&_cvar, nullptr);
- assert(result == 0);
-
-#endif // _WIN32
-}
-
-/**
- *
- */
-P3DConditionVar::
-~P3DConditionVar() {
-#ifdef _WIN32
- DeleteCriticalSection(&_lock);
- CloseHandle(_event_signal);
-
-#else // _WIN32
- int result = pthread_mutex_destroy(&_lock);
- assert(result == 0);
-
- result = pthread_cond_destroy(&_cvar);
- assert(result == 0);
-
-#endif // _WIN32
-}
-
-/**
- * Acquires the internal lock. The lock should be held during any calls to
- * wait() or notify().
- */
-void P3DConditionVar::
-acquire() {
-#ifdef _WIN32
- EnterCriticalSection(&_lock);
-
-#else // _WIN32
- int result = pthread_mutex_lock(&_lock);
- assert(result == 0);
-
-#endif // _WIN32
-}
-
-/**
- * Requires the lock to be held on entry. Releases the lock, waits for
- * another thread to call notify(), then reacquires the lock on exit.
- */
-void P3DConditionVar::
-wait() {
-#ifdef _WIN32
- LeaveCriticalSection(&_lock);
-
- DWORD result = WaitForSingleObject(_event_signal, INFINITE);
- assert(result == WAIT_OBJECT_0);
-
- EnterCriticalSection(&_lock);
-
-#else // _WIN32
- int result = pthread_cond_wait(&_cvar, &_lock);
- assert(result == 0);
-
-#endif // _WIN32
-}
-
-/**
- * As above, but waits no longer than timeout seconds before returning.
- */
-void P3DConditionVar::
-wait(double timeout) {
-#ifdef _WIN32
- LeaveCriticalSection(&_lock);
-
- DWORD result = WaitForSingleObject(_event_signal, (DWORD)(timeout * 1000.0));
- assert(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT);
-
- EnterCriticalSection(&_lock);
-#else // _WIN32
-
- struct timeval now;
- gettimeofday(&now, nullptr);
-
- // Convert from timeval to timespec
- struct timespec ts;
- ts.tv_sec = now.tv_sec;
- ts.tv_nsec = now.tv_usec * 1000;
-
- int seconds = (int)floor(timeout);
- ts.tv_sec += seconds;
- ts.tv_nsec += (int)((timeout - seconds) * 1000000.0);
- if (ts.tv_nsec > 1000000) {
- ts.tv_nsec -= 1000000;
- ++ts.tv_sec;
- }
-
- int result = pthread_cond_timedwait(&_cvar, &_lock, &ts);
- if (result != 0 && result != ETIMEDOUT) {
- errno = result;
- perror("pthread_cond_timedwait");
- }
-
-#endif // _WIN32
-}
-
-/**
- * Waits a single thread blocked on wait(), if any. If no threads are
- * waiting, the event is lost. The lock should be held during this call.
- */
-void P3DConditionVar::
-notify() {
-#ifdef _WIN32
- SetEvent(_event_signal);
-
-#else // _WIN32
- int result = pthread_cond_signal(&_cvar);
- assert(result == 0);
-
-#endif // _WIN32
-}
-
-/**
- * Releases the internal lock.
- */
-void P3DConditionVar::
-release() {
-#ifdef _WIN32
- LeaveCriticalSection(&_lock);
-
-#else // _WIN32
- int result = pthread_mutex_unlock(&_lock);
- assert(result == 0);
-
-#endif // _WIN32
-}
diff --git a/direct/src/plugin/p3dConditionVar.h b/direct/src/plugin/p3dConditionVar.h
deleted file mode 100644
index f4de8f549d..0000000000
--- a/direct/src/plugin/p3dConditionVar.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dConditionVar.h
- * @author drose
- * @date 2009-07-02
- */
-
-#ifndef P3DCONDITIONVAR_H
-#define P3DCONDITIONVAR_H
-
-#include "p3d_plugin_common.h"
-
-/**
- * A simple condition-variable like object. It doesn't support the full
- * condition-var semantics, but it works well enough with one waiter and one
- * signaller.
- */
-class P3DConditionVar {
-public:
- P3DConditionVar();
- ~P3DConditionVar();
-
- void acquire();
- void wait();
- void wait(double timeout);
- void notify();
- void release();
-
-private:
-#ifdef _WIN32
- CRITICAL_SECTION _lock;
- HANDLE _event_signal;
-
-#else // _WIN32
- pthread_mutex_t _lock;
- pthread_cond_t _cvar;
-#endif // _WIN32
-};
-
-#include "p3dConditionVar.I"
-
-#endif
diff --git a/direct/src/plugin/p3dDownload.I b/direct/src/plugin/p3dDownload.I
deleted file mode 100644
index c248948612..0000000000
--- a/direct/src/plugin/p3dDownload.I
+++ /dev/null
@@ -1,126 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dDownload.I
- * @author drose
- * @date 2009-06-11
- */
-
-/**
- * Returns the URL that we are querying.
- */
-const std::string &P3DDownload::
-get_url() const {
- return _url;
-}
-
-/**
- * Specifies the particular P3DInstance that is responsible for downloading
- * this object.
- */
-inline void P3DDownload::
-set_instance(P3DInstance *instance) {
- _instance = instance;
-}
-
-/**
- * Returns the particular P3DInstance that is responsible for downloading this
- * object.
- */
-inline P3DInstance *P3DDownload::
-get_instance() const {
- return _instance;
-}
-
-/**
- * Returns an indication of the progress through the download file, 0.0 to
- * 1.0. Returns 1.0 if the size of the file is not known.
- */
-inline double P3DDownload::
-get_download_progress() const {
- if (_total_expected_data == 0) {
- return 1.0;
- }
-
- return (double)_total_data / (double)_total_expected_data;
-}
-
-/**
- * Returns true if the download progress is known, or false if it is unknown
- * because the server hasn't told us the total size it will be feeding us. If
- * this is false, get_download_progress() will generally always return 1.0;
- * use get_total_bytes() to measure progress in this case.
- */
-inline bool P3DDownload::
-is_download_progress_known() const {
- return _progress_known;
-}
-
-/**
- * Returns the total number of bytes downloaded so far.
- */
-inline size_t P3DDownload::
-get_total_data() const {
- return _total_data;
-}
-
-/**
- * Sets the total number of bytes expected to be downloaded. This is used to
- * compute the progress. Normally, this can be set from the download server,
- * but there may be cases when the download server doesn't accurately report
- * it.
- */
-inline void P3DDownload::
-set_total_expected_data(size_t expected_data) {
- _total_expected_data = expected_data;
- _progress_known = true;
-}
-
-/**
- * Returns true if the download has finished, either successfully or
- * otherwise, or false if it is still in progress.
- */
-inline bool P3DDownload::
-get_download_finished() const {
- return _status != P3D_RC_in_progress;
-}
-
-/**
- * Returns true if the download has finished successfully, or false if it is
- * still in progress or if it has failed.
- */
-inline bool P3DDownload::
-get_download_success() const {
- return _status == P3D_RC_done;
-}
-
-/**
- * Returns true if the download has failed because the instance is about to be
- * shut down, or false if it hasn't failed, or failed for some other reason.
- */
-inline bool P3DDownload::
-get_download_terminated() const {
- return _status == P3D_RC_shutdown;
-}
-
-/**
- * Called only by P3DInstance to set a unique ID for this particular download
- * object.
- */
-inline void P3DDownload::
-set_download_id(int download_id) {
- _download_id = download_id;
-}
-
-/**
- * Returns the unique ID set by the P3DInstance.
- */
-inline int P3DDownload::
-get_download_id() const {
- return _download_id;
-}
diff --git a/direct/src/plugin/p3dDownload.cxx b/direct/src/plugin/p3dDownload.cxx
deleted file mode 100644
index 3b6a6cca7a..0000000000
--- a/direct/src/plugin/p3dDownload.cxx
+++ /dev/null
@@ -1,192 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dDownload.cxx
- * @author drose
- * @date 2009-06-11
- */
-
-#include "p3dDownload.h"
-
-/**
- *
- */
-P3DDownload::
-P3DDownload() {
- _status = P3D_RC_in_progress;
- _http_status_code = 0;
- _total_data = 0;
- _total_expected_data = 0;
- _last_reported_time = 0;
- _progress_known = false;
-
- _canceled = false;
- _download_id = 0;
- _instance = nullptr;
-}
-
-/**
- *
- */
-P3DDownload::
-P3DDownload(const P3DDownload ©) :
- _url(copy._url),
- _total_expected_data(copy._total_expected_data),
- _progress_known(copy._progress_known)
-{
- _status = P3D_RC_in_progress;
- _http_status_code = 0;
- _total_data = 0;
- _last_reported_time = 0;
-
- _canceled = false;
- _download_id = 0;
- _instance = nullptr;
-}
-
-/**
- *
- */
-P3DDownload::
-~P3DDownload() {
-}
-
-/**
- * Supplies the source URL for the download.
- */
-void P3DDownload::
-set_url(const std::string &url) {
- _url = url;
-}
-
-/**
- * Cancels a running download. download_finished() will not be called, but
- * the P3DDownload object itself will eventually be deleted by its owning
- * P3DInstance.
- */
-void P3DDownload::
-cancel() {
- _canceled = true;
- if (_status == P3D_RC_in_progress) {
- _status = P3D_RC_generic_error;
- }
-}
-
-/**
- * Resets the download to its initial state, for re-trying the same download.
- */
-void P3DDownload::
-clear() {
- _status = P3D_RC_in_progress;
- _http_status_code = 0;
- _total_data = 0;
- _last_reported_time = 0;
-
- _canceled = false;
- _download_id = 0;
-}
-
-/**
- * Called by P3DInstance as more data arrives from the host. Returns true on
- * success, false if the download should be aborted.
- */
-bool P3DDownload::
-feed_url_stream(P3D_result_code result_code,
- int http_status_code,
- size_t total_expected_data,
- const unsigned char *this_data,
- size_t this_data_size) {
- if (_canceled) {
- return false;
- }
-
- _status = result_code;
- _http_status_code = http_status_code;
-
- if (this_data_size != 0) {
- if (!receive_data(this_data, this_data_size)) {
- // Failure writing to disk or some such.
- _status = P3D_RC_generic_error;
- download_finished(false);
- return false;
- }
-
- _total_data += this_data_size;
- }
-
- total_expected_data = std::max(total_expected_data, _total_data);
- if (total_expected_data > _total_expected_data) {
- // If the expected data grows during the download, we don't really know
- // how much we're getting.
- _progress_known = false;
- _total_expected_data = total_expected_data;
- }
- if (_total_expected_data > 0 &&
- (double)_total_data / (double)_total_expected_data < 0.9) {
- // But if we're not close to our target yet, let's say we do know (at
- // least until we get there and the target moves again).
- _progress_known = true;
- }
-
- download_progress();
-
- if (_status != P3D_RC_in_progress) {
- // We're done.
- if (get_download_success()) {
- _progress_known = true;
- }
- download_finished(get_download_success());
- }
-
- return true;
-}
-
-
-/**
- * Called as new data is downloaded. Returns true on success, false on
- * failure.
- */
-bool P3DDownload::
-receive_data(const unsigned char *this_data, size_t this_data_size) {
- return false;
-}
-
-/**
- * Intended to be overloaded to generate an occasional callback as new data
- * comes in.
- */
-void P3DDownload::
-download_progress() {
- time_t now = time(nullptr);
- if (now - _last_reported_time > 10) {
- _last_reported_time = now;
- nout << "Downloading " << get_url() << ": ";
- if (_progress_known) {
- nout << int(get_download_progress() * 1000.0) / 10.0 << "%";
- } else {
- nout << int((double)get_total_data() / 104857.6) / 10.0 << "M";
- }
- nout << ", " << this << "\n";
- }
-}
-
-/**
- * Intended to be overloaded to generate a callback when the download
- * finishes, either successfully or otherwise. The bool parameter is true if
- * the download was successful.
- */
-void P3DDownload::
-download_finished(bool success) {
- nout << "Downloaded " << get_url() << ": ";
- if (_progress_known) {
- nout << int(get_download_progress() * 1000.0) / 10.0 << "%";
- } else {
- nout << int((double)get_total_data() / 104857.6) / 10.0 << "M";
- }
- nout << ", " << this << ", success = " << success << "\n";
-}
diff --git a/direct/src/plugin/p3dDownload.h b/direct/src/plugin/p3dDownload.h
deleted file mode 100644
index ad2cd872a5..0000000000
--- a/direct/src/plugin/p3dDownload.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dDownload.h
- * @author drose
- * @date 2009-06-11
- */
-
-#ifndef P3DDOWNLOAD_H
-#define P3DDOWNLOAD_H
-
-#include "p3d_plugin_common.h"
-#include "p3dReferenceCount.h"
-class P3DInstance;
-
-#include
-
-/**
- * This represents a request to download a single file from a URL, with no
- * particular destination. It is intended to be used as an abstract base
- * class; to use it, subclass it and redefine the appropriate callback
- * methods.
- */
-class P3DDownload : public P3DReferenceCount {
-public:
- P3DDownload();
- P3DDownload(const P3DDownload ©);
- virtual ~P3DDownload();
-
- void set_url(const std::string &url);
- inline const std::string &get_url() const;
-
- inline void set_instance(P3DInstance *instance);
- inline P3DInstance *get_instance() const;
-
- inline double get_download_progress() const;
- inline bool is_download_progress_known() const;
- inline bool get_download_finished() const;
- inline bool get_download_success() const;
- inline bool get_download_terminated() const;
- inline size_t get_total_data() const;
- inline void set_total_expected_data(size_t expected_data);
-
- void cancel();
- void clear();
-
-public:
- // These are intended to be called only by P3DInstance.
- inline void set_download_id(int download_id);
- inline int get_download_id() const;
-
- bool feed_url_stream(P3D_result_code result_code,
- int http_status_code,
- size_t total_expected_data,
- const unsigned char *this_data,
- size_t this_data_size);
-
-protected:
- virtual bool receive_data(const unsigned char *this_data,
- size_t this_data_size);
- virtual void download_progress();
- virtual void download_finished(bool success);
-
-protected:
- P3D_result_code _status;
- int _http_status_code;
-
- size_t _total_data;
- size_t _total_expected_data;
- time_t _last_reported_time;
- bool _progress_known;
-
-private:
- bool _canceled;
- int _download_id;
- std::string _url;
- P3DInstance *_instance;
-};
-
-#include "p3dDownload.I"
-
-#endif
diff --git a/direct/src/plugin/p3dFileDownload.I b/direct/src/plugin/p3dFileDownload.I
deleted file mode 100644
index af4e2b1ec8..0000000000
--- a/direct/src/plugin/p3dFileDownload.I
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileDownload.I
- * @author drose
- * @date 2009-06-11
- */
-
-/**
- * Returns the filename that we are downloading into.
- */
-const std::string &P3DFileDownload::
-get_filename() const {
- return _filename;
-}
diff --git a/direct/src/plugin/p3dFileDownload.cxx b/direct/src/plugin/p3dFileDownload.cxx
deleted file mode 100644
index a978c3640e..0000000000
--- a/direct/src/plugin/p3dFileDownload.cxx
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileDownload.cxx
- * @author drose
- * @date 2009-06-11
- */
-
-#include "p3dFileDownload.h"
-#include "p3dInstanceManager.h"
-#include "mkdir_complete.h"
-#include "wstring_encode.h"
-
-/**
- *
- */
-P3DFileDownload::
-P3DFileDownload() {
-}
-
-/**
- *
- */
-P3DFileDownload::
-P3DFileDownload(const P3DFileDownload ©) :
- P3DDownload(copy)
-{
- // We don't copy the filename. You have to copy it yourself.
-}
-
-/**
- * Supplies the target local filename for the download. Returns true on
- * success, false on failure.
- */
-bool P3DFileDownload::
-set_filename(const std::string &filename) {
- _filename = filename;
-
- return open_file();
-}
-
-/**
- * Opens the local file for receiving the download. Returns true on success,
- * false on failure.
- */
-bool P3DFileDownload::
-open_file() {
- if (!mkfile_complete(_filename, nout)) {
- nout << "Failed to create " << _filename << "\n";
- return false;
- }
-
- _file.clear();
-#ifdef _WIN32
- std::wstring filename_w;
- if (string_to_wstring(filename_w, _filename)) {
- _file.open(filename_w.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
- }
-#else // _WIN32
- _file.open(_filename.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
-#endif // _WIN32
- if (!_file) {
- nout << "Failed to open " << _filename << " in write mode\n";
- return false;
- }
-
- return true;
-}
-
-/**
- * Closes the local file.
- */
-void P3DFileDownload::
-close_file() {
- _file.close();
-}
-
-/**
- * Called as new data is downloaded. Returns true on success, false on
- * failure.
- */
-bool P3DFileDownload::
-receive_data(const unsigned char *this_data, size_t this_data_size) {
- _file.write((const char *)this_data, this_data_size);
-
- if (!_file) {
- return false;
- }
-
- return true;
-}
-
-/**
- * Intended to be overloaded to generate a callback when the download
- * finishes, either successfully or otherwise. The bool parameter is true if
- * the download was successful.
- */
-void P3DFileDownload::
-download_finished(bool success) {
- P3DDownload::download_finished(success);
- _file.close();
-}
diff --git a/direct/src/plugin/p3dFileDownload.h b/direct/src/plugin/p3dFileDownload.h
deleted file mode 100644
index 923f8ec0ec..0000000000
--- a/direct/src/plugin/p3dFileDownload.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileDownload.h
- * @author drose
- * @date 2009-06-11
- */
-
-#ifndef P3DFILEDOWNLOAD_H
-#define P3DFILEDOWNLOAD_H
-
-#include "p3d_plugin_common.h"
-#include "p3dDownload.h"
-
-#include
-
-/**
- * This is a specialization on P3DDownload that specifically writes a disk
- * file.
- */
-class P3DFileDownload : public P3DDownload {
-public:
- P3DFileDownload();
- P3DFileDownload(const P3DFileDownload ©);
-
- bool set_filename(const std::string &filename);
- inline const std::string &get_filename() const;
-
-protected:
- virtual bool open_file();
- void close_file();
- virtual bool receive_data(const unsigned char *this_data,
- size_t this_data_size);
- virtual void download_finished(bool success);
-
-protected:
- ofstream _file;
-
-private:
- std::string _filename;
-};
-
-#include "p3dFileDownload.I"
-
-#endif
diff --git a/direct/src/plugin/p3dFileParams.I b/direct/src/plugin/p3dFileParams.I
deleted file mode 100644
index 8565c47a90..0000000000
--- a/direct/src/plugin/p3dFileParams.I
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileParams.I
- * @author drose
- * @date 2009-06-23
- */
-
-/**
- * Returns the filename that was passed to set_p3d_filename().
- */
-inline const std::string &P3DFileParams::
-get_p3d_filename() const {
- return _p3d_filename;
-}
-
-/**
- * Returns the p3d file offset, the location in the file where the p3d data
- * starts.
- */
-inline int P3DFileParams::
-get_p3d_offset() const {
- return _p3d_offset;
-}
-
-/**
- * Returns the string that was passed to set_p3d_url().
- */
-inline const std::string &P3DFileParams::
-get_p3d_url() const {
- return _p3d_url;
-}
-
-/**
- * Returns the number of tokens in the params.
- */
-inline int P3DFileParams::
-get_num_tokens() const {
- return _tokens.size();
-}
-
-/**
- * Returns the keyword of the nth token.
- */
-inline const std::string &P3DFileParams::
-get_token_keyword(int n) const {
- assert(n >= 0 && n < (int)_tokens.size());
- return _tokens[n]._keyword;
-}
-
-/**
- * Returns the value of the nth token.
- */
-inline const std::string &P3DFileParams::
-get_token_value(int n) const {
- assert(n >= 0 && n < (int)_tokens.size());
- return _tokens[n]._value;
-}
diff --git a/direct/src/plugin/p3dFileParams.cxx b/direct/src/plugin/p3dFileParams.cxx
deleted file mode 100644
index a32ede4b95..0000000000
--- a/direct/src/plugin/p3dFileParams.cxx
+++ /dev/null
@@ -1,193 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileParams.cxx
- * @author drose
- * @date 2009-06-23
- */
-
-#include "p3dFileParams.h"
-#include
-
-using std::string;
-
-/**
- *
- */
-P3DFileParams::
-P3DFileParams() : _p3d_offset(0) {
-}
-
-/**
- *
- */
-P3DFileParams::
-P3DFileParams(const P3DFileParams ©) :
- _p3d_filename(copy._p3d_filename),
- _p3d_offset(copy._p3d_offset),
- _p3d_url(copy._p3d_url),
- _tokens(copy._tokens),
- _args(copy._args)
-{
-}
-
-/**
- *
- */
-void P3DFileParams::
-operator = (const P3DFileParams &other) {
- _p3d_filename = other._p3d_filename;
- _p3d_offset = other._p3d_offset;
- _p3d_url = other._p3d_url;
- _tokens = other._tokens;
- _args = other._args;
-}
-
-/**
- * Specifies the file that contains the instance data.
- */
-void P3DFileParams::
-set_p3d_filename(const string &p3d_filename) {
- _p3d_filename = p3d_filename;
-}
-
-/**
- * Specifies the location in the file where the p3d file data starts.
- */
-void P3DFileParams::
-set_p3d_offset(const int &p3d_offset) {
- _p3d_offset = p3d_offset;
-}
-
-/**
- * Specifies the original URL that hosted the p3d file, if any. This is for
- * documentation purposes only; it is communicated to the child Panda3D
- * process.
- */
-void P3DFileParams::
-set_p3d_url(const string &p3d_url) {
- _p3d_url = p3d_url;
-}
-
-/**
- * Replaces all the tokens associated with the instance.
- */
-void P3DFileParams::
-set_tokens(const P3D_token tokens[], size_t num_tokens) {
- _tokens.clear();
-
- for (size_t i = 0; i < num_tokens; ++i) {
- set_token(tokens[i]._keyword, tokens[i]._value);
- }
-}
-
-/**
- * Sets an individual token value.
- */
-void P3DFileParams::
-set_token(const char *keyword, const char *value) {
- Token token;
- if (keyword != nullptr) {
- // Make the token lowercase, since HTML is case-insensitive but we're not.
- for (const char *p = keyword; *p; ++p) {
- token._keyword += tolower(*p);
- }
- }
- if (value != nullptr) {
- token._value = value;
- }
- _tokens.push_back(token);
-}
-
-/**
- * Specifies the command-line arguments associated with the instance.
- */
-void P3DFileParams::
-set_args(int argc, const char *argv[]) {
- _args.clear();
-
- for (int i = 0; i < argc; ++i) {
- const char *arg = argv[i];
- if (arg == nullptr) {
- arg = "";
- }
- _args.push_back(arg);
- }
-}
-
-/**
- * Returns the value associated with the first appearance of the named token,
- * or empty string if the token does not appear.
- */
-string P3DFileParams::
-lookup_token(const string &keyword) const {
- Tokens::const_iterator ti;
- for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
- if ((*ti)._keyword == keyword) {
- return (*ti)._value;
- }
- }
-
- return string();
-}
-
-/**
- * Returns the integer value associated with the first appearance of the named
- * token, or zero if the token does not appear or is not an integer.
- */
-int P3DFileParams::
-lookup_token_int(const string &keyword) const {
- string value = lookup_token(keyword);
- return atoi(value.c_str());
-}
-
-/**
- * Returns true if the named token appears in the list, false otherwise.
- */
-bool P3DFileParams::
-has_token(const string &keyword) const {
- Tokens::const_iterator ti;
- for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
- if ((*ti)._keyword == keyword) {
- return true;
- }
- }
-
- return false;
-}
-
-/**
- * Returns a newly-allocated XML structure that corresponds to the file
- * parameter data within this instance.
- */
-TiXmlElement *P3DFileParams::
-make_xml() {
- TiXmlElement *xfparams = new TiXmlElement("fparams");
-
- xfparams->SetAttribute("p3d_filename", _p3d_filename);
- xfparams->SetAttribute("p3d_offset", _p3d_offset);
- xfparams->SetAttribute("p3d_url", _p3d_url);
-
- Tokens::const_iterator ti;
- for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
- const Token &token = (*ti);
- TiXmlElement *xtoken = new TiXmlElement("token");
- xtoken->SetAttribute("keyword", token._keyword);
- xtoken->SetAttribute("value", token._value);
- xfparams->LinkEndChild(xtoken);
- }
-
- Args::const_iterator ai;
- for (ai = _args.begin(); ai != _args.end(); ++ai) {
- TiXmlElement *xarg = new TiXmlElement("arg");
- xarg->SetAttribute("value", (*ai));
- xfparams->LinkEndChild(xarg);
- }
-
- return xfparams;
-}
diff --git a/direct/src/plugin/p3dFileParams.h b/direct/src/plugin/p3dFileParams.h
deleted file mode 100644
index 53b2630680..0000000000
--- a/direct/src/plugin/p3dFileParams.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFileParams.h
- * @author drose
- * @date 2009-06-23
- */
-
-#ifndef P3DFILEPARAMS_H
-#define P3DFILEPARAMS_H
-
-#include "p3d_plugin_common.h"
-#include "get_tinyxml.h"
-#include
-
-/**
- * Encapsulates the file parameters: the p3d_filename, and extra tokens.
- */
-class P3DFileParams {
-public:
- P3DFileParams();
- P3DFileParams(const P3DFileParams ©);
- void operator = (const P3DFileParams &other);
-
- void set_p3d_filename(const std::string &p3d_filename);
- void set_p3d_offset(const int &p3d_offset);
- void set_p3d_url(const std::string &p3d_url);
- void set_tokens(const P3D_token tokens[], size_t num_tokens);
- void set_token(const char *keyword, const char *value);
- void set_args(int argc, const char *argv[]);
-
- inline const std::string &get_p3d_filename() const;
- inline int get_p3d_offset() const;
- inline const std::string &get_p3d_url() const;
- std::string lookup_token(const std::string &keyword) const;
- int lookup_token_int(const std::string &keyword) const;
- bool has_token(const std::string &keyword) const;
-
- inline int get_num_tokens() const;
- inline const std::string &get_token_keyword(int n) const;
- inline const std::string &get_token_value(int n) const;
-
- TiXmlElement *make_xml();
-
-private:
- class Token {
- public:
- std::string _keyword;
- std::string _value;
- };
- typedef std::vector Tokens;
- typedef std::vector Args;
-
- std::string _p3d_filename;
- int _p3d_offset;
- std::string _p3d_url;
- Tokens _tokens;
- Args _args;
-};
-
-#include "p3dFileParams.I"
-
-#endif
diff --git a/direct/src/plugin/p3dFloatObject.cxx b/direct/src/plugin/p3dFloatObject.cxx
deleted file mode 100644
index 3262d45a54..0000000000
--- a/direct/src/plugin/p3dFloatObject.cxx
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFloatObject.cxx
- * @author drose
- * @date 2009-06-30
- */
-
-#include "p3dFloatObject.h"
-
-/**
- *
- */
-P3DFloatObject::
-P3DFloatObject(double value) : _value(value) {
-}
-
-/**
- *
- */
-P3DFloatObject::
-P3DFloatObject(const P3DFloatObject ©) :
- P3DObject(copy),
- _value(copy._value)
-{
-}
-
-/**
- * Returns the fundamental type of this kind of object.
- */
-P3D_object_type P3DFloatObject::
-get_type() {
- return P3D_OT_float;
-}
-
-/**
- * Returns the object value coerced to a boolean, if possible.
- */
-bool P3DFloatObject::
-get_bool() {
- return (_value != 0.0);
-}
-
-/**
- * Returns the object value coerced to an integer, if possible.
- */
-int P3DFloatObject::
-get_int() {
- return (int)_value;
-}
-
-/**
- * Returns the object value coerced to a floating-point value, if possible.
- */
-double P3DFloatObject::
-get_float() {
- return _value;
-}
-
-/**
- * Fills the indicated C++ string object with the value of this object coerced
- * to a string.
- */
-void P3DFloatObject::
-make_string(std::string &value) {
- std::ostringstream strm;
- strm << _value;
- value = strm.str();
-}
diff --git a/direct/src/plugin/p3dFloatObject.h b/direct/src/plugin/p3dFloatObject.h
deleted file mode 100644
index 13f7ba8358..0000000000
--- a/direct/src/plugin/p3dFloatObject.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dFloatObject.h
- * @author drose
- * @date 2009-06-30
- */
-
-#ifndef P3DFLOATOBJECT_H
-#define P3DFLOATOBJECT_H
-
-#include "p3d_plugin_common.h"
-#include "p3dObject.h"
-
-/**
- * An object type that contains a floating-point value.
- */
-class P3DFloatObject : public P3DObject {
-public:
- P3DFloatObject(double value);
- P3DFloatObject(const P3DFloatObject ©);
-
-public:
- virtual P3D_object_type get_type();
- virtual bool get_bool();
- virtual int get_int();
- virtual double get_float();
- virtual void make_string(std::string &value);
-
-private:
- double _value;
-};
-
-#endif
diff --git a/direct/src/plugin/p3dHost.I b/direct/src/plugin/p3dHost.I
deleted file mode 100644
index 9fb3672205..0000000000
--- a/direct/src/plugin/p3dHost.I
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dHost.I
- * @author drose
- * @date 2009-08-21
- */
-
-/**
- * Returns true if the host_dir has already been set, false if not. If this
- * returns true it is safe to call get_host_dir().
- */
-inline bool P3DHost::
-has_host_dir() const {
- return (!_host_dir.empty());
-}
-
-/**
- * Returns the local directory into which files downloaded from this host will
- * be installed. It may not be safe to call this before the host has fully
- * bootstrapped; if there is some danger of calling this early in the
- * initialization process, you should check has_host_dir() first.
- */
-inline const std::string &P3DHost::
-get_host_dir() const {
- assert(has_host_dir());
- return _host_dir;
-}
-
-/**
- * Returns the root URL of this particular host, as passed from the package
- * file. This is a unique string that identifies each host.
- */
-inline const std::string &P3DHost::
-get_host_url() const {
- return _host_url;
-}
-
-/**
- * Returns the root URL of this host, for constructing the URL to download
- * contents.xml only. This is the same as get_host_url(), except it is
- * guaranteed to end in a slash character.
- *
- * Also see get_download_url_prefix().
- */
-inline const std::string &P3DHost::
-get_host_url_prefix() const {
- return _host_url_prefix;
-}
-
-/**
- * Returns the root URL of this host, for downloading everything other than
- * the contents.xml file. This is often the same as get_host_url_prefix(),
- * but it may be different in the case of an https server for contents.xml.
- */
-inline const std::string &P3DHost::
-get_download_url_prefix() const {
- return _download_url_prefix;
-}
-
-/**
- * Returns the descriptive name provided for this host, if any. Returns the
- * url if no descriptive name is provided. This will be available after
- * read_contents_file() has been called.
- */
-inline const std::string &P3DHost::
-get_descriptive_name() const {
- return _descriptive_name;
-}
-
-/**
- * Returns true if a contents.xml file has been successfully read for this
- * host, false otherwise.
- */
-inline bool P3DHost::
-has_contents_file() const {
- return (_xcontents != nullptr);
-}
-
-/**
- * Returns a number that increments whenever a new version of the contents.xml
- * file has been read. This number is local to the session only; it has
- * nothing to do with the "seq" value written into the contents.xml file
- * itself.
- *
- * This can be used by packages to determine whether they need to redownload
- * from scratch.
- */
-inline int P3DHost::
-get_contents_iseq() const {
- return _contents_iseq;
-}
-
-/**
- * Returns true if the indicated pathname has the same md5 hash as the
- * contents.xml file (as provided by the server), false otherwise.
- */
-inline bool P3DHost::
-check_contents_hash(const std::string &pathname) const {
- return _contents_spec.check_hash(pathname);
-}
diff --git a/direct/src/plugin/p3dHost.cxx b/direct/src/plugin/p3dHost.cxx
deleted file mode 100644
index db189e14c4..0000000000
--- a/direct/src/plugin/p3dHost.cxx
+++ /dev/null
@@ -1,1056 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dHost.cxx
- * @author drose
- * @date 2009-08-21
- */
-
-#include "p3dHost.h"
-#include "p3dInstanceManager.h"
-#include "p3dPackage.h"
-#include "mkdir_complete.h"
-#include "wstring_encode.h"
-#include "xml_helpers.h"
-#include
-
-#include
-
-#ifndef _WIN32
-#include
-#endif
-
-using std::ios;
-using std::ostringstream;
-using std::string;
-using std::wstring;
-
-/**
- * Use P3DInstanceManager::get_host() to construct a new P3DHost.
- */
-P3DHost::
-P3DHost(const string &host_url, const string &host_dir) :
- _host_url(host_url),
- _host_dir(host_dir)
-{
- // Ensure that the download URL ends with a slash.
- _host_url_prefix = _host_url;
- if (!_host_url_prefix.empty() && _host_url_prefix[_host_url_prefix.size() - 1] != '/') {
- _host_url_prefix += "/";
- }
- _download_url_prefix = _host_url_prefix;
-
- _descriptive_name = _host_url;
-
- _xcontents = nullptr;
- _contents_expiration = 0;
- _contents_iseq = 0;
-}
-
-/**
- *
- */
-P3DHost::
-~P3DHost() {
- if (_xcontents != nullptr) {
- delete _xcontents;
- }
-
- Packages::iterator mi;
- for (mi = _packages.begin(); mi != _packages.end(); ++mi) {
- PackageMap &package_map = (*mi).second;
- PackageMap::iterator pi;
- for (pi = package_map.begin(); pi != package_map.end(); ++pi) {
- PlatformPackages &ppackages = (*pi).second;
- PlatformPackages::iterator ppi;
- for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) {
- delete (*ppi);
- }
- }
- }
- _packages.clear();
-
- FailedPackages::iterator pi;
- for (pi = _failed_packages.begin(); pi != _failed_packages.end(); ++pi) {
- delete (*pi);
- }
- _failed_packages.clear();
-}
-
-/**
- * Returns the pre-defined alternate host with the indicated token, if one is
- * defined for this token, or the original host if there is no alternate host
- * defined for this token.
- *
- * This is intended to implement test versions and the like, for instance in
- * which a particular p3d file may reference a package on one particular host,
- * but there is an alternate version to be tested on a different host. The
- * HTML code that embeds the p3d file can choose to set the alt_host token to
- * redirect the p3d file to the alternate host.
- *
- * The actual URL for the alternate host is embedded within the host's
- * contents.xml as a security measure, to prevent people from tricking a p3d
- * file into running untrusted code by redirecting it to an arbitrary URL.
- */
-P3DHost *P3DHost::
-get_alt_host(const string &alt_host) {
- assert(_xcontents != nullptr);
-
- AltHosts::iterator hi;
- hi = _alt_hosts.find(alt_host);
- if (hi != _alt_hosts.end() && (*hi).second != _host_url) {
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- return inst_mgr->get_host((*hi).second);
- }
- return this;
-}
-
-/**
- * Returns true if a contents.xml file has been successfully read for this
- * host and is still current, false otherwise.
- */
-bool P3DHost::
-has_current_contents_file(P3DInstanceManager *inst_mgr) const {
- if (inst_mgr->get_verify_contents() == P3D_VC_never
- || inst_mgr->get_verify_contents() == P3D_VC_none) {
- // If we're not asking to verify contents, then contents.xml files never
- // expire.
- return has_contents_file();
- }
-
- time_t now = time(nullptr);
- return now < _contents_expiration && (_xcontents != nullptr);
-}
-
-/**
- * Reads the contents.xml file in the standard filename, if possible.
- *
- * Returns true on success, false on failure.
- */
-bool P3DHost::
-read_contents_file() {
- if (_host_dir.empty()) {
- // If we haven't got a host_dir yet, we can't read the contents.
- return false;
- }
-
- string standard_filename = _host_dir + "/contents.xml";
- return read_contents_file(standard_filename, false);
-}
-
-/**
- * Reads the contents.xml file in the indicated filename. On success, writes
- * the contents.xml file into the standard location (if it's not there
- * already).
- *
- * Returns true on success, false on failure.
- */
-bool P3DHost::
-read_contents_file(const string &contents_filename, bool fresh_download) {
- TiXmlDocument doc(contents_filename.c_str());
- if (!doc.LoadFile()) {
- return false;
- }
-
- TiXmlElement *xcontents = doc.FirstChildElement("contents");
- if (xcontents == nullptr) {
- return false;
- }
-
- if (_xcontents != nullptr) {
- delete _xcontents;
- }
- _xcontents = (TiXmlElement *)xcontents->Clone();
- ++_contents_iseq;
- _contents_spec = FileSpec();
-
- int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE;
- xcontents->Attribute("max_age", &max_age);
-
- // Get the latest possible expiration time, based on the max_age indication.
- // Any expiration time later than this is in error.
- time_t now = time(nullptr);
- _contents_expiration = now + (time_t)max_age;
-
- if (fresh_download) {
- _contents_spec.read_hash(contents_filename);
-
- // Update the XML with the new download information.
- TiXmlElement *xorig = xcontents->FirstChildElement("orig");
- while (xorig != nullptr) {
- xcontents->RemoveChild(xorig);
- xorig = xcontents->FirstChildElement("orig");
- }
-
- xorig = new TiXmlElement("orig");
- xcontents->LinkEndChild(xorig);
- _contents_spec.store_xml(xorig);
-
- xorig->SetAttribute("expiration", (int)_contents_expiration);
-
- } else {
- // Read the download hash and expiration time from the XML.
- int expiration = 0;
- TiXmlElement *xorig = xcontents->FirstChildElement("orig");
- if (xorig != nullptr) {
- _contents_spec.load_xml(xorig);
- xorig->Attribute("expiration", &expiration);
- }
- if (!_contents_spec.has_hash()) {
- _contents_spec.read_hash(contents_filename);
- }
-
- _contents_expiration = std::min(_contents_expiration, (time_t)expiration);
- }
-
- nout << "read contents.xml, max_age = " << max_age
- << ", expires in " << std::max(_contents_expiration, now) - now
- << " s\n";
-
- TiXmlElement *xhost = _xcontents->FirstChildElement("host");
- if (xhost != nullptr) {
- const char *url = xhost->Attribute("url");
- if (url != nullptr && _host_url == string(url)) {
- // We're the primary host. This is the normal case.
- read_xhost(xhost);
-
- // Build up the list of alternate hosts.
- TiXmlElement *xalthost = xhost->FirstChildElement("alt_host");
- while (xalthost != nullptr) {
- const char *keyword = xalthost->Attribute("keyword");
- const char *url = xalthost->Attribute("url");
- if (keyword != nullptr && url != nullptr) {
- _alt_hosts[keyword] = url;
- }
- xalthost = xalthost->NextSiblingElement("alt_host");
- }
-
- } else {
- // We're not the primary host; perhaps we're an alternate host.
- TiXmlElement *xalthost = xhost->FirstChildElement("alt_host");
- while (xalthost != nullptr) {
- const char *url = xalthost->Attribute("url");
- if (url != nullptr && _host_url == string(url)) {
- // Yep, we're this alternate host.
- read_xhost(xalthost);
- break;
- }
- xalthost = xalthost->NextSiblingElement("alt_host");
- }
- }
- }
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
- if (_host_dir.empty()) {
- determine_host_dir("");
- }
- assert(!_host_dir.empty());
-
- string standard_filename = _host_dir + "/contents.xml";
-
- if (inst_mgr->get_verify_contents() != P3D_VC_never) {
- mkdir_complete(_host_dir, nout);
-
- if (fresh_download) {
- if (!save_xml_file(&doc, standard_filename)) {
- nout << "Couldn't save to " << standard_filename << "\n";
- }
- } else {
- if (standardize_filename(standard_filename) !=
- standardize_filename(contents_filename)) {
- if (!copy_file(contents_filename, standard_filename)) {
- nout << "Couldn't copy to " << standard_filename << "\n";
- }
- }
- }
-
- if (_host_url == inst_mgr->get_host_url()) {
- // If this is also the plugin host, then copy the contents.xml file into
- // the root Panda directory as well, for the next plugin iteration.
- string top_filename = inst_mgr->get_root_dir() + "/contents.xml";
- if (standardize_filename(top_filename) !=
- standardize_filename(standard_filename)) {
- if (!copy_file(standard_filename, top_filename)) {
- nout << "Couldn't copy to " << top_filename << "\n";
- }
- }
- }
- }
-
- return true;
-}
-
-/**
- * Reads the host data from the (or ) entry in the
- * contents.xml file, or from a p3d_info.xml file.
- */
-void P3DHost::
-read_xhost(TiXmlElement *xhost) {
- const char *descriptive_name = xhost->Attribute("descriptive_name");
- if (descriptive_name != nullptr && _descriptive_name.empty()) {
- _descriptive_name = descriptive_name;
- }
-
- const char *host_dir_basename = xhost->Attribute("host_dir");
- if (host_dir_basename == nullptr) {
- host_dir_basename = "";
- }
- if (_host_dir.empty()) {
- determine_host_dir(host_dir_basename);
- }
-
- // Get the "download" URL, which is the source from which we download
- // everything other than the contents.xml file.
- const char *download_url = xhost->Attribute("download_url");
- if (download_url != nullptr) {
- _download_url_prefix = download_url;
- }
- if (!_download_url_prefix.empty()) {
- if (_download_url_prefix[_download_url_prefix.size() - 1] != '/') {
- _download_url_prefix += "/";
- }
- } else {
- _download_url_prefix = _host_url_prefix;
- }
-
- TiXmlElement *xmirror = xhost->FirstChildElement("mirror");
- while (xmirror != nullptr) {
- const char *url = xmirror->Attribute("url");
- if (url != nullptr) {
- add_mirror(url);
- }
- xmirror = xmirror->NextSiblingElement("mirror");
- }
-}
-
-/**
- * Returns a (possibly shared) pointer to the indicated package.
- *
- * The package_seq value should be the expected minimum package_seq value for
- * the indicated package. If the given seq value is higher than the
- * package_seq value in the contents.xml file cached for the host, it is a
- * sign that the contents.xml file is out of date and needs to be
- * redownloaded.
- */
-P3DPackage *P3DHost::
-get_package(const string &package_name, const string &package_version,
- const string &package_platform, const string &package_seq,
- const string &alt_host) {
- if (!alt_host.empty()) {
- if (_xcontents != nullptr) {
- // If we're asking for an alt host and we've already read our
- // contents.xml file, then we already know all of our hosts, and we can
- // start the package off with the correct host immediately.
- P3DHost *new_host = get_alt_host(alt_host);
- return new_host->get_package(package_name, package_version,
- package_platform, package_seq);
- }
-
- // If we haven't read contents.xml yet, we need to create the package
- // first, then let it be responsible for downloading our contents.xml, and
- // it can migrate to its alt_host after that.
- }
-
- string key = package_name + "_" + package_version;
- PlatformPackages &ppackages = _packages[alt_host][key];
- PlatformPackages::iterator ppi;
- P3DPackage *package = nullptr;
-
- // First, look for an exact match of the platform.
- for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) {
- if ((*ppi)->get_package_platform() == package_platform) {
- package = *ppi;
- break;
- }
- }
-
- // If an exact match isn't found, look for a generic platform.
- if (package == nullptr) {
- for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) {
- if ((*ppi)->get_package_platform().empty()) {
- package = *ppi;
- break;
- }
- }
- }
-
- if (package != nullptr) {
- if (package->get_failed()) {
- // If the package has previously failed, move it aside and try again
- // (maybe it just failed because the user interrupted it).
- nout << "Package " << key << " has previously failed; trying again.\n";
- _failed_packages.push_back(package);
- ppackages.erase(ppi);
- package = nullptr;
- }
- }
-
- if (package == nullptr) {
- package =
- new P3DPackage(this, package_name, package_version, package_platform, alt_host);
- ppackages.push_back(package);
- }
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- if (!package_seq.empty() && has_current_contents_file(inst_mgr)) {
- // If we were given a specific package_seq file to verify, and we believe
- // we have a valid contents.xml file, then check the seq value in the
- // contents.
- FileSpec desc_file;
- string seq;
- bool solo;
- if (get_package_desc_file(desc_file, seq, solo,
- package_name, package_version, package_platform)) {
- nout << package_name << ": asked for seq " << package_seq
- << ", we have seq " << seq << "\n";
- if (compare_seq(package_seq, seq) > 0) {
- // The requested seq value is higher than the one we have on file; our
- // contents.xml file must be out of date after all.
- nout << "expiring contents.xml for " << get_host_url() << "\n";
- _contents_expiration = 0;
- }
- }
- }
-
- return package;
-}
-
-/**
- * Chooses the most appropriate platform for the indicated package based on
- * what this hardware supports and what is actually available. Also fills in
- * per_platform, which is a boolean value indicating whether the directory
- * structure contains the platform directory or not.
- */
-bool P3DHost::
-choose_suitable_platform(string &selected_platform,
- bool &per_platform,
- const string &package_name,
- const string &package_version,
- const string &package_platform) {
- if (_xcontents == nullptr) {
- return false;
- }
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
- TiXmlElement *xpackage;
-
- // If the platform is initially unspecified, we allow searching for any
- // available supported platform.
- if (package_platform == "") {
- int num_supported_platforms = inst_mgr->get_num_supported_platforms();
- for (int pi = 0; pi < num_supported_platforms; ++pi) {
- string supported_platform = inst_mgr->get_supported_platform(pi);
- xpackage = _xcontents->FirstChildElement("package");
- while (xpackage != nullptr) {
- const char *name = xpackage->Attribute("name");
- const char *platform = xpackage->Attribute("platform");
- const char *version = xpackage->Attribute("version");
- if (platform == nullptr) {
- platform = "";
- }
- if (version == nullptr) {
- version = "";
- }
- if (name != nullptr &&
- package_name == name &&
- supported_platform == platform &&
- package_version == version) {
- // Here's the matching package definition.
- selected_platform = platform;
- per_platform = parse_bool_attrib(xpackage, "per_platform", false);
- return true;
- }
-
- xpackage = xpackage->NextSiblingElement("package");
- }
- }
- }
-
- // Now, we look for an exact match for the expected platform.
- xpackage = _xcontents->FirstChildElement("package");
- while (xpackage != nullptr) {
- const char *name = xpackage->Attribute("name");
- const char *platform = xpackage->Attribute("platform");
- const char *version = xpackage->Attribute("version");
- if (platform == nullptr) {
- platform = "";
- }
- if (version == nullptr) {
- version = "";
- }
- if (name != nullptr &&
- package_name == name &&
- package_platform == platform &&
- package_version == version) {
- // Here's the matching package definition.
- selected_platform = platform;
- per_platform = parse_bool_attrib(xpackage, "per_platform", false);
- return true;
- }
-
- xpackage = xpackage->NextSiblingElement("package");
- }
-
- // Look one more time, this time looking for a non-platform-specific
- // version.
- xpackage = _xcontents->FirstChildElement("package");
- while (xpackage != nullptr) {
- const char *name = xpackage->Attribute("name");
- const char *platform = xpackage->Attribute("platform");
- const char *version = xpackage->Attribute("version");
- if (platform == nullptr) {
- platform = "";
- }
- if (version == nullptr) {
- version = "";
- }
- if (name != nullptr &&
- package_name == name &&
- *platform == '\0' &&
- package_version == version) {
- selected_platform = platform;
- per_platform = parse_bool_attrib(xpackage, "per_platform", false);
- return true;
- }
-
- xpackage = xpackage->NextSiblingElement("package");
- }
-
- // Couldn't find a suitable platform.
- return false;
-}
-
-/**
- * Fills the indicated FileSpec with the hash information for the package's
- * desc file, and also determines the package's platform. Returns true if
- * successful, false if the package is unknown. This requires
- * has_contents_file() to return true in order to be successful.
- */
-bool P3DHost::
-get_package_desc_file(FileSpec &desc_file, // out
- string &package_seq, // out
- bool &package_solo, // out
- const string &package_name, // in
- const string &package_version, // in
- const string &package_platform) { // in
- if (_xcontents == nullptr) {
- return false;
- }
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
- // Scan the contents data for the indicated package. We expect to match the
- // platform precisely, because we previously called
- // choose_suitable_platform().
- TiXmlElement *xpackage = _xcontents->FirstChildElement("package");
- while (xpackage != nullptr) {
- const char *name = xpackage->Attribute("name");
- const char *platform = xpackage->Attribute("platform");
- const char *version = xpackage->Attribute("version");
- const char *seq = xpackage->Attribute("seq");
- const char *solo = xpackage->Attribute("solo");
- if (platform == nullptr) {
- platform = "";
- }
- if (version == nullptr) {
- version = "";
- }
- if (seq == nullptr) {
- seq = "";
- }
- if (name != nullptr && platform != nullptr &&
- package_name == name &&
- package_platform == platform &&
- package_version == version) {
- // Here's the matching package definition.
- desc_file.load_xml(xpackage);
- package_seq = seq;
- package_solo = false;
- if (solo != nullptr) {
- package_solo = (atoi(solo) != 0);
- }
- return true;
- }
-
- xpackage = xpackage->NextSiblingElement("package");
- }
-
- // Couldn't find the named package.
- return false;
-}
-
-/**
- * Removes the indicated package from the cache of packages known by this
- * host. This is invoked from the Python side by AppRunner.deletePackages(),
- * so that we remove the package before deleting its files.
- */
-void P3DHost::
-forget_package(P3DPackage *package, const string &alt_host) {
- string key = package->get_package_name() + "_" + package->get_package_version();
- nout << "Forgetting package " << key << "\n";
-
- PlatformPackages &ppackages = _packages[alt_host][key];
-
- PlatformPackages::iterator ppi = find(ppackages.begin(), ppackages.end(), package);
- if (ppi != ppackages.end()) {
- // Hmm, this is a memory leak. But we allow it to remain, since it's an
- // unusual circumstance (uninstalling), and it's safer to leak than to
- // risk a floating pointer.
- ppackages.erase(ppi);
- }
-}
-
-/**
- * This is called by P3DPackage when it migrates from this host to its final
- * alt_host, after downloading the contents.xml file for this file and
- * learning the true URL for its target alt_host.
- */
-void P3DHost::
-migrate_package_host(P3DPackage *package, const string &alt_host, P3DHost *new_host) {
- assert(new_host != this);
- assert(new_host == get_alt_host(alt_host));
-
- string key = package->get_package_name() + "_" + package->get_package_version();
-
- PlatformPackages &ppackages = _packages[alt_host][key];
-
- PlatformPackages::iterator ppi = find(ppackages.begin(), ppackages.end(), package);
-
- assert(ppi != ppackages.end());
- ppackages.erase(ppi);
-
- PlatformPackages &new_ppackages = new_host->_packages[""][key];
- new_ppackages.push_back(package);
-}
-
-/**
- * Selects num_mirrors elements, chosen at random, from the _mirrors list.
- * Adds the selected mirrors to result. If there are fewer than num_mirrors
- * elements in the list, adds only as many mirrors as we can get.
- */
-void P3DHost::
-choose_random_mirrors(std::vector &result, int num_mirrors) {
- std::vector selected;
-
- size_t num_to_select = std::min(_mirrors.size(), (size_t)num_mirrors);
- while (num_to_select > 0) {
- size_t i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size());
- while (find(selected.begin(), selected.end(), i) != selected.end()) {
- // Already found this i, find a new one.
- i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size());
- }
- selected.push_back(i);
- result.push_back(_mirrors[i]);
- --num_to_select;
- }
-}
-
-/**
- * Adds a new URL to serve as a mirror for this host. The mirrors will be
- * consulted first, before consulting the host directly.
- */
-void P3DHost::
-add_mirror(string mirror_url) {
- // Ensure the URL ends in a slash.
- if (!mirror_url.empty() && mirror_url[mirror_url.size() - 1] != '/') {
- mirror_url += '/';
- }
-
- // Add it to the _mirrors list, but only if it's not already there.
- if (find(_mirrors.begin(), _mirrors.end(), mirror_url) == _mirrors.end()) {
- _mirrors.push_back(mirror_url);
- }
-}
-
-/**
- * Removes the host directory and all its contents from the user's hard disk.
- */
-void P3DHost::
-uninstall() {
- if (_host_dir.empty()) {
- nout << "Cannot uninstall " << _descriptive_name << ": host directory not yet known.\n";
- return;
- }
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
- // Check if we're even allowed to.
- if (inst_mgr->get_verify_contents() == P3D_VC_never) {
- nout << "Not allowed to uninstall " << _descriptive_name << ".\n";
- return;
- }
-
- // First, explicitly uninstall each of our packages.
- Packages::iterator mi;
- for (mi = _packages.begin(); mi != _packages.end(); ++mi) {
- PackageMap &package_map = (*mi).second;
- PackageMap::iterator pi;
- for (pi = package_map.begin(); pi != package_map.end(); ++pi) {
- PlatformPackages &ppackages = (*pi).second;
- PlatformPackages::iterator ppi;
- for (ppi = ppackages.begin(); ppi != ppackages.end(); ++ppi) {
- P3DPackage *package = (*ppi);
- package->uninstall();
- }
- }
- }
-
- // Then, uninstall the host itself.
- nout << "Uninstalling " << _descriptive_name << " from " << _host_dir << "\n";
- inst_mgr->delete_directory_recursively(_host_dir);
- inst_mgr->forget_host(this);
-}
-
-/**
- * Hashes the host_url into a (mostly) unique directory string, which will be
- * the root of the host's install tree. Stores the result in _host_dir.
- *
- * This code is duplicated in Python, in HostInfo.determineHostDir().
- */
-void P3DHost::
-determine_host_dir(const string &host_dir_basename) {
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- _host_dir = inst_mgr->get_root_dir();
- _host_dir += "/hosts";
-
- if (!host_dir_basename.empty()) {
- // If the contents.xml specified a host_dir parameter, use it.
- inst_mgr->append_safe_dir(_host_dir, host_dir_basename);
- return;
- }
-
- // If we didn't get a host_dir parameter, we have to make one up.
- _host_dir += "/";
- string hostname;
-
- // Look for a server name in the URL. Including this string in the
- // directory name makes it friendlier for people browsing the directory.
-
- // We can't use URLSpec here, because we don't link with Panda3D. We have to
- // do it by hand.
- size_t p = _host_url.find("://");
- if (p != string::npos) {
- size_t start = p + 3;
- size_t end = _host_url.find("/", start);
- // Now start .. end is something like "username@host:port".
-
- size_t at = _host_url.find("@", start);
- if (at < end) {
- start = at + 1;
- }
-
- size_t colon = _host_url.find(":", start);
- if (colon < end) {
- end = colon;
- }
-
- // Now start .. end is just the hostname.
- hostname = _host_url.substr(start, end - start);
- }
-
-/*
- * Now build a hash string of the whole URL. We'll use MD5 to get a pretty
- * good hash, with a minimum chance of collision. Even if there is a hash
- * collision, though, it's not the end of the world; it just means that both
- * hosts will dump their packages into the same directory, and they'll fight
- * over the toplevel contents.xml file. Assuming they use different version
- * numbers (which should be safe since they have the same hostname), there
- * will be minimal redownloading.
- */
-
- static const size_t hash_size = 16;
- unsigned char md[hash_size];
-
- size_t keep_hash = hash_size;
-
- if (!hostname.empty()) {
- _host_dir += hostname;
- _host_dir += "_";
-
- // If we successfully got a hostname, we don't really need the full hash.
- // We'll keep half of it.
- keep_hash = keep_hash / 2;
- }
-
- MD5_CTX ctx;
- MD5_Init(&ctx);
- MD5_Update(&ctx, _host_url.data(), _host_url.size());
- MD5_Final(md, &ctx);
-
- for (size_t i = 0; i < keep_hash; ++i) {
- int high = (md[i] >> 4) & 0xf;
- int low = md[i] & 0xf;
- _host_dir += P3DInstanceManager::encode_hexdigit(high);
- _host_dir += P3DInstanceManager::encode_hexdigit(low);
- }
-}
-
-
-/**
- * Attempts to change the filename into some standard form for comparison with
- * other filenames. On a case-insensitive filesystem, this converts the
- * filename to lowercase. On Windows, it further replaces forward slashes
- * with backslashes.
- */
-string P3DHost::
-standardize_filename(const string &filename) {
-#if defined(_WIN32) || defined(__APPLE__)
- string new_filename;
- for (string::const_iterator si = filename.begin();
- si != filename.end();
- ++si) {
- char ch = *si;
-#ifdef _WIN32
- if (ch == '/') {
- ch = '\\';
- }
-#endif // _WIN32
- new_filename += tolower(ch);
- }
- return new_filename;
-#else // _WIN32 || __APPLE__
- return filename;
-#endif // _WIN32 || __APPLE__
-}
-
-/**
- * Copies the data in the file named by from_filename into the file named by
- * to_filename.
- */
-bool P3DHost::
-copy_file(const string &from_filename, const string &to_filename) {
-#ifdef _WIN32
- ifstream in;
- wstring from_filename_w;
- if (string_to_wstring(from_filename_w, from_filename)) {
- in.open(from_filename_w.c_str(), ios::in | ios::binary);
- }
-
- // Copy to a temporary file first, in case (a) the filenames actually refer
- // to the same file, or (b) in case we have different processes writing to
- // the same file, and (c) to prevent partially overwriting the file should
- // something go wrong.
- ostringstream strm;
- strm << to_filename << ".t";
- strm << GetCurrentProcessId() << "_" << GetCurrentThreadId();
- string temp_filename = strm.str();
- ofstream out;
- wstring temp_filename_w;
- if (string_to_wstring(temp_filename_w, temp_filename)) {
- out.open(temp_filename_w.c_str(), ios::out | ios::binary);
- }
-
- static const size_t buffer_size = 4096;
- char buffer[buffer_size];
-
- in.read(buffer, buffer_size);
- std::streamsize count = in.gcount();
- while (count != 0) {
- out.write(buffer, count);
- if (out.fail()) {
- unlink(temp_filename.c_str());
- return false;
- }
- in.read(buffer, buffer_size);
- count = in.gcount();
- }
- out.close();
-
- wstring to_filename_w;
- string_to_wstring(to_filename_w, to_filename);
-
- if (!in.eof()) {
- _wunlink(temp_filename_w.c_str());
- return false;
- }
-
- if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) {
- return true;
- }
-
- _wunlink(to_filename_w.c_str());
- if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) {
- return true;
- }
-
- _wunlink(temp_filename_w.c_str());
- return false;
-
-#else // _WIN32
-
- ifstream in;
- in.open(from_filename.c_str(), ios::in | ios::binary);
-
- // Copy to a temporary file first, in case (a) the filenames actually refer
- // to the same file, or (b) in case we have different processes writing to
- // the same file, and (c) to prevent partially overwriting the file should
- // something go wrong.
- ostringstream strm;
- strm << to_filename << ".t";
- strm << getpid();
-
- string temp_filename = strm.str();
- ofstream out;
- out.open(temp_filename.c_str(), ios::out | ios::binary);
-
- static const size_t buffer_size = 4096;
- char buffer[buffer_size];
-
- in.read(buffer, buffer_size);
- size_t count = in.gcount();
- while (count != 0) {
- out.write(buffer, count);
- if (out.fail()) {
- unlink(temp_filename.c_str());
- return false;
- }
- in.read(buffer, buffer_size);
- count = in.gcount();
- }
- out.close();
-
- if (!in.eof()) {
- unlink(temp_filename.c_str());
- return false;
- }
-
- if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
- return true;
- }
-
- unlink(to_filename.c_str());
- if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
- return true;
- }
-
- unlink(temp_filename.c_str());
- return false;
-#endif // _WIN32
-}
-
-/**
- * Stores the XML document to the file named by to_filename, safely.
- */
-bool P3DHost::
-save_xml_file(TiXmlDocument *doc, const string &to_filename) {
- // Save to a temporary file first, in case (a) we have different processes
- // writing to the same file, and (b) to prevent partially overwriting the
- // file should something go wrong.
-
-#ifdef _WIN32
- ostringstream strm;
- strm << to_filename << ".t";
- strm << GetCurrentProcessId() << "_" << GetCurrentThreadId();
- string temp_filename = strm.str();
-
- wstring temp_filename_w;
- string_to_wstring(temp_filename_w, temp_filename);
- wstring to_filename_w;
- string_to_wstring(to_filename_w, to_filename);
-
- if (!doc->SaveFile(temp_filename.c_str())) {
- _wunlink(temp_filename_w.c_str());
- return false;
- }
-
- if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) {
- return true;
- }
-
- _wunlink(to_filename_w.c_str());
- if (_wrename(temp_filename_w.c_str(), to_filename_w.c_str()) == 0) {
- return true;
- }
-
- _wunlink(temp_filename_w.c_str());
- return false;
-
-#else // _WIN32
- ostringstream strm;
- strm << to_filename << ".t";
- strm << getpid();
- string temp_filename = strm.str();
-
- if (!doc->SaveFile(temp_filename.c_str())) {
- unlink(temp_filename.c_str());
- return false;
- }
-
- if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
- return true;
- }
-
- unlink(to_filename.c_str());
- if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
- return true;
- }
-
- unlink(temp_filename.c_str());
- return false;
-#endif // _WIN32
-}
-
-/**
- * Compares the two dotted-integer sequence values numerically. Returns -1 if
- * seq_a sorts first, 1 if seq_b sorts first, 0 if they are equivalent.
- */
-int P3DHost::
-compare_seq(const string &seq_a, const string &seq_b) {
- const char *num_a = seq_a.c_str();
- const char *num_b = seq_b.c_str();
- int comp = compare_seq_int(num_a, num_b);
- while (comp == 0) {
- if (*num_a != '.') {
- if (*num_b != '.') {
- // Both strings ran out together.
- return 0;
- }
- // a ran out first.
- return -1;
- } else if (*num_b != '.') {
- // b ran out first.
- return 1;
- }
-
- // Increment past the dot.
- ++num_a;
- ++num_b;
- comp = compare_seq(num_a, num_b);
- }
-
- return comp;
-}
-
-/**
- * Numerically compares the formatted integer value at num_a with num_b.
- * Increments both num_a and num_b to the next character following the valid
- * integer.
- */
-int P3DHost::
-compare_seq_int(const char *&num_a, const char *&num_b) {
- long int a;
- char *next_a;
- long int b;
- char *next_b;
-
- a = strtol((char *)num_a, &next_a, 10);
- b = strtol((char *)num_b, &next_b, 10);
-
- num_a = next_a;
- num_b = next_b;
-
- if (a < b) {
- return -1;
- } else if (b < a) {
- return 1;
- } else {
- return 0;
- }
-}
diff --git a/direct/src/plugin/p3dHost.h b/direct/src/plugin/p3dHost.h
deleted file mode 100644
index fd880ccde0..0000000000
--- a/direct/src/plugin/p3dHost.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dHost.h
- * @author drose
- * @date 2009-08-21
- */
-
-#ifndef P3DHOST_H
-#define P3DHOST_H
-
-#include "p3d_plugin_common.h"
-#include "fileSpec.h"
-#include
-
-class FileSpec;
-class P3DInstanceManager;
-class P3DPackage;
-
-/**
- * Represents a particular download host serving up Panda3D packages.
- */
-class P3DHost {
-private:
- P3DHost(const std::string &host_url, const std::string &host_dir = "");
- ~P3DHost();
-
-public:
- inline bool has_host_dir() const;
- inline const std::string &get_host_dir() const;
- inline const std::string &get_host_url() const;
- inline const std::string &get_host_url_prefix() const;
- inline const std::string &get_download_url_prefix() const;
- inline const std::string &get_descriptive_name() const;
-
- P3DHost *get_alt_host(const std::string &alt_host);
-
- inline bool has_contents_file() const;
- bool has_current_contents_file(P3DInstanceManager *inst_mgr) const;
- inline int get_contents_iseq() const;
- inline bool check_contents_hash(const std::string &pathname) const;
-
- bool read_contents_file();
- bool read_contents_file(const std::string &contents_filename, bool fresh_download);
- void read_xhost(TiXmlElement *xhost);
-
- P3DPackage *get_package(const std::string &package_name,
- const std::string &package_version,
- const std::string &package_platform,
- const std::string &package_seq,
- const std::string &alt_host = "");
- bool choose_suitable_platform(std::string &selected_platform,
- bool &per_platform,
- const std::string &package_name,
- const std::string &package_version,
- const std::string &package_platform);
- bool get_package_desc_file(FileSpec &desc_file,
- std::string &package_seq,
- bool &package_solo,
- const std::string &package_name,
- const std::string &package_version,
- const std::string &package_platform);
-
- void forget_package(P3DPackage *package, const std::string &alt_host = "");
- void migrate_package_host(P3DPackage *package, const std::string &alt_host, P3DHost *new_host);
-
- void choose_random_mirrors(std::vector &result, int num_mirrors);
- void add_mirror(std::string mirror_url);
-
- void uninstall();
-
-private:
- void determine_host_dir(const std::string &host_dir_basename);
-
- static std::string standardize_filename(const std::string &filename);
- static bool copy_file(const std::string &from_filename, const std::string &to_filename);
- static bool save_xml_file(TiXmlDocument *doc, const std::string &to_filename);
- static int compare_seq(const std::string &seq_a, const std::string &seq_b);
- static int compare_seq_int(const char *&num_a, const char *&num_b);
-
-private:
- std::string _host_dir;
- std::string _host_url;
- std::string _host_url_prefix;
- std::string _download_url_prefix;
- std::string _descriptive_name;
- TiXmlElement *_xcontents;
- time_t _contents_expiration;
- int _contents_iseq;
- FileSpec _contents_spec;
-
- typedef std::vector Mirrors;
- Mirrors _mirrors;
-
- typedef std::map AltHosts;
- AltHosts _alt_hosts;
-
- typedef std::vector PlatformPackages;
- typedef std::map PackageMap;
- typedef std::map Packages;
- Packages _packages;
- typedef std::vector FailedPackages;
- FailedPackages _failed_packages;
-
- friend class P3DInstanceManager;
-};
-
-#include "p3dHost.I"
-
-#endif
diff --git a/direct/src/plugin/p3dInstance.I b/direct/src/plugin/p3dInstance.I
deleted file mode 100644
index 63765832d3..0000000000
--- a/direct/src/plugin/p3dInstance.I
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dInstance.I
- * @author drose
- * @date 2009-05-29
- */
-
-/**
- * Returns the current file parameters.
- */
-inline const P3DFileParams &P3DInstance::
-get_fparams() const {
- return _fparams;
-}
-
-/**
- * Returns the current window parameters.
- */
-inline const P3DWindowParams &P3DInstance::
-get_wparams() const {
- return _wparams;
-}
-
-/**
- * Returns a unique integer for each instance in the system.
- */
-inline int P3DInstance::
-get_instance_id() const {
- return _instance_id;
-}
-
-/**
- * Returns a string that uniquely identifies this session. This is a
- * constructed string that includes the supplied session_name, the python and
- * panda version, and the publisher, as well as any other relevant details; it
- * is guaranteed to be unique for each unique session required for different
- * P3DInstances.
- */
-inline const std::string &P3DInstance::
-get_session_key() const {
- return _session_key;
-}
-
-/**
- * Returns the platform of this particular session. Before the panda3d
- * package has been seen, this is the empty string; once we have downloaded
- * the info file for the panda3d package, it is filled in with whatever
- * platform is provided (that we're also runtime-compatible with).
- *
- * Presumably all of the platform-specific packages that are downloaded
- * subsequently must be of the exact same platform.
- */
-inline const std::string &P3DInstance::
-get_session_platform() const {
- return _session_platform;
-}
-
-/**
- * Returns the P3DSession that is hosting this instance, or NULL if the
- * instance is not running.
- */
-inline P3DSession *P3DInstance::
-get_session() const {
- return _session;
-}
-
-/**
- * Returns a pointer to the asynchronous notification function that was passed
- * to the constructor, if any, or NULL if asynchronous notifications are not
- * required.
- */
-inline P3D_request_ready_func *P3DInstance::
-get_request_ready_func() const {
- return _func;
-}
-
-/**
- * Returns true if this instance's p3d file is trusted and ready to launch,
- * false if it needs to be approved by the user.
- */
-inline bool P3DInstance::
-is_trusted() const {
- return _p3d_trusted;
-}
-
-/**
- * Returns true if this instance is allowed to be scripted by its embedding
- * web page, false otherwise. This may not be known until the p3d file has
- * been fully downloaded and opened.
- */
-inline bool P3DInstance::
-get_matches_script_origin() const {
- return _matches_script_origin;
-}
-
-/**
- * Returns true if this instance has already been started within some session,
- * false otherwise.
- */
-inline bool P3DInstance::
-is_started() const {
- return (_session != nullptr);
-}
-
-/**
- * Returns true if this instance has tried and failed to launch for some
- * reason.
- */
-inline bool P3DInstance::
-is_failed() const {
- return _failed;
-}
-
-/**
- *
- */
-inline P3DInstance::ImageFile::
-ImageFile() {
- _use_standard_image = true;
- _temp_filename = nullptr;
- _image_placement = P3DSplashWindow::IP_none;
-}
-
-/**
- *
- */
-inline P3DInstance::ImageFile::
-~ImageFile() {
- cleanup();
-}
-
-/**
- * Removes the temporary file, if any.
- */
-inline void P3DInstance::ImageFile::
-cleanup() {
- if (_temp_filename != nullptr) {
- delete _temp_filename;
- _temp_filename = nullptr;
- }
-}
diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx
deleted file mode 100644
index 220e28c667..0000000000
--- a/direct/src/plugin/p3dInstance.cxx
+++ /dev/null
@@ -1,3936 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file p3dInstance.cxx
- * @author drose
- * @date 2009-05-29
- */
-
-#include "p3dInstance.h"
-#include "p3dInstanceManager.h"
-#include "p3dDownload.h"
-#include "p3dSession.h"
-#include "p3dPackage.h"
-#include "p3dSplashWindow.h"
-#include "p3dWinSplashWindow.h"
-#include "p3dOsxSplashWindow.h"
-#include "p3dX11SplashWindow.h"
-#include "p3dObject.h"
-#include "p3dMainObject.h"
-#include "p3dUndefinedObject.h"
-#include "p3dMultifileReader.h"
-#include "p3dTemporaryFile.h"
-#include "parse_color.h"
-
-#include
-#include
-
-#ifdef __APPLE__
-#include
-#include
-
-using std::max;
-using std::min;
-using std::ostream;
-using std::ostringstream;
-using std::stringstream;
-using std::string;
-using std::vector;
-
-// Lifted from NSEvent.h (which is Objective-C).
-enum {
- NSAlphaShiftKeyMask = 1 << 16,
- NSShiftKeyMask = 1 << 17,
- NSControlKeyMask = 1 << 18,
- NSAlternateKeyMask = 1 << 19,
- NSCommandKeyMask = 1 << 20,
- NSNumericPadKeyMask = 1 << 21,
- NSHelpKeyMask = 1 << 22,
- NSFunctionKeyMask = 1 << 23,
- NSDeviceIndependentModifierFlagsMask = 0xffff0000U
-};
-
-#endif // __APPLE__
-
-#ifdef _WIN32
-typedef P3DWinSplashWindow SplashWindowType;
-#elif defined(__APPLE__) && !__LP64__
-typedef P3DOsxSplashWindow SplashWindowType;
-#elif defined(HAVE_X11)
-typedef P3DX11SplashWindow SplashWindowType;
-#else
-typedef P3DSplashWindow SplashWindowType;
-#endif
-
-// The amount of time (in seconds) over which we average the total download
-// time, for smoothing out the time estimate.
-static const double time_average = 10.0;
-
-// These are the various image files we might download for use in the splash
-// window. This list must match the ImageType enum.
-const char *P3DInstance::_image_type_names[P3DInstance::IT_num_image_types] = {
- "download",
- "unauth",
- "ready",
- "failed",
- "launch",
- "active",
- "auth_ready",
- "auth_rollover",
- "auth_click",
- "play_ready",
- "play_rollover",
- "play_click",
- "none", // Not really used.
-};
-
-static void
-write_str(ostream &out, const wchar_t *str) {
- const wchar_t *p = str;
- while (*p != 0) {
- out << (int)*p << ' ';
- ++p;
- }
-}
-
-/**
- *
- */
-P3DInstance::
-P3DInstance(P3D_request_ready_func *func,
- const P3D_token tokens[], size_t num_tokens,
- int argc, const char *argv[], void *user_data) :
- _func(func)
-{
- _dom_object = nullptr;
- _main_object = new P3DMainObject;
- _main_object->set_instance(this);
- _user_data = user_data;
- _request_pending = false;
- _total_time_reports = 0;
- _temp_p3d_filename = nullptr;
- _image_package = nullptr;
- _current_background_image = IT_none;
- _current_button_image = IT_none;
- _got_fparams = false;
- _got_wparams = false;
- _p3d_trusted = false;
- _xpackage = nullptr;
- _certlist_package = nullptr;
- _p3dcert_package = nullptr;
-
- _fparams.set_tokens(tokens, num_tokens);
- _fparams.set_args(argc, argv);
-
- nout << "Creating P3DInstance " << this << ": ";
- for (int i = 0; i < _fparams.get_num_tokens(); ++i) {
- nout << " " << _fparams.get_token_keyword(i)
- << "=\"" << _fparams.get_token_value(i) << "\"";
- }
- nout << "\n";
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- _instance_id = inst_mgr->get_unique_id();
- _hidden = (_fparams.lookup_token_int("hidden") != 0);
- _matches_run_origin = true;
- _matches_script_origin = false;
- _allow_python_dev = false;
- _keep_user_env = (_fparams.lookup_token_int("keep_user_env") != 0);
- _auto_start = (_fparams.lookup_token_int("auto_start") != 0);
- _stop_on_ready = (_fparams.lookup_token_int("stop_on_ready") != 0);
- _auto_install = true;
- if (_fparams.has_token("auto_install")) {
- _auto_install = (_fparams.lookup_token_int("auto_install") != 0);
- }
- _auth_button_clicked = false;
- _failed = false;
- _session = nullptr;
- _auth_session = nullptr;
- _panda3d_package = nullptr;
- _splash_window = nullptr;
- _instance_window_opened = false;
- _instance_window_attached = false;
- _stuff_to_download = false;
- _download_package_index = 0;
- _prev_downloaded = 0;
- _total_download_size = 0;
- _total_downloaded = 0;
- _packages_specified = false;
- _download_started = false;
- _download_complete = false;
- _instance_started = false;
-
- INIT_LOCK(_request_lock);
- _requested_stop = false;
-
-#ifdef __APPLE__
- _shared_fd = -1;
- _shared_mmap_size = 0;
- _swbuffer = nullptr;
- _reversed_buffer = nullptr;
- _buffer_data = nullptr;
- _data_provider = nullptr;
- _buffer_color_space = nullptr;
- _buffer_image = nullptr;
-
- // We have to start with _mouse_active true; firefox doesn't send activate
- // events.
- _mouse_active = true;
- _modifiers = 0;
- _frame_timer = nullptr;
-#endif // __APPLE__
-
- // Set some initial properties.
- _main_object->set_float_property("instanceDownloadProgress", 0.0);
- _main_object->set_float_property("downloadProgress", 0.0);
- _main_object->set_undefined_property("downloadElapsedSeconds");
- _main_object->set_undefined_property("downloadElapsedFormatted");
- _main_object->set_undefined_property("downloadRemainingSeconds");
- _main_object->set_undefined_property("downloadRemainingFormatted");
- _main_object->set_string_property("downloadPackageName", "");
- _main_object->set_string_property("downloadPackageDisplayName", "");
- _main_object->set_bool_property("downloadComplete", false);
- _main_object->set_string_property("status", "initial");
-
- ostringstream stream;
- stream << inst_mgr->get_plugin_major_version() << "."
- << inst_mgr->get_plugin_minor_version() << "."
- << inst_mgr->get_plugin_sequence_version();
- if (!inst_mgr->get_plugin_official_version()) {
- stream << "c";
- }
-
- // The plugin version as a single number, with three digits reserved for
- // each component.
- int numeric_version =
- inst_mgr->get_plugin_major_version() * 1000000 +
- inst_mgr->get_plugin_minor_version() * 1000 +
- inst_mgr->get_plugin_sequence_version();
- if (!inst_mgr->get_plugin_official_version()) {
- // Subtract 1 if we are not an official version.
- --numeric_version;
- }
-
- _main_object->set_string_property("pluginVersionString", stream.str());
- _main_object->set_int_property("pluginMajorVersion", inst_mgr->get_plugin_major_version());
- _main_object->set_int_property("pluginMinorVersion", inst_mgr->get_plugin_minor_version());
- _main_object->set_int_property("pluginSequenceVersion", inst_mgr->get_plugin_sequence_version());
- _main_object->set_bool_property("pluginOfficialVersion", inst_mgr->get_plugin_official_version());
- _main_object->set_int_property("pluginNumericVersion", numeric_version);
- _main_object->set_string_property("pluginDistributor", inst_mgr->get_plugin_distributor());
- _main_object->set_string_property("coreapiHostUrl", inst_mgr->get_coreapi_host_url());
- time_t timestamp = inst_mgr->get_coreapi_timestamp();
- _main_object->set_int_property("coreapiTimestamp", (int)timestamp);
- const char *timestamp_string = ctime(×tamp);
- if (timestamp_string == nullptr) {
- timestamp_string = "";
- }
- _main_object->set_string_property("coreapiTimestampString", timestamp_string);
- _main_object->set_string_property("coreapiVersionString", inst_mgr->get_coreapi_set_ver());
-
- _main_object->set_bool_property("trustedEnvironment", (int)inst_mgr->get_trusted_environment());
- _main_object->set_bool_property("consoleEnvironment", (int)inst_mgr->get_console_environment());
-
- // We'll start off with the "download" image displayed in the splash window
- // (when it opens), until we get stuff downloaded.
- set_background_image(IT_download);
-
- // We'd better ask for the image package up front, even if it turns out we
- // don't need it for this particular app. We'll probably use it eventually,
- // and it's good to have it loaded early, so we can put up a splash image
- // (for instance, the above IT_download image) while we download the real
- // contents.
- P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url());
- _image_package = host->get_package("images", "", "", "");
- if (_image_package != nullptr) {
- _image_package->add_instance(this);
- }
-
- // Check if the window size has been explicitly set to 0. This means we
- // have an explicitly hidden plugin, and we should be prepared not to get a
- // wparams from the browser.
- if (_fparams.has_token("width") && _fparams.has_token("height") &&
- (_fparams.lookup_token_int("width") == 0 ||
- _fparams.lookup_token_int("height") == 0)) {
- P3D_window_handle dummy_handle;
- memset(&dummy_handle, 0, sizeof(dummy_handle));
- P3DWindowParams wparams(P3D_WT_hidden, 0, 0, 0, 0, dummy_handle);
- set_wparams(wparams);
- }
-}
-
-/**
- *
- */
-P3DInstance::
-~P3DInstance() {
- assert(_session == nullptr);
- cleanup();
-
- if (_dom_object != nullptr) {
- P3D_OBJECT_DECREF(_dom_object);
- _dom_object = nullptr;
- }
-
- if (_main_object != nullptr) {
- nout << "panda_script_object ref = "
- << _main_object->_ref_count << "\n";
- _main_object->set_instance(nullptr);
- P3D_OBJECT_DECREF(_main_object);
- _main_object = nullptr;
- }
-
- Downloads::iterator di;
- for (di = _downloads.begin(); di != _downloads.end(); ++di) {
- P3DDownload *download = (*di).second;
- if (download->get_instance() == this) {
- download->set_instance(nullptr);
- }
- p3d_unref_delete(download);
- }
- _downloads.clear();
-
- DESTROY_LOCK(_request_lock);
-
- // TODO: Is it possible for someone to delete an instance while a download
- // is still running? Who will crash when this happens?
-}
-
-/**
- * Invalidates the instance and removes any structures prior to deleting.
- */
-void P3DInstance::
-cleanup() {
- _failed = true;
-
- if (_auth_session != nullptr) {
- _auth_session->shutdown(false);
- p3d_unref_delete(_auth_session);
- _auth_session = nullptr;
- }
-
- for (int i = 0; i < (int)IT_num_image_types; ++i) {
- _image_files[i].cleanup();
- }
-
- // Tell all of the packages that we're no longer in business for them.
- Packages::iterator pi;
- for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
- (*pi)->remove_instance(this);
- }
- _packages.clear();
- if (_image_package != nullptr) {
- _image_package->remove_instance(this);
- _image_package = nullptr;
- }
-
- if (_certlist_package != nullptr) {
- _certlist_package->remove_instance(this);
- _certlist_package = nullptr;
- }
-
- if (_p3dcert_package != nullptr) {
- _p3dcert_package->remove_instance(this);
- _p3dcert_package = nullptr;
- }
-
- if (_splash_window != nullptr) {
- delete _splash_window;
- _splash_window = nullptr;
- }
-
- if (_temp_p3d_filename != nullptr) {
- delete _temp_p3d_filename;
- _temp_p3d_filename = nullptr;
- }
-
- if (_xpackage != nullptr) {
- delete _xpackage;
- _xpackage = nullptr;
- }
-
-#ifdef __APPLE__
- if (_frame_timer != nullptr) {
- CFRunLoopTimerInvalidate(_frame_timer);
- CFRelease(_frame_timer);
- _frame_timer = nullptr;
- }
-
- free_swbuffer();
-#endif
-
- TiXmlDocument *doc = nullptr;
- ACQUIRE_LOCK(_request_lock);
- RawRequests::iterator ri;
- for (ri = _raw_requests.begin(); ri != _raw_requests.end(); ++ri) {
- doc = (*ri);
- delete doc;
- }
- _raw_requests.clear();
- RELEASE_LOCK(_request_lock);
-
- BakedRequests::iterator bi;
- for (bi = _baked_requests.begin(); bi != _baked_requests.end(); ++bi) {
- P3D_request *request = (*bi);
- finish_request(request, false);
- }
- _baked_requests.clear();
-
- Downloads::iterator di;
- for (di = _downloads.begin(); di != _downloads.end(); ++di) {
- P3DDownload *download = (*di).second;
- download->cancel();
- }
-}
-
-
-/**
- * Specifies a URL that should be contacted to download the instance data.
- * Normally this, or set_p3d_filename() or make_p3d_stream(), is only called
- * once.
- *
- * The instance data at the other end of this URL is key. We can't start the
- * instance until we have downloaded the instance file and examined the
- * p3d_info.xml, and we know what Python version we need and so forth.
- */
-void P3DInstance::
-set_p3d_url(const string &p3d_url) {
- if (p3d_url.empty()) {
- nout << "No p3d URL specified. Cannot run.\n";
- set_failed();
- return;
- }
- _fparams.set_p3d_url(p3d_url);
-
- // Save the last part of the URL as the p3d_basename, for reporting purposes
- // or whatever.
- determine_p3d_basename(p3d_url);
-
- // Make a temporary file to receive the instance data.
- assert(_temp_p3d_filename == nullptr);
- _temp_p3d_filename = new P3DTemporaryFile(".p3d");
- _stuff_to_download = true;
-
- // Maybe it's time to open a splash window now.
- make_splash_window();
-
- // Mark the time we started downloading, so we'll know when to reveal the
- // progress bar, and we can predict the total download time.
-#ifdef _WIN32
- _start_dl_tick = GetTickCount();
-#else
- gettimeofday(&_start_dl_timeval, nullptr);
-#endif
- _show_dl_instance_progress = false;
-
- // Start downloading the data.
- InstanceDownload *download = new InstanceDownload(this);
- download->set_url(p3d_url);
- download->set_filename(_temp_p3d_filename->get_filename());
- if (_fparams.has_token("p3d_size")) {
- download->set_total_expected_data(_fparams.lookup_token_int("p3d_size"));
- }
-
- _main_object->set_string_property("status", "downloading_instance");
- start_download(download);
-}
-
-/**
- * Indicates an intention to transmit the p3d data as a stream. Should return
- * a new unique stream ID to receive it.
- */
-int P3DInstance::
-make_p3d_stream(const string &p3d_url) {
- _fparams.set_p3d_url(p3d_url);
-
- // Save the last part of the URL as the p3d_basename, for reporting purposes
- // or whatever.
- determine_p3d_basename(p3d_url);
-
- // Make a temporary file to receive the instance data.
- assert(_temp_p3d_filename == nullptr);
- _temp_p3d_filename = new P3DTemporaryFile(".p3d");
- _stuff_to_download = true;
-
- // Maybe it's time to open a splash window now.
- make_splash_window();
-
- // Mark the time we started downloading, so we'll know when to reveal the
- // progress bar.
-#ifdef _WIN32
- _start_dl_tick = GetTickCount();
-#else
- gettimeofday(&_start_dl_timeval, nullptr);
-#endif
- _show_dl_instance_progress = false;
-
- // Start downloading the data.
- InstanceDownload *download = new InstanceDownload(this);
- download->set_url(p3d_url);
- download->set_filename(_temp_p3d_filename->get_filename());
- if (_fparams.has_token("p3d_size")) {
- download->set_total_expected_data(_fparams.lookup_token_int("p3d_size"));
- }
-
- _main_object->set_string_property("status", "downloading_instance");
- return start_download(download, false);
-}
-
-/**
- * Specifies the file that contains the instance data. Normally this is only
- * called once.
- */
-void P3DInstance::
-set_p3d_filename(const string &p3d_filename, const int &p3d_offset) {
- determine_p3d_basename(p3d_filename);
- priv_set_p3d_filename(p3d_filename, p3d_offset);
-}
-
-/**
- * Changes the window parameters, e.g. to resize or reposition the window; or
- * sets the parameters for the first time, creating the initial window.
- */
-void P3DInstance::
-set_wparams(const P3DWindowParams &wparams) {
- bool prev_got_wparams = _got_wparams;
- _got_wparams = true;
- _wparams = wparams;
-
- nout << "set_wparams: " << wparams.get_window_type()
- << " " << wparams.get_win_width() << " " << wparams.get_win_height()
- << "\n";
-
- if (_hidden || _wparams.get_win_width() == 0 || _wparams.get_win_height() == 0) {
- // If we're a hidden app, or if the window has no size, then it is really
- // a hidden window, regardless of what type it claims to be.
- _wparams.set_window_type(P3D_WT_hidden);
- }
-
- if (_wparams.get_window_type() != P3D_WT_hidden) {
- // Update or create the splash window.
- if (_splash_window != nullptr) {
- _splash_window->set_wparams(_wparams);
- } else {
- make_splash_window();
- }
- }
-
- // It doesn't make much sense to go further than this point if the instance
- // is already in the failed state.
- if (is_failed()) {
- return;
- }
-
- if (_wparams.get_window_type() != P3D_WT_hidden) {
-#ifdef __APPLE__
- // On Mac, we have to communicate the results of the rendering back via
- // shared memory, instead of directly parenting windows to the browser.
- // Set up this mechanism.
- int x_size = _wparams.get_win_width();
- int y_size = _wparams.get_win_height();
- if (x_size != 0 && y_size != 0) {
- if (_swbuffer == nullptr || _swbuffer->get_x_size() != x_size ||
- _swbuffer->get_y_size() != y_size) {
- // We need to open a new shared buffer.
- alloc_swbuffer();
- }
-
- if (_swbuffer == nullptr) {
- nout << "Could not open swbuffer\n";
- }
- }
-#endif // __APPLE__
- }
-
- // Update the instance in the sub-process.
- if (_session != nullptr) {
- TiXmlDocument *doc = new TiXmlDocument;
- TiXmlElement *xcommand = new TiXmlElement("command");
- xcommand->SetAttribute("cmd", "setup_window");
- xcommand->SetAttribute("instance_id", get_instance_id());
- TiXmlElement *xwparams = _wparams.make_xml(this);
-
- doc->LinkEndChild(xcommand);
- xcommand->LinkEndChild(xwparams);
-
- _session->send_command(doc);
- }
-
- if (!prev_got_wparams) {
- // If this was the first set_wparams call, try to start the app.
- if (_p3d_trusted && get_packages_ready()) {
- ready_to_start();
- }
- }
-}
-
-/**
- * Returns a pointer to the top-level scriptable object of the instance, to be
- * used by JavaScript code in the browser to control this program.
- */
-P3D_object *P3DInstance::
-get_panda_script_object() const {
- nout << "get_panda_script_object\n";
- return _main_object;
-}
-
-/**
- * Stores a pointer to the top-level window object of the browser, to be used
- * by Panda code to control JavaScript. The new object's reference count is
- * incremented, and the previous object's is decremented.
- */
-void P3DInstance::
-set_browser_script_object(P3D_object *browser_script_object) {
- nout << "set_browser_script_object\n";
- if (browser_script_object != _dom_object) {
- P3D_OBJECT_XDECREF(_dom_object);
- _dom_object = browser_script_object;
- if (_dom_object != nullptr) {
- P3D_OBJECT_INCREF(_dom_object);
- }
-
- if (_session != nullptr) {
- send_browser_script_object();
- }
- }
-
- // Query the origin: protocol, hostname, and port. We'll use this to limit
- // access to the scripting interfaces for a particular p3d file.
- _origin_protocol.clear();
- _origin_hostname.clear();
- _origin_port.clear();
- if (_dom_object != nullptr) {
- P3D_object *location = P3D_OBJECT_GET_PROPERTY(_dom_object, "location");
- if (location != nullptr) {
- P3D_object *protocol = P3D_OBJECT_GET_PROPERTY(location, "protocol");
- if (protocol != nullptr) {
- int size = P3D_OBJECT_GET_STRING(protocol, nullptr, 0);
- char *buffer = new char[size];
- P3D_OBJECT_GET_STRING(protocol, buffer, size);
- _origin_protocol = string(buffer, size);
- delete [] buffer;
- P3D_OBJECT_DECREF(protocol);
- }
-
- P3D_object *hostname = P3D_OBJECT_GET_PROPERTY(location, "hostname");
- if (hostname != nullptr) {
- int size = P3D_OBJECT_GET_STRING(hostname, nullptr, 0);
- char *buffer = new char[size];
- P3D_OBJECT_GET_STRING(hostname, buffer, size);
- _origin_hostname = string(buffer, size);
- delete [] buffer;
- P3D_OBJECT_DECREF(hostname);
- }
-
- P3D_object *port = P3D_OBJECT_GET_PROPERTY(location, "port");
- if (port != nullptr) {
- int size = P3D_OBJECT_GET_STRING(port, nullptr, 0);
- char *buffer = new char[size];
- P3D_OBJECT_GET_STRING(port, buffer, size);
- _origin_port = string(buffer, size);
- delete [] buffer;
- P3D_OBJECT_DECREF(port);
- }
-
- if (_origin_hostname.empty() && _origin_protocol == "file:") {
- _origin_hostname = "localhost";
- }
-
- if (_origin_port.empty()) {
- // Maybe the actual URL doesn't include the port, in which case it is
- // implicit.
- if (_origin_protocol == "http:") {
- _origin_port = "80";
- } else if (_origin_protocol == "https:") {
- _origin_port = "443";
- }
- }
-
- P3D_OBJECT_DECREF(location);
- }
- }
-
- nout << "origin is " << _origin_protocol << "//" << _origin_hostname;
- if (!_origin_port.empty()) {
- nout << ":" << _origin_port;
- }
- nout << "\n";
-}
-
-
-/**
- * Returns true if the instance has any pending requests at the time of this
- * call, false otherwise.
- */
-bool P3DInstance::
-has_request() {
- return _request_pending;
-}
-
-/**
- * Returns a newly-allocated P3D_request corresponding to the pending request
- * for the host, or NULL if there is no pending request. If the return value
- * is non-NULL, it should eventually be passed back to finish_request() for
- * cleanup.
- */
-P3D_request *P3DInstance::
-get_request() {
- bake_requests();
- if (_baked_requests.empty()) {
- // No requests ready.
- _request_pending = false;
- return nullptr;
- }
-
- P3D_request *request = _baked_requests.front();
- _baked_requests.pop_front();
- _request_pending = !_baked_requests.empty();
-
- if (request != nullptr) {
- switch (request->_request_type) {
- case P3D_RT_notify:
- {
- // Also eval the associated HTML token, if any.
- string message = request->_request._notify._message;
- string expression = _fparams.lookup_token(message);
- nout << "notify: " << message << " " << expression << "\n";
- if (!expression.empty() && _dom_object != nullptr) {
- P3D_object *result = P3D_OBJECT_EVAL(_dom_object, expression.c_str());
- P3D_OBJECT_XDECREF(result);
- }
- }
- break;
-
- case P3D_RT_stop:
- {
- // We also send an implicit message when Python requests itself to
- // shutdown.
- _main_object->set_pyobj(nullptr);
- _main_object->set_string_property("status", "stopped");
-
- string message = "onpythonstop";
- string expression = _fparams.lookup_token(message);
- nout << "notify: " << message << " " << expression << "\n";
- if (!expression.empty() && _dom_object != nullptr) {
- P3D_object *result = P3D_OBJECT_EVAL(_dom_object, expression.c_str());
- P3D_OBJECT_XDECREF(result);
- }
- }
- break;
-
- case P3D_RT_callback:
- {
- // And when the callback request is extracted, we make the callback.
- P3D_callback_func *func = request->_request._callback._func;
- void *data = request->_request._callback._data;
- (*func)(data);
- }
- break;
-
- case P3D_RT_forget_package:
- {
- // We're being asked to forget a particular package.
- const char *host_url = request->_request._forget_package._host_url;
- const char *package_name = request->_request._forget_package._package_name;
- const char *package_version = request->_request._forget_package._package_version;
- if (package_version == nullptr) {
- package_version = "";
- }
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- P3DHost *host = inst_mgr->get_host(host_url);
- if (package_name != nullptr) {
- P3DPackage *package = host->get_package(package_name, package_version, _session_platform, "");
- host->forget_package(package);
- } else {
- // If a NULL package name is given, forget the whole host.
- inst_mgr->forget_host(host);
- }
- }
- break;
-
- default:
- break;
- }
- }
-
- return request;
-}
-
-/**
- * Copies requests from the _raw_requests queue, which is built up in one or
- * more sub-threads, into the _baked_requests queue, which is publicly
- * presented to the browser. Along the way, some requests (like script
- * requests) are handled immediately.
- *
- * At the end of this call, _baked_requests will contain the current set of
- * requests pending for the browser.
- *
- * This method should only be called in the main thread.
- */
-void P3DInstance::
-bake_requests() {
- while (true) {
- // Get the latest request from the read thread.
- TiXmlDocument *doc = nullptr;
- ACQUIRE_LOCK(_request_lock);
- if (!_raw_requests.empty()) {
- doc = _raw_requests.front();
- _raw_requests.pop_front();
- }
- RELEASE_LOCK(_request_lock);
-
- if (doc == nullptr) {
- // No more requests to process right now.
- return;
- }
-
- // Now we've got a request in XML form; convert it to P3D_request form.
- TiXmlElement *xrequest = doc->FirstChildElement("request");
- assert(xrequest != nullptr);
- P3D_request *request = make_p3d_request(xrequest);
- delete doc;
-
- if (request != nullptr) {
- _baked_requests.push_back(request);
- }
- }
-}
-
-/**
- * May be called in any thread to add a new XML request to the pending_request
- * queue for this instance. The XML document will be deleted when the request
- * is eventually handled.
- */
-void P3DInstance::
-add_raw_request(TiXmlDocument *doc) {
- ACQUIRE_LOCK(_request_lock);
- _raw_requests.push_back(doc);
- _request_pending = true;
- RELEASE_LOCK(_request_lock);
-
- // We don't decode the XML yet, since we might be running in any thread
- // here. We'll decode it in the main thread, where it's safe.
-
- // Tell the world we've got a new request.
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- inst_mgr->signal_request_ready(this);
- if (_session != nullptr) {
- _session->signal_request_ready(this);
- }
-}
-
-/**
- * May be called in the main thread only to add a new request to the
- * baked_request queue. This request queue is directly passed on the browser
- * without further processing at this level.
- */
-void P3DInstance::
-add_baked_request(P3D_request *request) {
- assert(request->_instance == nullptr);
- request->_instance = this;
- ref();
-
- _baked_requests.push_back(request);
- _request_pending = true;
-
- // Tell the world we've got a new request.
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- inst_mgr->signal_request_ready(this);
-}
-
-/**
- * Deallocates a previously-returned request from get_request(). If handled
- * is true, the request has been handled by the host; otherwise, it has been
- * ignored.
- */
-void P3DInstance::
-finish_request(P3D_request *request, bool handled) {
- assert(request != nullptr);
- if (request->_instance == nullptr) {
- nout << "Ignoring empty request " << request << "\n";
- return;
- }
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- if (inst_mgr->validate_instance(request->_instance) == nullptr) {
- // nout << "Ignoring unknown request " << request << "\n";
- return;
- }
-
- switch (request->_request_type) {
- case P3D_RT_stop:
- break;
-
- case P3D_RT_get_url:
- if (request->_request._get_url._url != nullptr) {
- free((char *)request->_request._get_url._url);
- request->_request._get_url._url = nullptr;
- }
- break;
-
- case P3D_RT_notify:
- if (request->_request._notify._message != nullptr) {
- free((char *)request->_request._notify._message);
- request->_request._notify._message = nullptr;
- }
- break;
-
- case P3D_RT_forget_package:
- if (request->_request._forget_package._host_url != nullptr) {
- free((char *)request->_request._forget_package._host_url);
- request->_request._forget_package._host_url = nullptr;
- }
- if (request->_request._forget_package._package_name != nullptr) {
- free((char *)request->_request._forget_package._package_name);
- request->_request._forget_package._package_name = nullptr;
- }
- if (request->_request._forget_package._package_version != nullptr) {
- free((char *)request->_request._forget_package._package_version);
- request->_request._forget_package._package_version = nullptr;
- }
- break;
-
- default:
- break;
- }
-
- p3d_unref_delete(((P3DInstance *)request->_instance));
- request->_instance = nullptr;
-
- delete request;
-}
-
-/**
- * Called by the host in response to a get_url request, this sends the data
- * retrieved from the requested URL, a piece at a time.
- */
-bool P3DInstance::
-feed_url_stream(int unique_id,
- P3D_result_code result_code,
- int http_status_code,
- size_t total_expected_data,
- const unsigned char *this_data,
- size_t this_data_size) {
- Downloads::iterator di = _downloads.find(unique_id);
- if (di == _downloads.end()) {
- nout << "Unexpected feed_url_stream for " << unique_id << "\n";
- // Don't know this request.
- return false;
- }
-
- P3DDownload *download = (*di).second;
- assert(download->get_instance() == this);
- bool download_ok = download->feed_url_stream
- (result_code, http_status_code, total_expected_data,
- this_data, this_data_size);
-
- if (!download_ok || download->get_download_finished()) {
- // All done.
- if (download->get_instance() == this) {
- download->set_instance(nullptr);
- }
- _downloads.erase(di);
- p3d_unref_delete(download);
- }
-
- return download_ok;
-}
-
-/**
- * Responds to the os-generated window event. Returns true if the event is
- * handled, false if ignored.
- */
-bool P3DInstance::
-handle_event(const P3D_event_data &event) {
- bool retval = false;
- if (_splash_window != nullptr) {
- if (_splash_window->handle_event(event)) {
- retval = true;
- }
- }
-
-#if defined(__APPLE__)
- if (event._event_type == P3D_ET_osx_event_record) {
- retval = handle_event_osx_event_record(event);
- } else if (event._event_type == P3D_ET_osx_cocoa) {
- retval = handle_event_osx_cocoa(event);
- } else {
- assert(false);
- }
-#endif // __APPLE__
-
- return retval;
-}
-
-/**
- * Returns the log filename for this particular session, if the session was
- * started and if it has a log file. Returns empty string if the session
- * never started or if it lacks a log file.
- *
- * This is the same value returned by P3DSession::get_log_pathname(), except
- * that it remains valid even after the session has closed.
- */
-const string &P3DInstance::
-get_log_pathname() const {
- if (_session != nullptr) {
- return _session->_log_pathname;
- }
- return _log_pathname;
-}
-
-/**
- * Adds the package to the list of packages used by this instance. The
- * instance will share responsibility for downloading the package with any of
- * the other instances that use the same package.
- *
- * The seq value should be the expected minimum package_seq value for the
- * indicated package. If the given seq value is higher than the package_seq
- * value in the contents.xml file cached for the host, it is a sign that the
- * contents.xml file is out of date and needs to be redownloaded.
- */
-void P3DInstance::
-add_package(const string &name, const string &version, const string &seq,
- P3DHost *host) {
- string alt_host = _fparams.lookup_token("alt_host");
-
- // Look up in the p3d_info.xml file to see if this p3d file has a specific
- // alt_host indication for this host_url.
- string alt_host_url = find_alt_host_url(host->get_host_url(), alt_host);
- if (!alt_host_url.empty()) {
- // If it does, we go ahead and switch to that host now, instead of
- // bothering to contact the original host.
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- host = inst_mgr->get_host(alt_host_url);
- alt_host.clear();
- }
-
- if (!host->has_contents_file()) {
- // Since we haven't downloaded this host's contents.xml file yet, get its
- // additional host information.
- get_host_info(host);
- }
-
- P3DPackage *package = host->get_package(name, version, _session_platform,
- seq, alt_host);
- add_package(package);
-}
-
-/**
- * Adds the package to the list of packages used by this instance. The
- * instance will share responsibility for downloading the package with any of
- * the other instances that use the same package.
- */
-void P3DInstance::
-add_package(P3DPackage *package) {
- if (find(_packages.begin(), _packages.end(), package) != _packages.end()) {
- // Already have this package.
- return;
- }
-
- if (package->get_package_name() == "panda3d") {
- _panda3d_package = package;
- }
-
- _packages.push_back(package);
-
- // This call must be at the end of this method, because it might ultimately
- // start the application before it returns (if this was the last required
- // package).
- package->add_instance(this);
-}
-
-/**
- * Indicates that the given package is destructing and this instance should no
- * longer retain a pointer to it. This is normally called only by the
- * P3DPackage destructor, and it invalidates the instance.
- */
-void P3DInstance::
-remove_package(P3DPackage *package) {
- Packages::iterator pi = find(_packages.begin(), _packages.end(), package);
- if (pi != _packages.end()) {
- _packages.erase(pi);
- }
- pi = find(_downloading_packages.begin(), _downloading_packages.end(), package);
- if (pi != _downloading_packages.end()) {
- _downloading_packages.erase(pi);
- }
- if (package == _image_package) {
- _image_package = nullptr;
- }
- if (package == _certlist_package) {
- _certlist_package = nullptr;
- }
- if (package == _p3dcert_package) {
- _p3dcert_package = nullptr;
- }
- if (package == _panda3d_package) {
- _panda3d_package = nullptr;
- }
-
- set_failed();
-}
-
-/**
- * Returns true if all of the packages required by the instance have their
- * information available and are ready to be downloaded, false if one or more
- * of them is still waiting for information (or has failed).
- */
-bool P3DInstance::
-get_packages_info_ready() const {
- if (!_packages_specified) {
- // We haven't even specified the full set of required packages yet.
- return false;
- }
-
- Packages::const_iterator pi;
- for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
- if (!(*pi)->get_info_ready()) {
- return false;
- }
- }
-
- return true;
-}
-
-/**
- * Returns true if all of the packages required by the instance (as specified
- * in previous calls to add_package()) have been fully downloaded and are
- * ready to run, or false if one or more of them still requires downloading.
- */
-bool P3DInstance::
-get_packages_ready() const {
- if (!_packages_specified) {
- // We haven't even specified the full set of required packages yet.
- return false;
- }
-
- Packages::const_iterator pi;
- for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
- if (!(*pi)->get_ready()) {
- return false;
- }
- }
-
- return true;
-}
-
-/**
- * Returns true if any of the packages required by the instance have failed to
- * download (and thus we will never be ready).
- */
-bool P3DInstance::
-get_packages_failed() const {
- Packages::const_iterator pi;
- for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
- if ((*pi)->get_failed()) {
- return true;
- }
- }
-
- return false;
-}
-
-/**
- * Adds a newly-allocated P3DDownload object to the download queue, and issues
- * the request to start it downloading. As the download data comes in, it
- * will be fed to the download object.
- *
- * This increments the P3DDownload object's reference count, and will
- * decrement it (and possibly delete the object) after download_finished() has
- * been called.
- *
- * add_request should be true to actually request the URL from the plugin, or
- * false not to. Normally, this should always be set true, except in the one
- * special case of make_p3d_stream(), in which case the plugin is already
- * prepared to send the stream and doesn't need to have it requested.
- *
- * Returns the unique ID of this stream.
- */
-int P3DInstance::
-start_download(P3DDownload *download, bool add_request) {
- assert(download->get_download_id() == 0);
- assert(!download->get_url().empty());
-
- if (is_failed()) {
- // Can't download anything more after failure.
- download->cancel();
- return 0;
- }
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
- int download_id = inst_mgr->get_unique_id();
- download->set_download_id(download_id);
- download->set_instance(this);
-
- download->ref();
- bool inserted = _downloads.insert(Downloads::value_type(download_id, download)).second;
- assert(inserted);
-
- // add_request will be false only for the initial p3d stream, which the
- // plugin already knows about. For all other download streams, add_request
- // is true in order to ask the plugin for the stream.
- if (add_request) {
- P3D_request *request = new P3D_request;
- request->_instance = nullptr;
- request->_request_type = P3D_RT_get_url;
- request->_request._get_url._url = strdup(download->get_url().c_str());
- request->_request._get_url._unique_id = download_id;
-
- add_baked_request(request);
- }
-
- return download_id;
-}
-
-/**
- * Asks the host to shut down this particular instance, presumably because the
- * user has indicated it should exit. This call may be made in any thread.
- */
-void P3DInstance::
-request_stop_sub_thread() {
- // Atomically check _requested_stop.
- bool add_request = false;
- ACQUIRE_LOCK(_request_lock);
- if (!_requested_stop) {
- _requested_stop = true;
- add_request = true;
- }
- RELEASE_LOCK(_request_lock);
-
- // If we haven't requested a stop already, do it now.
- if (add_request) {
- TiXmlDocument *doc = new TiXmlDocument;
- TiXmlElement *xrequest = new TiXmlElement("request");
- xrequest->SetAttribute("rtype", "stop");
- doc->LinkEndChild(xrequest);
-
- add_raw_request(doc);
- }
-}
-
-/**
- * Asks the host to shut down this particular instance, presumably because the
- * user has indicated it should exit. This call may only be made in the main
- * thread.
- */
-void P3DInstance::
-request_stop_main_thread() {
- // Atomically check _requested_stop.
- bool add_request = false;
- ACQUIRE_LOCK(_request_lock);
- if (!_requested_stop) {
- _requested_stop = true;
- add_request = true;
- }
- RELEASE_LOCK(_request_lock);
-
- // If we haven't requested a stop already, do it now.
- if (add_request) {
- _requested_stop = true;
- P3D_request *request = new P3D_request;
- request->_instance = nullptr;
- request->_request_type = P3D_RT_stop;
- add_baked_request(request);
- }
-}
-
-/**
- * Asks the host to refresh the plugin window. This is only relevant for
- * windowless plugins, for instance, the way OSX plugins always run.
- */
-void P3DInstance::
-request_refresh() {
- P3D_request *request = new P3D_request;
- request->_instance = nullptr;
- request->_request_type = P3D_RT_refresh;
- add_baked_request(request);
-}
-
-/**
- * Asks the host to make a callback later.
- */
-void P3DInstance::
-request_callback(P3D_callback_func *func, void *data) {
- P3D_request *request = new P3D_request;
- request->_instance = nullptr;
- request->_request_type = P3D_RT_callback;
- request->_request._callback._func = func;
- request->_request._callback._data = data;
- add_baked_request(request);
-}
-
-/**
- * Returns a newly-allocated XML structure that corresponds to the data within
- * this instance.
- */
-TiXmlElement *P3DInstance::
-make_xml() {
- assert(_got_fparams);
-
- TiXmlElement *xinstance = new TiXmlElement("instance");
- xinstance->SetAttribute("instance_id", _instance_id);
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- xinstance->SetAttribute("root_dir", inst_mgr->get_root_dir());
- xinstance->SetAttribute("log_directory", inst_mgr->get_log_directory());
- xinstance->SetAttribute("verify_contents", (int)inst_mgr->get_verify_contents());
-
- // Tell the Panda process that it was started by a plugin that knows about
- // the new per_platform flag.
- xinstance->SetAttribute("respect_per_platform", 1);
-
- if (!inst_mgr->get_super_mirror().empty()) {
- xinstance->SetAttribute("super_mirror", inst_mgr->get_super_mirror());
- }
-
-
- TiXmlElement *xfparams = _fparams.make_xml();
- xinstance->LinkEndChild(xfparams);
-
- if (_got_wparams) {
- TiXmlElement *xwparams = _wparams.make_xml(this);
- xinstance->LinkEndChild(xwparams);
- }
-
- Packages::const_iterator pi;
- for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
- TiXmlElement *xpackage = (*pi)->make_xml();
- xinstance->LinkEndChild(xpackage);
- }
-
- TiXmlElement *xmain = _session->p3dobj_to_xml(_main_object);
- xmain->SetValue("main");
- xinstance->LinkEndChild(xmain);
-
- return xinstance;
-}
-
-/**
- * Called by the P3DSplashWindow code (maybe in a sub-thread) when the user
- * clicks the button visible on the splash window. This will forward the
- * event to the main thread via the request callback mechanism.
- */
-void P3DInstance::
-splash_button_clicked_sub_thread() {
- TiXmlDocument *doc = new TiXmlDocument;
- TiXmlElement *xrequest = new TiXmlElement("request");
- xrequest->SetAttribute("rtype", "notify");
- xrequest->SetAttribute("message", "buttonclick");
- doc->LinkEndChild(xrequest);
-
- add_raw_request(doc);
-}
-
-/**
- * Called only in the main thread, indirectly from
- * splash_button_clicked_sub_thread(), as the result of the user clicking on
- * the button visible in the splash window.
- */
-void P3DInstance::
-splash_button_clicked_main_thread() {
- if (is_failed()) {
- // Can't click the button after we've failed.
- nout << "Ignoring click for failed instance\n";
- return;
- }
-
- if (!_p3d_trusted) {
- auth_button_clicked();
- } else if (_session == nullptr) {
- play_button_clicked();
- } else {
- nout << "Ignoring click for already-started instance\n";
- }
-}
-
-/**
- * Called to authorize the p3d file by the user clicking the red "auth"
- * button.
- */
-void P3DInstance::
-auth_button_clicked() {
- // Delete the previous session and create a new one.
- if (_auth_session != nullptr) {
- _auth_session->shutdown(false);
- p3d_unref_delete(_auth_session);
- _auth_session = nullptr;
- }
-
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- _auth_session = inst_mgr->authorize_instance(this);
- _auth_session->ref();
-}
-
-/**
- * Called to start the game by the user clicking the green "play" button, or
- * by JavaScript calling play().
- */
-void P3DInstance::
-play_button_clicked() {
- if (_session == nullptr && _p3d_trusted) {
- set_button_image(IT_none);
- if (!_download_started) {
- // Now we initiate the download.
- _auto_install = true;
- _auto_start = true;
- if (get_packages_info_ready()) {
- ready_to_install();
- }
-
- } else {
- set_background_image(IT_launch);
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- inst_mgr->start_instance(this);
- }
- }
-}
-
-/**
- * Called by the P3DAuthSession code in a sub-thread when the auth dialog
- * exits (for instance, because the user approved the certificate, or
- * cancelled).
- */
-void P3DInstance::
-auth_finished_sub_thread() {
- TiXmlDocument *doc = new TiXmlDocument;
- TiXmlElement *xrequest = new TiXmlElement("request");
- xrequest->SetAttribute("rtype", "notify");
- xrequest->SetAttribute("message", "authfinished");
- doc->LinkEndChild(xrequest);
-
- add_raw_request(doc);
-}
-
-/**
- * Called only in the main thread, indirectly from auth_finished_sub_thread(),
- * as the result of the user closing the auth dialog.
- */
-void P3DInstance::
-auth_finished_main_thread() {
- // Set this flag to indicate that the user has clicked on the red "auth"
- // button. This eliminates the need to click on the green "start" button.
- _auth_button_clicked = true;
-
- // After the authorization program has returned, check the signature again.
- check_p3d_signature();
-}
-
-/**
- * Stops the instance (if it is running) and deletes any packages referenced
- * by the instance. This is normally called by JavaScript, via
- * P3DMainObject::call_uninstall().
- */
-bool P3DInstance::
-uninstall_packages() {
- if (_packages.empty()) {
- // If we have no packages (for instance, because we're untrusted), we
- // can't uninstall anything.
- nout << "Uninstall failed: no packages.\n";
- return false;
- }
-
- nout << "Uninstalling " << _packages.size() << " packages\n";
-
- Packages::const_iterator pi;
- for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
- P3DPackage *package = (*pi);
- if (package != _image_package && package != _certlist_package &&
- package != _p3dcert_package) {
- package->uninstall();
- }
- }
-
- // Also clean up the start directory, if we have a custom start dir. We
- // won't do this if verify_contents is 'none'.
- P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
- if (inst_mgr->get_verify_contents() != P3D_VC_never) {
- string start_dir_suffix = get_start_dir_suffix();
- if (!start_dir_suffix.empty()) {
- string start_dir = inst_mgr->get_start_dir() + start_dir_suffix;
- nout << "Cleaning up start directory " << start_dir << "\n";
- inst_mgr->delete_directory_recursively(start_dir);
- }
- }
-
- return true;
-}
-
-/**
- * Stops the instance (if it is running) and deletes all packages downloaded
- * from any of the host(s) referenced by the instance. This is a more
- * aggressive uninstall than uninstall_packages(). This is normally called by
- * JavaScript, via P3DMainObject::call_uninstall().
- */
-bool P3DInstance::
-uninstall_host() {
- if (_packages.empty()) {
- // If we have no packages (for instance, because we're untrusted), we
- // can't uninstall anything.
- nout << "Uninstall failed: no packages.\n";
- return false;
- }
-
- uninstall_packages();
-
- // Collect the set of hosts referenced by this instance.
- std::set hosts;
- Packages::const_iterator pi;
- for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
- P3DPackage *package = (*pi);
- if (package != _image_package && package != _certlist_package &&
- package != _p3dcert_package) {
- hosts.insert(package->get_host());
- }
- }
- nout << "Uninstalling " << hosts.size() << " hosts\n";
-
- // Uninstall all of them.
- std::set::iterator hi;
- for (hi = hosts.begin(); hi != hosts.end(); ++hi) {
- P3DHost *host = (*hi);
- host->uninstall();
- }
-
- return true;
-}
-
-/**
- * The private implementation of set_p3d_filename(), this does all the work
- * except for updating p3d_basename. It is intended to be called internally,
- * and might be passed a temporary filename.
- */
-void P3DInstance::
-priv_set_p3d_filename(const string &p3d_filename, const int &p3d_offset) {
- if (!_fparams.get_p3d_filename().empty()) {
- nout << "p3d_filename already set to: " << _fparams.get_p3d_filename()
- << ", trying to set to " << p3d_filename << "\n";
- return;
- }
-
- _fparams.set_p3d_filename(p3d_filename);
- // The default for p3d_offset is -1, which means not to change it.
- if (p3d_offset >= 0) {
- _fparams.set_p3d_offset(p3d_offset);
- }
- _got_fparams = true;
-
- _main_object->set_float_property("instanceDownloadProgress", 1.0);
-
- // Generate a special notification: onpluginload, indicating the plugin has
- // read its parameters and is ready to be queried (even if Python has not
- // yet started).
- send_notify("onpluginload");
-
- if (!_mf_reader.open_read(_fparams.get_p3d_filename(), _fparams.get_p3d_offset())) {
- if (_fparams.get_p3d_offset() == 0) {
- nout << "Couldn't read " << _fparams.get_p3d_filename() << "\n";
- } else {
- nout << "Couldn't read " << _fparams.get_p3d_filename()
- << " at offset " << _fparams.get_p3d_offset() << "\n";
- }
- set_failed();
- return;
- }
-
- check_p3d_signature();
-}
-
-/**
- * Determines _p3d_basename from the indicated URL.
- */
-void P3DInstance::
-determine_p3d_basename(const string &p3d_url) {
- string file_part = p3d_url;
- size_t question = file_part.find('?');
- if (question != string::npos) {
- file_part = file_part.substr(0, question);
- }
- size_t slash = file_part.rfind('/');
- if (slash != string::npos) {
- file_part = file_part.substr(slash + 1);
- }
- _p3d_basename = file_part;
-
- nout << "p3d_basename = " << _p3d_basename << "\n";
-}
-
-/**
- * Returns true if the indicated origin_match string, one of either run_origin
- * or script_origin from the p3d_info.xml file, matches the origin of the page
- * that embedded the p3d file.
- */
-bool P3DInstance::
-check_matches_origin(const string &origin_match) {
- // First, separate the string up at the semicolons.
- size_t p = 0;
- size_t semicolon = origin_match.find(';');
- while (semicolon != string::npos) {
- if (check_matches_origin_one(origin_match.substr(p, semicolon - p))) {
- return true;
- }
- p = semicolon + 1;
- semicolon = origin_match.find(';', p);
- }
- if (check_matches_origin_one(origin_match.substr(p))) {
- return true;
- }
-
- // It doesn't match any of the semicolon-delimited strings within
- // origin_match.
- return false;
-}
-
-/**
- * Called for each semicolon-delimited string within origin_match passed to
- * check_matches_origin().
- */
-bool P3DInstance::
-check_matches_origin_one(const string &origin_match) {
- // Do we have a protocol?
- size_t p = 0;
- size_t colon = origin_match.find(':');
- if (colon + 1 < origin_match.length() && origin_match[colon + 1] == '/') {
- // Yes. It should therefore match the protocol we have in the origin.
- string protocol = origin_match.substr(0, colon + 1);
- if (!check_matches_component(_origin_protocol, protocol)) {
- return false;
- }
- p = colon + 2;
- // We'll support both http:hostname and http:hostname, in case the user is
- // sloppy.
- if (p < origin_match.length() && origin_match[p] == '/') {
- ++p;
- }
- colon = origin_match.find(':', p);
- }
-
- // Do we have a port?
- if (colon < origin_match.length() && isdigit(origin_match[colon + 1])) {
- // Yes. It should therefore match the port we have in the origin.
- string port = origin_match.substr(colon + 1);
- if (!check_matches_component(_origin_port, port)) {
- return false;
- }
- }
-
- // The hostname should also match what we have in the origin.
- string hostname = origin_match.substr(p, colon - p);
- if (!check_matches_hostname(_origin_hostname, hostname)) {
- return false;
- }
-
- // Everything matches.
- return true;
-}
-
-/**
- * Matches the hostname of check_matches_origin: the individual components of
- * the hostname are matched independently, with '**.' allowed at the beginning
- * to indicate zero or more prefixes. Returns true on match, false on
- * failure.
- */
-bool P3DInstance::
-check_matches_hostname(const string &orig, const string &match) {
- // First, separate both strings up at the dots.
- vector orig_components;
- separate_components(orig_components, orig);
-
- vector