diff --git a/direct/src/plugin/Sources.pp b/direct/src/plugin/Sources.pp
index 92e66cd358..f9efe6ce34 100644
--- a/direct/src/plugin/Sources.pp
+++ b/direct/src/plugin/Sources.pp
@@ -18,6 +18,7 @@
p3dFileDownload.h p3dFileDownload.I \
p3dInstance.h p3dInstance.I \
p3dInstanceManager.h p3dInstanceManager.I \
+ p3dMultifileReader.h p3dMultifileReader.I \
p3dPackage.h p3dPackage.I \
p3dSession.h p3dSession.I
@@ -27,6 +28,7 @@
p3dFileDownload.cxx \
p3dInstance.cxx \
p3dInstanceManager.cxx \
+ p3dMultifileReader.cxx \
p3dPackage.cxx \
p3dSession.cxx
diff --git a/direct/src/plugin/make_package.py b/direct/src/plugin/make_package.py
index 6bc1370aa8..5e35bcf1e0 100755
--- a/direct/src/plugin/make_package.py
+++ b/direct/src/plugin/make_package.py
@@ -27,10 +27,7 @@ Options:
import sys
import getopt
import os
-import tarfile
-import gzip
-import stat
-import md5
+import zlib
import direct
from pandac.PandaModules import *
@@ -42,25 +39,15 @@ class FileSpec:
def __init__(self, filename, pathname):
self.filename = filename
self.pathname = pathname
- self.size = 0
- self.timestamp = 0
- self.hash = None
- s = os.stat(self.pathname)
- self.size = s[stat.ST_SIZE]
- self.timestamp = s[stat.ST_MTIME]
+ self.size = pathname.getFileSize()
+ self.timestamp = pathname.getTimestamp()
- m = md5.new()
- f = open(self.pathname, 'rb')
- data = f.read(4096)
- while data:
- m.update(data)
- data = f.read(4096)
- f.close()
+ hv = HashVal()
+ hv.hashFile(pathname)
+ self.hash = hv.asHex()
- self.hash = m.hexdigest()
-
- def get_params(self):
+ def getParams(self):
return 'filename="%s" size=%s timestamp=%s hash="%s"' % (
self.filename, self.size, self.timestamp, self.hash)
@@ -85,9 +72,11 @@ class PackageMaker:
self.cleanDir(self.stageDir)
- uncompressedArchiveBasename = '%s.tar' % (self.packageFullname)
- uncompressedArchivePathname = os.path.join(self.stageDir, uncompressedArchiveBasename)
- self.archive = tarfile.open(uncompressedArchivePathname, 'w')
+ uncompressedArchiveBasename = '%s.mf' % (self.packageFullname)
+ uncompressedArchivePathname = Filename(self.stageDir, uncompressedArchiveBasename)
+ self.archive = Multifile()
+ if not self.archive.openWrite(uncompressedArchivePathname):
+ raise IOError, "Couldn't open %s for writing" % (uncompressedArchivePathname)
self.components = []
@@ -96,34 +85,37 @@ class PackageMaker:
uncompressedArchive = FileSpec(uncompressedArchiveBasename, uncompressedArchivePathname)
- compressedArchiveBasename = '%s.tgz' % (self.packageFullname)
- compressedArchivePathname = os.path.join(self.stageDir, compressedArchiveBasename)
+ compressedArchiveBasename = '%s.mf.pz' % (self.packageFullname)
+ compressedArchivePathname = Filename(self.stageDir, compressedArchiveBasename)
print "\ncompressing"
- f = open(uncompressedArchivePathname, 'rb')
- gz = gzip.open(compressedArchivePathname, 'w', 9)
- data = f.read(4096)
+
+ source = open(uncompressedArchivePathname.toOsSpecific(), 'rb')
+ target = open(compressedArchivePathname.toOsSpecific(), 'w')
+ z = zlib.compressobj(9)
+ data = source.read(4096)
while data:
- gz.write(data)
- data = f.read(4096)
- gz.close()
- f.close()
+ target.write(z.compress(data))
+ data = source.read(4096)
+ target.write(z.flush())
+ target.close()
+ source.close()
compressedArchive = FileSpec(compressedArchiveBasename, compressedArchivePathname)
- os.unlink(uncompressedArchivePathname)
+ uncompressedArchivePathname.unlink()
descFileBasename = '%s.xml' % (self.packageFullname)
- descFilePathname = os.path.join(self.stageDir, descFileBasename)
+ descFilePathname = Filename(self.stageDir, descFileBasename)
- f = open(descFilePathname, 'w')
+ f = open(descFilePathname.toOsSpecific(), 'w')
print >> f, ''
print >> f, ''
print >> f, '' % (self.packageName, self.packageVersion)
- print >> f, ' ' % (uncompressedArchive.get_params())
- print >> f, ' ' % (compressedArchive.get_params())
+ print >> f, ' ' % (uncompressedArchive.getParams())
+ print >> f, ' ' % (compressedArchive.getParams())
for file in self.components:
- print >> f, ' ' % (file.get_params())
+ print >> f, ' ' % (file.getParams())
print >> f, ''
f.close()
@@ -132,46 +124,41 @@ class PackageMaker:
""" Remove all the files in the named directory. Does not
operate recursively. """
- for filename in os.listdir(dirname):
- pathname = os.path.join(dirname, filename)
- try:
- os.unlink(pathname)
- except OSError:
- pass
+ for filename in os.listdir(dirname.toOsSpecific()):
+ pathname = Filename(dirname, filename)
+ pathname.unlink()
def addComponents(self):
""" Walks through all the files in the start directory and
adds them to the archive. Recursively visits
sub-directories. """
- startDir = self.startDir
+ startDir = self.startDir.toOsSpecific()
if startDir.endswith(os.sep):
startDir = startDir[:-1]
- elif os.altsep and startDir.endswith(os.altsep):
- startDir = startDir[:-1]
prefix = startDir + os.sep
for dirpath, dirnames, filenames in os.walk(startDir):
if dirpath == startDir:
localpath = ''
else:
assert dirpath.startswith(prefix)
- localpath = dirpath[len(prefix):]
+ localpath = dirpath[len(prefix):] + '/'
for basename in filenames:
- file = FileSpec(os.path.join(localpath, basename),
- os.path.join(startDir, basename))
+ file = FileSpec(localpath + basename,
+ Filename(self.startDir, basename))
print file.filename
self.components.append(file)
- self.archive.add(file.pathname, file.filename, recursive = False)
+ self.archive.addSubfile(file.filename, file.pathname, 0)
def makePackage(args):
opts, args = getopt.getopt(args, 'd:p:v:h')
pm = PackageMaker()
- pm.startDir = '.'
+ pm.startDir = Filename('.')
for option, value in opts:
if option == '-d':
- pm.stageDir = Filename.fromOsSpecific(value).toOsSpecific()
+ pm.stageDir = Filename.fromOsSpecific(value)
elif option == '-p':
pm.packageName = value
elif option == '-v':
diff --git a/direct/src/plugin/p3dMultifileReader.I b/direct/src/plugin/p3dMultifileReader.I
new file mode 100644
index 0000000000..9552da3063
--- /dev/null
+++ b/direct/src/plugin/p3dMultifileReader.I
@@ -0,0 +1,40 @@
+// Filename: p3dMultifileReader.I
+// Created by: drose (15Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+// Function: P3DMultifileReader::read_uint16
+// Access: Private
+// Description: Extracts an unsigned short from the file.
+////////////////////////////////////////////////////////////////////
+inline unsigned int P3DMultifileReader::
+read_uint16() {
+ unsigned int a = _in.get();
+ unsigned int b = _in.get();
+ return (b << 8) | a;
+}
+
+////////////////////////////////////////////////////////////////////
+// Function: P3DMultifileReader::read_uint32
+// Access: Private
+// Description: Extracts an unsigned long from the file.
+////////////////////////////////////////////////////////////////////
+inline unsigned int P3DMultifileReader::
+read_uint32() {
+ unsigned int a = _in.get();
+ unsigned int b = _in.get();
+ unsigned int c = _in.get();
+ unsigned int d = _in.get();
+ return (d << 24) | (c << 16) | (b << 8) | a;
+}
diff --git a/direct/src/plugin/p3dMultifileReader.cxx b/direct/src/plugin/p3dMultifileReader.cxx
new file mode 100644
index 0000000000..3b09f5bc6e
--- /dev/null
+++ b/direct/src/plugin/p3dMultifileReader.cxx
@@ -0,0 +1,162 @@
+// Filename: p3dMultifileReader.cxx
+// Created by: drose (15Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "p3dMultifileReader.h"
+
+// This sequence of bytes begins each Multifile to identify it as a
+// Multifile.
+const char P3DMultifileReader::_header[] = "pmf\0\n\r";
+const size_t P3DMultifileReader::_header_size = 6;
+
+const int P3DMultifileReader::_current_major_ver = 1;
+const int P3DMultifileReader::_current_minor_ver = 1;
+
+////////////////////////////////////////////////////////////////////
+// Function: P3DMultifileReader::Constructor
+// Access: Public
+// Description:
+////////////////////////////////////////////////////////////////////
+P3DMultifileReader::
+P3DMultifileReader() {
+}
+
+////////////////////////////////////////////////////////////////////
+// Function: P3DMultifileReader::extract
+// Access: Public
+// Description: Reads the named multifile, and extracts all files
+// within it to the indicated directory. Returns true
+// on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool P3DMultifileReader::
+extract(const string &pathname, const string &to_dir) {
+ _subfiles.clear();
+
+ _in.open(pathname.c_str(), ios::in | ios::binary);
+ if (!_in) {
+ cerr << "Couldn't open " << pathname << "\n";
+ return false;
+ }
+
+ for (size_t i = 0; i < _header_size; ++i) {
+ int ch = _in.get();
+ if (ch != _header[i]) {
+ cerr << "Failed header check: " << pathname << "\n";
+ return false;
+ }
+ }
+
+ unsigned int major = read_uint16();
+ unsigned int minor = read_uint16();
+ if (major != _current_major_ver || minor != _current_minor_ver) {
+ cerr << "Incompatible multifile version: " << pathname << "\n";
+ return false;
+ }
+
+ unsigned int scale = read_uint32();
+ if (scale != 1) {
+ cerr << "Unsupported scale factor in " << pathname << "\n";
+ return false;
+ }
+
+ // We don't care about the timestamp.
+ read_uint32();
+
+ if (!read_index()) {
+ cerr << "Error reading multifile index\n";
+ return false;
+ }
+
+ // Now walk through all of the files.
+ Subfiles::iterator si;
+ for (si = _subfiles.begin(); si != _subfiles.end(); ++si) {
+ const Subfile &s = (*si);
+ cerr << s._filename << "\n";
+
+ string output_pathname = to_dir + "/" + s._filename;
+ ofstream out(output_pathname.c_str(), ios::out | ios::trunc | ios::binary);
+ if (!out) {
+ cerr << "Unable to create " << output_pathname << "\n";
+ return false;
+ }
+
+ _in.seekg(s._start);
+
+ static const size_t buffer_size = 1024;
+ char buffer[buffer_size];
+
+ size_t remaining_data = s._length;
+ _in.read(buffer, min(buffer_size, remaining_data));
+ size_t count = _in.gcount();
+ while (count != 0) {
+ remaining_data -= count;
+ out.write(buffer, count);
+ _in.read(buffer, min(buffer_size, remaining_data));
+ count = _in.gcount();
+ }
+
+ if (remaining_data != 0) {
+ cerr << "Unable to extract " << s._filename << "\n";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////
+// Function: P3DMultifileReader::read_index
+// Access: Public
+// Description: Assuming the file stream is positioned at the first
+// record, reads all of the records into the _subfiles
+// list. Returns true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool P3DMultifileReader::
+read_index() {
+ unsigned int next_entry = read_uint32();
+ if (!_in) {
+ return false;
+ }
+ while (next_entry != 0) {
+ Subfile s;
+ s._start = read_uint32();
+ s._length = read_uint32();
+ unsigned int flags = read_uint16();
+ if (flags != 0) {
+ cerr << "Unsupported per-subfile options in multifile\n";
+ return false;
+ }
+ read_uint32();
+ size_t name_length = read_uint16();
+ char *buffer = new char[name_length];
+ _in.read(buffer, name_length);
+
+ // The filenames are xored with 0xff just for fun.
+ for (size_t ni = 0; ni < name_length; ++ni) {
+ buffer[ni] ^= 0xff;
+ }
+
+ s._filename = string(buffer, name_length);
+ delete[] buffer;
+
+ _subfiles.push_back(s);
+
+ _in.seekg(next_entry);
+ next_entry = read_uint32();
+ if (!_in) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/direct/src/plugin/p3dMultifileReader.h b/direct/src/plugin/p3dMultifileReader.h
new file mode 100644
index 0000000000..d8efd15ab5
--- /dev/null
+++ b/direct/src/plugin/p3dMultifileReader.h
@@ -0,0 +1,59 @@
+// Filename: p3dMultifileReader.h
+// Created by: drose (15Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef P3DMULTIFILEREADER_H
+#define P3DMULTIFILEREADER_H
+
+#include "p3d_plugin_common.h"
+
+////////////////////////////////////////////////////////////////////
+// Class : P3DMultifileReader
+// Description : A way-simple implementation of Panda's multifile
+// reader. See panda/src/express/multifile.cxx for a
+// full description of the binary format. This
+// implementation doesn't support per-subfile
+// compression or encryption.
+////////////////////////////////////////////////////////////////////
+class P3DMultifileReader {
+public:
+ P3DMultifileReader();
+
+ bool extract(const string &pathname, const string &to_dir);
+
+private:
+ bool read_index();
+ inline unsigned int read_uint16();
+ inline unsigned int read_uint32();
+
+ ifstream _in;
+
+ class Subfile {
+ public:
+ string _filename;
+ size_t _start;
+ size_t _length;
+ };
+
+ typedef vector Subfiles;
+ Subfiles _subfiles;
+
+ static const char _header[];
+ static const size_t _header_size;
+ static const int _current_major_ver;
+ static const int _current_minor_ver;
+};
+
+#include "p3dMultifileReader.I"
+
+#endif
diff --git a/direct/src/plugin/p3dPackage.I b/direct/src/plugin/p3dPackage.I
index 379b853251..96aec4d1d0 100755
--- a/direct/src/plugin/p3dPackage.I
+++ b/direct/src/plugin/p3dPackage.I
@@ -47,6 +47,26 @@ get_package_dir() const {
return _package_dir;
}
+////////////////////////////////////////////////////////////////////
+// Function: P3DPackage::get_package_name
+// Access: Public
+// Description: Returns the name of this package.
+////////////////////////////////////////////////////////////////////
+inline const string &P3DPackage::
+get_package_name() const {
+ return _package_name;
+}
+
+////////////////////////////////////////////////////////////////////
+// Function: P3DPackage::get_package_version
+// Access: Public
+// Description: Returns the version string of this package.
+////////////////////////////////////////////////////////////////////
+inline const string &P3DPackage::
+get_package_version() const {
+ return _package_version;
+}
+
////////////////////////////////////////////////////////////////////
// Function: P3DPackage::decode_hexdigit
// Access: Private
diff --git a/direct/src/plugin/p3dPackage.cxx b/direct/src/plugin/p3dPackage.cxx
index 91d535c14b..2a4b5a516f 100755
--- a/direct/src/plugin/p3dPackage.cxx
+++ b/direct/src/plugin/p3dPackage.cxx
@@ -14,10 +14,10 @@
#include "p3dPackage.h"
#include "p3dInstanceManager.h"
+#include "p3dMultifileReader.h"
#include "openssl/md5.h"
#include "zlib.h"
-#include "libtar.h"
#include
#include
@@ -301,8 +301,8 @@ uncompress_archive() {
string source_pathname = _package_dir + "/" + _compressed_archive._filename;
string target_pathname = _package_dir + "/" + _uncompressed_archive._filename;
- gzFile source = gzopen(source_pathname.c_str(), "rb");
- if (source == NULL) {
+ ifstream source(source_pathname.c_str(), ios::in | ios::binary);
+ if (!source) {
cerr << "Couldn't open " << source_pathname << "\n";
report_done(false);
return;
@@ -312,38 +312,85 @@ uncompress_archive() {
if (!target) {
cerr << "Couldn't write to " << target_pathname << "\n";
report_done(false);
+ return;
}
- static const int buffer_size = 1024;
- char buffer[buffer_size];
+ z_stream z;
+ z.next_in = Z_NULL;
+ z.avail_in = 0;
+ z.zalloc = Z_NULL;
+ z.zfree = Z_NULL;
+ z.opaque = Z_NULL;
+ z.msg = (char *)"no error message";
- int count = gzread(source, buffer, buffer_size);
- while (count > 0) {
- target.write(buffer, count);
- count = gzread(source, buffer, buffer_size);
+ int result = inflateInit(&z);
+ if (result < 0) {
+ cerr << z.msg << "\n";
+ report_done(false);
+ return;
+ }
+
+ static const int decompress_buffer_size = 1024;
+ char decompress_buffer[decompress_buffer_size];
+ static const int write_buffer_size = 1024;
+ char write_buffer[write_buffer_size];
+
+ bool eof = false;
+ int flush = 0;
+
+ while (true) {
+ if (z.avail_in == 0 && !eof) {
+ source.read(decompress_buffer, decompress_buffer_size);
+ size_t read_count = source.gcount();
+ eof = (read_count == 0 || source.eof() || source.fail());
+
+ z.next_in = (Bytef *)decompress_buffer;
+ z.avail_in = read_count;
+ }
+
+ z.next_out = (Bytef *)write_buffer;
+ z.avail_out = write_buffer_size;
+ int result = inflate(&z, flush);
+ if (z.avail_out < write_buffer_size) {
+ target.write(write_buffer, write_buffer_size - z.avail_out);
+ if (!target) {
+ cerr << "Couldn't write entire file to " << target_pathname << "\n";
+ report_done(false);
+ return;
+ }
+ }
+
+ if (result == Z_STREAM_END) {
+ // Here's the end of the file.
+ break;
+
+ } else if (result == Z_BUF_ERROR && flush == 0) {
+ // We might get this if no progress is possible, for instance if
+ // the input stream is truncated. In this case, tell zlib to
+ // dump everything it's got.
+ flush = Z_FINISH;
+
+ } else if (result < 0) {
+ cerr << z.msg << "\n";
+ inflateEnd(&z);
+ report_done(false);
+ return;
+ }
}
- if (count < 0) {
- cerr << "gzip error decompressing " << source_pathname << "\n";
- int errnum;
- cerr << gzerror(source, &errnum) << "\n";
- gzclose(source);
- report_done(false);
- return;
- }
-
- gzclose(source);
-
- if (!target) {
- cerr << "Couldn't write entire file to " << target_pathname << "\n";
+ result = inflateEnd(&z);
+ if (result < 0) {
+ cerr << z.msg << "\n";
report_done(false);
return;
}
+ source.close();
target.close();
if (!_uncompressed_archive.full_verify(_package_dir)) {
- cerr << "after uncompressing " << target_pathname << ", failed hash check\n";
+ cerr << "after uncompressing " << target_pathname
+ << ", failed hash check\n";
report_done(false);
return;
}
@@ -364,25 +411,14 @@ extract_archive() {
cerr << "extracting " << _uncompressed_archive._filename << "\n";
string source_pathname = _package_dir + "/" + _uncompressed_archive._filename;
-
- TAR *tar = NULL;
- int result = tar_open
- (&tar, (char *)source_pathname.c_str(), NULL, O_RDONLY, 0666, TAR_VERBOSE);
- if (result != 0) {
- cerr << "Unable to open " << source_pathname << "\n";
+ P3DMultifileReader reader;
+ if (!reader.extract(source_pathname, _package_dir)) {
+ cerr << "Failure extracting " << _uncompressed_archive._filename
+ << "\n";
report_done(false);
return;
}
- while (th_read(tar) == 0) {
- string basename = th_get_pathname(tar);
- cerr << basename << "\n";
- string pathname = _package_dir + "/" + basename;
- tar_extract_file(tar, (char *)pathname.c_str());
- }
-
- tar_close(tar);
-
cerr << "done extracting\n";
report_done(true);
}
diff --git a/direct/src/plugin/p3dPackage.h b/direct/src/plugin/p3dPackage.h
index 9832d869d6..93b14f373e 100755
--- a/direct/src/plugin/p3dPackage.h
+++ b/direct/src/plugin/p3dPackage.h
@@ -43,6 +43,8 @@ public:
inline bool get_ready() const;
inline bool get_failed() const;
inline const string &get_package_dir() const;
+ inline const string &get_package_name() const;
+ inline const string &get_package_version() const;
void set_callback(Callback *callback);
void cancel_callback(Callback *callback);
diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx
index a1e72af918..2e49a4d29c 100644
--- a/direct/src/plugin/p3dSession.cxx
+++ b/direct/src/plugin/p3dSession.cxx
@@ -649,7 +649,12 @@ package_ready(P3DPackage *package, bool success) {
if (this == _session->_panda3d_callback) {
_session->_panda3d_callback = NULL;
if (package == _session->_panda3d) {
- _session->start_p3dpython();
+ if (success) {
+ _session->start_p3dpython();
+ } else {
+ cerr << "Failed to install " << package->get_package_name()
+ << "_" << package->get_package_version() << "\n";
+ }
} else {
cerr << "Unexpected panda3d package: " << package << "\n";
}
diff --git a/direct/src/plugin/p3d_plugin_composite1.cxx b/direct/src/plugin/p3d_plugin_composite1.cxx
index 3ff57ea1e3..950714b314 100644
--- a/direct/src/plugin/p3d_plugin_composite1.cxx
+++ b/direct/src/plugin/p3d_plugin_composite1.cxx
@@ -3,5 +3,6 @@
#include "p3dFileDownload.cxx"
#include "p3dInstance.cxx"
#include "p3dInstanceManager.cxx"
+#include "p3dMultifileReader.cxx"
#include "p3dPackage.cxx"
#include "p3dSession.cxx"