Browse Source

vfs-implicit-mf

David Rose 17 years ago
parent
commit
d7f1f67136

+ 10 - 0
panda/src/express/config_express.cxx

@@ -83,6 +83,16 @@ ConfigVariableBool vfs_implicit_pz
           "case, the VirtualFileSystem will implicitly open the .pz file "
           "and decompress it on-the-fly."));
 
+ConfigVariableBool vfs_implicit_mf
+("vfs-implicit-mf", true,
+ PRC_DESC("When this is true, the VirtualFileSystem will automatically "
+          "mount multifiles on-the-fly when they are used as directories.  "
+          "For instance, opening the file /c/files/foo.mf/dirname/mytex.jpg "
+          "will implicitly retrieve a file named 'dirname/mytex.jpg' "
+          "within the multifile /c/files/foo.mf, even if the multifile "
+          "has not already been mounted.  This makes all of your multifiles "
+          "act like directories."));
+
 ConfigVariableBool use_vfs
 ("use-vfs", true,
  PRC_DESC("Set this true to use the VirtualFileSystem mechanism for loading "

+ 1 - 1
panda/src/express/config_express.h

@@ -49,7 +49,7 @@ extern ConfigVariableInt multifile_encryption_iteration_count;
 
 extern ConfigVariableBool vfs_case_sensitive;
 extern ConfigVariableBool vfs_implicit_pz;
-extern ConfigVariableBool vfs_auto_unwrap_pz;
+extern ConfigVariableBool vfs_implicit_mf;
 
 extern EXPCL_PANDAEXPRESS ConfigVariableBool use_vfs;
 

+ 13 - 0
panda/src/express/multifile.I

@@ -24,6 +24,19 @@ get_multifile_name() const {
   return _multifile_name;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Multifile::set_multifile_name
+//       Access: Published
+//  Description: Replaces the filename of the Multifile.  This is
+//               primarily used for documentation purposes only;
+//               changing this name does not open the indicated file.
+//               See open_read() or open_write() for that.
+////////////////////////////////////////////////////////////////////
+INLINE void Multifile::
+set_multifile_name(const Filename &multifile_name) {
+  _multifile_name = multifile_name;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Multifile::is_read_valid
 //       Access: Published

+ 28 - 18
panda/src/express/multifile.cxx

@@ -102,6 +102,7 @@ Multifile::
 Multifile() {
   _read = (istream *)NULL;
   _write = (ostream *)NULL;
+  _owns_stream = false;
   _next_index = 0;
   _last_index = 0;
   _needs_repack = false;
@@ -179,18 +180,18 @@ open_read(const Filename &multifile_name) {
 //               istream.  There must be seek functionality via
 //               seekg() and tellg() on the istream.
 //
-//               The Multifile does *not* close or delete the istream
-//               when Multifile.close() is called.  It is the caller's
-//               responsibility to ensure that the istream pointer
-//               does not destruct during the lifetime of the
-//               Multifile.
+//               If owns_pointer is true, then the Multifile assumes
+//               ownership of the stream pointer and will delete it
+//               when the multifile is closed, including if this
+//               function returns false.
 ////////////////////////////////////////////////////////////////////
 bool Multifile::
-open_read(istream *multifile_stream) {
+open_read(istream *multifile_stream, bool owns_pointer) {
   close();
   _timestamp = time(NULL);
   _timestamp_dirty = true;
   _read = multifile_stream;
+  _owns_stream = owns_pointer;
   _read->seekg(0, ios::beg);
   return read_index();
 }
@@ -230,18 +231,18 @@ open_write(const Filename &multifile_name) {
 //               ostream.  There must be seek functionality via
 //               seekp() and tellp() on the pstream.
 //
-//               The Multifile does *not* close or delete the ostream
-//               when Multifile.close() is called.  It is the caller's
-//               responsibility to ensure that the ostream pointer
-//               does not destruct during the lifetime of the
-//               Multifile.
+//               If owns_pointer is true, then the Multifile assumes
+//               ownership of the stream pointer and will delete it
+//               when the multifile is closed, including if this
+//               function returns false.
 ////////////////////////////////////////////////////////////////////
 bool Multifile::
-open_write(ostream *multifile_stream) {
+open_write(ostream *multifile_stream, bool owns_pointer) {
   close();
   _timestamp = time(NULL);
   _timestamp_dirty = true;
   _write = multifile_stream;
+  _owns_stream = owns_pointer;
   _write->seekp(0, ios::beg);
   return true;
 }
@@ -293,19 +294,19 @@ open_read_write(const Filename &multifile_name) {
 //               via seekg()/seekp() and tellg()/tellp() on the
 //               iostream.
 //
-//               The Multifile does *not* close or delete the iostream
-//               when Multifile.close() is called.  It is the caller's
-//               responsibility to ensure that the iostream pointer
-//               does not destruct during the lifetime of the
-//               Multifile.
+//               If owns_pointer is true, then the Multifile assumes
+//               ownership of the stream pointer and will delete it
+//               when the multifile is closed, including if this
+//               function returns false.
 ////////////////////////////////////////////////////////////////////
 bool Multifile::
-open_read_write(iostream *multifile_stream) {
+open_read_write(iostream *multifile_stream, bool owns_pointer) {
   close();
   _timestamp = time(NULL);
   _timestamp_dirty = true;
   _read = multifile_stream;
   _write = multifile_stream;
+  _owns_stream = owns_pointer;
   _write->seekp(0, ios::beg);
 
   // Check whether the read stream is empty.
@@ -338,8 +339,17 @@ close() {
     flush();
   }
 
+  if (_owns_stream) {
+    if (_read != (istream *)NULL) {
+      VirtualFileSystem::close_read_file(_read);
+    } else if (_write != (ostream *)NULL) {
+      delete _write;
+    }
+  }
+
   _read = (istream *)NULL;
   _write = (ostream *)NULL;
+  _owns_stream = false;
   _next_index = 0;
   _last_index = 0;
   _needs_repack = false;

+ 5 - 3
panda/src/express/multifile.h

@@ -40,14 +40,15 @@ private:
 
 PUBLISHED:
   BLOCKING bool open_read(const Filename &multifile_name);
-  BLOCKING bool open_read(istream *multifile_stream);
+  BLOCKING bool open_read(istream *multifile_stream, bool owns_pointer = false);
   BLOCKING bool open_write(const Filename &multifile_name);
-  BLOCKING bool open_write(ostream *multifile_stream);
+  BLOCKING bool open_write(ostream *multifile_stream, bool owns_pointer = false);
   BLOCKING bool open_read_write(const Filename &multifile_name);
-  BLOCKING bool open_read_write(iostream *multifile_stream);
+  BLOCKING bool open_read_write(iostream *multifile_stream, bool owns_pointer = false);
   BLOCKING void close();
 
   INLINE const Filename &get_multifile_name() const;
+  INLINE void set_multifile_name(const Filename &multifile_name);
 
   INLINE bool is_read_valid() const;
   INLINE bool is_write_valid() const;
@@ -164,6 +165,7 @@ private:
 
   istream *_read;
   ostream *_write;
+  bool _owns_stream;
   streampos _next_index;
   streampos _last_index;
 

+ 11 - 0
panda/src/express/virtualFileSimple.I

@@ -27,6 +27,17 @@ VirtualFileSimple(VirtualFileMount *mount, const Filename &local_filename,
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: VirtualFileSimple::get_mount
+//       Access: Published, Virtual
+//  Description: Returns the VirtualFileMount this file is associated
+//               with.
+////////////////////////////////////////////////////////////////////
+INLINE VirtualFileMount *VirtualFileSimple::
+get_mount() const {
+  return _mount;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileSimple::is_implicit_pz_file
 //       Access: Published

+ 1 - 0
panda/src/express/virtualFileSimple.h

@@ -33,6 +33,7 @@ public:
                            bool implicit_pz_file);
 
   virtual VirtualFileSystem *get_file_system() const;
+  INLINE VirtualFileMount *get_mount() const;
   virtual Filename get_filename() const;
 
   virtual bool has_file() const;

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

@@ -33,7 +33,7 @@ exists(const Filename &filename) const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool VirtualFileSystem::
 is_directory(const Filename &filename) const {
-  PT(VirtualFile) file = get_file(filename);
+  PT(VirtualFile) file = get_file(filename, true);
   return (file != (VirtualFile *)NULL && file->is_directory());
 }
 
@@ -45,7 +45,7 @@ is_directory(const Filename &filename) const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool VirtualFileSystem::
 is_regular_file(const Filename &filename) const {
-  PT(VirtualFile) file = get_file(filename);
+  PT(VirtualFile) file = get_file(filename, true);
   return (file != (VirtualFile *)NULL && file->is_regular_file());
 }
 
@@ -59,7 +59,7 @@ is_regular_file(const Filename &filename) const {
 ////////////////////////////////////////////////////////////////////
 INLINE void VirtualFileSystem::
 ls(const string &filename) const {
-  PT(VirtualFile) file = get_file(filename);
+  PT(VirtualFile) file = get_file(filename, true);
   if (file == (VirtualFile *)NULL) {
     express_cat.info()
       << "Not found: " << filename << "\n";
@@ -79,7 +79,7 @@ ls(const string &filename) const {
 ////////////////////////////////////////////////////////////////////
 INLINE void VirtualFileSystem::
 ls_all(const string &filename) const {
-  PT(VirtualFile) file = get_file(filename);
+  PT(VirtualFile) file = get_file(filename, true);
   if (file == (VirtualFile *)NULL) {
     express_cat.info()
       << "Not found: " << filename << "\n";
@@ -123,7 +123,7 @@ read_file(const Filename &filename, bool auto_unwrap) const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool VirtualFileSystem::
 read_file(const Filename &filename, string &result, bool auto_unwrap) const {
-  PT(VirtualFile) file = get_file(filename);
+  PT(VirtualFile) file = get_file(filename, false);
   return (file != (VirtualFile *)NULL && file->read_file(result, auto_unwrap));
 }
 
@@ -142,6 +142,6 @@ read_file(const Filename &filename, string &result, bool auto_unwrap) const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool VirtualFileSystem::
 read_file(const Filename &filename, pvector<unsigned char> &result, bool auto_unwrap) const {
-  PT(VirtualFile) file = get_file(filename);
+  PT(VirtualFile) file = get_file(filename, false);
   return (file != (VirtualFile *)NULL && file->read_file(result, auto_unwrap));
 }

+ 233 - 83
panda/src/express/virtualFileSystem.cxx

@@ -35,6 +35,7 @@ VirtualFileSystem *VirtualFileSystem::_global_ptr = NULL;
 VirtualFileSystem::
 VirtualFileSystem() {
   _cwd = "/";
+  _mount_seq = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -55,7 +56,7 @@ VirtualFileSystem::
 ////////////////////////////////////////////////////////////////////
 bool VirtualFileSystem::
 mount(Multifile *multifile, const string &mount_point, int flags) {
-  VirtualFileMountMultifile *new_mount = 
+  PT(VirtualFileMountMultifile) new_mount = 
     new VirtualFileMountMultifile(multifile);
   return mount(new_mount, mount_point, flags);
 }
@@ -91,7 +92,7 @@ mount(const Filename &physical_filename, const string &mount_point,
   }
 
   if (physical_filename.is_directory()) {
-    VirtualFileMountSystem *new_mount =
+    PT(VirtualFileMountSystem) new_mount =
       new VirtualFileMountSystem(physical_filename);
     return mount(new_mount, mount_point, flags);
   } else {
@@ -120,12 +121,10 @@ mount(const Filename &physical_filename, const string &mount_point,
 ////////////////////////////////////////////////////////////////////
 bool VirtualFileSystem::
 mount(VirtualFileMount *mount, const string &mount_point, int flags) {
-  nassertr(mount->_file_system == NULL, false);
-  mount->_file_system = this;
-  mount->_mount_point = normalize_mount_point(mount_point);
-  mount->_mount_flags = flags;
-  _mounts.push_back(mount);
-  return true;
+  _lock.acquire();
+  bool result = do_mount(mount, mount_point, flags);
+  _lock.release();
+  return result;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -137,6 +136,7 @@ mount(VirtualFileMount *mount, const string &mount_point, int flags) {
 ////////////////////////////////////////////////////////////////////
 int VirtualFileSystem::
 unmount(Multifile *multifile) {
+  _lock.acquire();
   Mounts::iterator ri, wi;
   wi = ri = _mounts.begin();
   while (ri != _mounts.end()) {
@@ -163,6 +163,8 @@ unmount(Multifile *multifile) {
 
   int num_removed = _mounts.end() - wi;
   _mounts.erase(wi, _mounts.end());
+  ++_mount_seq;
+  _lock.release();
   return num_removed;
 }
 
@@ -175,6 +177,7 @@ unmount(Multifile *multifile) {
 ////////////////////////////////////////////////////////////////////
 int VirtualFileSystem::
 unmount(const Filename &physical_filename) {
+  _lock.acquire();
   Mounts::iterator ri, wi;
   wi = ri = _mounts.begin();
   while (ri != _mounts.end()) {
@@ -214,6 +217,8 @@ unmount(const Filename &physical_filename) {
 
   int num_removed = _mounts.end() - wi;
   _mounts.erase(wi, _mounts.end());
+  ++_mount_seq;
+  _lock.release();
   return num_removed;
 }
 
@@ -226,6 +231,7 @@ unmount(const Filename &physical_filename) {
 ////////////////////////////////////////////////////////////////////
 int VirtualFileSystem::
 unmount(VirtualFileMount *mount) {
+  _lock.acquire();
   Mounts::iterator ri, wi;
   wi = ri = _mounts.begin();
   while (ri != _mounts.end()) {
@@ -243,6 +249,8 @@ unmount(VirtualFileMount *mount) {
 
   int num_removed = _mounts.end() - wi;
   _mounts.erase(wi, _mounts.end());
+  ++_mount_seq;
+  _lock.release();
   return num_removed;
 }
 
@@ -255,6 +263,7 @@ unmount(VirtualFileMount *mount) {
 ////////////////////////////////////////////////////////////////////
 int VirtualFileSystem::
 unmount_point(const string &mount_point) {
+  _lock.acquire();
   Filename nmp = normalize_mount_point(mount_point);
   Mounts::iterator ri, wi;
   wi = ri = _mounts.begin();
@@ -275,6 +284,8 @@ unmount_point(const string &mount_point) {
 
   int num_removed = _mounts.end() - wi;
   _mounts.erase(wi, _mounts.end());
+  ++_mount_seq;
+  _lock.release();
   return num_removed;
 }
 
@@ -286,6 +297,7 @@ unmount_point(const string &mount_point) {
 ////////////////////////////////////////////////////////////////////
 int VirtualFileSystem::
 unmount_all() {
+  _lock.acquire();
   Mounts::iterator ri;
   for (ri = _mounts.begin(); ri != _mounts.end(); ++ri) {
     (*ri)->_file_system = NULL;
@@ -293,6 +305,8 @@ unmount_all() {
 
   int num_removed = _mounts.size();
   _mounts.clear();
+  ++_mount_seq;
+  _lock.release();
   return num_removed;
 }
 
@@ -304,7 +318,10 @@ unmount_all() {
 ////////////////////////////////////////////////////////////////////
 int VirtualFileSystem::
 get_num_mounts() const {
-  return _mounts.size();
+  ((VirtualFileSystem *)this)->_lock.acquire();
+  int result = _mounts.size();
+  ((VirtualFileSystem *)this)->_lock.release();
+  return result;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -312,10 +329,16 @@ get_num_mounts() const {
 //       Access: Published
 //  Description: Returns the nth mount in the system.
 ////////////////////////////////////////////////////////////////////
-VirtualFileMount *VirtualFileSystem::
+PT(VirtualFileMount) VirtualFileSystem::
 get_mount(int n) const {
-  nassertr(n >= 0 && n < (int)_mounts.size(), NULL);
-  return _mounts[n];
+  ((VirtualFileSystem *)this)->_lock.acquire();
+  nassertd(n >= 0 && n < (int)_mounts.size()) {
+    ((VirtualFileSystem *)this)->_lock.release();
+    return NULL;
+  }
+  PT(VirtualFileMount) result = _mounts[n];
+  ((VirtualFileSystem *)this)->_lock.release();
+  return result;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -331,17 +354,21 @@ get_mount(int n) const {
 ////////////////////////////////////////////////////////////////////
 bool VirtualFileSystem::
 chdir(const string &new_directory) {
+  _lock.acquire();
   if (new_directory == "/") {
     // We can always return to the root.
     _cwd = new_directory;
+    _lock.release();
     return true;
   }
 
-  PT(VirtualFile) file = get_file(new_directory, true);
+  PT(VirtualFile) file = do_get_file(new_directory, true);
   if (file != (VirtualFile *)NULL && file->is_directory()) {
     _cwd = file->get_filename();
+    _lock.release();
     return true;
   }
+  _lock.release();
   return false;
 }
 
@@ -350,9 +377,12 @@ chdir(const string &new_directory) {
 //       Access: Published
 //  Description: Returns the current directory name.  See chdir().
 ////////////////////////////////////////////////////////////////////
-const Filename &VirtualFileSystem::
+Filename VirtualFileSystem::
 get_cwd() const {
-  return _cwd;
+  ((VirtualFileSystem *)this)->_lock.acquire();
+  Filename result = _cwd;
+  ((VirtualFileSystem *)this)->_lock.release();
+  return result;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -373,72 +403,10 @@ get_cwd() const {
 ////////////////////////////////////////////////////////////////////
 PT(VirtualFile) VirtualFileSystem::
 get_file(const Filename &filename, bool status_only) const {
-  nassertr(!filename.empty(), NULL);
-  Filename pathname(filename);
-  if (pathname.is_local()) {
-    pathname = Filename(_cwd, filename);
-    if (filename.is_text()) {
-      pathname.set_text();
-    }
-  }
-  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.
-  PT(VirtualFile) found_file = NULL;
-  VirtualFileComposite *composite_file = NULL;
-
-  Mounts::const_reverse_iterator rmi;
-  for (rmi = _mounts.rbegin(); rmi != _mounts.rend(); ++rmi) {
-    VirtualFileMount *mount = (*rmi);
-    string mount_point = mount->get_mount_point();
-    if (strpath == mount_point) {
-      // Here's an exact match on the mount point.  This filename is
-      // the root directory of this mount object.
-      if (consider_match(found_file, composite_file, mount, "", pathname,
-                         false, status_only)) {
-        return found_file;
-      }
-    } else if (mount_point.empty()) {
-      // This is the root mount point; all files are in here.
-      if (consider_match(found_file, composite_file, mount, strpath, 
-                         pathname, false, status_only)) {
-        return found_file;
-      }
-#ifdef HAVE_ZLIB
-      if (vfs_implicit_pz) {
-        if (consider_match(found_file, composite_file, mount, strpath_pz, 
-                           pathname, true, status_only)) {
-          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 (consider_match(found_file, composite_file, mount, local_filename, 
-                         pathname, false, status_only)) {
-        return found_file;
-      }
-#ifdef HAVE_ZLIB
-      if (vfs_implicit_pz) {
-        // Bingo!
-        if (consider_match(found_file, composite_file, mount, local_filename_pz,
-                           pathname, true, status_only)) {
-          return found_file;
-        }
-      }
-#endif  // HAVE_ZLIB
-    }
-  }
-  return found_file;
+  ((VirtualFileSystem *)this)->_lock.acquire();
+  PT(VirtualFile) result = do_get_file(filename, status_only);
+  ((VirtualFileSystem *)this)->_lock.release();
+  return result;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -575,12 +543,14 @@ find_all_files(const Filename &filename, const DSearchPath &searchpath,
 ////////////////////////////////////////////////////////////////////
 void VirtualFileSystem::
 write(ostream &out) const {
+  ((VirtualFileSystem *)this)->_lock.acquire();
   out << "_cwd" << _cwd << "\n_mounts:\n";
   Mounts::const_iterator mi;
   for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
     VirtualFileMount *mount = (*mi);
     mount->write(out);
   }
+  ((VirtualFileSystem *)this)->_lock.release();
 }
 
 
@@ -708,7 +678,7 @@ open_read_file(const Filename &filename, bool auto_unwrap) const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileSystem::close_read_file
-//       Access: Published
+//       Access: Published, Static
 //  Description: Closes a file opened by a previous call to
 //               open_read_file().  This really just deletes the
 //               istream pointer, but it is recommended to use this
@@ -716,7 +686,7 @@ open_read_file(const Filename &filename, bool auto_unwrap) const {
 //               work around compiler issues.
 ////////////////////////////////////////////////////////////////////
 void VirtualFileSystem::
-close_read_file(istream *stream) const {
+close_read_file(istream *stream) {
   if (stream != (istream *)NULL) {
     // For some reason--compiler bug in gcc 3.2?--explicitly deleting
     // the stream pointer does not call the appropriate global delete
@@ -782,6 +752,8 @@ scan_mount_points(vector_string &names, const Filename &path) const {
 //               to standard form (relative to the current directory,
 //               with no double slashes, and not terminating with a
 //               slash).  The initial slash is removed.
+//
+//               Assumes the lock is already held.
 ////////////////////////////////////////////////////////////////////
 Filename VirtualFileSystem::
 normalize_mount_point(const string &mount_point) const {
@@ -794,6 +766,125 @@ normalize_mount_point(const string &mount_point) const {
   return nmp.get_fullpath().substr(1);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: VirtualFileSystem::do_mount
+//       Access: Private
+//  Description: The private implementation of mount().  Assumes the
+//               lock is already held.
+////////////////////////////////////////////////////////////////////
+bool VirtualFileSystem::
+do_mount(VirtualFileMount *mount, const string &mount_point, int flags) {
+  nassertr(mount->_file_system == NULL, false);
+  mount->_file_system = this;
+  mount->_mount_point = normalize_mount_point(mount_point);
+  mount->_mount_flags = flags;
+  _mounts.push_back(mount);
+  ++_mount_seq;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VirtualFileSystem::do_get_file
+//       Access: Private
+//  Description: The private implementation of get_file().  Assumes
+//               the lock is already held.
+////////////////////////////////////////////////////////////////////
+PT(VirtualFile) VirtualFileSystem::
+do_get_file(const Filename &filename, bool status_only) const {
+  nassertr(!filename.empty(), NULL);
+  Filename pathname(filename);
+  if (pathname.is_local()) {
+    pathname = Filename(_cwd, filename);
+    if (filename.is_text()) {
+      pathname.set_text();
+    }
+  }
+  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.
+  PT(VirtualFile) found_file = NULL;
+  VirtualFileComposite *composite_file = NULL;
+
+  // We use an index instead of an iterator, since the vector might
+  // change if implicit mounts are added during this loop.
+  unsigned int start_seq = _mount_seq;
+
+  size_t i = _mounts.size();
+  while (i > 0) {
+    --i;
+    VirtualFileMount *mount = _mounts[i];
+    string mount_point = mount->get_mount_point();
+    if (strpath == mount_point) {
+      // Here's an exact match on the mount point.  This filename is
+      // the root directory of this mount object.
+      if (consider_match(found_file, composite_file, mount, "", pathname,
+                         false, status_only)) {
+        return found_file;
+      }
+    } else if (mount_point.empty()) {
+      // This is the root mount point; all files are in here.
+      if (consider_match(found_file, composite_file, mount, strpath, 
+                         pathname, false, status_only)) {
+        return found_file;
+      }
+#ifdef HAVE_ZLIB
+      if (vfs_implicit_pz) {
+        if (consider_match(found_file, composite_file, mount, strpath_pz, 
+                           pathname, true, status_only)) {
+          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 (consider_match(found_file, composite_file, mount, local_filename, 
+                         pathname, false, status_only)) {
+        return found_file;
+      }
+#ifdef HAVE_ZLIB
+      if (vfs_implicit_pz) {
+        // Bingo!
+        if (consider_match(found_file, composite_file, mount, local_filename_pz,
+                           pathname, true, status_only)) {
+          return found_file;
+        }
+      }
+#endif  // HAVE_ZLIB
+    }
+
+    // If we discover that a file has been implicitly mounted during
+    // one of the above operations, start over from the beginning of
+    // the loop.
+    if (start_seq != _mount_seq) {
+      start_seq = _mount_seq;
+      i = _mounts.size();
+    }
+  }
+
+  if (found_file == (VirtualFile *)NULL && vfs_implicit_mf) {
+    // The file wasn't found, as-is.  Does it appear to be an implicit
+    // .mf file reference?
+    ((VirtualFileSystem *)this)->consider_mount_mf(filename);
+
+    if (start_seq != _mount_seq) {
+      // Yes, it was, or some nested file was.  Now that we've
+      // implicitly mounted the .mf file, go back and look again.
+      return do_get_file(filename, status_only);
+    }
+  }
+
+  return found_file;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileSystem::consider_match
 //       Access: Private
@@ -859,6 +950,65 @@ consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_fil
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: VirtualFileSystem::consider_mount_mf
+//       Access: Private
+//  Description: The indicated filename was not found.  Check to see
+//               if it is using an implicit reference to a .mf file as
+//               a directory, that hasn't already been mounted.  If it
+//               is, mount the .mf file in-place, and return true; if
+//               it is not, or if its .mf file is already mounted
+//               in-place, return false.
+//
+//               Assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+bool VirtualFileSystem::
+consider_mount_mf(const Filename &filename) {
+  Filename dirname = filename.get_dirname();
+  if (dirname.empty()) {
+    // Reached the top directory; no .mf file references.
+    return false;
+  }
+  if (is_directory(dirname)) {
+    // Reached a real (or already-mounted) directory; no unmounted .mf
+    // file references.
+    return false;
+  }
+  if (dirname.get_extension() == "mf") {
+    // Hey, here's a multifile reference!
+    dirname.set_binary();
+    PT(VirtualFile) file = do_get_file(dirname, false);
+    if (file == (VirtualFile *)NULL || !file->is_regular_file()) {
+      // Oh, never mind.  Not a real file.
+      return false;
+    }
+
+    PT(Multifile) multifile = new Multifile;
+   
+    istream *stream = file->open_read_file(false);
+    if (stream == (istream *)NULL) {
+      // Couldn't read file.
+      return false;
+    }
+
+    if (!multifile->open_read(stream, true)) {
+      // Invalid multifile.
+      return false;
+    }
+
+    multifile->set_multifile_name(dirname.get_basename());
+    express_cat.info()
+      << "Implicitly mounting " << dirname << "\n";
+
+    PT(VirtualFileMountMultifile) new_mount = 
+      new VirtualFileMountMultifile(multifile);
+    return do_mount(new_mount, dirname, MF_read_only);
+  }
+
+  // Recurse.
+  return consider_mount_mf(dirname);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileSystem::parse_option
 //       Access: Private, Static

+ 10 - 3
panda/src/express/virtualFileSystem.h

@@ -23,6 +23,7 @@
 #include "dSearchPath.h"
 #include "pointerTo.h"
 #include "config_express.h"
+#include "mutexImpl.h"
 #include "pvector.h"
 
 class Multifile;
@@ -59,10 +60,10 @@ PUBLISHED:
   BLOCKING int unmount_all();
 
   int get_num_mounts() const;
-  VirtualFileMount *get_mount(int n) const;
+  PT(VirtualFileMount) get_mount(int n) const;
 
   BLOCKING bool chdir(const string &new_directory);
-  BLOCKING const Filename &get_cwd() const;
+  BLOCKING Filename get_cwd() const;
 
   BLOCKING PT(VirtualFile) get_file(const Filename &filename, bool status_only = false) const;
   BLOCKING PT(VirtualFile) find_file(const Filename &filename, 
@@ -86,7 +87,7 @@ PUBLISHED:
 
   BLOCKING INLINE string read_file(const Filename &filename, bool auto_unwrap) const;
   BLOCKING istream *open_read_file(const Filename &filename, bool auto_unwrap) const;
-  BLOCKING void close_read_file(istream *stream) const;
+  BLOCKING static void close_read_file(istream *stream);
 
 public:
   INLINE bool read_file(const Filename &filename, string &result, bool auto_unwrap) const;
@@ -96,15 +97,21 @@ public:
 
 private:
   Filename normalize_mount_point(const string &mount_point) const;
+  bool do_mount(VirtualFileMount *mount, const string &mount_point, int flags);
+  PT(VirtualFile) do_get_file(const Filename &filename, bool status_only) const;
   bool consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file,
                       VirtualFileMount *mount, const string &local_filename,
                       const Filename &original_filename, bool implicit_pz_file,
                       bool status_only) const;
+  bool consider_mount_mf(const Filename &filename);
   static void parse_option(const string &option,
                            int &flags, string &password);
 
+  MutexImpl _lock;
   typedef pvector<PT(VirtualFileMount) > Mounts;
   Mounts _mounts;
+  unsigned int _mount_seq;
+
   Filename _cwd;
 
   static VirtualFileSystem *_global_ptr;