|
|
@@ -8,6 +8,7 @@ import os
|
|
|
import glob
|
|
|
import marshal
|
|
|
import new
|
|
|
+from direct.showbase import Loader
|
|
|
from direct.showutil import FreezeTool
|
|
|
from direct.directnotify.DirectNotifyGlobal import *
|
|
|
from pandac.PandaModules import *
|
|
|
@@ -31,10 +32,13 @@ class Packager:
|
|
|
self.deleteTemp = deleteTemp
|
|
|
|
|
|
class Package:
|
|
|
- def __init__(self, packageName):
|
|
|
+ def __init__(self, packageName, packager):
|
|
|
self.packageName = packageName
|
|
|
+ self.packager = packager
|
|
|
self.version = 'dev'
|
|
|
self.files = []
|
|
|
+ self.compressionLevel = 0
|
|
|
+ self.importedMapsDir = 'imported_maps'
|
|
|
|
|
|
# This records the current list of modules we have added so
|
|
|
# far.
|
|
|
@@ -52,31 +56,58 @@ class Packager:
|
|
|
except OSError:
|
|
|
pass
|
|
|
|
|
|
- multifile = Multifile()
|
|
|
- multifile.openReadWrite(packageFilename)
|
|
|
+ self.multifile = Multifile()
|
|
|
+ self.multifile.openReadWrite(packageFilename)
|
|
|
|
|
|
- sourceFilename = {}
|
|
|
- targetFilename = {}
|
|
|
+ # Build up a cross-reference of files we've already
|
|
|
+ # discovered.
|
|
|
+ self.sourceFilenames = {}
|
|
|
+ self.targetFilenames = {}
|
|
|
for file in self.files:
|
|
|
if not file.newName:
|
|
|
file.newName = file.filename
|
|
|
- sourceFilename[file.filename] = file
|
|
|
- targetFilename[file.newName] = file
|
|
|
+
|
|
|
+ # Convert the source filename to an unambiguous
|
|
|
+ # filename for searching.
|
|
|
+ filename = Filename(file.filename)
|
|
|
+ filename.makeCanonical()
|
|
|
+
|
|
|
+ self.sourceFilenames[filename] = file
|
|
|
+ self.targetFilenames[file.newName] = file
|
|
|
|
|
|
for file in self.files:
|
|
|
ext = file.filename.getExtension()
|
|
|
if ext == 'py':
|
|
|
self.addPyFile(file)
|
|
|
else:
|
|
|
- # An ordinary file.
|
|
|
- multifile.addSubfile(file.newName, file.filename, 0)
|
|
|
+ if ext == 'pz':
|
|
|
+ # Strip off an implicit .pz extension.
|
|
|
+ filename = Filename(file.filename)
|
|
|
+ filename.setExtension('')
|
|
|
+ filename = Filename(filename.cStr())
|
|
|
+ ext = filename.getExtension()
|
|
|
+
|
|
|
+ filename = Filename(file.newName)
|
|
|
+ if filename.getExtension() == 'pz':
|
|
|
+ filename.setExtension('')
|
|
|
+ file.newName = filename.cStr()
|
|
|
+
|
|
|
+ if ext == 'egg':
|
|
|
+ self.addEggFile(file)
|
|
|
+ elif ext == 'bam':
|
|
|
+ self.addBamFile(file)
|
|
|
+ elif ext in self.packager.imageExtensions:
|
|
|
+ self.addTexture(file)
|
|
|
+ else:
|
|
|
+ # An ordinary file.
|
|
|
+ self.multifile.addSubfile(file.newName, file.filename, self.compressionLevel)
|
|
|
|
|
|
# Pick up any unfrozen Python files.
|
|
|
self.freezer.done()
|
|
|
- self.freezer.addToMultifile(multifile)
|
|
|
+ self.freezer.addToMultifile(self.multifile)
|
|
|
|
|
|
- multifile.repack()
|
|
|
- multifile.close()
|
|
|
+ self.multifile.repack()
|
|
|
+ self.multifile.close()
|
|
|
|
|
|
# Now that all the files have been packed, we can delete
|
|
|
# the temporary files.
|
|
|
@@ -106,6 +137,122 @@ class Packager:
|
|
|
self.freezer.addModule(moduleName, newName = moduleName,
|
|
|
filename = file.filename)
|
|
|
|
|
|
+ def addEggFile(self, file):
|
|
|
+ # Precompile egg files to bam's.
|
|
|
+ np = self.packager.loader.loadModel(file.filename, okMissing = True)
|
|
|
+ if not np:
|
|
|
+ raise StandardError, 'Could not read egg file %s' % (file.filename)
|
|
|
+
|
|
|
+ bamName = Filename(file.newName)
|
|
|
+ bamName.setExtension('bam')
|
|
|
+ self.addNode(np.node(), bamName.cStr())
|
|
|
+
|
|
|
+ def addBamFile(self, file):
|
|
|
+ # Load the bam file so we can massage its textures.
|
|
|
+ bamFile = BamFile()
|
|
|
+ if not bamFile.openRead(file.filename):
|
|
|
+ raise StandardError, 'Could not read bam file %s' % (file.filename)
|
|
|
+
|
|
|
+ if not bamFile.resolve():
|
|
|
+ raise StandardError, 'Could not resolve bam file %s' % (file.filename)
|
|
|
+
|
|
|
+ node = bamFile.readNode()
|
|
|
+ if not node:
|
|
|
+ raise StandardError, 'Not a model file: %s' % (file.filename)
|
|
|
+
|
|
|
+ self.addNode(node, file.newName)
|
|
|
+
|
|
|
+ def addNode(self, node, filename):
|
|
|
+ """ Converts the indicated node to a bam stream, and adds the
|
|
|
+ bam file to the multifile under the indicated filename. """
|
|
|
+
|
|
|
+ # If the Multifile already has a file by this name, don't
|
|
|
+ # bother adding it again.
|
|
|
+ if self.multifile.findSubfile(filename) >= 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 filename 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(filename, 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()
|
|
|
+
|
|
|
+ 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. """
|
|
|
+
|
|
|
+ assert not filename.isLocal()
|
|
|
+
|
|
|
+ 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 in self.targetFilenames:
|
|
|
+ uniqueId += 1
|
|
|
+ newName = '%s/%s_%s.%s' % (
|
|
|
+ self.importedMapsDir, filename.getBasenameWoExtension(),
|
|
|
+ uniqueId, filename.getExtension())
|
|
|
+
|
|
|
+ file = Packager.PackFile(filename, newName = newName)
|
|
|
+ self.sourceFilenames[filename] = file
|
|
|
+ self.targetFilenames[newName] = file
|
|
|
+ self.addTexture(file)
|
|
|
+
|
|
|
+ return newName
|
|
|
+
|
|
|
+ def addTexture(self, file):
|
|
|
+ """ Adds a texture image to the output. """
|
|
|
+
|
|
|
+ if self.multifile.findSubfile(file.newName) >= 0:
|
|
|
+ # Already have this texture.
|
|
|
+ return
|
|
|
+
|
|
|
+ # Texture file formats are generally already compressed and
|
|
|
+ # not further compressible.
|
|
|
+ self.multifile.addSubfile(file.newName, file.filename, 0)
|
|
|
+
|
|
|
def __init__(self):
|
|
|
|
|
|
# The following are config settings that the caller may adjust
|
|
|
@@ -141,6 +288,17 @@ class Packager:
|
|
|
# are server-side only and should be ignored by the Scrubber.
|
|
|
self.dcClientSuffixes = ['OV']
|
|
|
|
|
|
+ # Get the list of filename extensions that are recognized as
|
|
|
+ # image files.
|
|
|
+ self.imageExtensions = []
|
|
|
+ for type in PNMFileTypeRegistry.getGlobalPtr().getTypes():
|
|
|
+ self.imageExtensions += type.getExtensions()
|
|
|
+
|
|
|
+ # A Loader for loading models.
|
|
|
+ self.loader = Loader.Loader(self)
|
|
|
+ self.sfxManagerList = None
|
|
|
+ self.musicManager = None
|
|
|
+
|
|
|
def setup(self):
|
|
|
""" Call this method to initialize the class after filling in
|
|
|
some of the values in the constructor. """
|
|
|
@@ -337,7 +495,7 @@ class Packager:
|
|
|
basename of the package. Follow this with a number of calls
|
|
|
to file() etc., and close the package with endPackage(). """
|
|
|
|
|
|
- package = self.Package(packageName)
|
|
|
+ package = self.Package(packageName, self)
|
|
|
if self.currentPackage:
|
|
|
package.freezer.excludeFrom(self.currentPackage.freezer)
|
|
|
|