Browse Source

implicitly decompress/compress for input/output files named .pz; also support vfs-implicit-pz.

David Rose 20 years ago
parent
commit
420ab68136
44 changed files with 834 additions and 474 deletions
  1. 10 8
      panda/src/audiotraits/fmodAudioManager.cxx
  2. 1 1
      panda/src/audiotraits/milesAudioManager.cxx
  3. 4 4
      panda/src/downloadertools/Sources.pp
  4. 0 83
      panda/src/downloadertools/pcompress.cxx
  5. 0 37
      panda/src/downloadertools/pdecompress.cxx
  6. 82 69
      panda/src/downloadertools/pdecrypt.cxx
  7. 98 79
      panda/src/downloadertools/pencrypt.cxx
  8. 138 0
      panda/src/downloadertools/punzip.cxx
  9. 151 0
      panda/src/downloadertools/pzip.cxx
  10. 1 0
      panda/src/egg/Sources.pp
  11. 41 51
      panda/src/egg/eggData.cxx
  12. 6 17
      panda/src/egg2pg/load_egg_file.cxx
  13. 12 0
      panda/src/egg2pg/loaderFileTypeEgg.cxx
  14. 1 0
      panda/src/egg2pg/loaderFileTypeEgg.h
  15. 12 3
      panda/src/express/config_express.cxx
  16. 2 0
      panda/src/express/config_express.h
  17. 2 2
      panda/src/express/virtualFile.I
  18. 3 3
      panda/src/express/virtualFile.cxx
  19. 3 3
      panda/src/express/virtualFile.h
  20. 16 2
      panda/src/express/virtualFileSimple.I
  21. 19 3
      panda/src/express/virtualFileSimple.cxx
  22. 5 2
      panda/src/express/virtualFileSimple.h
  23. 24 6
      panda/src/express/virtualFileSystem.I
  24. 46 16
      panda/src/express/virtualFileSystem.cxx
  25. 4 4
      panda/src/express/virtualFileSystem.h
  26. 5 0
      panda/src/framework/windowFramework.cxx
  27. 5 14
      panda/src/pgraph/bamFile.cxx
  28. 34 29
      panda/src/pgraph/loader.cxx
  29. 12 0
      panda/src/pgraph/loaderFileType.cxx
  30. 1 0
      panda/src/pgraph/loaderFileType.h
  31. 12 0
      panda/src/pgraph/loaderFileTypeBam.cxx
  32. 1 0
      panda/src/pgraph/loaderFileTypeBam.h
  33. 1 0
      panda/src/pnmimage/Sources.pp
  34. 14 0
      panda/src/pnmimage/pnmFileTypeRegistry.cxx
  35. 10 1
      panda/src/pnmimage/pnmImageHeader.cxx
  36. 8 18
      panda/src/pnmtext/freetypeFont.cxx
  37. 1 0
      panda/src/putil/Sources.pp
  38. 8 15
      panda/src/putil/datagramInputFile.cxx
  39. 12 1
      panda/src/putil/datagramOutputFile.cxx
  40. 1 1
      pandatool/src/bam/eggToBam.cxx
  41. 0 1
      pandatool/src/eggbase/eggWriter.cxx
  42. 2 0
      pandatool/src/progbase/Sources.pp
  43. 25 1
      pandatool/src/progbase/withOutputFile.cxx
  44. 1 0
      pandatool/src/progbase/withOutputFile.h

+ 10 - 8
panda/src/audiotraits/fmodAudioManager.cxx

@@ -193,12 +193,8 @@ get_sound(const string &file_name, bool positional) {
     } else { // the suffix is of a supported type
       audio_debug("FmodAudioManager::get_sound: \""<<path<<"\" is a supported sound file format.");
       // resolve the path normally
-      if (use_vfs) {
-        VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-        vfs->resolve_filename(path, get_sound_path());
-      } else {
-        path.resolve_filename(get_sound_path());
-      }
+      VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+      vfs->resolve_filename(path, get_sound_path());
     }
   } else { // no suffix given. Search for supported file types of the same name.
     audio_debug("FmodAudioManager::get_sound: \""<<path<<"\" has no extension. Searching for supported files with the same name.");
@@ -854,7 +850,13 @@ dec_refcount(const string& file_name) {
 char* FmodAudioManager::
 load(const Filename& filename, size_t &size) const {
   // Check file type (based on filename suffix
-  string suffix = downcase(filename.get_extension());
+  string suffix = filename.get_extension();
+#ifdef HAVE_ZLIB
+  if (suffix == "pz") {
+    suffix = Filename(filename.get_basename_wo_extension()).get_extension();
+  }
+#endif  // HAVE_ZLIB
+  suffix = downcase(suffix);
   bool bSupported = false;
   if (suffix == "wav" || suffix == "mp3" || suffix == "mid"
       || suffix == "rmi" || suffix == "midi"
@@ -880,7 +882,7 @@ load(const Filename& filename, size_t &size) const {
     return NULL;
   }
   
-  audioFile = vfs->open_read_file(binary_filename);
+  audioFile = vfs->open_read_file(binary_filename, true);
 
   if (audioFile == (istream *)NULL) {
     // Unable to open.

+ 1 - 1
panda/src/audiotraits/milesAudioManager.cxx

@@ -250,7 +250,7 @@ load(Filename file_name) {
   PT(SoundData) sd = new SoundData;
 
   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  if (!vfs->read_file(file_name, sd->_raw_data)) {
+  if (!vfs->read_file(file_name, sd->_raw_data, true)) {
     milesAudio_cat.warning()
       << "Unable to read " << file_name << "\n";
     return NULL;

+ 4 - 4
panda/src/downloadertools/Sources.pp

@@ -70,22 +70,22 @@
 #end bin_target
 
 #begin bin_target
-  #define TARGET pcompress
+  #define TARGET pzip
   #define BUILD_TARGET $[HAVE_ZLIB]
   #define USE_PACKAGES $[USE_PACKAGES] zlib
 
   #define SOURCES \
-    pcompress.cxx
+    pzip.cxx
 
 #end bin_target
 
 #begin bin_target
-  #define TARGET pdecompress
+  #define TARGET punzip
   #define BUILD_TARGET $[HAVE_ZLIB]
   #define USE_PACKAGES $[USE_PACKAGES] zlib
 
   #define SOURCES \
-    pdecompress.cxx
+    punzip.cxx
 
 #end bin_target
 

+ 0 - 83
panda/src/downloadertools/pcompress.cxx

@@ -1,83 +0,0 @@
-// Filename: pcompress.cxx
-// Created by:  
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include "filename.h"
-#include "zStream.h"
-#include "notify.h"
-
-int
-main(int argc, char *argv[]) {
-  if (argc < 2) {
-    cerr << "Usage: pcompress <file> [<dest_file>]" << endl;
-    return 1;
-  }
-
-  bool implicit_dest_file;
-  Filename source_file = Filename::from_os_specific(argv[1]);
-  Filename dest_file;
-  if (argc < 3) {
-    dest_file = source_file.get_fullpath() + ".pz";
-    implicit_dest_file = true;
-  } else {
-    dest_file = Filename::from_os_specific(argv[2]);
-    implicit_dest_file = false;
-  }
-
-  // Open source file
-  ifstream read_stream;
-  source_file.set_binary();
-  if (!source_file.open_read(read_stream)) {
-    cerr << "failed to open: " << source_file << endl;
-    return 1;
-  }
-
-  // Open destination file
-  ofstream write_stream;
-  dest_file.set_binary();
-  if (!dest_file.open_write(write_stream, true)) {
-    cerr << "failed to open: " << dest_file << endl;
-    return 1;
-  }
-
-  bool fail = false;
-  {
-    OCompressStream compress(&write_stream, false);
-    
-    int ch = read_stream.get();
-    while (!read_stream.eof() && !read_stream.fail()) {
-      compress.put(ch);
-      ch = read_stream.get();
-    }
-
-    fail = compress.fail() && !compress.eof();
-  }
-
-  read_stream.close();
-  write_stream.close();
-
-  if (fail) {
-    dest_file.unlink();
-
-  } else {
-    if (implicit_dest_file) {
-      source_file.unlink();
-    }
-  }
-
-  return 0;
-}

+ 0 - 37
panda/src/downloadertools/pdecompress.cxx

@@ -1,37 +0,0 @@
-// Filename: pdecompress.cxx
-// Created by:  
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include "filename.h"
-#include "decompressor.h"
-
-int
-main(int argc, char *argv[]) {
-  if (argc < 2) {
-    cerr << "Usage: pdecompress <file>" << endl;
-    return 1;
-  }
-
-  Filename source_file = argv[1];
-  Decompressor decompressor;
-  if (decompressor.decompress(source_file) == false) {
-    cerr << "Decompress failed" << endl;
-    return 1;
-  }
-
-  return 0;
-}

+ 82 - 69
panda/src/downloadertools/pdecrypt.cxx

@@ -28,23 +28,46 @@
   #endif
 #endif
 
+string password;
+bool got_password = false;
+
+bool
+do_decrypt(istream &read_stream, ostream &write_stream) {
+  IDecryptStream decrypt(&read_stream, false, password);
+
+  static const size_t buffer_size = 1024;
+  char buffer[buffer_size];
+
+  decrypt.read(buffer, buffer_size);
+  size_t count = decrypt.gcount();
+  while (count != 0) {
+    write_stream.write(buffer, count);
+    decrypt.read(buffer, buffer_size);
+    count = decrypt.gcount();
+  }
+  
+  return !decrypt.fail() || decrypt.eof() &&
+    (!write_stream.fail() || write_stream.eof());
+}
+
 void 
 usage() {
   cerr
     << "\n"
-    << "Usage: pdecrypt [opts] <file> [<dest_file>]\n\n"
+    << "Usage: pdecrypt [opts] file [file2 file3 ...]\n\n"
     
     << "This program reverses the operation of a previous pencrypt command.  It\n"
-    << "decrypts the contents of the source file by applying the indicated password.\n"
-    << "The encryption algorithm need not be specified; it can be determined by\n"
-    << "examining the header of the encrypted file.  The password must match exactly.\n"
-    << "If it does not, an error may or may not be reported; but the file will not be\n"
-    << "decrypted correctly even if no error is reported.\n\n"
+    << "decrypts the contents of the named source file(s) and removes the .pe\n"
+    << "extension.  The encryption algorithm need not be specified; it can be\n"
+    << "determined by examining the header of each encrypted file.  The password\n"
+    << "must match the encryption password exactly.  If it does not, an error may\n"
+    << "or may not be reported; but the file will not be decrypted correctly even\n"
+    << "if no error is reported.\n\n"
 
     << "Options:\n\n"
     
     << "  -p \"password\"\n"
-    << "      Specifies the password to use for descryption.  If this is not specified,\n"
+    << "      Specifies the password to use for decryption.  If this is not specified,\n"
     << "      the user is prompted from standard input.\n\n";
 }
 
@@ -54,9 +77,6 @@ main(int argc, char *argv[]) {
   extern int optind;
   const char *optstr = "p:h";
 
-  string password;
-  bool got_password = false;
-
   int flag = getopt(argc, argv, optstr);
 
   while (flag != EOF) {
@@ -83,69 +103,62 @@ main(int argc, char *argv[]) {
     return 1;
   }
 
-  bool implicit_dest_file;
-  Filename source_file = Filename::from_os_specific(argv[1]);
-  Filename dest_file;
-  if (argc < 3) {
-    if (source_file.get_extension() == "pe") {
-      dest_file = source_file;
-      dest_file.set_extension("");
-    } else {
-      cerr << "Input filename doesn't end in .pe; can't derive filename of output file.\n";
-      return 1;
-    }
-    implicit_dest_file = true;
-  } else {
-    dest_file = Filename::from_os_specific(argv[2]);
-    implicit_dest_file = false;
-  }
-
-  // Open source file
-  ifstream read_stream;
-  source_file.set_binary();
-  if (!source_file.open_read(read_stream)) {
-    cerr << "failed to open: " << source_file << endl;
-    return 1;
-  }
+  bool all_ok = true;
+  for (int i = 1; i < argc; i++) {
+    Filename source_file = Filename::from_os_specific(argv[i]);
+    if (source_file.get_extension() != "pe") {
+      cerr << source_file 
+           << " doesn't end in .pe; can't derive filename of output file.\n";
+      all_ok = false;
 
-  // Open destination file
-  ofstream write_stream;
-  dest_file.set_binary();
-  if (!dest_file.open_write(write_stream, true)) {
-    cerr << "failed to open: " << dest_file << endl;
-    return 1;
-  }
-
-  // Prompt for password.
-  if (!got_password) {
-    cerr << "Enter password: ";
-    getline(cin, password);
-  }
-
-  bool fail = false;
-  {
-    IDecryptStream decrypt(&read_stream, false, password);
-    
-    int ch = decrypt.get();
-    while (!decrypt.eof() && !decrypt.fail()) {
-      write_stream.put(ch);
-      ch = decrypt.get();
+    } else {
+      Filename dest_file = source_file.get_fullpath_wo_extension();
+
+      // Open source file
+      ifstream read_stream;
+      source_file.set_binary();
+      if (!source_file.open_read(read_stream)) {
+        cerr << "Couldn't read: " << source_file << endl;
+        all_ok = false;
+
+      } else {
+        // Open destination file
+        ofstream write_stream;
+        dest_file.set_binary();
+        if (!dest_file.open_write(write_stream, true)) {
+          cerr << "Failed to open: " << dest_file << endl;
+          all_ok = false;
+
+        } else {
+          // Prompt for password.
+          if (!got_password) {
+            cerr << "Enter password: ";
+            getline(cin, password);
+            got_password = true;
+          }
+
+          cerr << dest_file << "\n";
+          bool success = do_decrypt(read_stream, write_stream);
+          
+          read_stream.close();
+          write_stream.close();
+          
+          if (!success) {
+            cerr << "Failure decrypting " << source_file << "\n";
+            all_ok = false;
+            dest_file.unlink();
+            
+          } else {
+            source_file.unlink();
+          }
+        }
+      }
     }
-
-    fail = decrypt.fail() && !decrypt.eof();
   }
 
-  read_stream.close();
-  write_stream.close();
-
-  if (fail) {
-    dest_file.unlink();
-
+  if (all_ok) {
+    return 0;
   } else {
-    if (implicit_dest_file) {
-      source_file.unlink();
-    }
+    return 1;
   }
-
-  return 0;
 }

+ 98 - 79
panda/src/downloadertools/pencrypt.cxx

@@ -28,17 +28,59 @@
   #endif
 #endif
 
+string password;
+bool got_password = false;
+string algorithm;
+bool got_algorithm = false;
+int key_length = 0;
+bool got_key_length = false;
+int iteration_count = 0;
+bool got_iteration_count = false;
+
+bool
+do_encrypt(istream &read_stream, ostream &write_stream) {
+  OEncryptStream encrypt;
+  if (got_algorithm) {
+    encrypt.set_algorithm(algorithm);
+  }
+  if (got_key_length) {
+    encrypt.set_key_length(key_length);
+  }
+  if (got_iteration_count) {
+    encrypt.set_iteration_count(iteration_count);
+  }
+  encrypt.open(&write_stream, false, password);
+    
+  static const size_t buffer_size = 1024;
+  char buffer[buffer_size];
+
+  read_stream.read(buffer, buffer_size);
+  size_t count = read_stream.gcount();
+  while (count != 0) {
+    encrypt.write(buffer, count);
+    read_stream.read(buffer, buffer_size);
+    count = read_stream.gcount();
+  }
+  encrypt.close();
+
+  return !read_stream.fail() || read_stream.eof() &&
+    (!encrypt.fail() || encrypt.eof());
+}
+
 void 
 usage() {
   cerr
     << "\n"
-    << "Usage: pencrypt [opts] <file> [<dest_file>]\n\n"
+    << "Usage: pencrypt [opts] file [file2 file3 ...]\n\n"
     
-    << "This program will apply an encryption algorithm to a file, creating an\n"
-    << "encrypted version of the file which can only be recovered using pdecrypt and\n"
-    << "the same password that was supplied to pencrypt.  If the dest_file name is\n"
-    << "not specified, a default output name is generated by appending .pe to the\n"
-    << "input file name.\n\n"
+    << "This program will apply an encryption algorithm to a file (or multiple files),\n"
+    << "creating an encrypted version of each file which can only be recovered using\n"
+    << "pdecrypt and the same password that was supplied to pencrypt.  For each input\n"
+    << "file, an output name is generated by appending .pe to the input file name.\n\n"
+
+    << "Note that if you are adding files to a Panda multifile (.mf file) with\n"
+    << "the multify command, it is not necessary to encrypt them separately;\n"
+    << "multify has an inline encryption option.\n\n"
 
     << "Options:\n\n"
 
@@ -76,15 +118,6 @@ main(int argc, char *argv[]) {
   extern int optind;
   const char *optstr = "p:a:k:i:h";
 
-  string password;
-  bool got_password = false;
-  string algorithm;
-  bool got_algorithm = false;
-  int key_length = 0;
-  bool got_key_length = false;
-  int iteration_count = 0;
-  bool got_iteration_count = false;
-
   int flag = getopt(argc, argv, optstr);
 
   while (flag != EOF) {
@@ -126,73 +159,59 @@ main(int argc, char *argv[]) {
     return 1;
   }
 
-  bool implicit_dest_file;
-  Filename source_file = Filename::from_os_specific(argv[1]);
-  Filename dest_file;
-  if (argc < 3) {
-    dest_file = source_file.get_fullpath() + ".pe";
-    implicit_dest_file = true;
-  } else {
-    dest_file = Filename::from_os_specific(argv[2]);
-    implicit_dest_file = false;
-  }
-
-  // Open source file
-  ifstream read_stream;
-  source_file.set_binary();
-  if (!source_file.open_read(read_stream)) {
-    cerr << "failed to open: " << source_file << endl;
-    return 1;
-  }
-
-  // Open destination file
-  ofstream write_stream;
-  dest_file.set_binary();
-  if (!dest_file.open_write(write_stream, true)) {
-    cerr << "failed to open: " << dest_file << endl;
-    return 1;
-  }
-
-  // Prompt for password.
-  if (!got_password) {
-    cerr << "Enter password: ";
-    getline(cin, password);
-  }
-    
-  bool fail = false;
-  {
-    OEncryptStream encrypt;
-    if (got_algorithm) {
-      encrypt.set_algorithm(algorithm);
-    }
-    if (got_key_length) {
-      encrypt.set_key_length(key_length);
+  bool all_ok = true;
+  for (int i = 1; i < argc; i++) {
+    Filename source_file = Filename::from_os_specific(argv[i]);
+    if (source_file.get_extension() == "pe") {
+      cerr << source_file << " already ends .pe; skipping.\n";
+    } else {
+      Filename dest_file = source_file.get_fullpath() + ".pe";
+
+      // Open source file
+      ifstream read_stream;
+      source_file.set_binary();
+      if (!source_file.open_read(read_stream)) {
+        cerr << "Couldn't read: " << source_file << endl;
+        all_ok = false;
+
+      } else {
+        // Open destination file
+        ofstream write_stream;
+        dest_file.set_binary();
+        if (!dest_file.open_write(write_stream, true)) {
+          cerr << "Failed to open: " << dest_file << endl;
+          all_ok = false;
+
+        } else {
+          // Prompt for password.
+          if (!got_password) {
+            cerr << "Enter password: ";
+            getline(cin, password);
+            got_password = true;
+          }
+
+          cerr << dest_file << "\n";
+          bool success = do_encrypt(read_stream, write_stream);
+          
+          read_stream.close();
+          write_stream.close();
+          
+          if (!success) {
+            cerr << "Failure writing " << dest_file << "\n";
+            all_ok = false;
+            dest_file.unlink();
+            
+          } else {
+            bool ok = source_file.unlink();
+          }
+        }
+      }
     }
-    if (got_iteration_count) {
-      encrypt.set_iteration_count(iteration_count);
-    }
-    encrypt.open(&write_stream, false, password);
-    
-    int ch = read_stream.get();
-    while (!read_stream.eof() && !read_stream.fail()) {
-      encrypt.put(ch);
-      ch = read_stream.get();
-    }
-
-    fail = encrypt.fail() && !encrypt.eof();
   }
 
-  read_stream.close();
-  write_stream.close();
-
-  if (fail) {
-    dest_file.unlink();
-
+  if (all_ok) {
+    return 0;
   } else {
-    if (implicit_dest_file) {
-      source_file.unlink();
-    }
+    return 1;
   }
-
-  return 0;
 }

+ 138 - 0
panda/src/downloadertools/punzip.cxx

@@ -0,0 +1,138 @@
+// Filename: punzip.cxx
+// Created by:  
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "filename.h"
+#include "zStream.h"
+#include "notify.h"
+
+#ifndef HAVE_GETOPT
+  #include "gnu_getopt.h"
+#else
+  #ifdef HAVE_GETOPT_H
+    #include <getopt.h>
+  #endif
+#endif
+
+bool
+do_decompress(istream &read_stream, ostream &write_stream) {
+  IDecompressStream decompress(&read_stream, false);
+
+  static const size_t buffer_size = 1024;
+  char buffer[buffer_size];
+
+  decompress.read(buffer, buffer_size);
+  size_t count = decompress.gcount();
+  while (count != 0) {
+    write_stream.write(buffer, count);
+    decompress.read(buffer, buffer_size);
+    count = decompress.gcount();
+  }
+  
+  return !decompress.fail() || decompress.eof() &&
+    (!write_stream.fail() || write_stream.eof());
+}
+
+void
+usage() {
+  cerr
+    << "\nUsage: punzip file.pz [file2.pz file3.pz ...]\n\n"
+    
+    << "This program reverses the operation of a previous pzip command.  It\n"
+    << "uncompresses the contents of the named source file(s) and removes the .pz\n"
+    << "extension.\n\n";
+}
+
+int
+main(int argc, char *argv[]) {
+  extern char *optarg;
+  extern int optind;
+  const char *optstr = "h";
+
+  int flag = getopt(argc, argv, optstr);
+
+  while (flag != EOF) {
+    switch (flag) {
+    case 'h':
+    case '?':
+    default:
+      usage();
+      return 1;
+    }
+    flag = getopt(argc, argv, optstr);
+  }
+
+  argc -= (optind-1);
+  argv += (optind-1);
+
+  if (argc < 2) {
+    usage();
+    return 1;
+  }
+
+  bool all_ok = true;
+  for (int i = 1; i < argc; i++) {
+    Filename source_file = Filename::from_os_specific(argv[i]);
+    if (source_file.get_extension() != "pz") {
+      cerr << source_file 
+           << " doesn't end in .pz; can't derive filename of output file.\n";
+      all_ok = false;
+
+    } else {
+      Filename dest_file = source_file.get_fullpath_wo_extension();
+
+      // Open source file
+      ifstream read_stream;
+      source_file.set_binary();
+      if (!source_file.open_read(read_stream)) {
+        cerr << "Couldn't read: " << source_file << endl;
+        all_ok = false;
+
+      } else {
+        // Open destination file
+        ofstream write_stream;
+        dest_file.set_binary();
+        if (!dest_file.open_write(write_stream, true)) {
+          cerr << "Failed to open: " << dest_file << endl;
+          all_ok = false;
+
+        } else {
+          cerr << dest_file << "\n";
+          bool success = do_decompress(read_stream, write_stream);
+          
+          read_stream.close();
+          write_stream.close();
+          
+          if (!success) {
+            cerr << "Failure decompressing " << source_file << "\n";
+            all_ok = false;
+            dest_file.unlink();
+            
+          } else {
+            source_file.unlink();
+          }
+        }
+      }
+    }
+  }
+
+  if (all_ok) {
+    return 0;
+  } else {
+    return 1;
+  }
+}

+ 151 - 0
panda/src/downloadertools/pzip.cxx

@@ -0,0 +1,151 @@
+// Filename: pzip.cxx
+// Created by:  
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "filename.h"
+#include "zStream.h"
+#include "notify.h"
+
+#ifndef HAVE_GETOPT
+  #include "gnu_getopt.h"
+#else
+  #ifdef HAVE_GETOPT_H
+    #include <getopt.h>
+  #endif
+#endif
+
+bool
+do_compress(istream &read_stream, ostream &write_stream) {
+  OCompressStream compress(&write_stream, false);
+
+  static const size_t buffer_size = 1024;
+  char buffer[buffer_size];
+
+  read_stream.read(buffer, buffer_size);
+  size_t count = read_stream.gcount();
+  while (count != 0) {
+    compress.write(buffer, count);
+    read_stream.read(buffer, buffer_size);
+    count = read_stream.gcount();
+  }
+  compress.close();
+
+  return !read_stream.fail() || read_stream.eof() &&
+    (!compress.fail() || compress.eof());
+}
+
+void
+usage() {
+  cerr
+    << "\nUsage: pzip file [file2 file3 ...]\n\n"
+    
+    << "This program compresses the named file(s) using the Panda native\n"
+    << "compression algorithm (gzip in practice, but with a different file\n"
+    << "header).  The compressed versions are written to a file with the\n"
+    << "same name as the original, but the extension .pz added to the\n"
+    << "filename, and the original file is removed.\n\n"
+    
+    << "In many cases, Panda can read the resulting .pz file directly,\n"
+    << "exactly as if it were still in its uncompressed original form.\n"
+    << "In fact, unless vfs-implicit-pz is set to false in your Config.prc\n"
+    << "file, you can also load the file by referencing it with its original\n"
+    << "filename (without the .pz extension), even though it no longer exists\n"
+    << "under that filename, and Panda will find the .pz file and transparently\n"
+    << "decompress it on the fly, as if the original, uncompressed file still\n"
+    << "existed.\n\n"
+
+    << "Note that if you are adding files to a Panda multifile (.mf file) with\n"
+    << "the multify command, it is not necessary to compress them separately;\n"
+    << "multify has an inline compression option.\n\n";
+}
+
+int
+main(int argc, char *argv[]) {
+  extern char *optarg;
+  extern int optind;
+  const char *optstr = "h";
+
+  int flag = getopt(argc, argv, optstr);
+
+  while (flag != EOF) {
+    switch (flag) {
+    case 'h':
+    case '?':
+    default:
+      usage();
+      return 1;
+    }
+    flag = getopt(argc, argv, optstr);
+  }
+
+  argc -= (optind-1);
+  argv += (optind-1);
+
+  if (argc < 2) {
+    usage();
+    return 1;
+  }
+
+  bool all_ok = true;
+  for (int i = 1; i < argc; i++) {
+    Filename source_file = Filename::from_os_specific(argv[i]);
+    if (source_file.get_extension() == "pz") {
+      cerr << source_file << " already ends .pz; skipping.\n";
+    } else {
+      Filename dest_file = source_file.get_fullpath() + ".pz";
+
+      // Open source file
+      ifstream read_stream;
+      source_file.set_binary();
+      if (!source_file.open_read(read_stream)) {
+        cerr << "Couldn't read: " << source_file << endl;
+        all_ok = false;
+
+      } else {
+        // Open destination file
+        ofstream write_stream;
+        dest_file.set_binary();
+        if (!dest_file.open_write(write_stream, true)) {
+          cerr << "Failed to open: " << dest_file << endl;
+          all_ok = false;
+
+        } else {
+          cerr << dest_file << "\n";
+          bool success = do_compress(read_stream, write_stream);
+          
+          read_stream.close();
+          write_stream.close();
+          
+          if (!success) {
+            cerr << "Failure writing " << dest_file << "\n";
+            all_ok = false;
+            dest_file.unlink();
+            
+          } else {
+            source_file.unlink();
+          }
+        }
+      }
+    }
+  }
+
+  if (all_ok) {
+    return 0;
+  } else {
+    return 1;
+  }
+}

+ 1 - 0
panda/src/egg/Sources.pp

@@ -2,6 +2,7 @@
                    dtoolutil:c dtoolbase:c dtool:m
 #define YACC_PREFIX eggyy
 #define LFLAGS -i
+#define USE_PACKAGES zlib
 
 #begin lib_target
   #define TARGET egg

+ 41 - 51
panda/src/egg/eggData.cxx

@@ -23,12 +23,12 @@
 #include "eggComment.h"
 #include "eggPoolUniquifier.h"
 #include "config_egg.h"
-
 #include "config_util.h"
 #include "config_express.h"
 #include "string_utils.h"
 #include "dSearchPath.h"
 #include "virtualFileSystem.h"
+#include "zStream.h"
 
 extern int eggyyparse();
 #include "parserDefs.h"
@@ -47,30 +47,17 @@ TypeHandle EggData::_type_handle;
 ////////////////////////////////////////////////////////////////////
 bool EggData::
 resolve_egg_filename(Filename &egg_filename, const DSearchPath &searchpath) {
-  if (use_vfs) {
-    VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-
-    if (egg_filename.is_fully_qualified() && vfs->exists(egg_filename)) {
-      return true;
-    }
-
-    vfs->resolve_filename(egg_filename, searchpath, "egg") ||
-      vfs->resolve_filename(egg_filename, egg_path, "egg") ||
-      vfs->resolve_filename(egg_filename, get_model_path(), "egg");
-
-    return vfs->exists(egg_filename);
-
-  } else {
-    if (egg_filename.is_fully_qualified() && egg_filename.exists()) {
-      return true;
-    }
-
-    egg_filename.resolve_filename(searchpath, "egg") ||
-      egg_filename.resolve_filename(egg_path, "egg") ||
-      egg_filename.resolve_filename(get_model_path(), "egg");
-    
-    return egg_filename.exists();
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  
+  if (egg_filename.is_fully_qualified() && vfs->exists(egg_filename)) {
+    return true;
   }
+  
+  vfs->resolve_filename(egg_filename, searchpath, "egg") ||
+    vfs->resolve_filename(egg_filename, egg_path, "egg") ||
+    vfs->resolve_filename(egg_filename, get_model_path(), "egg");
+  
+  return vfs->exists(egg_filename);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -94,34 +81,20 @@ read(Filename filename, string display_name) {
     display_name = filename;
   }
 
-  if (use_vfs) {
-    VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-    
-    istream *file = vfs->open_read_file(filename);
-    if (file == (istream *)NULL) {
-      egg_cat.error() << "Unable to open " << display_name << "\n";
-      return false;
-    }
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
     
-    egg_cat.info()
-      << "Reading " << display_name << "\n";
-
-    bool read_ok = read(*file);
-    vfs->close_read_file(file);
-    return read_ok;
-
-  } else {
-    ifstream file;
-    if (!filename.open_read(file)) {
-      egg_cat.error() << "Unable to open " << display_name << "\n";
-      return false;
-    }
-    
-    egg_cat.info()
-      << "Reading " << display_name << "\n";
-    
-    return read(file);
+  istream *file = vfs->open_read_file(filename, true);
+  if (file == (istream *)NULL) {
+    egg_cat.error() << "Unable to open " << display_name << "\n";
+    return false;
   }
+  
+  egg_cat.info()
+    << "Reading " << display_name << "\n";
+  
+  bool read_ok = read(*file);
+  vfs->close_read_file(file);
+  return read_ok;
 }
 
 
@@ -243,8 +216,18 @@ collapse_equivalent_materials() {
 ////////////////////////////////////////////////////////////////////
 bool EggData::
 write_egg(Filename filename) {
-  filename.set_text();
   filename.unlink();
+  filename.set_text();
+
+#ifdef HAVE_ZLIB
+  bool pz_file = false;
+  if (filename.get_extension() == "pz") {
+    // The filename ends in .pz, which means to automatically compress
+    // the egg file that we write.
+    pz_file = true;
+    filename.set_binary();
+  }
+#endif  // HAVE_ZLIB
 
   ofstream file;
   if (!filename.open_write(file)) {
@@ -252,6 +235,13 @@ write_egg(Filename filename) {
     return false;
   }
 
+#ifdef HAVE_ZLIB
+  if (pz_file) {
+    OCompressStream compressor(&file, false);
+    return write_egg(compressor);
+  }
+#endif  // HAVE_ZLIB
+
   return write_egg(file);
 }
 

+ 6 - 17
panda/src/egg2pg/load_egg_file.cxx

@@ -73,34 +73,23 @@ load_from_loader(EggLoader &loader) {
 PT(PandaNode)
 load_egg_file(const string &filename, CoordinateSystem cs) {
   Filename egg_filename = Filename::text_filename(filename);
-  if (use_vfs) {
-    VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-    if (!vfs->exists(egg_filename)) {
-      egg2pg_cat.error()
-        << "Could not find " << egg_filename << "\n";
-      return NULL;
-    }
-
-  } else {
-    if (!egg_filename.exists()) {
-      egg2pg_cat.error()
-        << "Could not find " << egg_filename << "\n";
-      return NULL;
-    }
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  if (!vfs->exists(egg_filename)) {
+    egg2pg_cat.error()
+      << "Could not find " << egg_filename << "\n";
+    return NULL;
   }
 
   egg2pg_cat.info()
     << "Reading " << egg_filename << "\n";
 
-
   EggLoader loader;
   loader._data->set_egg_filename(egg_filename);
   loader._data->set_auto_resolve_externals(true);
   loader._data->set_coordinate_system(cs);
 
   bool okflag;
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  istream *istr = vfs->open_read_file(egg_filename);
+  istream *istr = vfs->open_read_file(egg_filename, true);
   if (istr == (istream *)NULL) {
     egg2pg_cat.error()
       << "Could not open " << egg_filename << " for reading.\n";

+ 12 - 0
panda/src/egg2pg/loaderFileTypeEgg.cxx

@@ -52,6 +52,18 @@ get_extension() const {
   return "egg";
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LoaderFileTypeEgg::supports_compressed
+//       Access: Published, Virtual
+//  Description: Returns true if this file type can transparently load
+//               compressed files (with a .pz extension), false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool LoaderFileTypeEgg::
+supports_compressed() const {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LoaderFileTypeEgg::load_file
 //       Access: Public, Virtual

+ 1 - 0
panda/src/egg2pg/loaderFileTypeEgg.h

@@ -33,6 +33,7 @@ public:
 
   virtual string get_name() const;
   virtual string get_extension() const;
+  virtual bool supports_compressed() const;
 
   virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &optoins) const;
 

+ 12 - 3
panda/src/express/config_express.cxx

@@ -125,12 +125,21 @@ ConfigVariableBool vfs_case_sensitive
           "and it has no effect on mounted multifile systems, which are "
           "always case-sensitive."));
 
+ConfigVariableBool vfs_implicit_pz
+("vfs-implicit-pz", true,
+ PRC_DESC("When this is true, the VirtualFileSystem will pretend a named "
+          "file exists even if it doesn't, as long as a filename with the "
+          "same name and the additional extension .pz does exist.  In this "
+          "case, the VirtualFileSystem will implicitly open the .pz file "
+          "and decompress it on-the-fly."));
+
 ConfigVariableBool use_vfs
 ("use-vfs", true,
  PRC_DESC("Set this true to use the VirtualFileSystem mechanism for loading "
-          "models, etc.  Since the VirtualFileSystem maps to the same as the "
-          "actual file system by default, there is probably no reason to set "
-          "this false, except for testing or if you mistrust the new code."));
+          "models, etc.  Since the VirtualFileSystem is now the de facto "
+          "filesystem for Panda, you should always keep this true, since "
+          "there is now code that assumes it to be true.  This variable "
+          "is now deprecated."));
 
 ConfigVariableBool collect_tcp
 ("collect-tcp", false,

+ 2 - 0
panda/src/express/config_express.h

@@ -79,6 +79,8 @@ extern ConfigVariableInt encryption_iteration_count;
 extern ConfigVariableInt multifile_encryption_iteration_count;
 
 extern ConfigVariableBool vfs_case_sensitive;
+extern ConfigVariableBool vfs_implicit_pz;
+extern ConfigVariableBool vfs_auto_unwrap_pz;
 
 extern EXPCL_PANDAEXPRESS ConfigVariableBool use_vfs;
 

+ 2 - 2
panda/src/express/virtualFile.I

@@ -44,9 +44,9 @@ get_original_filename() const {
 //  Description: Returns the entire contents of the file as a string.
 ////////////////////////////////////////////////////////////////////
 INLINE string VirtualFile::
-read_file() const {
+read_file(bool auto_unwrap) const {
   string result;
-  read_file(result);
+  read_file(result, auto_unwrap);
   return result;
 }
 

+ 3 - 3
panda/src/express/virtualFile.cxx

@@ -159,7 +159,7 @@ ls_all(ostream &out) const {
 //               Returns NULL on failure.
 ////////////////////////////////////////////////////////////////////
 istream *VirtualFile::
-open_read_file() const {
+open_read_file(bool auto_unwrap) const {
   return NULL;
 }
 
@@ -210,10 +210,10 @@ close_read_file(istream *stream) const {
 //               success, false otherwise.
 ////////////////////////////////////////////////////////////////////
 bool VirtualFile::
-read_file(string &result) const {
+read_file(string &result, bool auto_unwrap) const {
   result = string();
 
-  istream *in = open_read_file();
+  istream *in = open_read_file(auto_unwrap);
   if (in == (istream *)NULL) {
     express_cat.info()
       << "Unable to read " << get_filename() << "\n";

+ 3 - 3
panda/src/express/virtualFile.h

@@ -54,14 +54,14 @@ PUBLISHED:
   void ls(ostream &out = cout) const;
   void ls_all(ostream &out = cout) const;
 
-  INLINE string read_file() const;
-  virtual istream *open_read_file() const;
+  INLINE string read_file(bool auto_unwrap) const;
+  virtual istream *open_read_file(bool auto_unwrap) const;
   void close_read_file(istream *stream) const;
   virtual streampos get_file_size(istream *stream) const;
 
 public:
   INLINE void set_original_filename(const Filename &filename);
-  bool read_file(string &result) const;
+  bool read_file(string &result, bool auto_unwrap) const;
   static bool read_file(istream *stream, string &result);
   static bool read_file(istream *stream, string &result, size_t max_bytes);
 

+ 16 - 2
panda/src/express/virtualFileSimple.I

@@ -23,8 +23,22 @@
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE VirtualFileSimple::
-VirtualFileSimple(VirtualFileMount *mount, const Filename &local_filename) :
+VirtualFileSimple(VirtualFileMount *mount, const Filename &local_filename,
+                  bool implicit_pz_file) :
   _mount(mount),
-  _local_filename(local_filename)
+  _local_filename(local_filename),
+  _implicit_pz_file(implicit_pz_file)
 {
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: VirtualFileSimple::is_implicit_pz_file
+//       Access: Published
+//  Description: Returns true if this file is a .pz file that should
+//               be implicitly decompressed on load, or false if it is
+//               not a .pz file or if it should not be decompressed.
+////////////////////////////////////////////////////////////////////
+INLINE bool VirtualFileSimple::
+is_implicit_pz_file() const {
+  return _implicit_pz_file;
+}

+ 19 - 3
panda/src/express/virtualFileSimple.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "virtualFileSimple.h"
+#include "zStream.h"
 
 TypeHandle VirtualFileSimple::_type_handle;
 
@@ -86,10 +87,25 @@ is_regular_file() const {
 //               allocated istream on success (which you should
 //               eventually delete when you are done reading).
 //               Returns NULL on failure.
+//
+//               If auto_unwrap is true, an explicitly-named .pz file
+//               is automatically decompressed and the decompressed
+//               contents are returned.  This is different than
+//               vfs-implicit-pz, which will automatically decompress
+//               a file if the extension .pz is *not* given.
 ////////////////////////////////////////////////////////////////////
 istream *VirtualFileSimple::
-open_read_file() const {
-  return _mount->open_read_file(_local_filename);
+open_read_file(bool auto_unwrap) const {
+  istream *result = _mount->open_read_file(_local_filename);
+#ifdef HAVE_ZLIB
+  if (result != (istream *)NULL && 
+      (_implicit_pz_file || (auto_unwrap && _local_filename.get_extension() == "pz"))) {
+    // We have to slip in a layer to decompress the file on the fly.
+    IDecompressStream *wrapper = new IDecompressStream(result, true);
+    result = wrapper;
+  }
+#endif  // HAVE_ZLIB
+  return result;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -135,7 +151,7 @@ scan_local_directory(VirtualFileList *file_list,
     const string &basename = (*ni);
     if (mount_points.find(basename) == mount_points.end()) {
       Filename filename(_local_filename, basename);
-      VirtualFileSimple *file = new VirtualFileSimple(_mount, filename);
+      VirtualFileSimple *file = new VirtualFileSimple(_mount, filename, false);
       file_list->add_file(file);
     }
   }

+ 5 - 2
panda/src/express/virtualFileSimple.h

@@ -33,15 +33,17 @@
 class EXPCL_PANDAEXPRESS VirtualFileSimple : public VirtualFile {
 public:
   INLINE VirtualFileSimple(VirtualFileMount *mount,
-                           const Filename &local_filename);
+                           const Filename &local_filename,
+                           bool implicit_pz_file);
 
   virtual VirtualFileSystem *get_file_system() const;
   virtual Filename get_filename() const;
 
   virtual bool is_directory() const;
   virtual bool is_regular_file() const;
+  INLINE bool is_implicit_pz_file() const;
 
-  virtual istream *open_read_file() const;
+  virtual istream *open_read_file(bool auto_unwrap) const;
   virtual streampos get_file_size(istream *stream) const;
 
 protected:
@@ -51,6 +53,7 @@ protected:
 private:
   VirtualFileMount *_mount;
   Filename _local_filename;
+  bool _implicit_pz_file;
 
 public:
   virtual TypeHandle get_type() const {

+ 24 - 6
panda/src/express/virtualFileSystem.I

@@ -96,11 +96,17 @@ ls_all(const string &filename) const {
 //       Access: Published
 //  Description: Convenience function; returns the entire contents of
 //               the indicated file as a string.
+//
+//               If auto_unwrap is true, an explicitly-named .pz file
+//               is automatically decompressed and the decompressed
+//               contents are returned.  This is different than
+//               vfs-implicit-pz, which will automatically decompress
+//               a file if the extension .pz is *not* given.
 ////////////////////////////////////////////////////////////////////
 INLINE string VirtualFileSystem::
-read_file(const Filename &filename) const {
+read_file(const Filename &filename, bool auto_unwrap) const {
   string result;
-  bool okflag = read_file(filename, result);
+  bool okflag = read_file(filename, result, auto_unwrap);
   nassertr(okflag, string());
   return result;
 }
@@ -111,14 +117,20 @@ read_file(const Filename &filename) const {
 //  Description: Convenience function; returns a newly allocated
 //               istream if the file exists and can be read, or NULL
 //               otherwise.  Does not return an invalid istream.
+//
+//               If auto_unwrap is true, an explicitly-named .pz file
+//               is automatically decompressed and the decompressed
+//               contents are returned.  This is different than
+//               vfs-implicit-pz, which will automatically decompress
+//               a file if the extension .pz is *not* given.
 ////////////////////////////////////////////////////////////////////
 INLINE istream *VirtualFileSystem::
-open_read_file(const Filename &filename) const {
+open_read_file(const Filename &filename, bool auto_unwrap) const {
   PT(VirtualFile) file = get_file(filename);
   if (file == (VirtualFile *)NULL) {
     return NULL;
   }
-  istream *str = file->open_read_file();
+  istream *str = file->open_read_file(auto_unwrap);
   if (str != (istream *)NULL && str->fail()) {
     delete str;
     str = (istream *)NULL;
@@ -132,9 +144,15 @@ open_read_file(const Filename &filename) const {
 //  Description: Convenience function; fills the string up with the
 //               data from the indicated file, if it exists and can be
 //               read.  Returns true on success, false otherwise.
+//
+//               If auto_unwrap is true, an explicitly-named .pz file
+//               is automatically decompressed and the decompressed
+//               contents are returned.  This is different than
+//               vfs-implicit-pz, which will automatically decompress
+//               a file if the extension .pz is *not* given.
 ////////////////////////////////////////////////////////////////////
 INLINE bool VirtualFileSystem::
-read_file(const Filename &filename, string &result) const {
+read_file(const Filename &filename, string &result, bool auto_unwrap) const {
   PT(VirtualFile) file = get_file(filename);
-  return (file != (VirtualFile *)NULL && file->read_file(result));
+  return (file != (VirtualFile *)NULL && file->read_file(result, auto_unwrap));
 }

+ 46 - 16
panda/src/express/virtualFileSystem.cxx

@@ -290,6 +290,8 @@ get_file(const Filename &filename) const {
   }
   pathname.standardize();
   string strpath = pathname.get_filename_index(0).get_fullpath().substr(1);
+  // Also transparently look for a regular file suffixed .pz.
+  string strpath_pz = strpath + ".pz";
 
   // Now scan all the mount points, from the back (since later mounts
   // override more recent ones), until a match is found.
@@ -303,27 +305,47 @@ get_file(const Filename &filename) const {
     if (strpath == mount_point) {
       // Here's an exact match on the mount point.  This filename is
       // the root directory of this mount object.
-      if (found_match(found_file, composite_file, mount, "", pathname)) {
+      if (found_match(found_file, composite_file, mount, "", pathname,
+                      false)) {
         return found_file;
       }
     } else if (mount_point.empty()) {
       // This is the root mount point; all files are in here.
       if (mount->has_file(strpath)) {
         // Bingo!
-        if (found_match(found_file, composite_file, mount, strpath, pathname)) {
+        if (found_match(found_file, composite_file, mount, strpath, 
+                        pathname, false)) {
           return found_file;
         }
-      }            
+#ifdef HAVE_ZLIB
+      } else if (vfs_implicit_pz && mount->has_file(strpath_pz)) {
+        if (found_match(found_file, composite_file, mount, strpath_pz, 
+                        pathname, true)) {
+          return found_file;
+        }
+#endif  // HAVE_ZLIB
+      }
+
     } else if (strpath.length() > mount_point.length() &&
                strpath.substr(0, mount_point.length()) == mount_point &&
                strpath[mount_point.length()] == '/') {
       // This pathname falls within this mount system.
       Filename local_filename = strpath.substr(mount_point.length() + 1);
+      Filename local_filename_pz = strpath_pz.substr(mount_point.length() + 1);
       if (mount->has_file(local_filename)) {
         // Bingo!
-        if (found_match(found_file, composite_file, mount, local_filename, pathname)) {
+        if (found_match(found_file, composite_file, mount, local_filename, 
+                        pathname, false)) {
+          return found_file;
+        }
+#ifdef HAVE_ZLIB
+      } else if (vfs_implicit_pz && mount->has_file(local_filename_pz)) {
+        // Bingo!
+        if (found_match(found_file, composite_file, mount, local_filename_pz,
+                        pathname, true)) {
           return found_file;
         }
+#endif  // HAVE_ZLIB
       }            
     }
   }
@@ -670,15 +692,21 @@ normalize_mount_point(const string &mount_point) const {
 bool VirtualFileSystem::
 found_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file,
             VirtualFileMount *mount, const string &local_filename,
-            const Filename &original_filename) const {
+            const Filename &original_filename, bool implicit_pz_file) const {
   if (found_file == (VirtualFile *)NULL) {
     // This was our first match.  Save it.
-    found_file = new VirtualFileSimple(mount, local_filename);
+    found_file = new VirtualFileSimple(mount, local_filename, implicit_pz_file);
     found_file->set_original_filename(original_filename);
     if (!mount->is_directory(local_filename)) {
       // If it's not a directory, we're done.
       return true;
     }
+    // It is a directory, so save it for later.
+    if (implicit_pz_file) {
+      // Don't look for directories named file.pz.
+      found_file = NULL;
+    }
+
   } else {
     // This was our second match.  The previous match(es) must
     // have been directories.
@@ -686,17 +714,19 @@ found_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file,
       // However, this one isn't a directory.  We're done.
       return true;
     }
-
-    // At least two directories matched to the same path.  We
-    // need a composite directory.
-    if (composite_file == (VirtualFileComposite *)NULL) {
-      composite_file =
-        new VirtualFileComposite((VirtualFileSystem *)this, found_file->get_original_filename());
-      composite_file->set_original_filename(original_filename);
-      composite_file->add_component(found_file);
-      found_file = composite_file;
+    if (!implicit_pz_file) {
+      // At least two directories matched to the same path.  We
+      // need a composite directory.
+      if (composite_file == (VirtualFileComposite *)NULL) {
+        composite_file =
+          new VirtualFileComposite((VirtualFileSystem *)this, found_file->get_original_filename());
+        composite_file->set_original_filename(original_filename);
+        composite_file->add_component(found_file);
+        found_file = composite_file;
+      }
+      composite_file->add_component
+        (new VirtualFileSimple(mount, local_filename, false));
     }
-    composite_file->add_component(new VirtualFileSimple(mount, local_filename));
   }
 
   // Keep going, looking for more directories.

+ 4 - 4
panda/src/express/virtualFileSystem.h

@@ -82,12 +82,12 @@ PUBLISHED:
 
   static VirtualFileSystem *get_global_ptr();
 
-  INLINE string read_file(const Filename &filename) const;
-  INLINE istream *open_read_file(const Filename &filename) const;
+  INLINE string read_file(const Filename &filename, bool auto_unwrap = false) const;
+  INLINE istream *open_read_file(const Filename &filename, bool auto_unwrap = false) const;
   void close_read_file(istream *stream) const;
 
 public:
-  INLINE bool read_file(const Filename &filename, string &result) const;
+  INLINE bool read_file(const Filename &filename, string &result, bool auto_unwrap = false) const;
 
   void scan_mount_points(vector_string &names, const Filename &path) const;
 
@@ -95,7 +95,7 @@ private:
   Filename normalize_mount_point(const string &mount_point) const;
   bool found_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file,
                    VirtualFileMount *mount, const string &local_filename,
-                   const Filename &original_filename) const;
+                   const Filename &original_filename, bool implicit_pz_file) const;
   static void parse_option(const string &option,
                            int &flags, string &password);
 

+ 5 - 0
panda/src/framework/windowFramework.cxx

@@ -572,6 +572,11 @@ load_model(const NodePath &parent, Filename filename) {
   // an image file, based on the filename extension.
   bool is_image = false;
   string extension = filename.get_extension();
+#ifdef HAVE_ZLIB
+  if (extension == "pz") {
+    extension = Filename(filename.get_basename_wo_extension()).get_extension();
+  }
+#endif  // HAVE_ZLIB
   if (!extension.empty()) {
     LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
     LoaderFileType *model_type =

+ 5 - 14
panda/src/pgraph/bamFile.cxx

@@ -59,21 +59,12 @@ bool BamFile::
 open_read(const Filename &bam_filename, bool report_errors) {
   close();
 
-  if (use_vfs) {
-    VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-    if (!vfs->exists(bam_filename)) {
-      if (report_errors) {
-        loader_cat.error() << "Could not find " << bam_filename << "\n";
-      }
-      return false;
-    }
-  } else {
-    if (!bam_filename.exists()) {
-      if (report_errors) {
-        loader_cat.error() << "Could not find " << bam_filename << "\n";
-      }
-      return false;
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  if (!vfs->exists(bam_filename)) {
+    if (report_errors) {
+      loader_cat.error() << "Could not find " << bam_filename << "\n";
     }
+    return false;
   }
 
   loader_cat.info() << "Reading " << bam_filename << "\n";

+ 34 - 29
panda/src/pgraph/loader.cxx

@@ -99,6 +99,21 @@ find_all_files(const Filename &filename, const DSearchPath &search_path,
     load_file_types();
   }
   string extension = filename.get_extension();
+  string extra_ext;
+  bool pz_file = false;
+
+#ifdef HAVE_ZLIB
+  if (extension == "pz") {
+    // The extension ".pz" is special.  This is an explicitly-named
+    // compressed file.  We'll decompress it on the fly, if possible.
+    // (This relies on the auto_unwrap parameter to vfs->read_file(),
+    // which is different from the implicitly-named compressed file
+    // action that you might get if vfs-implicit-pz is enabled.)
+    extra_ext = extension;
+    extension = Filename(filename.get_basename_wo_extension()).get_extension();
+    pz_file = true;
+  }
+#endif  // HAVE_ZLIB
 
   int num_added = 0;
 
@@ -108,7 +123,8 @@ find_all_files(const Filename &filename, const DSearchPath &search_path,
     LoaderFileType *requested_type =
       reg->get_type_from_extension(extension);
 
-    if (requested_type != (LoaderFileType *)NULL) {
+    if (requested_type != (LoaderFileType *)NULL &&
+        (!pz_file || requested_type->supports_compressed())) {
       if (!filename.is_local()) {
         // Global filename, take it as it is.
         results.add_file(filename, requested_type);
@@ -117,12 +133,8 @@ find_all_files(const Filename &filename, const DSearchPath &search_path,
       } else {
         // Local filename, search along the path.
         DSearchPath::Results files;
-        if (use_vfs) {
-          VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-          num_added = vfs->find_all_files(filename, search_path, files);
-        } else {
-          num_added = search_path.find_all_files(filename, files);
-        }
+        VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+        num_added = vfs->find_all_files(filename, search_path, files);
         
         for (int i = 0; i < num_added; ++i) {
           results.add_file(files.get_file(i), requested_type);
@@ -141,18 +153,12 @@ find_all_files(const Filename &filename, const DSearchPath &search_path,
         LoaderFileType *type = reg->get_type(t);
         Filename file(filename);
         file.set_extension(type->get_extension());
+        file = file.get_fullpath() + extra_ext;
           
-        if (use_vfs) {
-          VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-          if (vfs->exists(file)) {
-            results.add_file(file, type);
-            ++num_added;
-          }
-        } else {
-          if (file.exists()) {
-            results.add_file(file, type);
-            ++num_added;
-          }
+        VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+        if (vfs->exists(file)) {
+          results.add_file(file, type);
+          ++num_added;
         }
       }
     } else {
@@ -165,18 +171,12 @@ find_all_files(const Filename &filename, const DSearchPath &search_path,
           LoaderFileType *type = reg->get_type(t);
           Filename file(directory, filename);
           file.set_extension(type->get_extension());
+          file = file.get_fullpath() + extra_ext;
           
-          if (use_vfs) {
-            VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-            if (vfs->exists(file)) {
-              results.add_file(file, type);
-              ++num_added;
-            }
-          } else {
-            if (file.exists()) {
-              results.add_file(file, type);
-              ++num_added;
-            }
+          VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+          if (vfs->exists(file)) {
+            results.add_file(file, type);
+            ++num_added;
           }
         }
       }
@@ -425,6 +425,11 @@ load_file(const Filename &filename, const LoaderOptions &options) const {
     // Couldn't find the file.  Either it doesn't exist, or it's an
     // unknown file type.  Report a useful message either way.
     string extension = filename.get_extension();
+#ifdef HAVE_ZLIB
+    if (extension == "pz") {
+      extension = Filename(filename.get_basename_wo_extension()).get_extension();
+    }
+#endif  // HAVE_ZLIB
     if (!extension.empty()) {
       LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
       LoaderFileType *requested_type =

+ 12 - 0
panda/src/pgraph/loaderFileType.cxx

@@ -51,6 +51,18 @@ get_additional_extensions() const {
   return string();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LoaderFileType::supports_compressed
+//       Access: Published, Virtual
+//  Description: Returns true if this file type can transparently load
+//               compressed files (with a .pz extension), false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool LoaderFileType::
+supports_compressed() const {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LoaderFileType::load_file
 //       Access: Public, Virtual

+ 1 - 0
panda/src/pgraph/loaderFileType.h

@@ -47,6 +47,7 @@ PUBLISHED:
   virtual string get_name() const=0;
   virtual string get_extension() const=0;
   virtual string get_additional_extensions() const;
+  virtual bool supports_compressed() const;
 
 public:
   virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options) const;

+ 12 - 0
panda/src/pgraph/loaderFileTypeBam.cxx

@@ -54,6 +54,18 @@ get_extension() const {
   return "bam";
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LoaderFileTypeBam::supports_compressed
+//       Access: Published, Virtual
+//  Description: Returns true if this file type can transparently load
+//               compressed files (with a .pz extension), false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool LoaderFileTypeBam::
+supports_compressed() const {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LoaderFileTypeBam::load_file
 //       Access: Public, Virtual

+ 1 - 0
panda/src/pgraph/loaderFileTypeBam.h

@@ -33,6 +33,7 @@ public:
 
   virtual string get_name() const;
   virtual string get_extension() const;
+  virtual bool supports_compressed() const;
 
   virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options) const;
 

+ 1 - 0
panda/src/pnmimage/Sources.pp

@@ -1,5 +1,6 @@
 #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
                    dtoolutil:c dtoolbase:c dtool:m
+#define USE_PACKAGES zlib
 
 #begin lib_target
   #define TARGET pnmimage

+ 14 - 0
panda/src/pnmimage/pnmFileTypeRegistry.cxx

@@ -139,6 +139,20 @@ get_type_from_extension(const string &filename) const {
     extension = filename.substr(dot + 1);
   }
 
+#ifdef HAVE_ZLIB
+  if (extension == "pz") {
+    // If the extension is .pz, then we've got a Panda-compressed
+    // image file.  Back up some more and get the extension before
+    // that.
+    size_t prev_dot = filename.rfind('.', dot - 1);
+    if (prev_dot == string::npos) {
+      extension = filename.substr(0, dot);
+    } else {
+      extension = filename.substr(prev_dot + 1, dot - prev_dot - 1);
+    }
+  }
+#endif  // HAVE_ZLIB
+
   if (extension.find('/') != string::npos) {
     // If we picked the whole filename and it contains slashes, or if
     // the rightmost dot wasn't in the basename of the filename, then

+ 10 - 1
panda/src/pnmimage/pnmImageHeader.cxx

@@ -22,6 +22,7 @@
 #include "pnmReader.h"
 #include "config_pnmimage.h"
 #include "virtualFileSystem.h"
+#include "zStream.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImageHeader::read_header
@@ -78,7 +79,7 @@ make_reader(const Filename &filename, PNMFileType *type,
   } else {
     VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
     owns_file = true;
-    file = vfs->open_read_file(filename);
+    file = vfs->open_read_file(filename, true);
   }
 
   if (file == (istream *)NULL) {
@@ -265,6 +266,14 @@ make_writer(const Filename &filename, PNMFileType *type) const {
     } else {
       owns_file = true;
       file = new_ostream;
+
+#ifdef HAVE_ZLIB
+      if (filename.get_extension() == "pz") {
+        // The filename ends in .pz, which means to automatically
+        // compress the image file that we write.
+        file = new OCompressStream(file, true);
+      }
+#endif  // HAVE_ZLIB
     }
   }
 

+ 8 - 18
panda/src/pnmtext/freetypeFont.cxx

@@ -84,24 +84,14 @@ load_font(const Filename &font_filename, int face_index) {
   bool exists = false;
   int error;
   Filename path(font_filename);
-  if (use_vfs) {
-    VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-    vfs->resolve_filename(path, get_model_path());
-    exists = vfs->read_file(path, _raw_font_data);
-    if (exists) {
-      error = FT_New_Memory_Face(_ft_library, 
-                                 (const FT_Byte *)_raw_font_data.data(),
-                                 _raw_font_data.length(),
-                                 face_index, &_face);
-    }
-  } else {
-    path.resolve_filename(get_model_path());
-    exists = path.exists();
-    if (exists) {
-      string os_specific = path.to_os_specific();
-      error = FT_New_Face(_ft_library, os_specific.c_str(),
-                          face_index, &_face);
-    }
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  vfs->resolve_filename(path, get_model_path());
+  exists = vfs->read_file(path, _raw_font_data, true);
+  if (exists) {
+    error = FT_New_Memory_Face(_ft_library, 
+                               (const FT_Byte *)_raw_font_data.data(),
+                               _raw_font_data.length(),
+                               face_index, &_face);
   }
 
   if (!exists) {

+ 1 - 0
panda/src/putil/Sources.pp

@@ -1,6 +1,7 @@
 #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
                   dtoolutil:c dtoolbase:c dtool:m
 #define LOCAL_LIBS express pandabase
+#define USE_PACKAGES zlib
 
 #begin lib_target
   #define TARGET putil

+ 8 - 15
panda/src/putil/datagramInputFile.cxx

@@ -39,22 +39,15 @@ open(Filename filename) {
   // DatagramInputFiles are always binary.
   filename.set_binary();
 
-  if (use_vfs) {
-    VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-    PT(VirtualFile) file = vfs->get_file(filename);
-    if (file == (VirtualFile *)NULL) {
-      // No such file.
-      return false;
-    }
-    _in = file->open_read_file();
-    _owns_in = (_in != (istream *)NULL);
-    return _owns_in && !_in->fail();
-    
-  } else {
-    _in = &_in_file;
-    _owns_in = false;
-    return filename.open_read(_in_file);
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  PT(VirtualFile) file = vfs->get_file(filename);
+  if (file == (VirtualFile *)NULL) {
+    // No such file.
+    return false;
   }
+  _in = file->open_read_file(true);
+  _owns_in = (_in != (istream *)NULL);
+  return _owns_in && !_in->fail();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 12 - 1
panda/src/putil/datagramOutputFile.cxx

@@ -18,6 +18,7 @@
 
 #include "datagramOutputFile.h"
 #include "streamWriter.h"
+#include "zStream.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramOutputFile::open
@@ -34,6 +35,16 @@ open(Filename filename) {
 
   _out = &_out_file;
   _owns_out = false;
+
+#ifdef HAVE_ZLIB
+  if (filename.get_extension() == "pz") {
+    // The filename ends in .pz, which means to automatically
+    // compress the bam file that we write.
+    _out = new OCompressStream(_out, _owns_out);
+    _owns_out = true;
+  }
+#endif  // HAVE_ZLIB
+
   return filename.open_write(_out_file);
 }
 
@@ -64,10 +75,10 @@ open(ostream &out) {
 ////////////////////////////////////////////////////////////////////
 void DatagramOutputFile::
 close() {
-  _out_file.close();
   if (_owns_out) {
     delete _out;
   }
+  _out_file.close();
   _out = (ostream *)NULL;
   _owns_out = false;
 

+ 1 - 1
pandatool/src/bam/eggToBam.cxx

@@ -182,7 +182,7 @@ run() {
   
   if (!bam_file.write_object(root)) {
     nout << "Error in writing.\n";
-      exit(1);
+    exit(1);
   }
 }
 

+ 0 - 1
pandatool/src/eggbase/eggWriter.cxx

@@ -228,7 +228,6 @@ handle_args(ProgramBase::Args &args) {
   if (!_got_path_directory && _got_output_filename) {
     // Put in the name of the output directory.
     _path_replace->_path_directory = _output_filename.get_dirname();
-    cerr << "_path_directory defaults to " << _path_replace->_path_directory << " in eggWriter\n";
   }
 
   return true;

+ 2 - 0
pandatool/src/progbase/Sources.pp

@@ -1,3 +1,5 @@
+#define USE_PACKAGES zlib
+
 #begin ss_lib_target
   #define TARGET progbase
   #define LOCAL_LIBS \

+ 25 - 1
pandatool/src/progbase/withOutputFile.cxx

@@ -18,6 +18,7 @@
 
 #include "withOutputFile.h"
 #include "executionEnvironment.h"
+#include "zStream.h"
 
 #include "notify.h"
 
@@ -34,6 +35,7 @@ WithOutputFile(bool allow_last_param, bool allow_stdout,
   _binary_output = binary_output;
   _got_output_filename = false;
   _output_ptr = (ostream *)NULL;
+  _owns_output_ptr = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -43,6 +45,10 @@ WithOutputFile(bool allow_last_param, bool allow_stdout,
 ////////////////////////////////////////////////////////////////////
 WithOutputFile::
 ~WithOutputFile() {
+  if (_owns_output_ptr) {
+    delete _output_ptr;
+    _owns_output_ptr = false;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -62,13 +68,23 @@ get_output() {
         exit(1);
       }
       _output_ptr = &cout;
+      _owns_output_ptr = false;
 
     } else {
       // Attempt to open the named file.
       unlink(_output_filename.c_str());
       _output_filename.make_dir();
 
-      if (_binary_output) {
+      bool pz_file = false;
+#ifdef HAVE_ZLIB
+      if (_output_filename.get_extension() == "pz") {
+        // The filename ends in .pz, which means to automatically compress
+        // the file that we write.
+        pz_file = true;
+      }
+#endif  // HAVE_ZLIB
+
+      if (_binary_output || pz_file) {
         _output_filename.set_binary();
       } else {
         _output_filename.set_text();
@@ -80,6 +96,14 @@ get_output() {
       }
       nout << "Writing " << _output_filename << "\n";
       _output_ptr = &_output_stream;
+      _owns_output_ptr = false;
+
+#ifdef HAVE_ZLIB
+      if (pz_file) {
+        _output_ptr = new OCompressStream(_output_ptr, _owns_output_ptr);
+        _owns_output_ptr = true;
+      }
+#endif  // HAVE_ZLIB
     }
   }
   return *_output_ptr;

+ 1 - 0
pandatool/src/progbase/withOutputFile.h

@@ -61,6 +61,7 @@ protected:
 private:
   ofstream _output_stream;
   ostream *_output_ptr;
+  bool _owns_output_ptr;
 };
 
 #include "withOutputFile.I"