*** empty log message ***
This commit is contained in:
parent
992d7a7a58
commit
0ce8a5861d
|
|
@ -0,0 +1,245 @@
|
|||
""" This module will pack a Panda application, consisting of a
|
||||
directory tree of .py files and models, into a multifile for
|
||||
distribution and running with RunAppMF.py. To run it, use:
|
||||
|
||||
python MakeAppMF.py app.mf [application_root]
|
||||
|
||||
where application_root is the root directory of the application. If
|
||||
it is omitted, the default is the current directory. The root
|
||||
directory should contain at a file named main.py, which is imported to
|
||||
start the application. If main.py contains a function called main(),
|
||||
this function is called after importing it; otherwise, taskMgr.run()
|
||||
is called.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import direct
|
||||
from pandac.PandaModules import *
|
||||
|
||||
vfs = VirtualFileSystem.getGlobalPtr()
|
||||
|
||||
class ArgumentError(AttributeError):
|
||||
pass
|
||||
|
||||
class AppPacker:
|
||||
|
||||
compression_level = 6
|
||||
imported_maps = 'imported_maps'
|
||||
|
||||
# Text files that are copied (and compressed) to the multifile
|
||||
# without processing.
|
||||
text_extensions = [ 'prc' ]
|
||||
|
||||
# Binary files that are copied and compressed without processing.
|
||||
binary_extensions = [ 'ttf', 'wav', 'mid' ]
|
||||
|
||||
# Binary files that are considered uncompressible, and are copied
|
||||
# without compression.
|
||||
uncompressible_extensions = [ 'mp3' ]
|
||||
|
||||
def __init__(self, multifile_name):
|
||||
# Make sure any pre-existing file is removed.
|
||||
Filename(multifile_name).unlink()
|
||||
|
||||
self.multifile = Multifile()
|
||||
if not self.multifile.openReadWrite(multifile_name):
|
||||
raise ArgumentError, 'Could not open %s for writing' % (multifile_name)
|
||||
self.imported_textures = {}
|
||||
|
||||
# Get the list of filename extensions that are recognized as
|
||||
# image files.
|
||||
self.image_extensions = []
|
||||
for type in PNMFileTypeRegistry.getGlobalPtr().getTypes():
|
||||
self.image_extensions += type.getExtensions()
|
||||
|
||||
def scan(self, root):
|
||||
self.root = Filename(root)
|
||||
self.root.makeAbsolute(vfs.getCwd())
|
||||
self._recurse(self.root)
|
||||
|
||||
self.multifile.repack()
|
||||
|
||||
def _recurse(self, filename):
|
||||
dirList = vfs.scanDirectory(filename)
|
||||
if dirList:
|
||||
# It's a directory name. Recurse.
|
||||
for subfile in dirList:
|
||||
self._recurse(subfile.getFilename())
|
||||
return
|
||||
|
||||
# It's a real file. Is it something we care about?
|
||||
ext = filename.getExtension().lower()
|
||||
outFilename = filename
|
||||
if ext == 'pz':
|
||||
# Strip off an implicit .pz extension.
|
||||
outFilename = Filename(filename)
|
||||
outFilename.setExtension('')
|
||||
outFilename = Filename(outFilename.cStr())
|
||||
ext = outFilename.getExtension().lower()
|
||||
|
||||
if ext == 'py':
|
||||
self.addPyFile(filename)
|
||||
elif ext == 'egg':
|
||||
self.addEggFile(filename, outFilename)
|
||||
elif ext == 'bam':
|
||||
self.addBamFile(filename, outFilename)
|
||||
elif ext in self.image_extensions:
|
||||
self.addTexture(filename)
|
||||
elif ext in self.text_extensions:
|
||||
self.addTextFile(filename)
|
||||
elif ext in self.binary_extensions:
|
||||
self.addBinaryFile(filename)
|
||||
elif ext in self.uncompressible_extensions:
|
||||
self.addUncompressibleFile(filename)
|
||||
|
||||
def addPyFile(self, filename):
|
||||
# For now, just add it as an ordinary file. Later we'll
|
||||
# precompile these to .pyo's.
|
||||
self.addTextFile(filename)
|
||||
|
||||
def addEggFile(self, filename, outFilename):
|
||||
# Precompile egg files to bam's.
|
||||
node = loadEggFile(filename)
|
||||
if not node:
|
||||
raise StandardError, 'Could not read egg file %s' % (filename)
|
||||
|
||||
self.addNode(node, outFilename)
|
||||
|
||||
def addBamFile(self, filename, outFilename):
|
||||
# Load the bam file so we can massage its textures.
|
||||
bamFile = BamFile()
|
||||
if not bamFile.openRead(filename):
|
||||
raise StandardError, 'Could not read bam file %s' % (filename)
|
||||
|
||||
if not bamFile.resolve():
|
||||
raise StandardError, 'Could not resolve bam file %s' % (filename)
|
||||
|
||||
node = bamFile.readNode()
|
||||
if not node:
|
||||
raise StandardError, 'Not a model file: %s' % (filename)
|
||||
|
||||
self.addNode(node, outFilename)
|
||||
|
||||
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. """
|
||||
|
||||
# Be sure to import all of the referenced textures, and tell
|
||||
# them their new location within the multifile.
|
||||
|
||||
for tex in NodePath(node).findAllTextures():
|
||||
tex.setFilename(self.addTexture(tex.getFullpath()))
|
||||
if tex.hasAlphaFilename():
|
||||
tex.setAlphaFilename(self.addTexture(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(BTMUnchanged)
|
||||
bamFile.writeObject(node)
|
||||
bamFile.close()
|
||||
node = None
|
||||
|
||||
# Now we have an in-memory bam file.
|
||||
rel = self.makeRelFilename(filename)
|
||||
rel.setExtension('bam')
|
||||
stream.seekg(0)
|
||||
self.multifile.addSubfile(rel.cStr(), stream, self.compression_level)
|
||||
|
||||
# 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 addTexture(self, filename):
|
||||
""" Adds the texture to the multifile, if it has not already
|
||||
been added. If it is not within the root directory, copies it
|
||||
in (virtually) into a directory within the multifile named
|
||||
imported_maps. Returns the new filename within the
|
||||
multifile. """
|
||||
|
||||
assert not filename.isLocal()
|
||||
|
||||
filename = Filename(filename)
|
||||
filename.makeAbsolute(vfs.getCwd())
|
||||
filename.makeTrueCase()
|
||||
|
||||
rel = self.imported_textures.get(filename.cStr())
|
||||
if not rel:
|
||||
rel = Filename(filename)
|
||||
if not rel.makeRelativeTo(self.root, False):
|
||||
# Not within the multifile.
|
||||
rel = Filename(self.imported_maps, filename.getBasename())
|
||||
|
||||
# Need to add it now.
|
||||
self.imported_textures[filename.cStr()] = rel
|
||||
filename = Filename(filename)
|
||||
filename.setBinary()
|
||||
self.multifile.addSubfile(rel.cStr(), filename, 0)
|
||||
|
||||
return rel
|
||||
|
||||
def mapTextureFilename(self, filename):
|
||||
""" Returns the filename within the multifile of the
|
||||
already-added texture. """
|
||||
|
||||
filename = Filename(filename)
|
||||
filename.makeAbsolute(vfs.getCwd())
|
||||
filename.makeTrueCase()
|
||||
|
||||
return self.imported_textures[filename.cStr()]
|
||||
|
||||
def addTextFile(self, filename):
|
||||
""" Adds a generic text file to the multifile. """
|
||||
|
||||
rel = self.makeRelFilename(filename)
|
||||
filename = Filename(filename)
|
||||
filename.setText()
|
||||
self.multifile.addSubfile(rel.cStr(), filename, self.compression_level)
|
||||
|
||||
def addBinaryFile(self, filename):
|
||||
""" Adds a generic binary file to the multifile. """
|
||||
|
||||
rel = self.makeRelFilename(filename)
|
||||
filename = Filename(filename)
|
||||
filename.setBinary()
|
||||
self.multifile.addSubfile(rel.cStr(), filename, self.compression_level)
|
||||
|
||||
def addUncompressibleFile(self, filename):
|
||||
""" Adds a generic binary file to the multifile, without compression. """
|
||||
|
||||
rel = self.makeRelFilename(filename)
|
||||
filename = Filename(filename)
|
||||
filename.setBinary()
|
||||
self.multifile.addSubfile(rel.cStr(), filename, 0)
|
||||
|
||||
def makeRelFilename(self, filename):
|
||||
""" Returns the same filename, relative to self.root """
|
||||
rel = Filename(filename)
|
||||
result = rel.makeRelativeTo(self.root, False)
|
||||
assert result
|
||||
return rel
|
||||
|
||||
|
||||
def makePackedApp(args):
|
||||
if not args:
|
||||
raise ArgumentError, "No destination app specified. Use:\npython MakeAppMF.py app.mf"
|
||||
|
||||
multifile_name = args[0]
|
||||
root = '.'
|
||||
if len(args) > 1:
|
||||
root = args[1]
|
||||
|
||||
if len(args) > 2:
|
||||
raise ArgumentError, "Too many arguments."
|
||||
|
||||
p = AppPacker(multifile_name)
|
||||
p.scan(root)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
makePackedApp(sys.argv[1:])
|
||||
except ArgumentError, e:
|
||||
print e.args[0]
|
||||
sys.exit(1)
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
""" This module can serve as a startup script to play a Panda
|
||||
application packed within a .mf file. Run via:
|
||||
|
||||
python StartMF.py app.mf
|
||||
python RunAppMF.py app.mf
|
||||
|
||||
This is currently experimental, but the intent is to work towards a
|
||||
prepackaged Panda distributable, designed to run applications exactly
|
||||
like this.
|
||||
|
||||
Also see MakeAppMF.py.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
|
@ -17,23 +19,22 @@ import __builtin__
|
|||
|
||||
MultifileRoot = '/mf'
|
||||
|
||||
class ArgumentError(AttributeError):
|
||||
pass
|
||||
|
||||
def runPackedApp(args):
|
||||
if not args:
|
||||
print "No Panda app specified. Use:"
|
||||
print "python StartMF.py app.mf"
|
||||
sys.exit(1)
|
||||
raise ArgumentError, "No Panda app specified. Use:\npython RunAppMF.py app.mf"
|
||||
|
||||
vfs = VirtualFileSystem.getGlobalPtr()
|
||||
|
||||
fname = Filename.fromOsSpecific(args[0])
|
||||
if not vfs.exists(fname):
|
||||
print "No such file: %s" % (args[0])
|
||||
sys.exit(1)
|
||||
raise ArgumentError, "No such file: %s" % (args[0])
|
||||
|
||||
mf = Multifile()
|
||||
if not mf.openRead(fname):
|
||||
print "Not a Panda Multifile: %s" % (args[0])
|
||||
sys.exit(1)
|
||||
raise ArgumentError, "Not a Panda Multifile: %s" % (args[0])
|
||||
|
||||
# Clear *all* the mount points, including "/", so that we no
|
||||
# longer access the disk directly.
|
||||
|
|
@ -78,5 +79,10 @@ def runPackedApp(args):
|
|||
import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
runPackedApp(sys.argv[1:])
|
||||
try:
|
||||
runPackedApp(sys.argv[1:])
|
||||
except ArgumentError, e:
|
||||
print e.args[0]
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue