Bladeren bron

support caching movie files in the model-cache-dir and in a txo file

David Rose 14 jaren geleden
bovenliggende
commit
285d70c29e
78 gewijzigde bestanden met toevoegingen van 3010 en 921 verwijderingen
  1. 8 6
      panda/src/audiotraits/fmodAudioSound.cxx
  2. 23 17
      panda/src/express/Sources.pp
  3. 4 0
      panda/src/express/config_express.cxx
  4. 3 4
      panda/src/express/datagramGenerator.I
  5. 52 4
      panda/src/express/datagramGenerator.cxx
  6. 8 2
      panda/src/express/datagramGenerator.h
  7. 83 1
      panda/src/express/datagramSink.cxx
  8. 10 0
      panda/src/express/datagramSink.h
  9. 1 1
      panda/src/express/express_composite1.cxx
  10. 2 0
      panda/src/express/express_composite2.cxx
  11. 33 0
      panda/src/express/fileReference.I
  12. 17 0
      panda/src/express/fileReference.cxx
  13. 57 0
      panda/src/express/fileReference.h
  14. 0 103
      panda/src/express/fileSystemInfo.I
  15. 0 48
      panda/src/express/fileSystemInfo.h
  16. 0 1
      panda/src/express/multifile.h
  17. 140 0
      panda/src/express/subfileInfo.I
  18. 5 6
      panda/src/express/subfileInfo.cxx
  19. 56 0
      panda/src/express/subfileInfo.h
  20. 23 0
      panda/src/express/temporaryFile.I
  21. 28 0
      panda/src/express/temporaryFile.cxx
  22. 54 0
      panda/src/express/temporaryFile.h
  23. 2 2
      panda/src/express/virtualFile.cxx
  24. 2 2
      panda/src/express/virtualFile.h
  25. 2 2
      panda/src/express/virtualFileMount.cxx
  26. 1 1
      panda/src/express/virtualFileMount.h
  27. 3 3
      panda/src/express/virtualFileMountMultifile.cxx
  28. 1 1
      panda/src/express/virtualFileMountMultifile.h
  29. 3 3
      panda/src/express/virtualFileMountSystem.cxx
  30. 1 1
      panda/src/express/virtualFileMountSystem.h
  31. 2 2
      panda/src/express/virtualFileSimple.cxx
  32. 1 1
      panda/src/express/virtualFileSimple.h
  33. 427 257
      panda/src/gobj/texture.cxx
  34. 19 5
      panda/src/gobj/texture.h
  35. 47 11
      panda/src/gobj/texturePool.cxx
  36. 1 1
      panda/src/gobj/texturePool.h
  37. 13 1
      panda/src/grutil/ffmpegTexture.I
  38. 269 58
      panda/src/grutil/ffmpegTexture.cxx
  39. 21 0
      panda/src/grutil/ffmpegTexture.h
  40. 154 49
      panda/src/grutil/movieTexture.cxx
  41. 10 2
      panda/src/grutil/movieTexture.h
  42. 21 2
      panda/src/movies/config_movies.cxx
  43. 0 15
      panda/src/movies/config_movies.h
  44. 3 0
      panda/src/movies/ffmpegAudio.h
  45. 13 9
      panda/src/movies/ffmpegAudioCursor.cxx
  46. 6 1
      panda/src/movies/ffmpegAudioCursor.h
  47. 74 2
      panda/src/movies/ffmpegVideo.cxx
  48. 18 3
      panda/src/movies/ffmpegVideo.h
  49. 168 34
      panda/src/movies/ffmpegVideoCursor.cxx
  50. 25 6
      panda/src/movies/ffmpegVideoCursor.h
  51. 12 0
      panda/src/movies/ffmpegVirtualFile.I
  52. 268 114
      panda/src/movies/ffmpegVirtualFile.cxx
  53. 44 9
      panda/src/movies/ffmpegVirtualFile.h
  54. 1 0
      panda/src/movies/movieAudio.cxx
  55. 14 1
      panda/src/movies/movieVideo.I
  56. 60 2
      panda/src/movies/movieVideo.cxx
  57. 19 4
      panda/src/movies/movieVideo.h
  58. 50 6
      panda/src/movies/movieVideoCursor.cxx
  59. 17 5
      panda/src/movies/movieVideoCursor.h
  60. 2 2
      panda/src/pgraph/bamFile.cxx
  61. 2 1
      panda/src/putil/bam.h
  62. 18 53
      panda/src/putil/bamCache.cxx
  63. 3 3
      panda/src/putil/bamCache.h
  64. 3 0
      panda/src/putil/bamEnums.cxx
  65. 4 1
      panda/src/putil/bamEnums.h
  66. 31 3
      panda/src/putil/bamReader.I
  67. 51 8
      panda/src/putil/bamReader.cxx
  68. 14 5
      panda/src/putil/bamReader.h
  69. 16 1
      panda/src/putil/bamWriter.I
  70. 69 2
      panda/src/putil/bamWriter.cxx
  71. 6 4
      panda/src/putil/bamWriter.h
  72. 24 1
      panda/src/putil/datagramInputFile.I
  73. 132 15
      panda/src/putil/datagramInputFile.cxx
  74. 12 4
      panda/src/putil/datagramInputFile.h
  75. 23 0
      panda/src/putil/datagramOutputFile.I
  76. 186 6
      panda/src/putil/datagramOutputFile.cxx
  77. 13 2
      panda/src/putil/datagramOutputFile.h
  78. 2 2
      panda/src/putil/typedWritable.cxx

+ 8 - 6
panda/src/audiotraits/fmodAudioSound.cxx

@@ -25,7 +25,7 @@
 #include "config_audio.h"
 #include "fmodAudioSound.h"
 #include "string_utils.h"
-#include "fileSystemInfo.h"
+#include "subfileInfo.h"
 
 TypeHandle FmodAudioSound::_type_handle;
 
@@ -104,9 +104,10 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) {
     }
     
     const char *name_or_data = _file_name.c_str();
+    string os_filename;
     
     pvector<unsigned char> mem_buffer;
-    FileSystemInfo info;
+    SubfileInfo info;
     if (preload) {
       // Pre-read the file right now, and pass it in as a memory
       // buffer.  This avoids threading issues completely, because all
@@ -129,9 +130,10 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) {
       // This is also safe, because FMod uses its own I/O operations
       // that don't involve Panda, so this can safely happen in an
       // FMod thread.
-      name_or_data = info.get_os_file_name().c_str();
-      sound_info.fileoffset = (unsigned int)info.get_file_start();
-      sound_info.length = (unsigned int)info.get_file_size();
+      os_filename = info.get_filename().to_os_specific();
+      name_or_data = os_filename.c_str();
+      sound_info.fileoffset = (unsigned int)info.get_start();
+      sound_info.length = (unsigned int)info.get_size();
       flags |= FMOD_CREATESTREAM;
       if (fmodAudio_cat.is_debug()) {
 	fmodAudio_cat.debug()
@@ -144,7 +146,7 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) {
       // Otherwise, if the Panda threading system is compiled in, we
       // can assign callbacks to read the file through the VFS.
       name_or_data = (const char *)file.p();
-      sound_info.length = (unsigned int)info.get_file_size();
+      sound_info.length = (unsigned int)info.get_size();
       sound_info.useropen = open_callback;
       sound_info.userclose = close_callback;
       sound_info.userread = read_callback;

+ 23 - 17
panda/src/express/Sources.pp

@@ -13,8 +13,8 @@
     ca_bundle_data_src.c \
     checksumHashGenerator.I checksumHashGenerator.h circBuffer.I \
     circBuffer.h \
-    config_express.h \
     compress_string.h \
+    config_express.h \
     copy_stream.h \
     datagram.I datagram.h datagramGenerator.I \
     datagramGenerator.h \
@@ -23,7 +23,7 @@
     encrypt_string.h \
     error_utils.h \
     export_dtool.h \
-    fileSystemInfo.h fileSystemInfo.I \
+    fileReference.h fileReference.I \
     hashGeneratorBase.I hashGeneratorBase.h \
     hashVal.I hashVal.h \
     indirectLess.I indirectLess.h \
@@ -34,11 +34,12 @@
     multifile.I multifile.h \
     namable.I \
     namable.h \
-    nodePointerToBase.h nodePointerToBase.I \
     nodePointerTo.h nodePointerTo.I \
+    nodePointerToBase.h nodePointerToBase.I \
     nodeReferenceCount.h nodeReferenceCount.I \
     openSSLWrapper.h openSSLWrapper.I \
     ordered_vector.h ordered_vector.I ordered_vector.T \
+    pStatCollectorForwardBase.h \
     password_hash.h \
     patchfile.I patchfile.h \
     pointerTo.I pointerTo.h \
@@ -47,13 +48,14 @@
     pointerToBase.I pointerToBase.h \
     pointerToVoid.I pointerToVoid.h \
     profileTimer.I profileTimer.h \
-    pStatCollectorForwardBase.h \
-    pta_uchar.h pta_float.h \
     pta_int.h \
+    pta_uchar.h pta_float.h \
     ramfile.I ramfile.h \
     referenceCount.I referenceCount.h \
     stringDecoder.h stringDecoder.I \
     subStream.I subStream.h subStreamBuf.h \
+    subfileInfo.h subfileInfo.I \
+    temporaryFile.h temporaryFile.I \
     textEncoder.h textEncoder.I \
     threadSafePointerTo.I threadSafePointerTo.h \
     threadSafePointerToBase.I threadSafePointerToBase.h \
@@ -61,8 +63,8 @@
     typedReferenceCount.I typedReferenceCount.h typedef.h \
     unicodeLatinMap.h \
     vector_uchar.h vector_float.h \
-    virtualFileComposite.h virtualFileComposite.I virtualFile.h \
     virtualFile.I virtualFileList.I virtualFileList.h virtualFileMount.h \
+    virtualFileComposite.h virtualFileComposite.I virtualFile.h \
     virtualFileMount.I virtualFileMountMultifile.h \
     virtualFileMountMultifile.I virtualFileMountSystem.h \
     virtualFileMountSystem.I virtualFileSimple.h virtualFileSimple.I \
@@ -77,24 +79,25 @@
 
   #define INCLUDED_SOURCES  \
     buffer.cxx checksumHashGenerator.cxx \
-    config_express.cxx \
     compress_string.cxx \
+    config_express.cxx \
     copy_stream.cxx \
     datagram.cxx datagramGenerator.cxx \
     datagramIterator.cxx \
     datagramSink.cxx dcast.cxx \
     encrypt_string.cxx \
     error_utils.cxx \
-    fileSystemInfo.cxx \
+    fileReference.cxx \
     hashGeneratorBase.cxx hashVal.cxx \
     memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \
     memoryUsagePointers.cxx multifile.cxx \
     namable.cxx \
-    nodePointerToBase.cxx \
     nodePointerTo.cxx \
+    nodePointerToBase.cxx \
     nodeReferenceCount.cxx \
     openSSLWrapper.cxx \
     ordered_vector.cxx \
+    pStatCollectorForwardBase.cxx \
     password_hash.cxx \
     patchfile.cxx \
     pointerTo.cxx \
@@ -102,13 +105,14 @@
     pointerToBase.cxx \
     pointerToVoid.cxx \
     profileTimer.cxx \
-    pStatCollectorForwardBase.cxx \
-    pta_uchar.cxx pta_float.cxx \
     pta_int.cxx \
+    pta_uchar.cxx pta_float.cxx \
     ramfile.cxx \
     referenceCount.cxx \
     stringDecoder.cxx \
     subStream.cxx subStreamBuf.cxx \
+    subfileInfo.cxx \
+    temporaryFile.cxx \
     textEncoder.cxx \
     threadSafePointerTo.cxx \
     threadSafePointerToBase.cxx \
@@ -133,8 +137,8 @@
     ca_bundle_data_src.c \
     checksumHashGenerator.I checksumHashGenerator.h circBuffer.I \
     circBuffer.h \
-    config_express.h \
     compress_string.h \
+    config_express.h \
     copy_stream.h \
     datagram.I datagram.h datagramGenerator.I \
     datagramGenerator.h \
@@ -142,7 +146,7 @@
     dcast.T dcast.h \
     encrypt_string.h \
     error_utils.h \
-    fileSystemInfo.h fileSystemInfo.I \
+    fileReference.h fileReference.I \
     hashGeneratorBase.I hashGeneratorBase.h \
     hashVal.I hashVal.h \
     indirectLess.I indirectLess.h \
@@ -153,11 +157,12 @@
     multifile.I multifile.h \
     namable.I \
     namable.h \
-    nodePointerToBase.h nodePointerToBase.I \
     nodePointerTo.h nodePointerTo.I \
+    nodePointerToBase.h nodePointerToBase.I \
     nodeReferenceCount.h nodeReferenceCount.I \
     openSSLWrapper.h openSSLWrapper.I \
     ordered_vector.h ordered_vector.I ordered_vector.T \
+    pStatCollectorForwardBase.h \
     password_hash.h \
     patchfile.I patchfile.h \
     pointerTo.I pointerTo.h \
@@ -166,13 +171,14 @@
     pointerToBase.I pointerToBase.h \
     pointerToVoid.I pointerToVoid.h \
     profileTimer.I profileTimer.h \
-    pStatCollectorForwardBase.h \
-    pta_uchar.h pta_float.h \
     pta_int.h \
+    pta_uchar.h pta_float.h \
     ramfile.I ramfile.h \
     referenceCount.I referenceCount.h \
     stringDecoder.h stringDecoder.I \
     subStream.I subStream.h subStreamBuf.h \
+    subfileInfo.h subfileInfo.I \
+    temporaryFile.h temporaryFile.I \
     textEncoder.h textEncoder.I \
     threadSafePointerTo.I threadSafePointerTo.h \
     threadSafePointerToBase.I threadSafePointerToBase.h \
@@ -180,8 +186,8 @@
     typedReferenceCount.I typedReferenceCount.h typedef.h \
     unicodeLatinMap.h \
     vector_uchar.h vector_float.h \
-    virtualFileComposite.h virtualFileComposite.I virtualFile.h \
     virtualFile.I virtualFileList.I virtualFileList.h virtualFileMount.h \
+    virtualFileComposite.h virtualFileComposite.I virtualFile.h \
     virtualFileMount.I virtualFileMountMultifile.h \
     virtualFileMountMultifile.I virtualFileMountSystem.h \
     virtualFileMountSystem.I virtualFileSimple.h virtualFileSimple.I \

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

@@ -25,6 +25,8 @@
 #include "virtualFileMountMultifile.h"
 #include "virtualFileMountSystem.h"
 #include "virtualFileSimple.h"
+#include "fileReference.h"
+#include "temporaryFile.h"
 #include "pandaSystem.h"
 #include "numeric_types.h"
 #include "namable.h"
@@ -98,6 +100,8 @@ init_libexpress() {
   VirtualFileMountMultifile::init_type();
   VirtualFileMountSystem::init_type();
   VirtualFileSimple::init_type();
+  FileReference::init_type();
+  TemporaryFile::init_type();
 
   init_system_type_handles();
 

+ 3 - 4
panda/src/express/datagramGenerator.I

@@ -15,11 +15,10 @@
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramGenerator::Constructor
-//       Access: Public
+//       Access: Published
 //  Description: Does nothing since this is class is just
 //               the definition of an interface
 ////////////////////////////////////////////////////////////////////
-
-INLINE DatagramGenerator::DatagramGenerator(){
+INLINE DatagramGenerator::
+DatagramGenerator() {
 }
-

+ 52 - 4
panda/src/express/datagramGenerator.cxx

@@ -16,10 +16,11 @@
 #include "pandabase.h"
 
 #include "datagramGenerator.h"
+#include "temporaryFile.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramGenerator::Destructor
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Does nothing since this is class is just
 //               the definition of an interface
 ////////////////////////////////////////////////////////////////////
@@ -27,21 +28,68 @@ DatagramGenerator::
 ~DatagramGenerator() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramGenerator::save_datagram
+//       Access: Published, Virtual
+//  Description: Skips over the next datagram without extracting it,
+//               but saves the relevant file information in the
+//               SubfileInfo object so that its data may be read
+//               later.  For non-file-based datagram generators, this
+//               may mean creating a temporary file and copying the
+//               contents of the datagram to disk.
+//
+//               Returns true on success, false on failure or if this
+//               method is unimplemented.
+////////////////////////////////////////////////////////////////////
+bool DatagramGenerator::
+save_datagram(SubfileInfo &info) {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramGenerator::get_filename
+//       Access: Published, Virtual
+//  Description: Returns the filename that provides the source for
+//               these datagrams, if any, or empty string if the
+//               datagrams do not originate from a file on disk.
+////////////////////////////////////////////////////////////////////
+const Filename &DatagramGenerator::
+get_filename() {
+  const FileReference *file = get_file();
+  if (file != (FileReference *)NULL) {
+    return file->get_filename();
+  }
+  static const Filename empty_filename;
+  return empty_filename;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramGenerator::get_file
-//       Access: Public, Virtual
+//       Access: Published, Virtual
+//  Description: Returns the FileReference that provides the source for
+//               these datagrams, if any, or NULL if the datagrams do
+//               not originate from a file on disk.
+////////////////////////////////////////////////////////////////////
+const FileReference *DatagramGenerator::
+get_file() {
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramGenerator::get_vfile
+//       Access: Published, Virtual
 //  Description: Returns the VirtualFile that provides the source for
 //               these datagrams, if any, or NULL if the datagrams do
 //               not originate from a VirtualFile.
 ////////////////////////////////////////////////////////////////////
 VirtualFile *DatagramGenerator::
-get_file() {
+get_vfile() {
   return NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramGenerator::get_file_pos
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Returns the current file position within the data
 //               stream, if any, or 0 if the file position is not
 //               meaningful or cannot be determined.

+ 8 - 2
panda/src/express/datagramGenerator.h

@@ -19,13 +19,16 @@
 
 #include "datagram.h"
 
+class SubfileInfo;
+class FileReference;
+class Filename;
 class VirtualFile;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : DatagramGenerator
 // Description : This class defines the abstract interace to any
 //               source of datagrams, whether it be from a file or
-//               from the net
+//               from the net.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDAEXPRESS DatagramGenerator {
 PUBLISHED:
@@ -33,10 +36,13 @@ PUBLISHED:
   virtual ~DatagramGenerator();
 
   virtual bool get_datagram(Datagram &data) = 0;
+  virtual bool save_datagram(SubfileInfo &info);
   virtual bool is_eof() = 0;
   virtual bool is_error() = 0;
 
-  virtual VirtualFile *get_file();
+  virtual const Filename &get_filename();
+  virtual const FileReference *get_file();
+  virtual VirtualFile *get_vfile();
   virtual streampos get_file_pos();
 };
 

+ 83 - 1
panda/src/express/datagramSink.cxx

@@ -13,6 +13,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "datagramSink.h"
+#include "fileReference.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramSink::Destructor
@@ -21,5 +22,86 @@
 //               the definition of an interface
 ////////////////////////////////////////////////////////////////////
 DatagramSink::
-~DatagramSink(){
+~DatagramSink() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramSink::copy_datagram
+//       Access: Published, Virtual
+//  Description: Copies the file data from the entire indicated
+//               file (via the vfs) as the next datagram.  This is
+//               intended to support potentially very large datagrams.
+//
+//               Returns true on success, false on failure or if this
+//               method is unimplemented.  On true, fills "result"
+//               with the information that references the copied file,
+//               if possible.
+////////////////////////////////////////////////////////////////////
+bool DatagramSink::
+copy_datagram(SubfileInfo &result, const Filename &filename) {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramSink::copy_datagram
+//       Access: Published, Virtual
+//  Description: Copies the file data from the range of the indicated
+//               file (outside of the vfs) as the next datagram.  This
+//               is intended to support potentially very large
+//               datagrams.
+//
+//               Returns true on success, false on failure or if this
+//               method is unimplemented.  On true, fills "result"
+//               with the information that references the copied file,
+//               if possible.
+////////////////////////////////////////////////////////////////////
+bool DatagramSink::
+copy_datagram(SubfileInfo &result, const SubfileInfo &source) {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramSink::get_filename
+//       Access: Published, Virtual
+//  Description: Returns the filename that provides the target for
+//               these datagrams, if any, or empty string if the
+//               datagrams do not get written to a file on disk.
+////////////////////////////////////////////////////////////////////
+const Filename &DatagramSink::
+get_filename() {
+  const FileReference *file = get_file();
+  if (file != (FileReference *)NULL) {
+    return file->get_filename();
+  }
+  static const Filename empty_filename;
+  return empty_filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramSink::get_file
+//       Access: Published, Virtual
+//  Description: Returns the FileReference that provides the target for
+//               these datagrams, if any, or NULL if the datagrams do
+//               not written to a file on disk.
+////////////////////////////////////////////////////////////////////
+const FileReference *DatagramSink::
+get_file() {
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramSink::get_file_pos
+//       Access: Published, Virtual
+//  Description: Returns the current file position within the data
+//               stream, if any, or 0 if the file position is not
+//               meaningful or cannot be determined.
+//
+//               For DatagramSinks that return a meaningful file
+//               position, this will be pointing to the first byte
+//               following the datagram returned after a call to
+//               put_datagram().
+////////////////////////////////////////////////////////////////////
+streampos DatagramSink::
+get_file_pos() {
+  return 0;
 }

+ 10 - 0
panda/src/express/datagramSink.h

@@ -19,6 +19,10 @@
 
 #include "datagram.h"
 
+class SubfileInfo;
+class FileReference;
+class Filename;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : DatagramSink
 // Description : This class defines the abstract interface to sending
@@ -31,8 +35,14 @@ PUBLISHED:
   virtual ~DatagramSink();
 
   virtual bool put_datagram(const Datagram &data) = 0;
+  virtual bool copy_datagram(SubfileInfo &result, const Filename &filename);
+  virtual bool copy_datagram(SubfileInfo &result, const SubfileInfo &source);
   virtual bool is_error() = 0;
   virtual void flush() = 0;
+
+  virtual const Filename &get_filename();
+  virtual const FileReference *get_file();
+  virtual streampos get_file_pos();
 };
 
 #include "datagramSink.I"

+ 1 - 1
panda/src/express/express_composite1.cxx

@@ -10,7 +10,7 @@
 #include "dcast.cxx"
 #include "encrypt_string.cxx"
 #include "error_utils.cxx"
-#include "fileSystemInfo.cxx"
+#include "fileReference.cxx"
 #include "hashGeneratorBase.cxx"
 #include "hashVal.cxx"
 #include "memoryInfo.cxx"

+ 2 - 0
panda/src/express/express_composite2.cxx

@@ -4,8 +4,10 @@
 #include "ramfile.cxx"
 #include "referenceCount.cxx"
 #include "stringDecoder.cxx"
+#include "subfileInfo.cxx"
 #include "subStream.cxx"
 #include "subStreamBuf.cxx"
+#include "temporaryFile.cxx"
 #include "textEncoder.cxx"
 #include "threadSafePointerTo.cxx"
 #include "threadSafePointerToBase.cxx"

+ 33 - 0
panda/src/express/fileReference.I

@@ -0,0 +1,33 @@
+// Filename: fileReference.I
+// Created by:  drose (23Jun11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FileReference::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE FileReference::
+FileReference(const Filename &filename) : _filename(filename) {
+}
+ 
+////////////////////////////////////////////////////////////////////
+//     Function: FileReference::get_filename
+//       Access: Published
+//  Description: Returns the filename of the reference.
+////////////////////////////////////////////////////////////////////
+INLINE const Filename &FileReference::
+get_filename() const {
+  return _filename;
+}

+ 17 - 0
panda/src/express/fileReference.cxx

@@ -0,0 +1,17 @@
+// Filename: fileReference.cxx
+// Created by:  drose (23Jun11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "fileReference.h"
+
+TypeHandle FileReference::_type_handle;

+ 57 - 0
panda/src/express/fileReference.h

@@ -0,0 +1,57 @@
+// Filename: fileReference.h
+// Created by:  drose (23Jun11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef FILEREFERENCE_H
+#define FILEREFERENCE_H
+
+#include "pandabase.h"
+
+#include "typedReferenceCount.h"
+#include "filename.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : FileReference
+// Description : Keeps a reference-counted pointer to a file on disk.
+//               As long as the FileReference is held, someone
+//               presumably has a use for this file.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS FileReference : public TypedReferenceCount {
+PUBLISHED:
+  INLINE FileReference(const Filename &filename);
+  INLINE const Filename &get_filename() const;
+
+protected:
+  Filename _filename;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "FileReference",
+                  TypedReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "fileReference.I"
+
+#endif

+ 0 - 103
panda/src/express/fileSystemInfo.I

@@ -1,103 +0,0 @@
-// Filename: fileSystemInfo.I
-// Created by:  drose (20Jun11)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) Carnegie Mellon University.  All rights reserved.
-//
-// All use of this software is subject to the terms of the revised BSD
-// license.  You should have received a copy of this license along
-// with this source code in a file named "LICENSE."
-//
-////////////////////////////////////////////////////////////////////
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: FileSystemInfo::Default Constructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE FileSystemInfo::
-FileSystemInfo() :
-  _file_start(0),
-  _file_size(0)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FileSystemInfo::Constructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE FileSystemInfo::
-FileSystemInfo(const string &os_file_name, streampos file_start, streamsize file_size) :
-  _os_file_name(os_file_name),
-  _file_start(file_start),
-  _file_size(file_size)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FileSystemInfo::Copy Constructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE FileSystemInfo::
-FileSystemInfo(const FileSystemInfo &copy) :
-  _os_file_name(copy._os_file_name),
-  _file_start(copy._file_start),
-  _file_size(copy._file_size)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FileSystemInfo::Copy Assignment Operator
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE void FileSystemInfo::
-operator = (const FileSystemInfo &copy) {
-  _os_file_name = copy._os_file_name;
-  _file_start = copy._file_start;
-  _file_size = copy._file_size;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FileSystemInfo::get_os_file_name
-//       Access: Published
-//  Description: Returns the os-specific filename that may be used to
-//               open this file.
-////////////////////////////////////////////////////////////////////
-INLINE const string &FileSystemInfo::
-get_os_file_name() const {
-  return _os_file_name;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FileSystemInfo::get_file_start
-//       Access: Published
-//  Description: Returns the offset within the file at which this file
-//               data begins.
-////////////////////////////////////////////////////////////////////
-INLINE streampos FileSystemInfo::
-get_file_start() const {
-  return _file_start;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FileSystemInfo::get_file_size
-//       Access: Published
-//  Description: Returns the number of consecutive bytes, beginning at
-//               get_file_start(), that correspond to this file data.
-////////////////////////////////////////////////////////////////////
-INLINE streamsize FileSystemInfo::
-get_file_size() const {
-  return _file_size;
-}
-
-INLINE ostream &
-operator << (ostream &out, const FileSystemInfo &info) {
-  info.output(out);
-  return out;
-}

+ 0 - 48
panda/src/express/fileSystemInfo.h

@@ -1,48 +0,0 @@
-// Filename: fileSystemInfo.h
-// Created by:  drose (20Jun11)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) Carnegie Mellon University.  All rights reserved.
-//
-// All use of this software is subject to the terms of the revised BSD
-// license.  You should have received a copy of this license along
-// with this source code in a file named "LICENSE."
-//
-////////////////////////////////////////////////////////////////////
-
-#ifndef FILESYSTEMINFO_H
-#define FILESYSTEMINFO_H
-
-#include "pandabase.h"
-
-////////////////////////////////////////////////////////////////////
-//       Class : FileSystemInfo
-// Description : This class is used to return data about an actual
-//               file on disk by VirtualFile::get_system_info().
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDAEXPRESS FileSystemInfo {
-PUBLISHED:
-  INLINE FileSystemInfo();
-  INLINE FileSystemInfo(const string &os_file_name, streampos file_start, streamsize file_size);
-  INLINE FileSystemInfo(const FileSystemInfo &copy);
-  INLINE void operator = (const FileSystemInfo &copy);
-
-  INLINE const string &get_os_file_name() const;
-  INLINE streampos get_file_start() const;
-  INLINE streamsize get_file_size() const;
-
-  void output(ostream &out) const;
-
-private:
-  string _os_file_name;
-  streampos _file_start;
-  streamsize _file_size;
-};
-
-INLINE ostream &operator << (ostream &out, const FileSystemInfo &info);
-
-#include "fileSystemInfo.I"
-
-#endif

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

@@ -26,7 +26,6 @@
 #include "referenceCount.h"
 #include "pvector.h"
 #include "openSSLWrapper.h"
-#include "fileSystemInfo.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : Multifile

+ 140 - 0
panda/src/express/subfileInfo.I

@@ -0,0 +1,140 @@
+// Filename: subfileInfo.I
+// Created by:  drose (20Jun11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: SubfileInfo::Default Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE SubfileInfo::
+SubfileInfo() :
+  _start(0),
+  _size(0)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SubfileInfo::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE SubfileInfo::
+SubfileInfo(const FileReference *file, streampos start, streamsize size) :
+  _file(file),
+  _start(start),
+  _size(size)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SubfileInfo::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE SubfileInfo::
+SubfileInfo(const Filename &filename, streampos start, streamsize size) :
+  _file(new FileReference(filename)),
+  _start(start),
+  _size(size)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SubfileInfo::Copy Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE SubfileInfo::
+SubfileInfo(const SubfileInfo &copy) :
+  _file(copy._file),
+  _start(copy._start),
+  _size(copy._size)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SubfileInfo::Copy Assignment Operator
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void SubfileInfo::
+operator = (const SubfileInfo &copy) {
+  _file = copy._file;
+  _start = copy._start;
+  _size = copy._size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SubfileInfo::is_empty
+//       Access: Published
+//  Description: Returns true if this SubfileInfo doesn't define any
+//               file, false if it has real data.
+////////////////////////////////////////////////////////////////////
+INLINE bool SubfileInfo::
+is_empty() const {
+  return _file == (FileReference *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SubfileInfo::get_file
+//       Access: Published
+//  Description: Returns the FileReference that represents this file.
+////////////////////////////////////////////////////////////////////
+INLINE const FileReference *SubfileInfo::
+get_file() const {
+  return _file;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SubfileInfo::get_filename
+//       Access: Published
+//  Description: A shortcut to the filename.
+////////////////////////////////////////////////////////////////////
+INLINE const Filename &SubfileInfo::
+get_filename() const {
+  if (_file != (FileReference *)NULL) {
+    return _file->get_filename();
+  }
+  static const Filename empty_filename;
+  return empty_filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SubfileInfo::get_start
+//       Access: Published
+//  Description: Returns the offset within the file at which this file
+//               data begins.
+////////////////////////////////////////////////////////////////////
+INLINE streampos SubfileInfo::
+get_start() const {
+  return _start;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SubfileInfo::get_size
+//       Access: Published
+//  Description: Returns the number of consecutive bytes, beginning at
+//               get_start(), that correspond to this file data.
+////////////////////////////////////////////////////////////////////
+INLINE streamsize SubfileInfo::
+get_size() const {
+  return _size;
+}
+
+INLINE ostream &
+operator << (ostream &out, const SubfileInfo &info) {
+  info.output(out);
+  return out;
+}

+ 5 - 6
panda/src/express/fileSystemInfo.cxx → panda/src/express/subfileInfo.cxx

@@ -1,4 +1,4 @@
-// Filename: fileSystemInfo.cxx
+// Filename: subfileInfo.cxx
 // Created by:  drose (20Jun11)
 //
 ////////////////////////////////////////////////////////////////////
@@ -12,15 +12,14 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#include "fileSystemInfo.h"
+#include "subfileInfo.h"
 
 ////////////////////////////////////////////////////////////////////
-//     Function: FileSystemInfo::output
+//     Function: SubfileInfo::output
 //       Access: Published
 //  Description: 
 ////////////////////////////////////////////////////////////////////
-void FileSystemInfo::
+void SubfileInfo::
 output(ostream &out) const {
-  out << "FileSystemInfo(" << _os_file_name << ", " << _file_start
-      << ", " << _file_size << ")";
+  out << "SubfileInfo(" << get_filename() << ", " << _start << ", " << _size << ")";
 }

+ 56 - 0
panda/src/express/subfileInfo.h

@@ -0,0 +1,56 @@
+// Filename: subfileInfo.h
+// Created by:  drose (20Jun11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef SUBFILEINFO_H
+#define SUBFILEINFO_H
+
+#include "pandabase.h"
+#include "fileReference.h"
+#include "pointerTo.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : SubfileInfo
+// Description : This class records a particular byte sub-range within
+//               an existing file on disk.  Generally, the filename is
+//               understood as a physical file on disk, and not to be
+//               looked up via the vfs.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS SubfileInfo {
+PUBLISHED:
+  INLINE SubfileInfo();
+  INLINE SubfileInfo(const FileReference *file, streampos start, streamsize size);
+  INLINE SubfileInfo(const Filename &filename, streampos start, streamsize size);
+  INLINE SubfileInfo(const SubfileInfo &copy);
+  INLINE void operator = (const SubfileInfo &copy);
+
+  INLINE bool is_empty() const;
+
+  INLINE const FileReference *get_file() const;
+  INLINE const Filename &get_filename() const;
+  INLINE streampos get_start() const;
+  INLINE streamsize get_size() const;
+
+  void output(ostream &out) const;
+
+private:
+  CPT(FileReference) _file;
+  streampos _start;
+  streamsize _size;
+};
+
+INLINE ostream &operator << (ostream &out, const SubfileInfo &info);
+
+#include "subfileInfo.I"
+
+#endif

+ 23 - 0
panda/src/express/temporaryFile.I

@@ -0,0 +1,23 @@
+// Filename: temporaryFile.I
+// Created by:  drose (23Jun11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TemporaryFile::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE TemporaryFile::
+TemporaryFile(const Filename &filename) : FileReference(filename) {
+}

+ 28 - 0
panda/src/express/temporaryFile.cxx

@@ -0,0 +1,28 @@
+// Filename: temporaryFile.cxx
+// Created by:  drose (23Jun11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "temporaryFile.h"
+
+TypeHandle TemporaryFile::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: TemporaryFile::Destructor
+//       Access: Published, Virtual
+//  Description: The destructor is responsible for removing the file
+//               if it exists.
+////////////////////////////////////////////////////////////////////
+TemporaryFile::
+~TemporaryFile() {
+  _filename.unlink();
+}

+ 54 - 0
panda/src/express/temporaryFile.h

@@ -0,0 +1,54 @@
+// Filename: temporaryFile.h
+// Created by:  drose (23Jun11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TEMPORARYFILE_H
+#define TEMPORARYFILE_H
+
+#include "pandabase.h"
+
+#include "fileReference.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : TemporaryFile
+// Description : This is a special kind of FileReference class that
+//               automatically deletes the file in question when it is
+//               deleted.  It is not responsible for creating,
+//               opening, or closing the file, however.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS TemporaryFile : public FileReference {
+PUBLISHED:
+  INLINE TemporaryFile(const Filename &filename);
+  virtual ~TemporaryFile();
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FileReference::init_type();
+    register_type(_type_handle, "TemporaryFile",
+                  FileReference::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "temporaryFile.I"
+
+#endif

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

@@ -216,7 +216,7 @@ get_timestamp() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFile::get_system_info
 //       Access: Published, Virtual
-//  Description: Populates the FileSystemInfo structure with the data
+//  Description: Populates the SubfileInfo structure with the data
 //               representing where the file actually resides on disk,
 //               if this is knowable.  Returns true if the file might
 //               reside on disk, and the info is populated, or false
@@ -224,7 +224,7 @@ get_timestamp() const {
 //               resides), in which case the info is meaningless.
 ////////////////////////////////////////////////////////////////////
 bool VirtualFile::
-get_system_info(FileSystemInfo &info) {
+get_system_info(SubfileInfo &info) {
   return false;
 }
 

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

@@ -18,7 +18,7 @@
 #include "pandabase.h"
 
 #include "filename.h"
-#include "fileSystemInfo.h"
+#include "subfileInfo.h"
 #include "pointerTo.h"
 #include "typedReferenceCount.h"
 #include "ordered_vector.h"
@@ -61,7 +61,7 @@ PUBLISHED:
   BLOCKING virtual off_t get_file_size() const;
   BLOCKING virtual time_t get_timestamp() const;
 
-  virtual bool get_system_info(FileSystemInfo &info);
+  virtual bool get_system_info(SubfileInfo &info);
 
 public:
   INLINE void set_original_filename(const Filename &filename);

+ 2 - 2
panda/src/express/virtualFileMount.cxx

@@ -143,7 +143,7 @@ close_read_file(istream *stream) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileMount::get_system_info
 //       Access: Public, Virtual
-//  Description: Populates the FileSystemInfo structure with the data
+//  Description: Populates the SubfileInfo structure with the data
 //               representing where the file actually resides on disk,
 //               if this is knowable.  Returns true if the file might
 //               reside on disk, and the info is populated, or false
@@ -151,7 +151,7 @@ close_read_file(istream *stream) const {
 //               resides), in which case the info is meaningless.
 ////////////////////////////////////////////////////////////////////
 bool VirtualFileMount::
-get_system_info(const Filename &file, FileSystemInfo &info) {
+get_system_info(const Filename &file, SubfileInfo &info) {
   return false;
 }
 

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

@@ -58,7 +58,7 @@ public:
   virtual off_t get_file_size(const Filename &file, istream *stream) const=0;
   virtual off_t get_file_size(const Filename &file) const=0;
   virtual time_t get_timestamp(const Filename &file) const=0;
-  virtual bool get_system_info(const Filename &file, FileSystemInfo &info);
+  virtual bool get_system_info(const Filename &file, SubfileInfo &info);
 
   virtual bool scan_directory(vector_string &contents, 
                               const Filename &dir) const=0;

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

@@ -174,7 +174,7 @@ get_timestamp(const Filename &file) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileMountMultifile::get_system_info
 //       Access: Public, Virtual
-//  Description: Populates the FileSystemInfo structure with the data
+//  Description: Populates the SubfileInfo structure with the data
 //               representing where the file actually resides on disk,
 //               if this is knowable.  Returns true if the file might
 //               reside on disk, and the info is populated, or false
@@ -182,7 +182,7 @@ get_timestamp(const Filename &file) const {
 //               resides), in which case the info is meaningless.
 ////////////////////////////////////////////////////////////////////
 bool VirtualFileMountMultifile::
-get_system_info(const Filename &file, FileSystemInfo &info) {
+get_system_info(const Filename &file, SubfileInfo &info) {
   Filename multifile_name = _multifile->get_multifile_name();
   if (multifile_name.empty()) {
     return false;
@@ -199,7 +199,7 @@ get_system_info(const Filename &file, FileSystemInfo &info) {
   streampos start = _multifile->get_subfile_internal_start(subfile_index);
   size_t length = _multifile->get_subfile_internal_length(subfile_index);
 
-  info = FileSystemInfo(multifile_name.to_os_specific(), start, length); 
+  info = SubfileInfo(multifile_name, start, length); 
   return true;
 }
 

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

@@ -45,7 +45,7 @@ public:
   virtual off_t get_file_size(const Filename &file, istream *stream) const;
   virtual off_t get_file_size(const Filename &file) const;
   virtual time_t get_timestamp(const Filename &file) const;
-  virtual bool get_system_info(const Filename &file, FileSystemInfo &info);
+  virtual bool get_system_info(const Filename &file, SubfileInfo &info);
 
   virtual bool scan_directory(vector_string &contents, 
                               const Filename &dir) const;

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

@@ -184,7 +184,7 @@ get_timestamp(const Filename &file) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileMountSystem::get_system_info
 //       Access: Public, Virtual
-//  Description: Populates the FileSystemInfo structure with the data
+//  Description: Populates the SubfileInfo structure with the data
 //               representing where the file actually resides on disk,
 //               if this is knowable.  Returns true if the file might
 //               reside on disk, and the info is populated, or false
@@ -192,9 +192,9 @@ get_timestamp(const Filename &file) const {
 //               resides), in which case the info is meaningless.
 ////////////////////////////////////////////////////////////////////
 bool VirtualFileMountSystem::
-get_system_info(const Filename &file, FileSystemInfo &info) {
+get_system_info(const Filename &file, SubfileInfo &info) {
   Filename pathname(_physical_filename, file);
-  info = FileSystemInfo(pathname.to_os_specific(), 0, pathname.get_file_size());
+  info = SubfileInfo(pathname, 0, pathname.get_file_size());
   return true;
 }
 

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

@@ -39,7 +39,7 @@ public:
   virtual off_t get_file_size(const Filename &file, istream *stream) const;
   virtual off_t get_file_size(const Filename &file) const;
   virtual time_t get_timestamp(const Filename &file) const;
-  virtual bool get_system_info(const Filename &file, FileSystemInfo &info);
+  virtual bool get_system_info(const Filename &file, SubfileInfo &info);
 
   virtual bool scan_directory(vector_string &contents, 
                               const Filename &dir) const;

+ 2 - 2
panda/src/express/virtualFileSimple.cxx

@@ -163,7 +163,7 @@ get_timestamp() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileSimple::get_system_info
 //       Access: Published, Virtual
-//  Description: Populates the FileSystemInfo structure with the data
+//  Description: Populates the SubfileInfo structure with the data
 //               representing where the file actually resides on disk,
 //               if this is knowable.  Returns true if the file might
 //               reside on disk, and the info is populated, or false
@@ -171,7 +171,7 @@ get_timestamp() const {
 //               resides), in which case the info is meaningless.
 ////////////////////////////////////////////////////////////////////
 bool VirtualFileSimple::
-get_system_info(FileSystemInfo &info) {
+get_system_info(SubfileInfo &info) {
   return _mount->get_system_info(_local_filename, info);
 }
 

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

@@ -46,7 +46,7 @@ PUBLISHED:
   virtual off_t get_file_size(istream *stream) const;
   virtual off_t get_file_size() const;
   virtual time_t get_timestamp() const;
-  virtual bool get_system_info(FileSystemInfo &info);
+  virtual bool get_system_info(SubfileInfo &info);
 
 public:
   virtual bool read_file(pvector<unsigned char> &result, bool auto_unwrap) const;

+ 427 - 257
panda/src/gobj/texture.cxx

@@ -680,8 +680,12 @@ get_aux_data(const string &key) const {
 //  Description: Reads the texture from a Panda texture object.  This
 //               defines the complete Texture specification, including
 //               the image data as well as all texture properties.
+//               This only works if the txo file contains a static
+//               Texture image, as opposed to a subclass of Texture
+//               such as a movie texture.
 //
-//               The filename is just for reference.
+//               Pass a real filename if it is available, or empty
+//               string if it is not.
 ////////////////////////////////////////////////////////////////////
 bool Texture::
 read_txo(istream &in, const string &filename) {
@@ -691,6 +695,79 @@ read_txo(istream &in, const string &filename) {
   return do_read_txo(in, filename);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::make_from_txo
+//       Access: Published, Static
+//  Description: Constructs a new Texture object from the txo file.
+//               This is similar to Texture::read_txo(), but it
+//               constructs and returns a new object, which allows it
+//               to return a subclass of Texture (for instance, a
+//               movie texture).
+//
+//               Pass a real filename if it is available, or empty
+//               string if it is not.
+////////////////////////////////////////////////////////////////////
+PT(Texture) Texture::
+make_from_txo(istream &in, const string &filename) {
+  DatagramInputFile din;
+
+  if (!din.open(in, filename)) {
+    gobj_cat.error()
+      << "Could not read texture object: " << filename << "\n";
+    return NULL;
+  }
+
+  string head;
+  if (!din.read_header(head, _bam_header.size())) {
+    gobj_cat.error()
+      << filename << " is not a texture object file.\n";
+    return NULL;
+  }
+
+  if (head != _bam_header) {
+    gobj_cat.error()
+      << filename << " is not a texture object file.\n";
+    return NULL;
+  }
+
+  BamReader reader(&din);
+  if (!reader.init()) {
+    return NULL;
+  }
+
+  TypedWritable *object = reader.read_object();
+
+  if (object != (TypedWritable *)NULL &&
+      object->is_exact_type(BamCacheRecord::get_class_type())) {
+    // Here's a special case: if the first object in the file is a
+    // BamCacheRecord, it's really a cache data file and not a true
+    // txo file; but skip over the cache data record and let the user
+    // treat it like an ordinary txo file.
+    object = reader.read_object();
+  }
+
+  if (object == (TypedWritable *)NULL) {
+    gobj_cat.error()
+      << "Texture object " << filename << " is empty.\n";
+    return NULL;
+
+  } else if (!object->is_of_type(Texture::get_class_type())) {
+    gobj_cat.error()
+      << "Texture object " << filename << " contains a "
+      << object->get_type() << ", not a Texture.\n";
+    return NULL;
+  }
+
+  PT(Texture) other = DCAST(Texture, object);
+  if (!reader.resolve()) {
+    gobj_cat.error()
+      << "Unable to fully resolve texture object file.\n";
+    return NULL;
+  }
+
+  return other;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::write_txo
 //       Access: Published
@@ -990,6 +1067,20 @@ get_keep_ram_image() const {
   return _keep_ram_image;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::is_cacheable
+//       Access: Published, Virtual
+//  Description: Returns true if there is enough information in this
+//               Texture object to write it to the bam cache
+//               successfully, false otherwise.  For most textures,
+//               this is the same as has_ram_image().
+////////////////////////////////////////////////////////////////////
+bool Texture::
+is_cacheable() const {
+  MutexHolder holder(_lock);
+  return do_has_bam_rawdata();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::get_num_loadable_ram_mipmap_images
 //       Access: Published
@@ -3110,59 +3201,8 @@ do_read_txo_file(const Filename &fullpath) {
 ////////////////////////////////////////////////////////////////////
 bool Texture::
 do_read_txo(istream &in, const string &filename) {
-  DatagramInputFile din;
-
-  if (!din.open(in)) {
-    gobj_cat.error()
-      << "Could not read texture object: " << filename << "\n";
-    return false;
-  }
-
-  string head;
-  if (!din.read_header(head, _bam_header.size())) {
-    gobj_cat.error()
-      << filename << " is not a texture object file.\n";
-    return false;
-  }
-
-  if (head != _bam_header) {
-    gobj_cat.error()
-      << filename << " is not a texture object file.\n";
-    return false;
-  }
-
-  BamReader reader(&din, filename);
-  if (!reader.init()) {
-    return false;
-  }
-
-  TypedWritable *object = reader.read_object();
-
-  if (object != (TypedWritable *)NULL &&
-      object->is_exact_type(BamCacheRecord::get_class_type())) {
-    // Here's a special case: if the first object in the file is a
-    // BamCacheRecord, it's really a cache data file and not a true
-    // txo file; but skip over the cache data record and let the user
-    // treat it like an ordinary txo file.
-    object = reader.read_object();
-  }
-
-  if (object == (TypedWritable *)NULL) {
-    gobj_cat.error()
-      << "Texture object " << filename << " is empty.\n";
-    return false;
-
-  } else if (!object->is_of_type(Texture::get_class_type())) {
-    gobj_cat.error()
-      << "Texture object " << filename << " contains a "
-      << object->get_type() << ", not a Texture.\n";
-    return false;
-  }
-
-  PT(Texture) other = DCAST(Texture, object);
-  if (!reader.resolve()) {
-    gobj_cat.error()
-      << "Unable to fully resolve texture object file.\n";
+  PT(Texture) other = make_from_txo(in, filename);
+  if (other == (Texture *)NULL) {
     return false;
   }
 
@@ -3506,10 +3546,10 @@ do_read_dds(istream &in, const string &filename, bool header_only) {
 bool Texture::
 do_write(const Filename &fullpath, int z, int n, bool write_pages, bool write_mipmaps) const {
   if (is_txo_filename(fullpath)) {
-    if (!do_has_ram_image()) {
-      ((Texture *)this)->do_get_ram_image();
+    if (!do_has_bam_rawdata()) {
+      ((Texture *)this)->do_get_bam_rawdata();
     }
-    nassertr(do_has_ram_image(), false);
+    nassertr(do_has_bam_rawdata(), false);
     return do_write_txo_file(fullpath);
   }
 
@@ -3671,7 +3711,7 @@ bool Texture::
 do_write_txo(ostream &out, const string &filename) const {
   DatagramOutputFile dout;
 
-  if (!dout.open(out)) {
+  if (!dout.open(out, filename)) {
     gobj_cat.error()
       << "Could not write texture object: " << filename << "\n";
     return false;
@@ -3683,7 +3723,7 @@ do_write_txo(ostream &out, const string &filename) const {
     return false;
   }
 
-  BamWriter writer(&dout, filename);
+  BamWriter writer(&dout);
   if (!writer.init()) {
     return false;
   }
@@ -3700,7 +3740,7 @@ do_write_txo(ostream &out, const string &filename) const {
   }
   _lock.acquire();
 
-  if (!do_has_ram_image()) {
+  if (!do_has_bam_rawdata()) {
     gobj_cat.error()
       << get_name() << " does not have ram image\n";
     return false;
@@ -5368,6 +5408,31 @@ do_reload() {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_has_bam_rawdata
+//       Access: Protected, Virtual
+//  Description: Returns true if there is a rawdata image that we have
+//               available to write to the bam stream.  For a normal
+//               Texture, this is the same thing as
+//               do_has_ram_image(), but a movie texture might define
+//               it differently.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+do_has_bam_rawdata() const {
+  return do_has_ram_image();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_get_bam_rawdata
+//       Access: Protected, Virtual
+//  Description: If do_has_bam_rawdata() returned false, this attempts
+//               to reload the rawdata image if possible.
+////////////////////////////////////////////////////////////////////
+void Texture::
+do_get_bam_rawdata() {
+  do_get_ram_image();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::convert_from_pnmimage
 //       Access: Private, Static
@@ -6727,6 +6792,213 @@ register_with_read_factory() {
   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::write_datagram
+//       Access: Public, Virtual
+//  Description: Function to write the important information in
+//               the particular object to a Datagram
+////////////////////////////////////////////////////////////////////
+void Texture::
+write_datagram(BamWriter *manager, Datagram &me) {
+  MutexHolder holder(_lock);
+
+  bool has_rawdata = false;
+  do_write_datagram_header(manager, me, has_rawdata);
+
+  do_write_datagram_body(manager, me);
+
+  // If we are also including the texture's image data, then stuff it
+  // in here.
+  if (has_rawdata) {
+    do_write_datagram_rawdata(manager, me);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::finalize
+//       Access: Public, Virtual
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
+////////////////////////////////////////////////////////////////////
+void Texture::
+finalize(BamReader *) {
+  // Unref the pointer that we explicitly reffed in make_from_bam().
+  unref();
+
+  // We should never get back to zero after unreffing our own count,
+  // because we expect to have been stored in a pointer somewhere.  If
+  // we do get to zero, it's a memory leak; the way to avoid this is
+  // to call unref_delete() above instead of unref(), but this is
+  // dangerous to do from within a virtual function.
+  nassertv(get_ref_count() != 0);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_write_datagram_header
+//       Access: Protected
+//  Description: Writes the header part of the texture to the
+//               Datagram.  This is the common part that is shared by
+//               all Texture subclasses, and contains the filename and
+//               rawdata flags.  This method is not virtual because
+//               all Texture subclasses must write the same data at
+//               this step.
+//
+//               This part must be read first before calling
+//               do_fillin_body() to determine whether to load the
+//               Texture from the TexturePool or directly from the bam
+//               stream.
+//
+//               After this call, has_rawdata will be filled with
+//               either true or false, according to whether we expect
+//               to write the texture rawdata to the bam stream
+//               following the texture body.
+////////////////////////////////////////////////////////////////////
+void Texture::
+do_write_datagram_header(BamWriter *manager, Datagram &me, bool &has_rawdata) {
+  // Write out the texture's raw pixel data if (a) the current Bam
+  // Texture Mode requires that, or (b) there's no filename, so the
+  // file can't be loaded up from disk, but the raw pixel data is
+  // currently available in RAM.
+
+  // Otherwise, we just write out the filename, and assume whoever
+  // loads the bam file later will have access to the image file on
+  // disk.
+  BamWriter::BamTextureMode file_texture_mode = manager->get_file_texture_mode();
+  has_rawdata = (file_texture_mode == BamWriter::BTM_rawdata || 
+		 (_filename.empty() && do_has_bam_rawdata()));
+  if (has_rawdata && !do_has_bam_rawdata()) {
+    do_get_bam_rawdata();
+    if (!do_has_bam_rawdata()) {
+      // No image data after all.
+      has_rawdata = false;
+    }
+  }
+
+  bool has_bam_dir = !manager->get_filename().empty();
+  Filename bam_dir = manager->get_filename().get_dirname();
+  Filename filename = _filename;
+  Filename alpha_filename = _alpha_filename;
+
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+
+  switch (file_texture_mode) {
+  case BamWriter::BTM_unchanged:
+  case BamWriter::BTM_rawdata:
+    break;
+
+  case BamWriter::BTM_fullpath:
+    filename = _fullpath;
+    alpha_filename = _alpha_fullpath;
+    break;
+
+  case BamWriter::BTM_relative:
+    filename = _fullpath;
+    alpha_filename = _alpha_fullpath;
+    bam_dir.make_absolute(vfs->get_cwd());
+    if (!has_bam_dir || !filename.make_relative_to(bam_dir, true)) {
+      filename.find_on_searchpath(get_model_path());
+    }
+    if (gobj_cat.is_debug()) {
+      gobj_cat.debug()
+        << "Texture file " << _fullpath
+        << " found as " << filename << "\n";
+    }
+    if (!has_bam_dir || !alpha_filename.make_relative_to(bam_dir, true)) {
+      alpha_filename.find_on_searchpath(get_model_path());
+    }
+    if (gobj_cat.is_debug()) {
+      gobj_cat.debug()
+        << "Alpha image " << _alpha_fullpath
+        << " found as " << alpha_filename << "\n";
+    }
+    break;
+
+  case BamWriter::BTM_basename:
+    filename = _fullpath.get_basename();
+    alpha_filename = _alpha_fullpath.get_basename();
+    break;
+
+  default:
+    gobj_cat.error()
+      << "Unsupported bam-texture-mode: " << (int)file_texture_mode << "\n";
+  }
+
+  if (filename.empty() && do_has_bam_rawdata()) {
+    // If we don't have a filename, we have to store rawdata anyway.
+    has_rawdata = true;
+  }
+
+  me.add_string(get_name());
+  me.add_string(filename);
+  me.add_string(alpha_filename);
+  me.add_uint8(_primary_file_num_channels);
+  me.add_uint8(_alpha_file_channel);
+  me.add_bool(has_rawdata);
+  me.add_uint8(_texture_type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_write_datagram_body
+//       Access: Protected, Virtual
+//  Description: Writes the body part of the texture to the
+//               Datagram.  This is generally all of the texture
+//               parameters except for the header and the rawdata.
+////////////////////////////////////////////////////////////////////
+void Texture::
+do_write_datagram_body(BamWriter *manager, Datagram &me) {
+  me.add_uint8(_wrap_u);
+  me.add_uint8(_wrap_v);
+  me.add_uint8(_wrap_w);
+  me.add_uint8(_minfilter);
+  me.add_uint8(_magfilter);
+  me.add_int16(_anisotropic_degree);
+  _border_color.write_datagram(me);
+  me.add_uint8(_compression);
+  me.add_uint8(_quality_level);
+
+  me.add_uint8(_format);
+  me.add_uint8(_num_components);
+
+  me.add_uint32(_orig_file_x_size);
+  me.add_uint32(_orig_file_y_size);
+
+  bool has_simple_ram_image = !_simple_ram_image._image.empty();
+  me.add_bool(has_simple_ram_image);
+
+  // Write out the simple image too, so it will be available later.
+  if (has_simple_ram_image) {
+    me.add_uint32(_simple_x_size);
+    me.add_uint32(_simple_y_size);
+    me.add_int32(_simple_image_date_generated);
+    me.add_uint32(_simple_ram_image._image.size());
+    me.append_data(_simple_ram_image._image, _simple_ram_image._image.size());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_write_datagram_rawdata
+//       Access: Protected, Virtual
+//  Description: Writes the rawdata part of the texture to the
+//               Datagram.
+////////////////////////////////////////////////////////////////////
+void Texture::
+do_write_datagram_rawdata(BamWriter *manager, Datagram &me) {
+  me.add_uint32(_x_size);
+  me.add_uint32(_y_size);
+  me.add_uint32(_z_size);
+  me.add_uint8(_component_type);
+  me.add_uint8(_component_width);
+  me.add_uint8(_ram_image_compression);
+  me.add_uint8(_ram_images.size());
+  for (size_t n = 0; n < _ram_images.size(); ++n) {
+    me.add_uint32(_ram_images[n]._page_size);
+    me.add_uint32(_ram_images[n]._image.size());
+    me.append_data(_ram_images[n]._image, _ram_images[n]._image.size());
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::make_from_bam
 //       Access: Protected, Static
@@ -6734,18 +7006,35 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 TypedWritable *Texture::
 make_from_bam(const FactoryParams &params) {
+  PT(Texture) dummy = new Texture;
+  return dummy->make_this_from_bam(params);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::make_this_from_bam
+//       Access: Protected, Virtual
+//  Description: Called by make_from_bam() once the particular
+//               subclass of Texture is known.  This is called on a
+//               newly-constructed Texture object of the appropriate
+//               subclass.  It will return either the same Texture
+//               object (e.g. this), or a different Texture object
+//               loaded via the TexturePool, as appropriate.
+////////////////////////////////////////////////////////////////////
+TypedWritable *Texture::
+make_this_from_bam(const FactoryParams &params) {
   // The process of making a texture is slightly different than making
   // other TypedWritable objects.  That is because all creation of
   // Textures should be done through calls to TexturePool, which
   // ensures that any loads of the same filename refer to the same
   // memory.
+
   DatagramIterator scan;
   BamReader *manager;
 
   parse_params(params, scan, manager);
 
-  // Get the filenames and texture type so we can look up the file on
-  // disk first.
+  // Get the header information--the filenames and texture type--so we
+  // can look up the file on disk first.
   string name = scan.get_string();
   Filename filename = scan.get_string();
   Filename alpha_filename = scan.get_string();
@@ -6757,9 +7046,12 @@ make_from_bam(const FactoryParams &params) {
 
   Texture *me = NULL;
   if (has_rawdata) {
-    // If the raw image data is included, then just create a Texture
-    // and don't load from the file.
-    me = new Texture(name);
+    // If the raw image data is included, then just load the texture
+    // directly from the stream, and return it.  In this case we
+    // return the "this" pointer, since it's a newly-created Texture
+    // object of the appropriate type.
+    me = this;
+    me->set_name(name);
     me->_filename = filename;
     me->_alpha_filename = alpha_filename;
     me->_primary_file_num_channels = primary_file_num_channels;
@@ -6767,13 +7059,21 @@ make_from_bam(const FactoryParams &params) {
     me->_texture_type = texture_type;
 
     // Read the texture attributes directly from the bam stream.
-    me->fillin(scan, manager, has_rawdata);
+    me->do_fillin_body(scan, manager);
+    me->do_fillin_rawdata(scan, manager);
+
+    // To manage the reference count, explicitly ref it now, then
+    // unref it in the finalize callback.
+    me->ref();
+    manager->register_finalize(me);
 
   } else {
-    // Now create a temporary Texture object to read all the
-    // attributes from the bam stream.
-    PT(Texture) dummy = new Texture("");
-    dummy->fillin(scan, manager, has_rawdata);
+    // The raw image data isn't included, so we'll be loading the
+    // Texture via the TexturePool.  In this case we use the "this"
+    // pointer as a temporary object to read all of the attributes
+    // from the bam stream.
+    Texture *dummy = this;
+    dummy->do_fillin_body(scan, manager);
 
     if (filename.empty()) {
       // This texture has no filename; since we don't have an image to
@@ -6828,8 +7128,15 @@ make_from_bam(const FactoryParams &params) {
     }
 
     if (me != (Texture *)NULL) {
-      me->fillin_from(dummy);
+      {
+	MutexHolder holder(me->_lock);
+	me->do_fillin_from(dummy);
+      }
       me->set_name(name);
+
+      // Since in this case me was loaded from the TexturePool,
+      // there's no need to explicitly manage the reference count.
+      // TexturePool will hold it safely.
     }
   }
 
@@ -6837,17 +7144,13 @@ make_from_bam(const FactoryParams &params) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Texture::fillin
-//       Access: Protected
-//  Description: Function that reads out of the datagram (or asks
-//               manager to read) all of the data that is needed to
-//               re-create this object and stores it in the appropiate
-//               place
+//     Function: Texture::do_fillin_body
+//       Access: Protected, Virtual
+//  Description: Reads in the part of the Texture that was written
+//               with do_write_datagram_body().
 ////////////////////////////////////////////////////////////////////
 void Texture::
-fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata) {
-  // We have already read in the filenames; don't read them again.
-
+do_fillin_body(DatagramIterator &scan, BamReader *manager) {
   _wrap_u = (WrapMode)scan.get_uint8();
   _wrap_v = (WrapMode)scan.get_uint8();
   _wrap_w = (WrapMode)scan.get_uint8();
@@ -6890,59 +7193,64 @@ fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata) {
     _simple_ram_image._page_size = u_size;
     ++_simple_image_modified;
   }
+}
 
-  if (has_rawdata) {
-    _x_size = scan.get_uint32();
-    _y_size = scan.get_uint32();
-    _z_size = scan.get_uint32();
-    _component_type = (ComponentType)scan.get_uint8();
-    _component_width = scan.get_uint8();
-    _ram_image_compression = CM_off;
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_fillin_rawdata
+//       Access: Protected, Virtual
+//  Description: Reads in the part of the Texture that was written
+//               with do_write_datagram_rawdata().
+////////////////////////////////////////////////////////////////////
+void Texture::
+do_fillin_rawdata(DatagramIterator &scan, BamReader *manager) {
+  _x_size = scan.get_uint32();
+  _y_size = scan.get_uint32();
+  _z_size = scan.get_uint32();
+  _component_type = (ComponentType)scan.get_uint8();
+  _component_width = scan.get_uint8();
+  _ram_image_compression = CM_off;
+  if (manager->get_file_minor_ver() >= 1) {
+    _ram_image_compression = (CompressionMode)scan.get_uint8();
+  }
+  
+  int num_ram_images = 1;
+  if (manager->get_file_minor_ver() >= 3) {
+    num_ram_images = scan.get_uint8();
+  }
+  
+  _ram_images.clear();
+  _ram_images.reserve(num_ram_images);
+  for (int n = 0; n < num_ram_images; ++n) {
+    _ram_images.push_back(RamImage());
+    _ram_images[n]._page_size = get_expected_ram_page_size();
     if (manager->get_file_minor_ver() >= 1) {
-      _ram_image_compression = (CompressionMode)scan.get_uint8();
-    }
-
-    int num_ram_images = 1;
-    if (manager->get_file_minor_ver() >= 3) {
-      num_ram_images = scan.get_uint8();
+      _ram_images[n]._page_size = scan.get_uint32();
     }
-
-    _ram_images.clear();
-    _ram_images.reserve(num_ram_images);
-    for (int n = 0; n < num_ram_images; ++n) {
-      _ram_images.push_back(RamImage());
-      _ram_images[n]._page_size = get_expected_ram_page_size();
-      if (manager->get_file_minor_ver() >= 1) {
-        _ram_images[n]._page_size = scan.get_uint32();
-      }
-
-      size_t u_size = scan.get_uint32();
-
-      // fill the _image buffer with image data
-      PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
-      for (size_t u_idx = 0; u_idx < u_size; ++u_idx) {
-        image[(int)u_idx] = scan.get_uint8();
-      }
-      _ram_images[n]._image = image;
+    
+    size_t u_size = scan.get_uint32();
+    
+    // fill the _image buffer with image data
+    PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
+    for (size_t u_idx = 0; u_idx < u_size; ++u_idx) {
+      image[(int)u_idx] = scan.get_uint8();
     }
-    _loaded_from_image = true;
-    do_set_pad_size(0, 0, 0);
-    ++_image_modified;
+    _ram_images[n]._image = image;
   }
+  _loaded_from_image = true;
+  do_set_pad_size(0, 0, 0);
+  ++_image_modified;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Texture::fillin_from
-//       Access: Protected
+//     Function: Texture::do_fillin_from
+//       Access: Protected, Virtual
 //  Description: Called in make_from_bam(), this method properly
 //               copies the attributes from the bam stream (as stored
 //               in dummy) into this texture, updating the modified
 //               flags appropriately.
 ////////////////////////////////////////////////////////////////////
 void Texture::
-fillin_from(Texture *dummy) {
-  MutexHolder holder(_lock);
-
+do_fillin_from(Texture *dummy) {
   // Use the setters instead of setting these directly, so we can
   // correctly avoid incrementing _properties_modified if none of
   // these actually change.  (Otherwise, we'd have to reload the
@@ -6995,144 +7303,6 @@ fillin_from(Texture *dummy) {
   }
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::write_datagram
-//       Access: Public
-//  Description: Function to write the important information in
-//               the particular object to a Datagram
-////////////////////////////////////////////////////////////////////
-void Texture::
-write_datagram(BamWriter *manager, Datagram &me) {
-  MutexHolder holder(_lock);
-
-  // Write out the texture's raw pixel data if (a) the current Bam
-  // Texture Mode requires that, or (b) there's no filename, so the
-  // file can't be loaded up from disk, but the raw pixel data is
-  // currently available in RAM.
-
-  // Otherwise, we just write out the filename, and assume whoever
-  // loads the bam file later will have access to the image file on
-  // disk.
-  BamWriter::BamTextureMode file_texture_mode = manager->get_file_texture_mode();
-  bool has_rawdata =
-    (file_texture_mode == BamWriter::BTM_rawdata || (do_has_ram_image() && _filename.empty()));
-  if (has_rawdata && !do_has_ram_image()) {
-    do_get_ram_image();
-    if (!do_has_ram_image()) {
-      // No image data after all.
-      has_rawdata = false;
-    }
-  }
-
-  bool has_bam_dir = !manager->get_filename().empty();
-  Filename bam_dir = manager->get_filename().get_dirname();
-  Filename filename = _filename;
-  Filename alpha_filename = _alpha_filename;
-
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-
-  switch (file_texture_mode) {
-  case BamWriter::BTM_unchanged:
-  case BamWriter::BTM_rawdata:
-    break;
-
-  case BamWriter::BTM_fullpath:
-    filename = _fullpath;
-    alpha_filename = _alpha_fullpath;
-    break;
-
-  case BamWriter::BTM_relative:
-    filename = _fullpath;
-    alpha_filename = _alpha_fullpath;
-    bam_dir.make_absolute(vfs->get_cwd());
-    if (!has_bam_dir || !filename.make_relative_to(bam_dir, true)) {
-      filename.find_on_searchpath(get_model_path());
-    }
-    if (gobj_cat.is_debug()) {
-      gobj_cat.debug()
-        << "Texture file " << _fullpath
-        << " found as " << filename << "\n";
-    }
-    if (!has_bam_dir || !alpha_filename.make_relative_to(bam_dir, true)) {
-      alpha_filename.find_on_searchpath(get_model_path());
-    }
-    if (gobj_cat.is_debug()) {
-      gobj_cat.debug()
-        << "Alpha image " << _alpha_fullpath
-        << " found as " << alpha_filename << "\n";
-    }
-    break;
-
-  case BamWriter::BTM_basename:
-    filename = _fullpath.get_basename();
-    alpha_filename = _alpha_fullpath.get_basename();
-    break;
-
-  default:
-    gobj_cat.error()
-      << "Unsupported bam-texture-mode: " << (int)file_texture_mode << "\n";
-  }
-
-  if (filename.empty() && do_has_ram_image()) {
-    // If we don't have a filename, we have to store rawdata anyway.
-    has_rawdata = true;
-  }
-
-  me.add_string(get_name());
-  me.add_string(filename);
-  me.add_string(alpha_filename);
-  me.add_uint8(_primary_file_num_channels);
-  me.add_uint8(_alpha_file_channel);
-  me.add_bool(has_rawdata);
-  me.add_uint8(_texture_type);
-
-  // The data beginning at this point is handled by fillin().
-  me.add_uint8(_wrap_u);
-  me.add_uint8(_wrap_v);
-  me.add_uint8(_wrap_w);
-  me.add_uint8(_minfilter);
-  me.add_uint8(_magfilter);
-  me.add_int16(_anisotropic_degree);
-  _border_color.write_datagram(me);
-  me.add_uint8(_compression);
-  me.add_uint8(_quality_level);
-
-  me.add_uint8(_format);
-  me.add_uint8(_num_components);
-
-  me.add_uint32(_orig_file_x_size);
-  me.add_uint32(_orig_file_y_size);
-
-  bool has_simple_ram_image = !_simple_ram_image._image.empty();
-  me.add_bool(has_simple_ram_image);
-
-  // Write out the simple image too, so it will be available later.
-  if (has_simple_ram_image) {
-    me.add_uint32(_simple_x_size);
-    me.add_uint32(_simple_y_size);
-    me.add_int32(_simple_image_date_generated);
-    me.add_uint32(_simple_ram_image._image.size());
-    me.append_data(_simple_ram_image._image, _simple_ram_image._image.size());
-  }
-
-  // If we are also including the texture's image data, then stuff it
-  // in here.
-  if (has_rawdata) {
-    me.add_uint32(_x_size);
-    me.add_uint32(_y_size);
-    me.add_uint32(_z_size);
-    me.add_uint8(_component_type);
-    me.add_uint8(_component_width);
-    me.add_uint8(_ram_image_compression);
-    me.add_uint8(_ram_images.size());
-    for (size_t n = 0; n < _ram_images.size(); ++n) {
-      me.add_uint32(_ram_images[n]._page_size);
-      me.add_uint32(_ram_images[n]._image.size());
-      me.append_data(_ram_images[n]._image, _ram_images[n]._image.size());
-    }
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::TextureType output operator
 //  Description:

+ 19 - 5
panda/src/gobj/texture.h

@@ -63,6 +63,8 @@ struct DDSHeader;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_GOBJ Texture : public TypedWritableReferenceCount, public Namable {
 PUBLISHED:
+  typedef PT(Texture) MakeTextureFunc();
+
   enum TextureType {
     TT_1d_texture,
     TT_2d_texture,
@@ -244,9 +246,10 @@ PUBLISHED:
   INLINE bool write(const Filename &fullpath, int z, int n, 
                     bool write_pages, bool write_mipmaps);
 
-  bool read_txo(istream &in, const string &filename = "stream");
-  bool write_txo(ostream &out, const string &filename = "stream") const;
-  bool read_dds(istream &in, const string &filename = "stream", bool header_only = false);
+  bool read_txo(istream &in, const string &filename = "");
+  static PT(Texture) make_from_txo(istream &in, const string &filename = "");
+  bool write_txo(ostream &out, const string &filename = "") const;
+  bool read_dds(istream &in, const string &filename = "", bool header_only = false);
 
   INLINE bool load(const PNMImage &pnmimage, const LoaderOptions &options = LoaderOptions());
   INLINE bool load(const PNMImage &pnmimage, int z, int n, const LoaderOptions &options = LoaderOptions());
@@ -328,6 +331,7 @@ PUBLISHED:
   INLINE void clear_ram_image();
   INLINE void set_keep_ram_image(bool keep_ram_image);
   virtual bool get_keep_ram_image() const;
+  virtual bool is_cacheable() const;
 
   INLINE bool compress_ram_image(CompressionMode compression = CM_on,
                                  QualityLevel quality_level = QL_default,
@@ -589,6 +593,9 @@ protected:
   virtual bool do_can_reload();
   bool do_reload();
 
+  virtual bool do_has_bam_rawdata() const;
+  virtual void do_get_bam_rawdata();
+
   // This nested class declaration is used below.
   class RamImage {
   public:
@@ -790,10 +797,17 @@ public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &me);
 
+  virtual void finalize(BamReader *manager);
+
 protected:
+  void do_write_datagram_header(BamWriter *manager, Datagram &me, bool &has_rawdata);
+  virtual void do_write_datagram_body(BamWriter *manager, Datagram &me);
+  virtual void do_write_datagram_rawdata(BamWriter *manager, Datagram &me);
   static TypedWritable *make_from_bam(const FactoryParams &params);
-  void fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata);
-  void fillin_from(Texture *dummy);
+  virtual TypedWritable *make_this_from_bam(const FactoryParams &params);
+  virtual void do_fillin_body(DatagramIterator &scan, BamReader *manager);
+  virtual void do_fillin_rawdata(DatagramIterator &scan, BamReader *manager);
+  virtual void do_fillin_from(Texture *dummy);
 
 public:
   static TypeHandle get_class_type() {

+ 47 - 11
panda/src/gobj/texturePool.cxx

@@ -264,12 +264,48 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
     // cache; it needs to be loaded from its source image(s).
     gobj_cat.info()
       << "Loading texture " << filename << "\n";
-    tex = make_texture(filename.get_extension());
-    if (!tex->read(filename, Filename(), primary_file_num_channels, 0,
-                   0, 0, false, read_mipmaps, record, options)) {
-      // This texture was not found or could not be read.
-      report_texture_unreadable(filename);
-      return NULL;
+
+    string ext = downcase(filename.get_extension());
+    if (ext == "txo" || ext == "bam") {
+      // Assume this is a txo file, which might conceivably contain a
+      // movie file or some other subclass of Texture.  In that case,
+      // use make_from_txo() to load it instead of read().
+      VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+
+      filename.set_binary();
+      PT(VirtualFile) file = vfs->get_file(filename);
+      if (file == (VirtualFile *)NULL) {
+	// No such file.
+	gobj_cat.error()
+	  << "Could not find " << filename << "\n";
+	return false;
+      }
+
+      if (gobj_cat.is_debug()) {
+	gobj_cat.debug()
+	  << "Reading texture object " << filename << "\n";
+      }
+
+      istream *in = file->open_read_file(true);
+      tex = Texture::make_from_txo(*in, filename);
+      vfs->close_read_file(in);
+
+      if (tex == (Texture *)NULL) {
+	return false;
+      }
+      tex->set_fullpath(filename);
+      tex->clear_alpha_fullpath();
+      tex->set_keep_ram_image(false);
+	
+    } else {
+      // Read it the conventional way.
+      tex = make_texture(ext);
+      if (!tex->read(filename, Filename(), primary_file_num_channels, 0,
+		     0, 0, false, read_mipmaps, record, options)) {
+	// This texture was not found or could not be read.
+	report_texture_unreadable(filename);
+	return NULL;
+      }
     }
 
     if (options.get_texture_flags() & LoaderOptions::TF_preload_simple) {
@@ -322,7 +358,7 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
     _textures[filename] = tex;
   }
 
-  if (store_record && tex->has_ram_image()) {
+  if (store_record && tex->is_cacheable()) {
     // Store the on-disk cache record for next time.
     record->set_data(tex, tex);
     cache->store(record);
@@ -454,7 +490,7 @@ ns_load_texture(const Filename &orig_filename,
     _textures[filename] = tex;
   }
 
-  if (store_record && tex->has_ram_image()) {
+  if (store_record && tex->is_cacheable()) {
     // Store the on-disk cache record for next time.
     record->set_data(tex, tex);
     cache->store(record);
@@ -566,7 +602,7 @@ ns_load_3d_texture(const Filename &filename_pattern,
     _textures[filename] = tex;
   }
 
-  if (store_record && tex->has_ram_image()) {
+  if (store_record && tex->is_cacheable()) {
     // Store the on-disk cache record for next time.
     record->set_data(tex, tex);
     cache->store(record);
@@ -672,7 +708,7 @@ ns_load_2d_texture_array(const Filename &filename_pattern,
     _textures[unique_filename] = tex;
   }
 
-  if (store_record && tex->has_ram_image()) {
+  if (store_record && tex->is_cacheable()) {
     // Store the on-disk cache record for next time.
     record->set_data(tex, tex);
     cache->store(record);
@@ -771,7 +807,7 @@ ns_load_cube_map(const Filename &filename_pattern, bool read_mipmaps,
     _textures[filename] = tex;
   }
 
-  if (store_record && tex->has_ram_image()) {
+  if (store_record && tex->is_cacheable()) {
     // Store the on-disk cache record for next time.
     record->set_data(tex, tex);
     cache->store(record);

+ 1 - 1
panda/src/gobj/texturePool.h

@@ -85,7 +85,7 @@ PUBLISHED:
   static void write(ostream &out);
 
 public:
-  typedef PT(Texture) MakeTextureFunc();
+  typedef Texture::MakeTextureFunc MakeTextureFunc;
   void register_texture_type(MakeTextureFunc *func, const string &extensions);
   void register_filter(TexturePoolFilter *filter);
   

+ 13 - 1
panda/src/grutil/ffmpegTexture.I

@@ -51,7 +51,8 @@ VideoPage() {
 ////////////////////////////////////////////////////////////////////
 INLINE FFMpegTexture::VideoPage::
 VideoPage(const FFMpegTexture::VideoPage &copy) :
-  _color(copy._color)
+  _color(copy._color),
+  _alpha(copy._alpha)
 {
 }
 
@@ -63,3 +64,14 @@ VideoPage(const FFMpegTexture::VideoPage &copy) :
 INLINE FFMpegTexture::VideoPage::
 ~VideoPage() {
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::VideoPage::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void FFMpegTexture::VideoPage::
+operator = (const FFMpegTexture::VideoPage &copy) {
+  _color = copy._color;
+  _alpha = copy._alpha;
+}

+ 269 - 58
panda/src/grutil/ffmpegTexture.cxx

@@ -21,6 +21,7 @@
 #include "config_grutil.h"
 #include "bamCacheRecord.h"
 #include "bamReader.h"
+#include "bamWriter.h"
 
 TypeHandle FFMpegTexture::_type_handle;
 
@@ -96,7 +97,7 @@ do_assign(const FFMpegTexture &copy) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: FFMPegTexture::modify_page
+//     Function: FFMpegTexture::modify_page
 //       Access: Private
 //  Description: Returns a reference to the zth VideoPage (level) of
 //               the texture.  In the case of a 2-d texture, there is
@@ -142,8 +143,8 @@ do_reconsider_video_properties(const FFMpegTexture::VideoStream &stream,
     num_frames = (int)((stream._format_context->duration*frame_rate)/AV_TIME_BASE);
     if (grutil_cat.is_debug()) {
       grutil_cat.debug()
-        << "Loaded " << stream._filename << ", " << num_frames << " frames at "
-        << frame_rate << " fps\n";
+        << "Loaded " << stream._filename << ", "
+	<< num_frames << " frames at " << frame_rate << " fps\n";
     }
   }
   
@@ -202,7 +203,7 @@ make_texture() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: FFMPegTexture::update_frame
+//     Function: FFMpegTexture::update_frame
 //       Access: Protected, Virtual
 //  Description: Called once per frame, as needed, to load the new
 //               image contents.
@@ -419,6 +420,30 @@ do_load_one(const PNMImage &pnmimage, const string &name, int z, int n,
   return Texture::do_load_one(pnmimage, name, z, n, options);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::do_has_bam_rawdata
+//       Access: Protected, Virtual
+//  Description: Returns true if there is a rawdata image that we have
+//               available to write to the bam stream.  For a normal
+//               Texture, this is the same thing as
+//               do_has_ram_image(), but a movie texture might define
+//               it differently.
+////////////////////////////////////////////////////////////////////
+bool FFMpegTexture::
+do_has_bam_rawdata() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::do_get_bam_rawdata
+//       Access: Protected, Virtual
+//  Description: If do_has_bam_rawdata() returned false, this attempts
+//               to reload the rawdata image if possible.
+////////////////////////////////////////////////////////////////////
+void FFMpegTexture::
+do_get_bam_rawdata() {
+}
+
 
 
 
@@ -429,20 +454,77 @@ do_load_one(const PNMImage &pnmimage, const string &name, int z, int n,
 ////////////////////////////////////////////////////////////////////
 void FFMpegTexture::
 register_with_read_factory() {
-  // Since Texture is such a funny object that is reloaded from the
-  // TexturePool each time, instead of actually being read fully from
-  // the bam file, and since the VideoTexture and FFMpegTexture
-  // classes don't really add any useful data to the bam record, we
-  // don't need to define make_from_bam(), fillin(), or
-  // write_datagram() in this class--we just inherit the same
-  // functions from Texture.
-
-  // We do, however, have to register this class with the BamReader,
-  // to avoid warnings about creating the wrong kind of object from
-  // the bam file.
   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::make_from_bam
+//       Access: Protected, Static
+//  Description: Factory method to generate an FFMpegTexture object
+////////////////////////////////////////////////////////////////////
+TypedWritable *FFMpegTexture::
+make_from_bam(const FactoryParams &params) {
+  PT(FFMpegTexture) dummy = new FFMpegTexture;
+  return dummy->make_this_from_bam(params);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::do_write_datagram_rawdata
+//       Access: Protected, Virtual
+//  Description: Writes the rawdata part of the texture to the
+//               Datagram.
+////////////////////////////////////////////////////////////////////
+void FFMpegTexture::
+do_write_datagram_rawdata(BamWriter *manager, Datagram &me) {
+  me.add_uint32(_x_size);
+  me.add_uint32(_y_size);
+  me.add_uint32(_z_size);
+  me.add_uint8(_component_type);
+  me.add_uint8(_component_width);
+  me.add_uint8(_ram_image_compression);
+
+  me.add_uint16(_pages.size());
+  for (size_t n = 0; n < _pages.size(); ++n) {
+    VideoPage &page = _pages[n];
+    page.write_datagram_rawdata(manager, me);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::do_fillin_rawdata
+//       Access: Protected, Virtual
+//  Description: Reads in the part of the Texture that was written
+//               with do_write_datagram_rawdata().
+////////////////////////////////////////////////////////////////////
+void FFMpegTexture::
+do_fillin_rawdata(DatagramIterator &scan, BamReader *manager) {
+  _x_size = scan.get_uint32();
+  _y_size = scan.get_uint32();
+  _z_size = scan.get_uint32();
+  _component_type = (ComponentType)scan.get_uint8();
+  _component_width = scan.get_uint8();
+  _ram_image_compression = CM_off;
+  _ram_image_compression = (CompressionMode)scan.get_uint8();
+  
+  int num_pages = scan.get_uint16();
+  _pages.clear();
+  for (int n = 0; n < num_pages; ++n) {
+    _pages.push_back(VideoPage());
+    VideoPage &page = _pages.back();
+    page.fillin_rawdata(scan, manager);
+
+    LoaderOptions options;
+    do_reconsider_video_properties(page._color, _num_components, n, options);
+    if (page._alpha.is_valid()) {
+      do_reconsider_video_properties(page._alpha, _num_components, n, options);
+    }
+  }
+
+  _loaded_from_image = true;
+  do_set_pad_size(0, 0, 0);
+  ++_image_modified;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: FFMpegTexture::VideoStream::Constructor
 //       Access: Public
@@ -474,15 +556,17 @@ VideoStream(const FFMpegTexture::VideoStream &copy) :
 {
   // Rather than copying the _capture pointer, we must open a new
   // stream that references the same file.
-  if (copy.is_valid()) {
-    if (copy.is_from_file()) {
+  if (copy.is_valid() && copy.is_from_file()) {
+    if (copy._file_info.is_empty()) {
       read(copy._filename);
+    } else {
+      read(copy._file_info);
     }
   }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: FFMpegTexture::VideoStream::Copy Constructor
+//     Function: FFMpegTexture::VideoStream::Destructor
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
@@ -491,6 +575,26 @@ FFMpegTexture::VideoStream::
   clear();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::VideoStream::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FFMpegTexture::VideoStream::
+operator = (const FFMpegTexture::VideoStream &copy) {
+  clear();
+
+  // Rather than copying the _capture pointer, we must open a new
+  // stream that references the same file.
+  if (copy.is_valid() && copy.is_from_file()) {
+    if (copy._file_info.is_empty()) {
+      read(copy._filename);
+    } else {
+      read(copy._file_info);
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: FFMpegTexture::VideoStream::get_frame_data
 //       Access: Public
@@ -546,7 +650,7 @@ get_frame_data(int frame_number) {
 
     double time_stamp = ((double)AV_TIME_BASE * frame_number * vstream->r_frame_rate.den) / vstream->r_frame_rate.num;
     double curr_time_stamp;
-    
+
     // find point in time
     av_seek_frame(_format_context, -1, (long long)time_stamp,
                   AVSEEK_FLAG_BACKWARD);
@@ -638,32 +742,147 @@ get_frame_data(int frame_number) {
 ////////////////////////////////////////////////////////////////////
 //     Function: FFMpegTexture::VideoStream::read
 //       Access: Public
-//  Description: Sets up the stream to read the indicated file.
-//               Returns true on success, false on failure.
+//  Description: Sets up the stream to read the indicated file from
+//               the VFS.  Returns true on success, false on failure.
 ////////////////////////////////////////////////////////////////////
 bool FFMpegTexture::VideoStream::
 read(const Filename &filename) {
   // Clear out the last stream
   clear();
   
-  string os_specific = filename.to_os_specific();
   // Open video file
-  int result = av_open_input_file(&_format_context, os_specific.c_str(), NULL, 
-                                  0, NULL);
-  if (result != 0) {
-    grutil_cat.error() << "ffmpeg AVERROR: " << result << endl;
+  if (!_ffvfile.open_vfs(filename)) {
+    grutil_cat.error()
+      << "couldn't open " << filename << "\n";
+    // Don't call clear(), because nothing happened yet
+    return false;
+  }
+
+  _filename = filename;
+  return continue_read();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::VideoStream::read
+//       Access: Public
+//  Description: Sets up the stream to read the indicated file,
+//               avoiding the VFS.  Returns true on success, false on
+//               failure.
+////////////////////////////////////////////////////////////////////
+bool FFMpegTexture::VideoStream::
+read(const SubfileInfo &info) {
+  // Clear out the last stream
+  clear();
+  
+  // Open video file
+  if (!_ffvfile.open_subfile(info)) {
+    grutil_cat.error()
+      << "couldn't open " << info << "\n";
     // Don't call clear(), because nothing happened yet
     return false;
   }
 
+  _file_info = info;
+  _filename = _file_info.get_filename();
+  return continue_read();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::VideoStream::clear
+//       Access: Public
+//  Description: Stops the video playback and frees the associated
+//               resources.
+////////////////////////////////////////////////////////////////////
+void FFMpegTexture::VideoStream::
+clear() {
+  if (_codec_context) {
+    avcodec_close(_codec_context);
+    _codec_context = NULL;
+  }
+
+  if (_frame) {
+    av_free(_frame);
+    _frame = NULL;
+  }
+  if (_frame_out) {
+    av_free(_frame_out);
+    _frame_out = NULL;
+  }
+
+  // We cannot close the format_context, since we didn't open it--the
+  // FfmpegVirtualFile will do that.
+  _format_context = NULL;
+
+  _next_frame_number = 0;
+  _filename = Filename();
+  _file_info = SubfileInfo();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::VideoStream::write_datagram_rawdata
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FFMpegTexture::VideoStream::
+write_datagram_rawdata(BamWriter *manager, Datagram &me) {
+  SubfileInfo result;
+  if (!_file_info.is_empty()) {
+    me.add_bool(true);
+    manager->write_file_data(result, _file_info);
+  } else if (!_filename.empty()) {
+    me.add_bool(true);
+    manager->write_file_data(result, _filename);
+  } else {
+    me.add_bool(false);
+  }
+
+  /* Not sure yet if this is a good idea.
+  if (!result.is_empty()) {
+    // If we've just copied the data to a local file, read it from
+    // there in the future.
+    _file_info = result;
+  }
+  */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::VideoStream::fillin_rawdata
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FFMpegTexture::VideoStream::
+fillin_rawdata(DatagramIterator &scan, BamReader *manager) {
+  bool got_info = scan.get_bool();
+  if (got_info) {
+    SubfileInfo info;
+    manager->read_file_data(info);
+    read(info);
+  } else {
+    clear();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::VideoStream::continue_read
+//       Access: Private
+//  Description: Once the FfmpegVirtualFile has been opened, continues
+//               to make the appropriate ffmpeg calls to open the
+//               stream.
+////////////////////////////////////////////////////////////////////
+bool FFMpegTexture::VideoStream::
+continue_read() {
+  _format_context = _ffvfile.get_format_context();
+  nassertr(_format_context != NULL, false);
+
   // Retrieve stream information
-  result = av_find_stream_info(_format_context);
+  int result = av_find_stream_info(_format_context);
   if (result < 0) {
     grutil_cat.error() << "ffmpeg AVERROR: " << result << endl;
     clear();
     return false;
   }
-  dump_format(_format_context, 0, os_specific.c_str(), false);
+  dump_format(_format_context, 0, _filename.c_str(), false);
   
   _stream_number = -1;
   for(size_t i = 0; i < _format_context->nb_streams; i++) {
@@ -752,41 +971,11 @@ read(const Filename &filename) {
   // We could put an option here for single channel frames.
   
   _next_frame_number = 0;
-  _filename = filename;
 
   return true;
 }
 
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: FFMpegTexture::VideoStream::clear
-//       Access: Public
-//  Description: Stops the video playback and frees the associated
-//               resources.
-////////////////////////////////////////////////////////////////////
-void FFMpegTexture::VideoStream::
-clear() {
-  if (_codec_context) {
-    avcodec_close(_codec_context);
-    _codec_context = NULL;
-  }
-  if (_format_context) {
-    av_close_input_file(_format_context);
-    _format_context = NULL;
-  }
-  if (_frame) {
-    av_free(_frame);
-    _frame = NULL;
-  }
-  if (_frame_out) {
-    av_free(_frame_out);
-    _frame_out = NULL;
-  }
-
-  _next_frame_number = 0;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: FFMpegTexture::VideoStream::read_video_frame
 //       Access: Private
@@ -830,6 +1019,28 @@ read_video_frame(AVPacket *packet) {
   return err;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::VideoPage::write_datagram_rawdata
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FFMpegTexture::VideoPage::
+write_datagram_rawdata(BamWriter *manager, Datagram &me) {
+  _color.write_datagram_rawdata(manager, me);
+  _alpha.write_datagram_rawdata(manager, me);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FFMpegTexture::VideoPage::fillin_rawdata
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FFMpegTexture::VideoPage::
+fillin_rawdata(DatagramIterator &scan, BamReader *manager) {
+  _color.fillin_rawdata(scan, manager);
+  _alpha.fillin_rawdata(scan, manager);
+}
+
 
 #endif  // HAVE_FFMpeg
 

+ 21 - 0
panda/src/grutil/ffmpegTexture.h

@@ -19,6 +19,7 @@
 #ifdef HAVE_FFMPEG
 
 #include "videoTexture.h"
+#include "ffmpegVirtualFile.h"
 
 extern "C" {
   #include "libavcodec/avcodec.h"
@@ -55,6 +56,9 @@ protected:
   virtual bool do_load_one(const PNMImage &pnmimage, const string &name,
                            int z, int n, const LoaderOptions &options);
 
+  virtual bool do_has_bam_rawdata() const;
+  virtual void do_get_bam_rawdata();
+
 private:    
   class VideoPage;
   class VideoStream;
@@ -70,25 +74,34 @@ private:
     VideoStream();
     VideoStream(const VideoStream &copy);
     ~VideoStream();
+    void operator = (const VideoStream &copy);
 
     bool read(const Filename &filename);
+    bool read(const SubfileInfo &info);
     void clear();
     INLINE bool is_valid() const;
     INLINE bool is_from_file() const;
     bool get_frame_data(int frame);
 
+    void write_datagram_rawdata(BamWriter *manager, Datagram &me);
+    void fillin_rawdata(DatagramIterator &scan, BamReader *manager);
+
   private:
+    bool continue_read();
     int read_video_frame(AVPacket *packet);
 
   public:
     AVCodecContext *_codec_context; 
     AVFormatContext *_format_context; 
+    FfmpegVirtualFile _ffvfile;
     
     int _stream_number;
     AVFrame *_frame;
     AVFrame *_frame_out;
 
     Filename _filename;
+    SubfileInfo _file_info;
+
     int _next_frame_number;
     int _image_size_bytes;
 
@@ -102,6 +115,10 @@ private:
     INLINE VideoPage();
     INLINE VideoPage(const VideoPage &copy);
     INLINE ~VideoPage();
+    INLINE void operator = (const VideoPage &copy);
+
+    void write_datagram_rawdata(BamWriter *manager, Datagram &me);
+    void fillin_rawdata(DatagramIterator &scan, BamReader *manager);
 
     VideoStream _color, _alpha;
   };
@@ -112,6 +129,10 @@ private:
 public:
   static void register_with_read_factory();
 
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  virtual void do_write_datagram_rawdata(BamWriter *manager, Datagram &me);
+  virtual void do_fillin_rawdata(DatagramIterator &scan, BamReader *manager);
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;

+ 154 - 49
panda/src/grutil/movieTexture.cxx

@@ -47,7 +47,7 @@ MovieTexture(const string &name) :
 //  Description: Creates a texture playing the specified movie.
 ////////////////////////////////////////////////////////////////////
 MovieTexture::
-MovieTexture(PT(MovieVideo) video) : 
+MovieTexture(MovieVideo *video) : 
   Texture(video->get_name())
 {
   do_load_one(video->open(), NULL, 0, LoaderOptions());
@@ -134,18 +134,6 @@ make_texture() {
   return new MovieTexture("");
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: MovieTexture::VideoPage::Constructor
-//       Access: Private
-//  Description: Creates a completely blank video page.
-////////////////////////////////////////////////////////////////////
-MovieTexture::VideoPage::
-VideoPage() :
-  _color(0),
-  _alpha(0)
-{
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: MovieTexture::do_recalculate_image_properties
 //       Access: Protected
@@ -161,8 +149,9 @@ do_recalculate_image_properties(CDWriter &cdata, const LoaderOptions &options) {
   int y_max = 1;
   bool alpha = false;
   double len = 0.0;
-  
-  for (int i=0; i<_z_size; i++) {
+
+  nassertv(cdata->_pages.size() == (size_t)_z_size);
+  for (int i = 0; i < _z_size; ++i) {
     MovieVideoCursor *t = cdata->_pages[i]._color;
     if (t) {
       if (t->size_x() > x_max) x_max = t->size_x();
@@ -299,27 +288,6 @@ do_load_one(const PNMImage &pnmimage, const string &name, int z, int n,
   return false;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: MovieTexture::register_with_read_factory
-//       Access: Public, Static
-//  Description: Factory method to generate a Texture object
-////////////////////////////////////////////////////////////////////
-void MovieTexture::
-register_with_read_factory() {
-  // Since Texture is such a funny object that is reloaded from the
-  // TexturePool each time, instead of actually being read fully from
-  // the bam file, and since the VideoTexture and MovieTexture
-  // classes don't really add any useful data to the bam record, we
-  // don't need to define make_from_bam(), fillin(), or
-  // write_datagram() in this class--we just inherit the same
-  // functions from Texture.
-
-  // We do, however, have to register this class with the BamReader,
-  // to avoid warnings about creating the wrong kind of object from
-  // the bam file.
-  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: MovieTexture::has_cull_callback
 //       Access: Public, Virtual
@@ -390,19 +358,6 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
   return true;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: MovieTexture::get_keep_ram_image
-//       Access: Published, Virtual
-//  Description: A MovieTexture must always keep its ram image, 
-//               since there is no way to reload it from the 
-//               source MovieVideo.
-////////////////////////////////////////////////////////////////////
-bool MovieTexture::
-get_keep_ram_image() const {
-  // A MovieTexture should never dump its RAM image.
-  return true;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: MovieTexture::do_make_copy
 //       Access: Protected, Virtual
@@ -476,6 +431,43 @@ do_reload_ram_image() {
   // Therefore, this is not needed.
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTexture::get_keep_ram_image
+//       Access: Published, Virtual
+//  Description: A MovieTexture must always keep its ram image, 
+//               since there is no way to reload it from the 
+//               source MovieVideo.
+////////////////////////////////////////////////////////////////////
+bool MovieTexture::
+get_keep_ram_image() const {
+  // A MovieTexture should never dump its RAM image.
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTexture::do_has_bam_rawdata
+//       Access: Protected, Virtual
+//  Description: Returns true if there is a rawdata image that we have
+//               available to write to the bam stream.  For a normal
+//               Texture, this is the same thing as
+//               do_has_ram_image(), but a movie texture might define
+//               it differently.
+////////////////////////////////////////////////////////////////////
+bool MovieTexture::
+do_has_bam_rawdata() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTexture::do_get_bam_rawdata
+//       Access: Protected, Virtual
+//  Description: If do_has_bam_rawdata() returned false, this attempts
+//               to reload the rawdata image if possible.
+////////////////////////////////////////////////////////////////////
+void MovieTexture::
+do_get_bam_rawdata() {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: MovieTexture::restart
 //       Access: Published
@@ -677,4 +669,117 @@ unsynchronize() {
   cdata->_synchronize = 0;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTexture::register_with_read_factory
+//       Access: Public, Static
+//  Description: Factory method to generate a Texture object
+////////////////////////////////////////////////////////////////////
+void MovieTexture::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTexture::make_from_bam
+//       Access: Protected, Static
+//  Description: Factory method to generate a MovieTexture object
+////////////////////////////////////////////////////////////////////
+TypedWritable *MovieTexture::
+make_from_bam(const FactoryParams &params) {
+  PT(MovieTexture) dummy = new MovieTexture("");
+  return dummy->make_this_from_bam(params);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTexture::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int MovieTexture::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = Texture::complete_pointers(p_list, manager);
+
+  CDWriter cdata(_cycler);
+  size_t num_pages = cdata->_pages.size();
+  for (size_t n = 0; n < num_pages; ++n) {
+    VideoPage &page = cdata->_pages[n];
+    page._color = DCAST(MovieVideoCursor, p_list[pi++]);
+    page._alpha = DCAST(MovieVideoCursor, p_list[pi++]);
+  }
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTexture::do_write_datagram_rawdata
+//       Access: Protected, Virtual
+//  Description: Writes the rawdata part of the texture to the
+//               Datagram.
+////////////////////////////////////////////////////////////////////
+void MovieTexture::
+do_write_datagram_rawdata(BamWriter *manager, Datagram &dg) {
+  CDReader cdata(_cycler);
+
+  dg.add_uint16(cdata->_pages.size());
+  for (size_t n = 0; n < cdata->_pages.size(); ++n) {
+    const VideoPage &page = cdata->_pages[n];
+    manager->write_pointer(dg, page._color);
+    manager->write_pointer(dg, page._alpha);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTexture::do_fillin_rawdata
+//       Access: Protected, Virtual
+//  Description: Reads in the part of the Texture that was written
+//               with do_write_datagram_rawdata().
+////////////////////////////////////////////////////////////////////
+void MovieTexture::
+do_fillin_rawdata(DatagramIterator &scan, BamReader *manager) {
+  CDWriter cdata(_cycler);
+
+  size_t num_pages = scan.get_uint16();
+  _z_size = (int)num_pages;
+
+  cdata->_pages.reserve(num_pages);
+  for (size_t n = 0; n < num_pages; ++n) {
+    cdata->_pages.push_back(VideoPage());
+    manager->read_pointer(scan);  // page._color
+    manager->read_pointer(scan);  // page._alpha
+  }
+
+  // We load one or more MovieVideoCursors during the above loop.  We
+  // need a finalize callback so we can initialize ourselves once
+  // those cursors have been read completely.
+  manager->register_finalize(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTexture::finalize
+//       Access: Public, Virtual
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
+////////////////////////////////////////////////////////////////////
+void MovieTexture::
+finalize(BamReader *manager) {
+  CDWriter cdata(_cycler);
+
+  // Insist that each of our video pages gets finalized before we do.
+  size_t num_pages = cdata->_pages.size();
+  for (size_t n = 0; n < num_pages; ++n) {
+    VideoPage &page = cdata->_pages[n];
+    manager->finalize_now(page._color);
+    manager->finalize_now(page._alpha);
+  }
+
+  do_recalculate_image_properties(cdata, LoaderOptions());
+
+  set_loaded_from_image();
+  set_loop(true);
+  play();
+}
+
 #endif  // HAVE_AUDIO

+ 10 - 2
panda/src/grutil/movieTexture.h

@@ -34,7 +34,7 @@
 class EXPCL_PANDA_GRUTIL MovieTexture : public Texture {
 PUBLISHED:
   MovieTexture(const string &name);
-  MovieTexture(PT(MovieVideo) video);
+  MovieTexture(MovieVideo *video);
 private:
   MovieTexture(const MovieTexture &copy);
 PUBLISHED:
@@ -71,6 +71,8 @@ protected:
 
   virtual void do_reload_ram_image();
   virtual bool get_keep_ram_image() const;
+  virtual bool do_has_bam_rawdata() const;
+  virtual void do_get_bam_rawdata();
   virtual bool do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
                            int z, int n, int primary_file_num_channels, int alpha_file_channel,
                            const LoaderOptions &options,
@@ -82,7 +84,6 @@ protected:
 
   class VideoPage {
   public:
-    VideoPage();
     PT(MovieVideoCursor) _color;
     PT(MovieVideoCursor) _alpha;
   };
@@ -120,6 +121,13 @@ protected:
 public:
   static void register_with_read_factory();
 
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+  virtual void do_write_datagram_rawdata(BamWriter *manager, Datagram &dg);
+  virtual void do_fillin_rawdata(DatagramIterator &scan, BamReader *manager);
+
+  virtual void finalize(BamReader *manager);
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;

+ 21 - 2
panda/src/movies/config_movies.cxx

@@ -15,6 +15,21 @@
 #include "config_movies.h"
 #include "dconfig.h"
 
+#include "movieVideo.h"
+#include "movieVideoCursor.h"
+
+#include "movieAudio.h"
+#include "movieAudioCursor.h"
+
+#include "inkblotVideo.h"
+#include "inkblotVideoCursor.h"
+
+#include "ffmpegVideo.h"
+#include "ffmpegVideoCursor.h"
+
+#include "ffmpegAudio.h"
+#include "ffmpegAudioCursor.h"
+
 #ifdef HAVE_FFMPEG
 extern "C" {
   #include "libavcodec/avcodec.h"
@@ -54,13 +69,17 @@ init_libmovies() {
   UserDataAudio::init_type();
   UserDataAudioCursor::init_type();
   MicrophoneAudio::init_type();
+
 #ifdef HAVE_FFMPEG
+  FfmpegVirtualFile::register_protocol();
+
   FfmpegVideo::init_type();
   FfmpegVideoCursor::init_type();
   FfmpegAudio::init_type();
   FfmpegAudioCursor::init_type();
-  av_register_all();
-  FfmpegVirtualFile::register_protocol();
+
+  FfmpegVideo::register_with_read_factory();
+  FfmpegVideoCursor::register_with_read_factory();
 #endif
 }
 

+ 0 - 15
panda/src/movies/config_movies.h

@@ -21,21 +21,6 @@
 #include "configVariableDouble.h"
 #include "dconfig.h"
 
-#include "movieVideo.h"
-#include "movieVideoCursor.h"
-
-#include "movieAudio.h"
-#include "movieAudioCursor.h"
-
-#include "inkblotVideo.h"
-#include "inkblotVideoCursor.h"
-
-#include "ffmpegVideo.h"
-#include "ffmpegVideoCursor.h"
-
-#include "ffmpegAudio.h"
-#include "ffmpegAudioCursor.h"
-
 ConfigureDecl(config_movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);
 NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);
 NotifyCategoryDecl(ffmpeg, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);

+ 3 - 0
panda/src/movies/ffmpegAudio.h

@@ -14,6 +14,9 @@
 
 #ifndef FFMPEGAUDIO_H
 #define FFMPEGAUDIO_H
+
+#include "pandabase.h"
+
 #ifdef HAVE_FFMPEG
 
 #include "movieAudio.h"

+ 13 - 9
panda/src/movies/ffmpegAudioCursor.cxx

@@ -42,13 +42,14 @@ FfmpegAudioCursor(FfmpegAudio *src) :
   _buffer(0),
   _buffer_alloc(0)
 {
-  string url = "pandavfs:";
-  url += _filename;
-  if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
+  if (!_ffvfile.open_vfs(_filename)) {
     cleanup();
     return;
   }
 
+  _format_ctx = _ffvfile.get_format_context();
+  nassertv(_format_ctx != NULL);
+
   if (av_find_stream_info(_format_ctx)<0) {
     cleanup();
     return;
@@ -130,22 +131,25 @@ cleanup() {
       av_free_packet(_packet);
     }
     delete _packet;
-    _packet = 0;
+    _packet = NULL;
   }
+
   if (_buffer_alloc) {
     delete[] _buffer_alloc;
     _buffer_alloc = 0;
-    _buffer = 0;
+    _buffer = NULL;
   }
+
   if ((_audio_ctx)&&(_audio_ctx->codec)) {
     avcodec_close(_audio_ctx);
   }
-  _audio_ctx = 0;
+  _audio_ctx = NULL;
+
   if (_format_ctx) {
-    av_close_input_file(_format_ctx);
-    _format_ctx = 0;
+    _ffvfile.close();
+    _format_ctx = NULL;
   }
-  _audio_ctx = 0;
+
   _audio_index = -1;
 }
 

+ 6 - 1
panda/src/movies/ffmpegAudioCursor.h

@@ -14,12 +14,16 @@
 
 #ifndef FFMPEGAUDIOCURSOR_H
 #define FFMPEGAUDIOCURSOR_H
-#ifdef HAVE_FFMPEG
 
 #include "pandabase.h"
+
+#ifdef HAVE_FFMPEG
+
+#include "movieAudioCursor.h"
 #include "namable.h"
 #include "texture.h"
 #include "pointerTo.h"
+#include "ffmpegVirtualFile.h"
 
 struct AVFormatContext;
 struct AVCodecContext;
@@ -52,6 +56,7 @@ protected:
   unsigned char *_packet_data;
   AVFormatContext *_format_ctx;
   AVCodecContext  *_audio_ctx;
+  FfmpegVirtualFile _ffvfile;
   int _audio_index;
   double _audio_timebase;
 

+ 74 - 2
panda/src/movies/ffmpegVideo.cxx

@@ -15,6 +15,7 @@
 #ifdef HAVE_FFMPEG
 
 #include "ffmpegVideo.h"
+#include "ffmpegVideoCursor.h"
 #include "config_movies.h"
 
 TypeHandle FfmpegVideo::_type_handle;
@@ -22,7 +23,9 @@ TypeHandle FfmpegVideo::_type_handle;
 ////////////////////////////////////////////////////////////////////
 //     Function: FfmpegVideo::Constructor
 //       Access: Public
-//  Description: xxx
+//  Description: Constructs an ffmpeg video that reads its contents
+//               from the indicate filename, which may be a file in
+//               the VFS.
 ////////////////////////////////////////////////////////////////////
 FfmpegVideo::
 FfmpegVideo(const Filename &name) :
@@ -31,6 +34,22 @@ FfmpegVideo(const Filename &name) :
   _filename = name;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideo::Constructor
+//       Access: Public
+//  Description: Constructs an ffmpeg video that reads its contents
+//               from the indicated subfile information.  This is
+//               normally used for low-level purposes only; you would
+//               normally use the constructor that takes a filename.
+////////////////////////////////////////////////////////////////////
+FfmpegVideo::
+FfmpegVideo(const SubfileInfo &info) :
+  MovieVideo(info.get_filename())
+{
+  _filename = info.get_filename();
+  _subfile_info = info;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: FfmpegVideo::Destructor
 //       Access: Public
@@ -52,10 +71,63 @@ open() {
     movies_cat.error() << "Could not open " << _filename << "\n";
     return NULL;
   } else {
-    return (MovieVideoCursor*)(FfmpegVideoCursor*)result;
+    return result.p();
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideo::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               FfmpegVideo.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideo::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideo::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideo::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  MovieVideo::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideo::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type FfmpegVideo is encountered
+//               in the Bam file.  It should create the FfmpegVideo
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *FfmpegVideo::
+make_from_bam(const FactoryParams &params) {
+  FfmpegVideo *video = new FfmpegVideo("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  video->fillin(scan, manager);
+
+  return video;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideo::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new FfmpegVideo.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideo::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  MovieVideo::fillin(scan, manager);
+}
 
 ////////////////////////////////////////////////////////////////////
 

+ 18 - 3
panda/src/movies/ffmpegVideo.h

@@ -14,11 +14,17 @@
 
 #ifndef FFMPEGVIDEO_H
 #define FFMPEGVIDEO_H
+
+#include "pandabase.h"
+
 #ifdef HAVE_FFMPEG
 
 #include "movieVideo.h"
 
 class FfmpegVideoCursor;
+class FactoryParams;
+class BamWriter;
+class BamReader;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : FfmpegVideo
@@ -26,13 +32,20 @@ class FfmpegVideoCursor;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_MOVIES FfmpegVideo : public MovieVideo {
 
- PUBLISHED:
+PUBLISHED:
   FfmpegVideo(const Filename &name);
+  FfmpegVideo(const SubfileInfo &info);
+
   virtual ~FfmpegVideo();
   virtual PT(MovieVideoCursor) open();
   
- private:
-  friend class FfmpegVideoCursor;
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
   
 public:
   static TypeHandle get_class_type() {
@@ -50,6 +63,8 @@ public:
 
 private:
   static TypeHandle _type_handle;
+
+  friend class FfmpegVideoCursor;
 };
 
 #include "ffmpegVideo.I"

+ 168 - 34
panda/src/movies/ffmpegVideoCursor.cxx

@@ -32,39 +32,69 @@ TypeHandle FfmpegVideoCursor::_type_handle;
   #define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
 #endif
 
-
 ////////////////////////////////////////////////////////////////////
-//     Function: FfmpegVideoCursor::Constructor
-//       Access: Public
-//  Description: xxx
+//     Function: FfmpegVideoCursor::Default Constructor
+//       Access: Protected
+//  Description: This constructor is only used when reading from a bam
+//               file.
 ////////////////////////////////////////////////////////////////////
 FfmpegVideoCursor::
-FfmpegVideoCursor(FfmpegVideo *src) :
-  MovieVideoCursor(src),
-  _filename(src->_filename),
-  _format_ctx(0),
+FfmpegVideoCursor() :
+  _packet(NULL),
+  _format_ctx(NULL),
+  _video_ctx(NULL),
   _video_index(-1),
-  _video_ctx(0),
-  _frame(0),
-  _frame_out(0),
-  _packet(0),
+  _frame(NULL),
+  _frame_out(NULL),
   _min_fseek(3.0)
 {
-  string url = "pandavfs:";
-  url += _filename;
-  if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
-    cleanup();
-    return;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::init_from
+//       Access: Protected
+//  Description: Specifies the source of the video cursor.  This is
+//               normally called only by the constructor or when
+//               reading from a bam file.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+init_from(FfmpegVideo *source) {
+  nassertv(source != NULL);
+  _source = source;
+  _filename = _source->get_filename();
+
+  if (!_source->get_subfile_info().is_empty()) {
+    // Read a subfile.
+    if (!_ffvfile.open_subfile(_source->get_subfile_info())) {
+      movies_cat.info() 
+	<< "Couldn't open " << _source->get_subfile_info() << "\n";
+      cleanup();
+      return;
+    }
+
+  } else {
+    // Read a filename.
+    if (!_ffvfile.open_vfs(_filename)) {
+      movies_cat.info() 
+	<< "Couldn't open " << _filename << "\n";
+      cleanup();
+      return;
+    }
   }
-  
-  if (av_find_stream_info(_format_ctx)<0) {
+
+  _format_ctx = _ffvfile.get_format_context();
+  nassertv(_format_ctx != NULL);
+
+  if (av_find_stream_info(_format_ctx) < 0) {
+    movies_cat.info() 
+      << "Couldn't find stream info\n";
     cleanup();
     return;
   }
   
   // Find the video stream
-  for(int i = 0; i < (int)_format_ctx->nb_streams; i++) {
-    if(_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+  for (int i = 0; i < (int)_format_ctx->nb_streams; ++i) {
+    if (_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
       _video_index = i;
       _video_ctx = _format_ctx->streams[i]->codec;
       _video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
@@ -72,16 +102,22 @@ FfmpegVideoCursor(FfmpegVideo *src) :
   }
   
   if (_video_ctx == 0) {
+    movies_cat.info() 
+      << "Couldn't find video_ctx\n";
     cleanup();
     return;
   }
 
-  AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
-  if(pVideoCodec == 0) {
+  AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id);
+  if (pVideoCodec == NULL) {
+    movies_cat.info() 
+      << "Couldn't find codec\n";
     cleanup();
     return;
   }
-  if(avcodec_open(_video_ctx, pVideoCodec)<0) {
+  if (avcodec_open(_video_ctx, pVideoCodec) < 0) {
+    movies_cat.info() 
+      << "Couldn't open codec\n";
     cleanup();
     return;
   }
@@ -109,6 +145,24 @@ FfmpegVideoCursor(FfmpegVideo *src) :
   _next_start = 0.0;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FfmpegVideoCursor::
+FfmpegVideoCursor(FfmpegVideo *src) :
+  _packet(NULL),
+  _format_ctx(NULL),
+  _video_ctx(NULL),
+  _video_index(-1),
+  _frame(NULL),
+  _frame_out(NULL),
+  _min_fseek(3.0)
+{
+  init_from(src);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: FfmpegVideoCursor::Destructor
 //       Access: Public
@@ -128,31 +182,34 @@ void FfmpegVideoCursor::
 cleanup() {
   if (_frame) {
     av_free(_frame);
-    _frame = 0;
+    _frame = NULL;
   }
+
   if (_frame_out) {
     _frame_out->data[0] = 0;
     av_free(_frame_out);
-    _frame_out = 0;
+    _frame_out = NULL;
   }
+
   if (_packet) {
     if (_packet->data) {
       av_free_packet(_packet);
     }
     delete _packet;
-    _packet = 0;
+    _packet = NULL;
   }
+
   if ((_video_ctx)&&(_video_ctx->codec)) {
     avcodec_close(_video_ctx);
   }
-  _video_ctx = 0;
+  _video_ctx = NULL;
+
   if (_format_ctx) {
-    av_close_input_file(_format_ctx);
-    _format_ctx = 0;
+    _ffvfile.close();
+    _format_ctx = NULL;
   }
-  _video_ctx = 0;
-  _video_index = -1;
 
+  _video_index = -1;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -171,7 +228,7 @@ export_frame(unsigned char *data, bool bgra, int bufx) {
 #ifdef HAVE_SWSCALE
     struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
                                _video_ctx->pix_fmt, _size_x, _size_y,
-                               PIX_FMT_BGRA, 2, NULL, NULL, NULL);
+                               PIX_FMT_BGRA, SWS_FAST_BILINEAR, NULL, NULL, NULL);
     nassertv(convert_ctx != NULL);
     sws_scale(convert_ctx, _frame->data, _frame->linesize,
               0, _size_y, _frame_out->data, _frame_out->linesize);
@@ -186,7 +243,7 @@ export_frame(unsigned char *data, bool bgra, int bufx) {
 #ifdef HAVE_SWSCALE
     struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
                                _video_ctx->pix_fmt, _size_x, _size_y,
-                               PIX_FMT_BGR24, 2, NULL, NULL, NULL);
+                               PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
     nassertv(convert_ctx != NULL);
     sws_scale(convert_ctx, _frame->data, _frame->linesize,
               0, _size_y, _frame_out->data, _frame_out->linesize);
@@ -385,5 +442,82 @@ fetch_into_buffer(double time, unsigned char *data, bool bgra) {
 }
 
 ////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               FfmpegVideo.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  MovieVideoCursor::write_datagram(manager, dg);
+
+  // No need to write any additional data here--all of it comes
+  // implicitly from the underlying MovieVideo, which we process in
+  // finalize().
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::finalize
+//       Access: Public, Virtual
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+finalize(BamReader *) {
+  if (_source != (MovieVideo *)NULL) {
+    FfmpegVideo *video;
+    DCAST_INTO_V(video, _source);
+    init_from(video);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type FfmpegVideo is encountered
+//               in the Bam file.  It should create the FfmpegVideo
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *FfmpegVideoCursor::
+make_from_bam(const FactoryParams &params) {
+  FfmpegVideoCursor *video = new FfmpegVideoCursor;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  video->fillin(scan, manager);
+
+  return video;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new FfmpegVideo.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  MovieVideoCursor::fillin(scan, manager);
+  
+  // The MovieVideoCursor gets the underlying MovieVideo pointer.  We
+  // need a finalize callback so we can initialize ourselves once that
+  // has been read completely.
+  manager->register_finalize(this);
+}
 
 #endif // HAVE_FFMPEG

+ 25 - 6
panda/src/movies/ffmpegVideoCursor.h

@@ -14,11 +14,15 @@
 
 #ifndef FFMPEGVIDEOCURSOR_H
 #define FFMPEGVIDEOCURSOR_H
-#ifdef HAVE_FFMPEG
 
 #include "pandabase.h"
+
+#ifdef HAVE_FFMPEG
+
+#include "movieVideoCursor.h"
 #include "texture.h"
 #include "pointerTo.h"
+#include "ffmpegVirtualFile.h"
 
 struct AVFormatContext;
 struct AVCodecContext;
@@ -31,17 +35,19 @@ struct AVFrame;
 // Description : 
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_MOVIES FfmpegVideoCursor : public MovieVideoCursor {
-  friend class FfmpegVideo;
+protected:
+  FfmpegVideoCursor();
+  void init_from(FfmpegVideo *src);
 
- PUBLISHED:
+PUBLISHED:
   FfmpegVideoCursor(FfmpegVideo *src);
   virtual ~FfmpegVideoCursor();
   
- public:
+public:
   virtual void fetch_into_texture(double time, Texture *t, int page);
   virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
 
- protected:
+protected:
   void fetch_packet(double default_time);
   void fetch_frame();
   void seek(double t);
@@ -54,13 +60,24 @@ class EXPCL_PANDA_MOVIES FfmpegVideoCursor : public MovieVideoCursor {
   double _packet_time;
   AVFormatContext *_format_ctx;
   AVCodecContext *_video_ctx;
-  int    _video_index;
+  FfmpegVirtualFile _ffvfile;
+  int _video_index;
   double _video_timebase;
   AVFrame *_frame;
   AVFrame *_frame_out;
   int _initial_dts;
   double _min_fseek;
   
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+  virtual void finalize(BamReader *manager);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  
 public:
   static TypeHandle get_class_type() {
     return _type_handle;
@@ -77,6 +94,8 @@ public:
 
 private:
   static TypeHandle _type_handle;
+
+  friend class FfmpegVideo;
 };
 
 #include "ffmpegVideoCursor.I"

+ 12 - 0
panda/src/movies/ffmpegVirtualFile.I

@@ -11,3 +11,15 @@
 // with this source code in a file named "LICENSE."
 //
 ////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVirtualFile::get_format_context
+//       Access: Public
+//  Description: Returns a pointer to the opened ffmpeg context, or
+//               NULL if the file was not successfully opened.
+////////////////////////////////////////////////////////////////////
+INLINE AVFormatContext *FfmpegVirtualFile::
+get_format_context() const {
+  return _format_context;
+}

+ 268 - 114
panda/src/movies/ffmpegVirtualFile.cxx

@@ -18,154 +18,184 @@
 #include "config_movies.h"
 #include "ffmpegVirtualFile.h"
 #include "virtualFileSystem.h"
-extern "C" {
-  #include "libavformat/avio.h"
-}
 
 #ifndef AVSEEK_SIZE
   #define AVSEEK_SIZE 0x10000
 #endif
 
 ////////////////////////////////////////////////////////////////////
-// These functions need to use C calling conventions.
+//     Function: FfmpegVirtualFile::Constructor
+//       Access: Public
+//  Description: 
 ////////////////////////////////////////////////////////////////////
-extern "C" {
-  static int       pandavfs_open(URLContext *h, const char *filename, int flags);
-  static int       pandavfs_read(URLContext *h, unsigned char *buf, int size);
-// This change happened a few days before 52.68.0
-#if LIBAVFORMAT_VERSION_INT < 3425280
-  static int       pandavfs_write(URLContext *h, unsigned char *buf, int size);
-#else
-  static int       pandavfs_write(URLContext *h, const unsigned char *buf, int size);
-#endif
-  static int64_t   pandavfs_seek(URLContext *h, int64_t pos, int whence);
-  static int       pandavfs_close(URLContext *h);
+FfmpegVirtualFile::
+FfmpegVirtualFile() : 
+  _format_context(NULL),
+  _in(NULL),
+  _owns_in(false)
+{
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: pandavfs_open
-//       Access: Static Function
-//  Description: A hook to open a panda VFS file.
+//     Function: FfmpegVirtualFile::Destructor
+//       Access: Public
+//  Description: 
 ////////////////////////////////////////////////////////////////////
-static int
-pandavfs_open(URLContext *h, const char *filename, int flags) {
-  if (flags != 0) {
-    if (movies_cat.is_debug()) {
-      movies_cat.debug()
-        << "ffmpeg is trying to write to the VFS.\n";
-    }
-    // We'll allow this, but just fail any actual writes.
-  }
+FfmpegVirtualFile::
+~FfmpegVirtualFile() {
+  close();
+}
 
-  filename += 9; // Skip over "pandavfs:"
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  istream *s = vfs->open_read_file(filename, true);
-  if (s == 0) {
-    return -1;
-  }
-  // Test whether seek works.
-  s->seekg(1, ios::beg);
-  int tel1 = s->tellg();
-  s->seekg(0, ios::beg);
-  int tel2 = s->tellg();
-  if (s->fail() || (tel1!=1) || (tel2!=0)) {
-    movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
-    delete s;
-    return -1;
-  }
-  h->priv_data = s;
-  return 0;
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVirtualFile::Copy Constructor
+//       Access: Private
+//  Description: These objects are not meant to be copied.
+////////////////////////////////////////////////////////////////////
+FfmpegVirtualFile::
+FfmpegVirtualFile(const FfmpegVirtualFile &copy) {
+  nassertv(false);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: pandavfs_read
-//       Access: Static Function
-//  Description: A hook to read a panda VFS file.
+//     Function: FfmpegVirtualFile::Copy Assignment Operator
+//       Access: Private
+//  Description: These objects are not meant to be copied.
 ////////////////////////////////////////////////////////////////////
-static int
-pandavfs_read(URLContext *h, unsigned char *buf, int size) {
-  istream *s = (istream*)(h->priv_data);
-  s->read((char*)buf, size);
-  int gc = s->gcount();
-  s->clear();
-  return gc;
+void FfmpegVirtualFile::
+operator = (const FfmpegVirtualFile &copy) {
+  nassertv(false);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: pandavfs_write
-//       Access: Static Function
-//  Description: A hook to write a panda VFS file.
+//     Function: FfmpegVirtualFile::open_vfs
+//       Access: Public
+//  Description: Opens the movie file via Panda's VFS.  Returns true
+//               on success, false on failure.  If successful, use
+//               get_format_context() to get the open file handle.
 ////////////////////////////////////////////////////////////////////
-static int
-#if LIBAVFORMAT_VERSION_INT < 3425280
-pandavfs_write(URLContext *h, unsigned char *buf, int size) {
-#else
-pandavfs_write(URLContext *h, const unsigned char *buf, int size) {
-#endif
-  movies_cat.warning()
-    << "ffmpeg is trying to write to the VFS.\n";
-  return -1;
+bool FfmpegVirtualFile::
+open_vfs(const Filename &filename) {
+  close();
+
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  Filename fname = filename;
+  fname.set_binary();
+  PT(VirtualFile) vfile = vfs->get_file(fname);
+  if (vfile == NULL) {
+    return false;
+  }
+
+  _in = vfile->open_read_file(true);
+  if (_in == NULL) {
+    return false;
+  }
+
+  _owns_in = true;
+  _start = 0;
+  _size = vfile->get_file_size(_in);
+
+  // I tried to use av_open_input_stream(), but it (a) required a lot
+  // of low-level stream analysis calls that really should be
+  // automatic (and are automatic in av_open_input_file()), and (b)
+  // was broken on the ffmpeg build I happened to grab.  Screw it,
+  // clearly av_open_input_file() is the preferred and more
+  // heavily-exercised interface.  So we'll continue to use url
+  // synthesis as a hacky hook into this interface.
+
+  // Nowadays we synthesize a "url" that references this pointer.
+  ostringstream strm;
+  strm << "pandavfs://" << (void *)this;
+  string url = strm.str();
+
+  // Now we can open the stream.
+  int result = 
+    av_open_input_file(&_format_context, url.c_str(), NULL, 0, NULL);
+  if (result < 0) {
+    close();
+    return false;
+  }
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: pandavfs_seek
-//       Access: Static Function
-//  Description: A hook to seek a panda VFS file.
+//     Function: FfmpegVirtualFile::open_subfile
+//       Access: Public
+//  Description: Opens the movie file directly from a file on disk
+//               (does not go through the VFS).  Returns true on
+//               success, false on failure.  If successful, use
+//               get_format_context() to get the open file handle.
 ////////////////////////////////////////////////////////////////////
-static int64_t
-pandavfs_seek(URLContext *h, int64_t pos, int whence) {
-  istream *s = (istream*)(h->priv_data);
-  switch(whence) {
-  case SEEK_SET: s->seekg(pos, ios::beg); break;
-  case SEEK_CUR: s->seekg(pos, ios::cur); break;
-  case SEEK_END: s->seekg(pos, ios::end); break;
-  case AVSEEK_SIZE: {
-    s->seekg(0, ios::cur);
-    int p = s->tellg();
-    s->seekg(-1, ios::end);
-    int size = s->tellg();
-    if (size < 0) {
-      movies_cat.error() << "Failed to determine filesize in ffmpegVirtualFile\n";
-      s->clear();
-      return -1;
-    }
-    size++;
-    s->seekg(p, ios::beg);
-    s->clear();
-    return size; }
-  default:
-    movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
-    s->clear();
-    return -1;
+bool FfmpegVirtualFile::
+open_subfile(const SubfileInfo &info) {
+  close();
+
+  Filename fname = info.get_filename();
+  fname.set_binary();
+  if (!fname.open_read(_file_in)) {
+    return false;
+  }
+
+  _in = &_file_in;
+  _owns_in = false;
+  _start = info.get_start();
+  _size = info.get_size();
+
+  _in->seekg(_start);
+
+  // I tried to use av_open_input_stream(), but it (a) required a lot
+  // of low-level ffmpeg calls that really shouldn't be part of the
+  // public API (and which aren't necessary with av_open_input_file()
+  // because they happen implicitly there), and (b) was completely
+  // broken on the ffmpeg build I happened to grab.  Screw it; clearly
+  // av_open_input_file() is the preferred and more heavily-exercised
+  // interface.  So we'll use it, even though it requires a bit of a
+  // hack.
+
+  // The hack is that we synthesize a "url" that references this
+  // pointer, then open that url.  This calls pandavfs_open(), which
+  // decodes the pointer and stores it for future callbacks.
+  ostringstream strm;
+  strm << "pandavfs://" << (void *)this;
+  string url = strm.str();
+
+  // Now we can open the stream.
+  int result = 
+    av_open_input_file(&_format_context, url.c_str(), NULL, 0, NULL);
+  if (result < 0) {
+    close();
+    return false;
   }
-  s->clear();
-  int tl = s->tellg();
-  return tl;
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: pandavfs_close
-//       Access: Static Function
-//  Description: A hook to close a panda VFS file.
+//     Function: FfmpegVirtualFile::close
+//       Access: Public
+//  Description: Explicitly closes the opened file.  This is also
+//               called implicitly by the destructor if necessary.
 ////////////////////////////////////////////////////////////////////
-static int
-pandavfs_close(URLContext *h) {
-  istream *s = (istream*)(h->priv_data);
-  delete s;
-  h->priv_data = 0;
-  return 0;
+void FfmpegVirtualFile::
+close() {
+  if (_format_context != NULL) {
+    av_close_input_file(_format_context);
+    _format_context = NULL;
+  }
+
+  if (_owns_in) {
+    nassertv(_in != NULL);
+    VirtualFileSystem::close_read_file(_in);
+    _owns_in = false;
+  }
+  _in = NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: FfmpegVirtualFile::register_protocol
 //       Access: Public, Static
-//  Description: Enables ffmpeg to access panda's VFS.
-//
-//               After calling this method, ffmpeg will be
-//               able to open "URLs" that look like this:
-//               
-//               pandavfs:/c/mygame/foo.avi
-//
+//  Description: Should be called at startup to attach the appropriate
+//               hooks between Panda and FFMpeg.
 ////////////////////////////////////////////////////////////////////
 void FfmpegVirtualFile::
 register_protocol() {
@@ -173,11 +203,22 @@ register_protocol() {
   if (initialized) {
     return;
   }
+
+  // Here's a good place to call this global ffmpeg initialization
+  // function.
+  av_register_all();
+
   static URLProtocol protocol;
   protocol.name = "pandavfs";
   protocol.url_open  = pandavfs_open;
   protocol.url_read  = pandavfs_read;
+
+#if LIBAVFORMAT_VERSION_INT < 3425280
+  protocol.url_write = (int (*)(URLContext *, unsigned char *, int))pandavfs_write;
+#else
   protocol.url_write = pandavfs_write;
+#endif
+
   protocol.url_seek  = pandavfs_seek;
   protocol.url_close = pandavfs_close;
 #if LIBAVFORMAT_VERSION_INT < 3415296
@@ -192,6 +233,119 @@ register_protocol() {
   av_log_set_callback(&log_callback);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVirtualFile::pandavfs_open
+//       Access: Private, Static
+//  Description: A callback to "open" a virtual file.  Actually, all
+//               this does is assign the pointer back to the
+//               FfmpegVirtualFile instance.
+////////////////////////////////////////////////////////////////////
+int FfmpegVirtualFile::
+pandavfs_open(URLContext *h, const char *filename, int flags) {
+  filename += 11; // Skip over "pandavfs://"
+  istringstream strm(filename);
+  void *ptr = 0;
+  strm >> ptr;
+
+  FfmpegVirtualFile *self = (FfmpegVirtualFile *)ptr;
+  h->priv_data = self;
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVirtualFile::pandavfs_read
+//       Access: Private, Static
+//  Description: A callback to read a virtual file.
+////////////////////////////////////////////////////////////////////
+int FfmpegVirtualFile::
+pandavfs_read(URLContext *h, unsigned char *buf, int size) {
+  FfmpegVirtualFile *self = (FfmpegVirtualFile *)(h->priv_data);
+  istream *in = self->_in;
+
+  // Since we may be simulating a subset of the opened stream, don't
+  // allow it to read past the "end".
+  streampos remaining = self->_start + (streampos)self->_size - in->tellg();
+  if (remaining < size) {
+    if (remaining <= 0) {
+      return 0;
+    }
+
+    size = (int)remaining;
+  }
+
+  in->read((char *)buf, size);
+  int gc = in->gcount();
+  in->clear();
+
+  return gc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVirtualFile::pandavfs_write
+//       Access: Private, Static
+//  Description: A callback to write a virtual file.  Unimplemented,
+//               because we use ffmpeg for playback only, not for
+//               encoding video streams.
+////////////////////////////////////////////////////////////////////
+int FfmpegVirtualFile::
+pandavfs_write(URLContext *h, const unsigned char *buf, int size) {
+  movies_cat.warning()
+    << "ffmpeg is trying to write to the VFS.\n";
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVirtualFile::pandavfs_seek
+//       Access: Private, Static
+//  Description: A callback to change the read position on an istream.
+////////////////////////////////////////////////////////////////////
+int64_t FfmpegVirtualFile::
+pandavfs_seek(URLContext *h, int64_t pos, int whence) {
+  FfmpegVirtualFile *self = (FfmpegVirtualFile *)(h->priv_data);
+  istream *in = self->_in;
+
+  switch(whence) {
+  case SEEK_SET: 
+    in->seekg(self->_start + pos, ios::beg); 
+    break;
+
+  case SEEK_CUR: 
+    in->seekg(pos, ios::cur); 
+    break;
+
+  case SEEK_END: 
+    // For seeks relative to the end, we actually compute the end
+    // based on _start + _size, and then use ios::beg.
+    in->seekg(self->_start + (streampos)self->_size + pos, ios::beg); 
+    break;
+
+  case AVSEEK_SIZE: 
+    return self->_size; 
+
+  default:
+    movies_cat.error() 
+      << "Illegal parameter to seek in ffmpegVirtualFile\n";
+    in->clear();
+    return -1;
+  }
+
+  in->clear();
+  return in->tellg() - self->_start;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVirtualFile::pandavfs_close
+//       Access: Private, Static
+//  Description: A hook to "close" a panda VFS file.  Actually it only
+//               clears the associated pointer.
+////////////////////////////////////////////////////////////////////
+int FfmpegVirtualFile::
+pandavfs_close(URLContext *h) {
+  FfmpegVirtualFile *self = (FfmpegVirtualFile *)(h->priv_data);
+  h->priv_data = 0;
+  return 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: FfmpegVirtualFile::log_callback
 //       Access: Private, Static

+ 44 - 9
panda/src/movies/ffmpegVirtualFile.h

@@ -14,27 +14,62 @@
 
 #ifndef FFMPEGVIRTUALFILE_H
 #define FFMPEGVIRTUALFILE_H
+
+#include "pandabase.h"
+
 #ifdef HAVE_FFMPEG
 
 #include "config_movies.h"
+#include "filename.h"
+#include "subfileInfo.h"
+
+extern "C" {
+  #include "libavformat/avio.h"
+}
 #include <stdarg.h>
 
+struct URLContext;
+struct AVFormatContext;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : FfmpegVirtualFile
-// Description : Enables ffmpeg to access panda's VFS.
-//
-//               Once register_protocol() is called, ffmpeg will be
-//               able to open "URLs" that look like this:
-//               
-//               pandavfs:/c/mygame/foo.avi
-//
+// Description : Enables ffmpeg to access panda's VFS.  Create an
+//               instance of the FfmpegVirtualFile for each ffmpeg
+//               stream you wish to open.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_MOVIES FfmpegVirtualFile {
- public:
+public:
+  FfmpegVirtualFile();
+  ~FfmpegVirtualFile();
+private:
+  FfmpegVirtualFile(const FfmpegVirtualFile &copy);
+  void operator = (const FfmpegVirtualFile &copy);
+
+public:
+  bool open_vfs(const Filename &filename);
+  bool open_subfile(const SubfileInfo &info);
+  void close();
+
+  INLINE AVFormatContext *get_format_context() const;
+
   static void register_protocol();
 
- private:
+private:
+  static int pandavfs_open(URLContext *h, const char *filename, int flags);
+  static int pandavfs_read(URLContext *h, unsigned char *buf, int size);
+  static int pandavfs_write(URLContext *h, const unsigned char *buf, int size);
+  static int64_t pandavfs_seek(URLContext *h, int64_t pos, int whence);
+  static int pandavfs_close(URLContext *h);
+
   static void log_callback(void *ptr, int level, const char *fmt, va_list v1);
+
+private:
+  AVFormatContext *_format_context;
+  streampos _start;
+  streamsize _size;
+  istream *_in;
+  pifstream _file_in;
+  bool _owns_in;
 };
 
 #include "ffmpegVirtualFile.I"

+ 1 - 0
panda/src/movies/movieAudio.cxx

@@ -14,6 +14,7 @@
 
 #include "movieAudio.h"
 #include "movieAudioCursor.h"
+#include "ffmpegAudio.h"
 
 TypeHandle MovieAudio::_type_handle;
 

+ 14 - 1
panda/src/movies/movieVideo.I

@@ -12,14 +12,27 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+
 ////////////////////////////////////////////////////////////////////
 //     Function: MovieVideo::get_filename
 //       Access: Published
 //  Description: Returns the movie's filename.  A movie is not
 //               guaranteed to have a filename, if not, then this
-//               function returns a null filename.
+//               function returns an empty filename.
 ////////////////////////////////////////////////////////////////////
 INLINE const Filename &MovieVideo::
 get_filename() const {
   return _filename;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideo::get_subfile_info
+//       Access: Published
+//  Description: If the movie is to be loaded from a subfile on disk,
+//               this returns the subfile info.  Check info.is_empty()
+//               to see if this is valid data.
+////////////////////////////////////////////////////////////////////
+INLINE const SubfileInfo &MovieVideo::
+get_subfile_info() const {
+  return _subfile_info;
+}

+ 60 - 2
panda/src/movies/movieVideo.cxx

@@ -14,6 +14,9 @@
 
 #include "movieVideo.h"
 #include "config_movies.h"
+#include "ffmpegVideo.h"
+#include "bamReader.h"
+#include "bamWriter.h"
 
 TypeHandle MovieVideo::_type_handle;
 
@@ -43,11 +46,12 @@ MovieVideo::
 ////////////////////////////////////////////////////////////////////
 //     Function: MovieVideo::open
 //       Access: Published, Virtual
-//  Description: Open this video, returning a MovieVideoCursor.
+//  Description: Open this video, returning a MovieVideoCursor of the
+//               appropriate type.  Returns NULL on error.
 ////////////////////////////////////////////////////////////////////
 PT(MovieVideoCursor) MovieVideo::
 open() {
-  return new MovieVideoCursor(this);
+  return NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -66,3 +70,57 @@ get(const Filename &name) {
 #endif
 }
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideo::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void MovieVideo::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritableReferenceCount::write_datagram(manager, dg);
+  dg.add_string(_filename);
+  
+  // Now we record the raw movie data directly into the bam stream.
+  // We always do this, regardless of bam-texture-mode; we generally
+  // won't get to this codepath if bam-texture-mode isn't rawdata
+  // anyway.
+
+  SubfileInfo result;
+  if (!_subfile_info.is_empty()) {
+    dg.add_bool(true);
+    manager->write_file_data(result, _subfile_info);
+  } else if (!_filename.empty()) {
+    dg.add_bool(true);
+    manager->write_file_data(result, _filename);
+  } else {
+    dg.add_bool(false);
+  }
+
+  /* Not sure yet if this is a good idea.
+  if (!result.is_empty()) {
+    // If we've just copied the data to a local file, read it from
+    // there in the future.
+    _subfile_info = result;
+  }
+  */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideo::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new MovieVideo.
+////////////////////////////////////////////////////////////////////
+void MovieVideo::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritableReferenceCount::fillin(scan, manager);
+  _filename = scan.get_string();
+
+  bool got_info = scan.get_bool();
+  if (got_info) {
+    manager->read_file_data(_subfile_info);
+  }
+}

+ 19 - 4
panda/src/movies/movieVideo.h

@@ -19,7 +19,13 @@
 #include "namable.h"
 #include "pointerTo.h"
 #include "typedWritableReferenceCount.h"
+#include "subfileInfo.h"
+#include "filename.h"
+
 class MovieVideoCursor;
+class FactoryParams;
+class BamWriter;
+class BamReader;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : MovieVideo
@@ -35,17 +41,26 @@ class MovieVideoCursor;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_MOVIES MovieVideo : public TypedWritableReferenceCount, public Namable {
 
- PUBLISHED:
+PUBLISHED:
   MovieVideo(const string &name = "Blank Video");
   virtual ~MovieVideo();
   virtual PT(MovieVideoCursor) open();
   static PT(MovieVideo) get(const Filename &name);
+
   INLINE const Filename &get_filename() const;
+  INLINE const SubfileInfo &get_subfile_info() const;
   
- protected:
+protected:
   Filename _filename;
+  SubfileInfo _subfile_info;
+
+public:
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  void fillin(DatagramIterator &scan, BamReader *manager);
   
- public:
+public:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
@@ -59,7 +74,7 @@ class EXPCL_PANDA_MOVIES MovieVideo : public TypedWritableReferenceCount, public
   }
   virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
 
- private:
+private:
   static TypeHandle _type_handle;
 };
 

+ 50 - 6
panda/src/movies/movieVideoCursor.cxx

@@ -16,16 +16,17 @@
 #include "config_movies.h"
 #include "pStatCollector.h"
 #include "pStatTimer.h"
+#include "bamReader.h"
+#include "bamWriter.h"
 
 TypeHandle MovieVideoCursor::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MovieVideoCursor::Constructor
-//       Access: Public
-//  Description: This constructor returns a null video stream --- a
-//               stream of plain blue and white frames that last one
-//               second each. To get more interesting video, you need
-//               to construct a subclass of this class.
+//     Function: MovieVideoCursor::Default Constructor
+//       Access: Protected
+//  Description: This is a virtual base class and should not be
+//               created directly.  Instead, create a more specialized
+//               class.
 ////////////////////////////////////////////////////////////////////
 MovieVideoCursor::
 MovieVideoCursor(MovieVideo *src) :
@@ -302,3 +303,46 @@ fetch_into_buffer(double time, unsigned char *data, bool bgra) {
   }
 }
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void MovieVideoCursor::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritableReferenceCount::write_datagram(manager, dg);
+
+  manager->write_pointer(dg, _source);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int MovieVideoCursor::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
+
+  _source = DCAST(MovieVideo, p_list[pi++]);
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new MovieVideoCursor.
+////////////////////////////////////////////////////////////////////
+void MovieVideoCursor::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritableReferenceCount::fillin(scan, manager);
+
+  manager->read_pointer(scan);  // _source
+}

+ 17 - 5
panda/src/movies/movieVideoCursor.h

@@ -18,7 +18,11 @@
 #include "pandabase.h"
 #include "texture.h"
 #include "pointerTo.h"
+
 class MovieVideo;
+class FactoryParams;
+class BamWriter;
+class BamReader;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : MovieVideoCursor
@@ -35,9 +39,10 @@ class MovieVideo;
 //               use separate MovieVideoCursor objects.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_MOVIES MovieVideoCursor : public TypedWritableReferenceCount {
+protected:
+  MovieVideoCursor(MovieVideo *src = NULL);
 
- PUBLISHED:
-  MovieVideoCursor(MovieVideo *src);
+PUBLISHED:
   virtual ~MovieVideoCursor();
   PT(MovieVideo) get_source() const;
   INLINE int size_x() const;
@@ -57,14 +62,14 @@ class EXPCL_PANDA_MOVIES MovieVideoCursor : public TypedWritableReferenceCount {
   virtual void fetch_into_texture_rgb(double time, Texture *t, int page);
   virtual void fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src);
 
- public:
+public:
   virtual void fetch_into_buffer(double time, unsigned char *block, bool bgra);
   
- private:
+private:
   void allocate_conversion_buffer();
   unsigned char *_conversion_buffer;
   
- protected:
+protected:
   PT(MovieVideo) _source;
   int _size_x;
   int _size_y;
@@ -77,6 +82,13 @@ class EXPCL_PANDA_MOVIES MovieVideoCursor : public TypedWritableReferenceCount {
   double _next_start;
   bool _streaming;
   bool _ready;
+
+public:
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+
+protected:
+  void fillin(DatagramIterator &scan, BamReader *manager);
   
 public:
   static TypeHandle get_class_type() {

+ 2 - 2
panda/src/pgraph/bamFile.cxx

@@ -417,7 +417,7 @@ continue_open_read(const string &bam_filename, bool report_errors) {
     return false;
   }
 
-  _reader = new BamReader(&_din, _bam_filename);
+  _reader = new BamReader(&_din);
   if (!_reader->init()) {
     close();
     return false;
@@ -444,7 +444,7 @@ continue_open_write(const string &bam_filename, bool report_errors) {
     return false;
   }
 
-  _writer = new BamWriter(&_dout, _bam_filename);
+  _writer = new BamWriter(&_dout);
 
   if (!_writer->init()) {
     close();

+ 2 - 1
panda/src/putil/bam.h

@@ -33,7 +33,7 @@ static const unsigned short _bam_major_ver = 6;
 // Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData.
 
 static const unsigned short _bam_first_minor_ver = 14;
-static const unsigned short _bam_minor_ver = 24;
+static const unsigned short _bam_minor_ver = 25;
 // Bumped to minor version 14 on 12/19/07 to change default ColorAttrib.
 // Bumped to minor version 15 on 4/9/08 to add TextureAttrib::_implicit_sort.
 // Bumped to minor version 16 on 5/13/08 to add Texture::_quality_level.
@@ -45,6 +45,7 @@ static const unsigned short _bam_minor_ver = 24;
 // Bumped to minor version 22 on 7/31/09 to add UvScrollNode R speed.
 // Bumped to minor version 23 on 5/4/10 to add internal TextureAttrib overrides.
 // Bumped to minor version 24 on 5/4/10 to add internal TexMatrixAttrib overrides.
+// Bumped to minor version 25 on 6/22/11 to add support for caching movie files.
 
 
 #endif

+ 18 - 53
panda/src/putil/bamCache.cxx

@@ -223,16 +223,8 @@ store(BamCacheRecord *record) {
   temp_pathname.set_extension(extension);
   temp_pathname.set_binary();
 
-  pofstream temp_file;
-  if (!temp_pathname.open_write(temp_file)) {
-    util_cat.error()
-      << "Could not open cache file: " << temp_pathname << "\n";
-    emergency_read_only();
-    return false;
-  }
-
   DatagramOutputFile dout;
-  if (!dout.open(temp_file)) {
+  if (!dout.open(temp_pathname)) {
     util_cat.error()
       << "Could not write cache file: " << temp_pathname << "\n";
     temp_pathname.unlink();
@@ -248,7 +240,7 @@ store(BamCacheRecord *record) {
   }
 
   {
-    BamWriter writer(&dout, temp_pathname);
+    BamWriter writer(&dout);
     if (!writer.init()) {
       temp_pathname.unlink();
       return false;
@@ -279,8 +271,8 @@ store(BamCacheRecord *record) {
     // delete any TypedWritables below that haven't been written yet.
   }
 
-  record->_record_size = temp_file.tellp();
-  temp_file.close();
+  record->_record_size = dout.get_file_pos();
+  dout.close();
 
   // Now move the file into place.
   if (!temp_pathname.rename_to(cache_pathname) && temp_pathname.exists()) {
@@ -669,22 +661,13 @@ check_cache_size() {
 //               success, or NULL on failure.
 ////////////////////////////////////////////////////////////////////
 BamCacheIndex *BamCache::
-do_read_index(Filename &index_pathname) {
+do_read_index(const Filename &index_pathname) {
   if (index_pathname.empty()) {
     return NULL;
   }
 
-  index_pathname.set_binary();
-  pifstream index_file;
-  if (!index_pathname.open_read(index_file)) {
-    util_cat.error()
-      << "Could not open index file: " << index_pathname << "\n";
-    return NULL;
-  }
-
   DatagramInputFile din;
-    
-  if (!din.open(index_file)) {
+  if (!din.open(index_pathname)) {
     util_cat.debug()
       << "Could not read index file: " << index_pathname << "\n";
     return NULL;
@@ -703,7 +686,7 @@ do_read_index(Filename &index_pathname) {
     return NULL;
   }
   
-  BamReader reader(&din, index_pathname);
+  BamReader reader(&din);
   if (!reader.init()) {
     return NULL;
   }
@@ -738,18 +721,9 @@ do_read_index(Filename &index_pathname) {
 //  Description: Writes the given index data to the specified filename.
 ////////////////////////////////////////////////////////////////////
 bool BamCache::
-do_write_index(Filename &index_pathname, const BamCacheIndex *index) {
-  index_pathname.set_binary();
-  pofstream index_file;
-  
-  if (!index_pathname.open_write(index_file)) {
-    util_cat.error()
-      << "Could not open index file: " << index_pathname << "\n";
-    return false;
-  }
-  
+do_write_index(const Filename &index_pathname, const BamCacheIndex *index) {
   DatagramOutputFile dout;
-  if (!dout.open(index_file)) {
+  if (!dout.open(index_pathname)) {
     util_cat.error()
       << "Could not write index file: " << index_pathname << "\n";
     index_pathname.unlink();
@@ -764,7 +738,7 @@ do_write_index(Filename &index_pathname, const BamCacheIndex *index) {
   }
 
   {
-    BamWriter writer(&dout, index_pathname);
+    BamWriter writer(&dout);
     if (!writer.init()) {
       index_pathname.unlink();
       return false;
@@ -776,7 +750,6 @@ do_write_index(Filename &index_pathname, const BamCacheIndex *index) {
     }
   }
 
-  index_file.close();
   return true;
 }
 
@@ -866,18 +839,9 @@ read_record(const Filename &source_pathname,
 //  Description: Actually reads a record from the file.
 ////////////////////////////////////////////////////////////////////
 PT(BamCacheRecord) BamCache::
-do_read_record(Filename &cache_pathname, bool read_data) {
-  cache_pathname.set_binary();
-  pifstream cache_file;
-  if (!cache_pathname.open_read(cache_file)) {
-    util_cat.debug()
-      << "Could not open cache file: " << cache_pathname << "\n";
-    return NULL;
-  }
-
+do_read_record(const Filename &cache_pathname, bool read_data) {
   DatagramInputFile din;
-    
-  if (!din.open(cache_file)) {
+  if (!din.open(cache_pathname)) {
     util_cat.debug()
       << "Could not read cache file: " << cache_pathname << "\n";
     return NULL;
@@ -896,7 +860,7 @@ do_read_record(Filename &cache_pathname, bool read_data) {
     return NULL;
   }
   
-  BamReader reader(&din, cache_pathname);
+  BamReader reader(&din);
   if (!reader.init()) {
     return NULL;
   }
@@ -944,10 +908,11 @@ do_read_record(Filename &cache_pathname, bool read_data) {
     }
   }
   
-  // Also get the file size.
-  cache_file.clear();
-  cache_file.seekg(0, ios::end);
-  record->_record_size = cache_file.tellg();
+  // Also get the total file size.
+  istream &in = din.get_stream();
+  in.clear();
+  in.seekg(0, ios::end);
+  record->_record_size = in.tellg();
 
   // And the last access time is now, duh.
   record->_record_access_time = time(NULL);

+ 3 - 3
panda/src/putil/bamCache.h

@@ -97,15 +97,15 @@ private:
 
   void emergency_read_only();
   
-  static BamCacheIndex *do_read_index(Filename &index_pathname);
-  static bool do_write_index(Filename &index_pathname, const BamCacheIndex *index);
+  static BamCacheIndex *do_read_index(const Filename &index_pathname);
+  static bool do_write_index(const Filename &index_pathname, const BamCacheIndex *index);
 
   PT(BamCacheRecord) find_and_read_record(const Filename &source_pathname,
                                           const Filename &cache_filename);
   PT(BamCacheRecord) read_record(const Filename &source_pathname,
                                  const Filename &cache_filename,
                                  int pass);
-  static PT(BamCacheRecord) do_read_record(Filename &cache_pathname, 
+  static PT(BamCacheRecord) do_read_record(const Filename &cache_pathname, 
                                            bool read_data);
 
   static string hash_filename(const string &filename);

+ 3 - 0
panda/src/putil/bamEnums.cxx

@@ -66,6 +66,9 @@ operator << (ostream &out, BamEnums::BamObjectCode boc) {
 
   case BamEnums::BOC_remove:
     return out << "remove";
+
+  case BamEnums::BOC_file_data:
+    return out << "file_data";
   }
 
   return out << "**invalid BamEnums::BamObjectCode value: (" << (int)boc << ")**";

+ 4 - 1
panda/src/putil/bamEnums.h

@@ -46,12 +46,15 @@ PUBLISHED:
   // (which does not).  A BOC_adjunct includes an object definition
   // but does not push the level; it is associated with the current
   // level.  BOC_remove lists object ID's that have been deallocated
-  // on the sender end.
+  // on the sender end.  BOC_file_data may appear at any level and
+  // indicates the following datagram contains auxiliary file data
+  // that may be referenced by a later object.
   enum BamObjectCode {
     BOC_push,
     BOC_pop,
     BOC_adjunct,
     BOC_remove,
+    BOC_file_data,
   };
 
   // This enum is used to control how textures are written to a bam

+ 31 - 3
panda/src/putil/bamReader.I

@@ -22,6 +22,17 @@ INLINE BamReaderAuxData::
 BamReaderAuxData() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamReader::get_source
+//       Access: Published
+//  Description: Returns the current source of the BamReader as set by
+//               set_source() or the constructor.
+////////////////////////////////////////////////////////////////////
+INLINE DatagramGenerator *BamReader::
+get_source() {
+  return _source;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamReader::get_filename
 //       Access: Published
@@ -32,7 +43,11 @@ BamReaderAuxData() {
 ////////////////////////////////////////////////////////////////////
 INLINE const Filename &BamReader::
 get_filename() const {
-  return _filename;
+  if (_source != (DatagramGenerator *)NULL) {
+    return _source->get_filename();
+  }
+  static const Filename empty_filename;
+  return empty_filename;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -134,14 +149,27 @@ get_current_minor_ver() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: BamReader::get_file
 //       Access: Public
+//  Description: Returns the FileReference that provides the source for
+//               these datagrams, if any, or NULL if the datagrams do
+//               not originate from a file on disk.
+////////////////////////////////////////////////////////////////////
+INLINE const FileReference *BamReader::
+get_file() {
+  nassertr(_source != NULL, NULL);
+  return _source->get_file();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamReader::get_vfile
+//       Access: Public
 //  Description: Returns the VirtualFile that provides the source for
 //               these datagrams, if any, or NULL if the datagrams do
 //               not originate from a VirtualFile.
 ////////////////////////////////////////////////////////////////////
 INLINE VirtualFile *BamReader::
-get_file() {
+get_vfile() {
   nassertr(_source != NULL, NULL);
-  return _source->get_file();
+  return _source->get_vfile();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 51 - 8
panda/src/putil/bamReader.cxx

@@ -39,8 +39,8 @@ const int BamReader::_cur_minor = _bam_minor_ver;
 //  Description:
 ////////////////////////////////////////////////////////////////////
 BamReader::
-BamReader(DatagramGenerator *source, const Filename &name)
-  : _source(source), _filename(name)
+BamReader(DatagramGenerator *source)
+  : _source(source)
 {
   _needs_init = true;
   _num_extra_objects = 0;
@@ -599,7 +599,7 @@ read_handle(DatagramIterator &scan) {
 
     type = TypeRegistry::ptr()->register_dynamic_type(name);
     bam_cat.warning()
-      << "Bam file '" << _filename << "' contains objects of unknown type: " 
+      << "Bam file '" << get_filename() << "' contains objects of unknown type: " 
       << type << "\n";
     new_type = true;
     _new_types.insert(type);
@@ -653,7 +653,7 @@ read_handle(DatagramIterator &scan) {
 //               complete_pointers() callback function will be called
 //               with an array of actual pointers, one for each time
 //               read_pointer() was called.  It is then the calling
-//               object's responsibilty to store these pointers in the
+//               object's responsibility to store these pointers in the
 //               object properly.
 ////////////////////////////////////////////////////////////////////
 void BamReader::
@@ -718,6 +718,30 @@ skip_pointer(DatagramIterator &scan) {
   read_object_id(scan);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamReader::read_file_data
+//       Access: Public
+//  Description: Reads a block of auxiliary file data from the Bam
+//               file.  This can be a block of arbitrary size, and it
+//               is assumed it may be quite large.  Rather than
+//               reading the entire block into memory, a file
+//               reference is returned to locate the block on disk.
+//               The data must have been written by a matching call to
+//               write_file_data().
+////////////////////////////////////////////////////////////////////
+void BamReader::
+read_file_data(SubfileInfo &info) {
+  // write_file_data() actually writes the blocks in datagrams prior
+  // to this particular datagram.  Assume we get the calls to
+  // read_file_data() in the same order as the corresponding calls to
+  // write_file_data(), and just pop the first one off the
+  // queue. There's no actual data written to the stream at this
+  // point.
+  nassertv(!_file_data_records.empty());
+  info = _file_data_records.front();
+  _file_data_records.pop_front();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamReader::read_cdata
 //       Access: Public
@@ -962,7 +986,9 @@ register_change_this(ChangeThisRefFunc func, TypedWritableReferenceCount *object
 ////////////////////////////////////////////////////////////////////
 void BamReader::
 finalize_now(TypedWritable *whom) {
-  nassertv(whom != (TypedWritable *)NULL);
+  if (whom == (TypedWritable *)NULL) {
+    return;
+  }
 
   Finalize::iterator fi = _finalize_list.find(whom);
   if (fi != _finalize_list.end()) {
@@ -1136,10 +1162,10 @@ read_pta_id(DatagramIterator &scan) {
 ////////////////////////////////////////////////////////////////////
 int BamReader::
 p_read_object() {
-  Datagram packet;
+  Datagram dg;
 
   // First, read a datagram for the object.
-  if (!get_datagram(packet)) {
+  if (!get_datagram(dg)) {
     // When we run out of datagrams, we're at the end of the file.
     if (bam_cat.is_debug()) {
       bam_cat.debug()
@@ -1149,7 +1175,7 @@ p_read_object() {
   }
 
   // Now extract the object definition from the datagram.
-  DatagramIterator scan(packet);
+  DatagramIterator scan(dg);
 
   // First, read the BamObjectCode.  In bam versions prior to 6.21,
   // there was no BamObjectCode in the stream.
@@ -1179,6 +1205,23 @@ p_read_object() {
     // next object id in the stream.  It's easiest to do this by
     // calling recursively.
     return p_read_object();
+
+  case BOC_file_data:
+    // Another special case.  This marks an auxiliary file data record
+    // that we skip over for now, but we note its position within the
+    // stream, so that we can hand it to a future object who may
+    // request it.
+    {
+      SubfileInfo info;
+      if (!_source->save_datagram(info)) {
+	bam_cat.error()
+	  << "Failed to read file data.\n";
+	return 0;
+      }
+      _file_data_records.push_back(info);
+    }
+
+    return p_read_object();
   }
 
   // An object definition in a Bam file consists of a TypeHandle

+ 14 - 5
panda/src/putil/bamReader.h

@@ -25,11 +25,13 @@
 #include "datagramIterator.h"
 #include "bamReaderParam.h"
 #include "bamEnums.h"
+#include "subfileInfo.h"
 #include "loaderOptions.h"
 #include "factory.h"
 #include "vector_int.h"
 #include "pset.h"
 #include "pmap.h"
+#include "pdeque.h"
 #include "dcast.h"
 #include "pipelineCyclerBase.h"
 #include "referenceCount.h"
@@ -125,10 +127,11 @@ public:
 
 PUBLISHED:
   // The primary interface for a caller.
-  BamReader(DatagramGenerator *source = NULL, const Filename &name = "");
+  BamReader(DatagramGenerator *source = NULL);
   ~BamReader();
 
   void set_source(DatagramGenerator *source);
+  INLINE DatagramGenerator *get_source();
 
   bool init();
 
@@ -163,6 +166,8 @@ public:
   void read_pointers(DatagramIterator &scan, int count);
   void skip_pointer(DatagramIterator &scan);
 
+  void read_file_data(SubfileInfo &info);
+
   void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler);
   void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler,
                   void *extra_data);
@@ -187,7 +192,8 @@ public:
 
   TypeHandle read_handle(DatagramIterator &scan);
 
-  INLINE VirtualFile *get_file();
+  INLINE const FileReference *get_file();
+  INLINE VirtualFile *get_vfile();
   INLINE streampos get_file_pos();
 
 public:
@@ -233,9 +239,6 @@ private:
   typedef phash_map<int, TypeHandle, int_hash> IndexMap;
   IndexMap _index_map;
 
-  // This is the filename of the BAM, or empty string if not in a file.
-  Filename _filename;
-
   LoaderOptions _loader_options;
 
   // This maps the object ID numbers encountered within the Bam file
@@ -307,6 +310,12 @@ private:
   PTAMap _pta_map;
   int _pta_id;
 
+  // This is a queue of the currently-pending file data blocks that we
+  // have recently encountered in the stream and still expect a
+  // subsequent object to request.
+  typedef pdeque<SubfileInfo> FileDataRecords;
+  FileDataRecords _file_data_records;
+
   // This is used internally to record all of the new types created
   // on-the-fly to satisfy bam requirements.  We keep track of this
   // just so we can suppress warning messages from attempts to create

+ 16 - 1
panda/src/putil/bamWriter.I

@@ -13,6 +13,17 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamWriter::get_target
+//       Access: Published
+//  Description: Returns the current target of the BamWriter as set by
+//               set_target() or the constructor.
+////////////////////////////////////////////////////////////////////
+INLINE DatagramSink *BamWriter::
+get_target() {
+  return _target;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamWriter::get_filename
 //       Access: Published
@@ -23,7 +34,11 @@
 ////////////////////////////////////////////////////////////////////
 INLINE const Filename &BamWriter::
 get_filename() const {
-  return _filename;
+  if (_target != (DatagramSink *)NULL) {
+    return _target->get_filename();
+  }
+  static const Filename empty_filename;
+  return empty_filename;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 69 - 2
panda/src/putil/bamWriter.cxx

@@ -30,8 +30,7 @@
 //  Description:
 ////////////////////////////////////////////////////////////////////
 BamWriter::
-BamWriter(DatagramSink *target, const Filename &name) :
-  _filename(name),
+BamWriter(DatagramSink *target) :
   _target(target)
 {
   ++_writing_seq;
@@ -320,6 +319,74 @@ write_pointer(Datagram &packet, const TypedWritable *object) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamWriter::write_file_data
+//       Access: Public
+//  Description: Writes a block of auxiliary file data from the
+//               indicated file (within the vfs).  This can be a block
+//               of arbitrary size, and it is assumed it may be quite
+//               large.  This must be balanced by a matching call to
+//               read_file_data() on restore.
+////////////////////////////////////////////////////////////////////
+void BamWriter::
+write_file_data(SubfileInfo &result, const Filename &filename) {
+  // We write file data by preceding with a singleton datagram that
+  // contains only the BOC_file_data token.
+  Datagram dg;
+  dg.add_uint8(BOC_file_data);
+  if (!_target->put_datagram(dg)) {
+    util_cat.error()
+      << "Unable to write data to output.\n";
+    return;
+  }
+
+  // Then we can write the file data itself, as its own (possibly
+  // quite large) followup datagram.
+  if (!_target->copy_datagram(result, filename)) {
+    util_cat.error()
+      << "Unable to write file data to output.\n";
+    return;
+  }
+
+  // Both of those get written to the bam stream prior to the datagram
+  // that represents this particular object, but they'll get pulled
+  // out in the same order and queued up in the BamReader.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamWriter::write_file_data
+//       Access: Public
+//  Description: Writes a block of auxiliary file data from the
+//               indicated file (outside of the vfs).  This can be a
+//               block of arbitrary size, and it is assumed it may be
+//               quite large.  This must be balanced by a matching
+//               call to read_file_data() on restore.
+////////////////////////////////////////////////////////////////////
+void BamWriter::
+write_file_data(SubfileInfo &result, const SubfileInfo &source) {
+  // We write file data by preceding with a singleton datagram that
+  // contains only the BOC_file_data token.
+  Datagram dg;
+  dg.add_uint8(BOC_file_data);
+  if (!_target->put_datagram(dg)) {
+    util_cat.error()
+      << "Unable to write data to output.\n";
+    return;
+  }
+
+  // Then we can write the file data itself, as its own (possibly
+  // quite large) followup datagram.
+  if (!_target->copy_datagram(result, source)) {
+    util_cat.error()
+      << "Unable to write file data to output.\n";
+    return;
+  }
+
+  // Both of those get written to the bam stream prior to the datagram
+  // that represents this particular object, but they'll get pulled
+  // out in the same order and queued up in the BamReader.
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamWriter::write_cdata
 //       Access: Public

+ 6 - 4
panda/src/putil/bamWriter.h

@@ -72,10 +72,11 @@
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_PUTIL BamWriter : public BamEnums {
 PUBLISHED:
-  BamWriter(DatagramSink *target = NULL, const Filename &name = "");
+  BamWriter(DatagramSink *target = NULL);
   ~BamWriter();
 
   void set_target(DatagramSink *target);
+  INLINE DatagramSink *get_target();
 
   bool init();
   INLINE const Filename &get_filename() const;
@@ -94,6 +95,10 @@ public:
   void consider_update(const TypedWritable *obj);
 
   void write_pointer(Datagram &packet, const TypedWritable *dest);
+
+  void write_file_data(SubfileInfo &result, const Filename &filename);
+  void write_file_data(SubfileInfo &result, const SubfileInfo &source);
+
   void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler);
   void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler,
                    void *extra_data);
@@ -108,9 +113,6 @@ private:
   int enqueue_object(const TypedWritable *object);
   bool flush_queue();
 
-  // This is the filename of the BAM, or null string if not in a file.
-  Filename _filename;
-
   BamEndian _file_endian;
   BamTextureMode _file_texture_mode;
 

+ 24 - 1
panda/src/putil/datagramInputFile.I

@@ -15,7 +15,7 @@
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::Constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE DatagramInputFile::
@@ -25,3 +25,26 @@ DatagramInputFile() {
   _in = (istream *)NULL;
   _owns_in = false;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramInputFile::open
+//       Access: Published
+//  Description: Opens the indicated filename for reading.  Returns
+//               true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+INLINE bool DatagramInputFile::
+open(const Filename &filename) {
+  return open(new FileReference(filename));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramInputFile::get_stream
+//       Access: Published
+//  Description: Returns the istream represented by the input file.
+////////////////////////////////////////////////////////////////////
+INLINE istream &DatagramInputFile::
+get_stream() {
+  static ifstream null_stream;
+  nassertr(_in != NULL, null_stream);
+  return *_in;
+}

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

@@ -13,6 +13,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "datagramInputFile.h"
+#include "temporaryFile.h"
 #include "numeric_types.h"
 #include "datagramIterator.h"
 #include "profileTimer.h"
@@ -24,19 +25,22 @@
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::open
-//       Access: Public
+//       Access: Published
 //  Description: Opens the indicated filename for reading.  Returns
 //               true on success, false on failure.
 ////////////////////////////////////////////////////////////////////
 bool DatagramInputFile::
-open(Filename filename) {
+open(const FileReference *file) {
   close();
 
+  _file = file;
+  _filename = _file->get_filename();
+
   // DatagramInputFiles are always binary.
-  filename.set_binary();
+  _filename.set_binary();
 
   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  _vfile = vfs->get_file(filename);
+  _vfile = vfs->get_file(_filename);
   if (_vfile == (VirtualFile *)NULL) {
     // No such file.
     return false;
@@ -48,7 +52,7 @@ open(Filename filename) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::open
-//       Access: Public
+//       Access: Published
 //  Description: Starts reading from the indicated stream.  Returns
 //               true on success, false on failure.  The
 //               DatagramInputFile does not take ownership of the
@@ -56,18 +60,23 @@ open(Filename filename) {
 //               it when you are done.
 ////////////////////////////////////////////////////////////////////
 bool DatagramInputFile::
-open(istream &in) {
+open(istream &in, const Filename &filename) {
   close();
 
   _in = &in;
   _owns_in = false;
+  _filename = filename;
+
+  if (!filename.empty()) {
+    _file = new FileReference(filename);
+  }
 
   return !_in->fail();
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::close
-//       Access: Public
+//       Access: Published
 //  Description: Closes the file.  This is also implicitly done when
 //               the DatagramInputFile destructs.
 ////////////////////////////////////////////////////////////////////
@@ -82,13 +91,16 @@ close() {
   _in = (istream *)NULL;
   _owns_in = false;
 
+  _file.clear();
+  _filename = Filename();
+
   _read_first_datagram = false;
   _error = false;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::read_header
-//       Access: Public
+//       Access: Published
 //  Description: Reads a sequence of bytes from the beginning of the
 //               datagram file.  This may be called any number of
 //               times after the file has been opened and before the
@@ -115,7 +127,7 @@ read_header(string &header, size_t num_bytes) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::get_datagram
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Reads the next datagram from the file.  Returns true
 //               on success, false if there is an error or end of
 //               file.
@@ -127,7 +139,7 @@ get_datagram(Datagram &data) {
 
   // First, get the size of the upcoming datagram.
   StreamReader reader(_in, false);
-  PN_uint32 num_bytes = reader.get_uint32();
+  size_t num_bytes = reader.get_uint32();
   if (_in->fail() || _in->eof()) {
     return false;
   }
@@ -175,9 +187,90 @@ get_datagram(Datagram &data) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramInputFile::save_datagram
+//       Access: Published, Virtual
+//  Description: Skips over the next datagram without extracting it,
+//               but saves the relevant file information in the
+//               SubfileInfo object so that its data may be read
+//               later.  For non-file-based datagram generators, this
+//               may mean creating a temporary file and copying the
+//               contents of the datagram to disk.
+//
+//               Returns true on success, false on failure or if this
+//               method is unimplemented.
+////////////////////////////////////////////////////////////////////
+bool DatagramInputFile::
+save_datagram(SubfileInfo &info) {
+  nassertr(_in != (istream *)NULL, false);
+  _read_first_datagram = true;
+
+  // First, get the size of the upcoming datagram.
+  StreamReader reader(_in, false);
+  size_t num_bytes = reader.get_uint32();
+  if (_in->fail() || _in->eof()) {
+    return false;
+  }
+
+  // If this stream is file-based, we can just point the SubfileInfo
+  // directly into this file.
+  if (_file != (FileReference *)NULL) {
+    info = SubfileInfo(_file, _in->tellg(), num_bytes);
+    _in->seekg(num_bytes, ios::cur);
+    return true;
+  }
+
+  // Otherwise, we have to dump the data into a temporary file.
+  PT(TemporaryFile) tfile = new TemporaryFile(Filename::temporary("", ""));
+  pofstream out;
+  Filename filename = tfile->get_filename();
+  filename.set_binary();
+  if (!filename.open_write(out)) {
+    util_cat.error()
+      << "Couldn't write to " << tfile->get_filename() << "\n";
+    return false;
+  }
+
+  if (util_cat.is_debug()) {
+    util_cat.debug()
+      << "Copying " << num_bytes << " bytes to " << tfile->get_filename() << "\n";
+  }
+
+  size_t num_remaining = num_bytes;
+  static const size_t buffer_size = 4096;
+  char buffer[buffer_size];
+  
+  _in->read(buffer, min(buffer_size, num_remaining));
+  size_t count = _in->gcount();
+  while (count != 0) {
+    out.write(buffer, count);
+    if (out.fail()) {
+      util_cat.error()
+	<< "Couldn't write " << num_bytes << " bytes to " 
+	<< tfile->get_filename() << "\n";
+      return false;
+    }
+    num_remaining -= count;
+    if (num_remaining == 0) {
+      break;
+    }
+    _in->read(buffer, min(buffer_size, num_remaining));
+    count = _in->gcount();
+  }
+
+  if (num_remaining != 0) {
+    util_cat.error()
+      << "Truncated data stream.\n";
+    return false;
+  }
+
+  info = SubfileInfo(tfile, 0, num_bytes);
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::is_eof
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Returns true if the file has reached the end-of-file.
 //               This test may only be made after a call to
 //               read_header() or get_datagram() has failed.
@@ -189,7 +282,7 @@ is_eof() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::is_error
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Returns true if the file has reached an error
 //               condition.
 ////////////////////////////////////////////////////////////////////
@@ -205,21 +298,45 @@ is_error() {
   return _error;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramInputFile::get_filename
+//       Access: Published, Virtual
+//  Description: Returns the filename that provides the source for
+//               these datagrams, if any, or empty string if the
+//               datagrams do not originate from a file on disk.
+////////////////////////////////////////////////////////////////////
+const Filename &DatagramInputFile::
+get_filename() {
+  return _filename;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::get_file
-//       Access: Public, Virtual
+//       Access: Published, Virtual
+//  Description: Returns the FileReference that provides the source for
+//               these datagrams, if any, or NULL if the datagrams do
+//               not originate from a file on disk.
+////////////////////////////////////////////////////////////////////
+const FileReference *DatagramInputFile::
+get_file() {
+  return _file;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramInputFile::get_vfile
+//       Access: Published, Virtual
 //  Description: Returns the VirtualFile that provides the source for
 //               these datagrams, if any, or NULL if the datagrams do
 //               not originate from a VirtualFile.
 ////////////////////////////////////////////////////////////////////
 VirtualFile *DatagramInputFile::
-get_file() {
+get_vfile() {
   return _vfile;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::get_file_pos
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Returns the current file position within the data
 //               stream, if any, or 0 if the file position is not
 //               meaningful or cannot be determined.

+ 12 - 4
panda/src/putil/datagramInputFile.h

@@ -19,6 +19,7 @@
 
 #include "datagramGenerator.h"
 #include "filename.h"
+#include "fileReference.h"
 #include "virtualFile.h"
 
 ////////////////////////////////////////////////////////////////////
@@ -28,29 +29,36 @@
 //               of datagrams.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_PUTIL DatagramInputFile : public DatagramGenerator {
-public:
+PUBLISHED:
   INLINE DatagramInputFile();
 
-  bool open(Filename filename);
-  bool open(istream &in);
+  bool open(const FileReference *file);
+  INLINE bool open(const Filename &filename);
+  bool open(istream &in, const Filename &filename = Filename());
+  INLINE istream &get_stream();
 
   void close();
 
   bool read_header(string &header, size_t num_bytes);
   virtual bool get_datagram(Datagram &data);
+  virtual bool save_datagram(SubfileInfo &info);
   virtual bool is_eof();
   virtual bool is_error();
 
-  virtual VirtualFile *get_file();
+  virtual const Filename &get_filename();
+  virtual const FileReference *get_file();
+  virtual VirtualFile *get_vfile();
   virtual streampos get_file_pos();
 
 private:
   bool _read_first_datagram;
   bool _error;
+  CPT(FileReference) _file;
   PT(VirtualFile) _vfile;
   pifstream _in_file;
   istream *_in;
   bool _owns_in;
+  Filename _filename;
 };
 
 #include "datagramInputFile.I"

+ 23 - 0
panda/src/putil/datagramOutputFile.I

@@ -25,3 +25,26 @@ DatagramOutputFile() {
   _out = (ostream *)NULL;
   _owns_out = false;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramOutputFile::open
+//       Access: Published
+//  Description: Opens the indicated filename for writing.  Returns
+//               true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+INLINE bool DatagramOutputFile::
+open(const Filename &filename) {
+  return open(new FileReference(filename));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramOutputFile::get_stream
+//       Access: Published
+//  Description: Returns the ostream represented by the output file.
+////////////////////////////////////////////////////////////////////
+INLINE ostream &DatagramOutputFile::
+get_stream() {
+  static ofstream null_stream;
+  nassertr(_out != NULL, null_stream);
+  return *_out;
+}

+ 186 - 6
panda/src/putil/datagramOutputFile.cxx

@@ -19,21 +19,24 @@
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramOutputFile::open
 //       Access: Public
-//  Description: Opens the indicated filename for reading.  Returns
+//  Description: Opens the indicated filename for writing.  Returns
 //               true if successful, false on failure.
 ////////////////////////////////////////////////////////////////////
 bool DatagramOutputFile::
-open(Filename filename) {
+open(const FileReference *file) {
   close();
 
+  _file = file;
+  _filename = _file->get_filename();
+
   // DatagramOutputFiles are always binary.
-  filename.set_binary();
+  _filename.set_binary();
 
   _out = &_out_file;
   _owns_out = false;
 
 #ifdef HAVE_ZLIB
-  if (filename.get_extension() == "pz") {
+  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);
@@ -41,7 +44,7 @@ open(Filename filename) {
   }
 #endif  // HAVE_ZLIB
 
-  return filename.open_write(_out_file);
+  return _filename.open_write(_out_file);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -54,11 +57,16 @@ open(Filename filename) {
 //               it when you are done.
 ////////////////////////////////////////////////////////////////////
 bool DatagramOutputFile::
-open(ostream &out) {
+open(ostream &out, const Filename &filename) {
   close();
 
   _out = &out;
   _owns_out = false;
+  _filename = filename;
+
+  if (!filename.empty()) {
+    _file = new FileReference(filename);
+  }
 
   return !_out->fail();
 }
@@ -78,6 +86,9 @@ close() {
   _out = (ostream *)NULL;
   _owns_out = false;
 
+  _file.clear();
+  _filename = Filename();
+
   _wrote_first_datagram = false;
   _error = false;
 }
@@ -123,6 +134,130 @@ put_datagram(const Datagram &data) {
   return !_out->fail();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramOutputFile::copy_datagram
+//       Access: Published, Virtual
+//  Description: Copies the file data from the entire indicated
+//               file (via the vfs) as the next datagram.  This is
+//               intended to support potentially very large datagrams.
+//
+//               Returns true on success, false on failure or if this
+//               method is unimplemented.  On true, fills "result"
+//               with the information that references the copied file,
+//               if possible.
+////////////////////////////////////////////////////////////////////
+bool DatagramOutputFile::
+copy_datagram(SubfileInfo &result, const Filename &filename) {
+  nassertr(_out != (ostream *)NULL, false);
+  _wrote_first_datagram = true;
+
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  PT(VirtualFile) vfile = vfs->get_file(filename);
+  if (vfile == NULL) {
+    return false;
+  }
+  istream *in = vfile->open_read_file(true);
+  if (in == NULL) {
+    return false;
+  }
+
+  off_t size = vfile->get_file_size(in);
+  size_t num_remaining = (size_t)size;
+  nassertr(num_remaining == size, false);
+
+  StreamWriter writer(_out, false);
+  writer.add_uint32(num_remaining);
+
+  static const size_t buffer_size = 4096;
+  char buffer[buffer_size];
+
+  streampos start = _out->tellp();
+  in->read(buffer, min(buffer_size, num_remaining));
+  size_t count = in->gcount();
+  while (count != 0) {
+    _out->write(buffer, count);
+    if (_out->fail()) {
+      vfile->close_read_file(in);
+      return false;
+    }
+    num_remaining -= count;
+    if (num_remaining == 0) {
+      break;
+    }
+    in->read(buffer, min(buffer_size, num_remaining));
+    count = in->gcount();
+  }
+
+  vfile->close_read_file(in);
+
+  if (num_remaining != 0) {
+    util_cat.error()
+      << "Truncated input stream.\n";
+    return false;
+  }
+  
+  result = SubfileInfo(_file, start, size);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramOutputFile::copy_datagram
+//       Access: Published, Virtual
+//  Description: Copies the file data from the range of the indicated
+//               file (outside of the vfs) as the next datagram.  This
+//               is intended to support potentially very large
+//               datagrams.
+//
+//               Returns true on success, false on failure or if this
+//               method is unimplemented.  On true, fills "result"
+//               with the information that references the copied file,
+//               if possible.
+////////////////////////////////////////////////////////////////////
+bool DatagramOutputFile::
+copy_datagram(SubfileInfo &result, const SubfileInfo &source) {
+  nassertr(_out != (ostream *)NULL, false);
+  _wrote_first_datagram = true;
+
+  pifstream in;
+  if (!source.get_filename().open_read(in)) {
+    return false;
+  }
+
+  size_t num_remaining = source.get_size();
+
+  StreamWriter writer(_out, false);
+  writer.add_uint32(num_remaining);
+
+  static const size_t buffer_size = 4096;
+  char buffer[buffer_size];
+  
+  streampos start = _out->tellp();
+  in.seekg(source.get_start());
+  in.read(buffer, min(buffer_size, num_remaining));
+  size_t count = in.gcount();
+  while (count != 0) {
+    _out->write(buffer, count);
+    if (_out->fail()) {
+      return false;
+    }
+    num_remaining -= count;
+    if (num_remaining == 0) {
+      break;
+    }
+    in.read(buffer, min(buffer_size, num_remaining));
+    count = in.gcount();
+  }
+
+  if (num_remaining != 0) {
+    util_cat.error()
+      << "Truncated input stream.\n";
+    return false;
+  }
+
+  result = SubfileInfo(_file, start, source.get_size());
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramOutputFile::is_error
 //       Access: Public, Virtual
@@ -153,3 +288,48 @@ flush() {
     _out->flush();
   }
 }
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramOutputFile::get_filename
+//       Access: Published, Virtual
+//  Description: Returns the filename that provides the target for
+//               these datagrams, if any, or empty string if the
+//               datagrams do not get written to a file on disk.
+////////////////////////////////////////////////////////////////////
+const Filename &DatagramOutputFile::
+get_filename() {
+  return _filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramOutputFile::get_file
+//       Access: Published, Virtual
+//  Description: Returns the FileReference that provides the target for
+//               these datagrams, if any, or NULL if the datagrams do
+//               not written to a file on disk.
+////////////////////////////////////////////////////////////////////
+const FileReference *DatagramOutputFile::
+get_file() {
+  return _file;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DatagramOutputFile::get_file_pos
+//       Access: Published, Virtual
+//  Description: Returns the current file position within the data
+//               stream, if any, or 0 if the file position is not
+//               meaningful or cannot be determined.
+//
+//               For DatagramOutputFiles that return a meaningful file
+//               position, this will be pointing to the first byte
+//               following the datagram returned after a call to
+//               put_datagram().
+////////////////////////////////////////////////////////////////////
+streampos DatagramOutputFile::
+get_file_pos() {
+  if (_out == (ostream *)NULL) {
+    return 0;
+  }
+  return _out->tellp();
+}

+ 13 - 2
panda/src/putil/datagramOutputFile.h

@@ -19,6 +19,7 @@
 
 #include "datagramSink.h"
 #include "filename.h"
+#include "fileReference.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : DatagramOutputFile
@@ -30,22 +31,32 @@ class EXPCL_PANDA_PUTIL DatagramOutputFile : public DatagramSink {
 public:
   INLINE DatagramOutputFile();
 
-  bool open(Filename filename);
-  bool open(ostream &out);
+  bool open(const FileReference *file);
+  INLINE bool open(const Filename &filename);
+  bool open(ostream &out, const Filename &filename = Filename());
+  INLINE ostream &get_stream();
 
   void close();
 
   bool write_header(const string &header);
   virtual bool put_datagram(const Datagram &data);
+  virtual bool copy_datagram(SubfileInfo &result, const Filename &filename);
+  virtual bool copy_datagram(SubfileInfo &result, const SubfileInfo &source);
   virtual bool is_error();
   virtual void flush();
 
+  virtual const Filename &get_filename();
+  virtual const FileReference *get_file();
+  virtual streampos get_file_pos();
+
 private:
   bool _wrote_first_datagram;
   bool _error;
+  CPT(FileReference) _file;
   pofstream _out_file;
   ostream *_out;
   bool _owns_out;
+  Filename _filename;
 };
 
 #include "datagramOutputFile.I"

+ 2 - 2
panda/src/putil/typedWritable.cxx

@@ -299,7 +299,7 @@ encode_to_bam_stream(string &data, BamWriter *writer) const {
         return false;
       }
 
-      BamWriter writer(&dout, "bam_stream");
+      BamWriter writer(&dout);
       if (!writer.init()) {
         return false;
       }
@@ -371,7 +371,7 @@ decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr,
       return false;
     }
 
-    BamReader reader(&din, "bam_stream");
+    BamReader reader(&din);
     if (!reader.init()) {
       return false;
     }