From e986614cfc670ffc2a19c78397818caf0a063658 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 16 Sep 2011 20:13:08 +0000 Subject: [PATCH] writable vfs --- dtool/src/dtoolutil/filename.cxx | 35 +++++ dtool/src/dtoolutil/filename.h | 1 + panda/src/downloader/virtualFileHTTP.cxx | 4 +- panda/src/downloader/virtualFileHTTP.h | 2 +- panda/src/downloader/virtualFileMountHTTP.cxx | 4 +- panda/src/downloader/virtualFileMountHTTP.h | 2 +- panda/src/express/virtualFile.I | 11 ++ panda/src/express/virtualFile.cxx | 133 +++++++++++----- panda/src/express/virtualFile.h | 11 +- panda/src/express/virtualFileMount.cxx | 142 ++++++++++++++++-- panda/src/express/virtualFileMount.h | 14 +- panda/src/express/virtualFileMountSystem.cxx | 85 +++++++++++ panda/src/express/virtualFileMountSystem.h | 4 + panda/src/express/virtualFileSimple.I | 5 +- panda/src/express/virtualFileSimple.cxx | 87 ++++++++++- panda/src/express/virtualFileSimple.h | 9 +- panda/src/express/virtualFileSystem.I | 29 ++++ panda/src/express/virtualFileSystem.cxx | 137 +++++++++++++++-- panda/src/express/virtualFileSystem.h | 25 ++- 19 files changed, 655 insertions(+), 85 deletions(-) diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx index 4d2aef89f5..c35803ba8d 100644 --- a/dtool/src/dtoolutil/filename.cxx +++ b/dtool/src/dtoolutil/filename.cxx @@ -1412,6 +1412,41 @@ is_regular_file() const { return isreg; } +//////////////////////////////////////////////////////////////////// +// Function: Filename::is_writable +// Access: Published +// Description: Returns true if the filename exists and is either a +// directory or a regular file that can be written to, +// or false otherwise. +//////////////////////////////////////////////////////////////////// +bool Filename:: +is_writable() const { + bool writable = false; + +#ifdef WIN32_VC + wstring os_specific = get_filename_index(0).to_os_specific_w(); + + DWORD results = GetFileAttributesW(os_specific.c_str()); + if (results != -1) { + if ((results & FILE_ATTRIBUTE_DIRECTORY) != 0) { + // Assume directories are writable. + writable = true; + } else if ((results & FILE_ATTRIBUTE_READONLY) == 0) { + // Not read-only means writable. + writable = true; + } + } +#else // WIN32_VC + string os_specific = get_filename_index(0).to_os_specific(); + + if (access(os_specific.c_str(), W_OK) == 0) { + writable = true; + } +#endif + + return writable; +} + //////////////////////////////////////////////////////////////////// // Function: Filename::is_directory // Access: Published diff --git a/dtool/src/dtoolutil/filename.h b/dtool/src/dtoolutil/filename.h index 4a79262352..d449e0645f 100644 --- a/dtool/src/dtoolutil/filename.h +++ b/dtool/src/dtoolutil/filename.h @@ -174,6 +174,7 @@ PUBLISHED: bool exists() const; bool is_regular_file() const; + bool is_writable() const; bool is_directory() const; bool is_executable() const; int compare_timestamps(const Filename &other, diff --git a/panda/src/downloader/virtualFileHTTP.cxx b/panda/src/downloader/virtualFileHTTP.cxx index eab9608d2a..82a707b978 100644 --- a/panda/src/downloader/virtualFileHTTP.cxx +++ b/panda/src/downloader/virtualFileHTTP.cxx @@ -29,11 +29,11 @@ TypeHandle VirtualFileHTTP::_type_handle; //////////////////////////////////////////////////////////////////// VirtualFileHTTP:: VirtualFileHTTP(VirtualFileMountHTTP *mount, const Filename &local_filename, - bool implicit_pz_file, bool status_only) : + bool implicit_pz_file, int open_flags) : _mount(mount), _local_filename(local_filename), _implicit_pz_file(implicit_pz_file), - _status_only(status_only) + _status_only(open_flags != 0) { URLSpec url(_mount->get_root()); url.set_path(_mount->get_root().get_path() + _local_filename.c_str()); diff --git a/panda/src/downloader/virtualFileHTTP.h b/panda/src/downloader/virtualFileHTTP.h index e263f996ce..128c6fbdeb 100644 --- a/panda/src/downloader/virtualFileHTTP.h +++ b/panda/src/downloader/virtualFileHTTP.h @@ -36,7 +36,7 @@ public: VirtualFileHTTP(VirtualFileMountHTTP *mount, const Filename &local_filename, bool implicit_pz_file, - bool status_only); + int open_flags); virtual ~VirtualFileHTTP(); virtual VirtualFileSystem *get_file_system() const; diff --git a/panda/src/downloader/virtualFileMountHTTP.cxx b/panda/src/downloader/virtualFileMountHTTP.cxx index bf17e0f14f..5c61b6e31d 100644 --- a/panda/src/downloader/virtualFileMountHTTP.cxx +++ b/panda/src/downloader/virtualFileMountHTTP.cxx @@ -182,9 +182,9 @@ is_regular_file(const Filename &) const { PT(VirtualFile) VirtualFileMountHTTP:: make_virtual_file(const Filename &local_filename, const Filename &original_filename, bool implicit_pz_file, - bool status_only) { + int open_flags) { PT(VirtualFileHTTP) vfile = - new VirtualFileHTTP(this, local_filename, implicit_pz_file, status_only); + new VirtualFileHTTP(this, local_filename, implicit_pz_file, open_flags); vfile->set_original_filename(original_filename); return vfile.p(); diff --git a/panda/src/downloader/virtualFileMountHTTP.h b/panda/src/downloader/virtualFileMountHTTP.h index 5b0e84da70..2fab8727da 100644 --- a/panda/src/downloader/virtualFileMountHTTP.h +++ b/panda/src/downloader/virtualFileMountHTTP.h @@ -45,7 +45,7 @@ public: virtual PT(VirtualFile) make_virtual_file(const Filename &local_filename, const Filename &original_filename, bool implicit_pz_file, - bool status_only); + int open_flags); virtual bool has_file(const Filename &file) const; virtual bool is_directory(const Filename &file) const; diff --git a/panda/src/express/virtualFile.I b/panda/src/express/virtualFile.I index d5bd741d98..8602a90bdf 100644 --- a/panda/src/express/virtualFile.I +++ b/panda/src/express/virtualFile.I @@ -46,6 +46,17 @@ read_file(bool auto_unwrap) const { return result; } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFile::write_file +// Access: Public +// Description: Writes the entire contents of the file as a string, +// if it is writable. +//////////////////////////////////////////////////////////////////// +INLINE bool VirtualFile:: +write_file(const string &data, bool auto_wrap) { + return write_file((const unsigned char *)data.data(), data.size(), auto_wrap); +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFile::set_original_filename // Access: Public diff --git a/panda/src/express/virtualFile.cxx b/panda/src/express/virtualFile.cxx index c148ef7813..ea3dd2370a 100644 --- a/panda/src/express/virtualFile.cxx +++ b/panda/src/express/virtualFile.cxx @@ -52,6 +52,18 @@ is_regular_file() const { return false; } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFile::is_writable +// Access: Published, Virtual +// Description: Returns true if this file may be written to, which +// implies write_file() may be called (unless it is a +// directory instead of a regular file). +//////////////////////////////////////////////////////////////////// +bool VirtualFile:: +is_writable() const { + return false; +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFile::scan_directory // Access: Published @@ -169,6 +181,76 @@ open_read_file(bool auto_unwrap) const { return NULL; } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFile::close_read_file +// Access: Published +// Description: Closes a file opened by a previous call to +// open_read_file(). This really just deletes the +// istream pointer, but it is recommended to use this +// interface instead of deleting it explicitly, to help +// work around compiler issues. +//////////////////////////////////////////////////////////////////// +void VirtualFile:: +close_read_file(istream *stream) const { + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFile::was_read_successful +// Access: Published, Virtual +// Description: Call this method after a reading the istream returned +// by open_read_file() to completion. If it returns +// true, the file was read completely and without error; +// if it returns false, there may have been some errors +// or a truncated file read. This is particularly +// likely if the stream is a VirtualFileHTTP. +//////////////////////////////////////////////////////////////////// +bool VirtualFile:: +was_read_successful() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFile::open_write_file +// Access: Published, Virtual +// Description: Opens the file for writing. Returns a newly +// allocated ostream on success (which you should +// eventually delete when you are done writing). +// Returns NULL on failure. +//////////////////////////////////////////////////////////////////// +ostream *VirtualFile:: +open_write_file(bool auto_wrap) { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFile::close_write_file +// Access: Published +// Description: Closes a file opened by a previous call to +// open_write_file(). This really just deletes the +// ostream pointer, but it is recommended to use this +// interface instead of deleting it explicitly, to help +// work around compiler issues. +//////////////////////////////////////////////////////////////////// +void VirtualFile:: +close_write_file(ostream *stream) { + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFile::was_write_successful +// Access: Published, Virtual +// Description: Call this method after a writing the ostream returned +// by open_write_file() to completion. If it returns +// true, the file was written completely and without +// error; if it returns false, there may have been some +// errors or a truncated file write. +//////////////////////////////////////////////////////////////////// +bool VirtualFile:: +was_write_successful() const { + return true; +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFile::get_file_size // Access: Published, Virtual @@ -228,46 +310,6 @@ get_system_info(SubfileInfo &info) { return false; } -//////////////////////////////////////////////////////////////////// -// Function: VirtualFile::close_read_file -// Access: Public -// Description: Closes a file opened by a previous call to -// open_read_file(). This really just deletes the -// istream pointer, but it is recommended to use this -// interface instead of deleting it explicitly, to help -// work around compiler issues. -//////////////////////////////////////////////////////////////////// -void VirtualFile:: -close_read_file(istream *stream) const { - if (stream != (istream *)NULL) { - // For some reason--compiler bug in gcc 3.2?--explicitly deleting - // the stream pointer does not call the appropriate global delete - // function; instead apparently calling the system delete - // function. So we call the delete function by hand instead. -#if !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) - stream->~istream(); - (*global_operator_delete)(stream); -#else - delete stream; -#endif - } -} - -//////////////////////////////////////////////////////////////////// -// Function: VirtualFile::was_read_successful -// Access: Public -// Description: Call this method after a reading the istream returned -// by open_read_file() to completion. If it returns -// true, the file was read completely and without error; -// if it returns false, there may have been some errors -// or a truncated file read. This is particularly -// likely if the stream is a VirtualFileHTTP. -//////////////////////////////////////////////////////////////////// -bool VirtualFile:: -was_read_successful() const { - return true; -} - //////////////////////////////////////////////////////////////////// // Function: VirtualFile::read_file // Access: Public @@ -303,6 +345,17 @@ read_file(pvector &result, bool auto_unwrap) const { return false; } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFile::write_file +// Access: Public, Virtual +// Description: Writes the indicated data to the file, if it is +// writable. Returns true on success, false otherwise. +//////////////////////////////////////////////////////////////////// +bool VirtualFile:: +write_file(const unsigned char *data, size_t data_size, bool auto_wrap) { + return false; +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFile::simple_read_file // Access: Public, Static diff --git a/panda/src/express/virtualFile.h b/panda/src/express/virtualFile.h index f697410c6a..98d0ebea4e 100644 --- a/panda/src/express/virtualFile.h +++ b/panda/src/express/virtualFile.h @@ -46,6 +46,7 @@ PUBLISHED: virtual bool has_file() const; virtual bool is_directory() const; virtual bool is_regular_file() const; + virtual bool is_writable() const; BLOCKING PT(VirtualFileList) scan_directory() const; @@ -55,8 +56,14 @@ PUBLISHED: BLOCKING INLINE string read_file(bool auto_unwrap) const; BLOCKING virtual istream *open_read_file(bool auto_unwrap) const; - BLOCKING void close_read_file(istream *stream) const; + BLOCKING virtual void close_read_file(istream *stream) const; virtual bool was_read_successful() const; + + BLOCKING INLINE bool write_file(const string &data, bool auto_wrap); + BLOCKING virtual ostream *open_write_file(bool auto_wrap); + BLOCKING virtual void close_write_file(ostream *stream); + virtual bool was_write_successful() const; + BLOCKING virtual off_t get_file_size(istream *stream) const; BLOCKING virtual off_t get_file_size() const; BLOCKING virtual time_t get_timestamp() const; @@ -67,11 +74,11 @@ public: INLINE void set_original_filename(const Filename &filename); bool read_file(string &result, bool auto_unwrap) const; virtual bool read_file(pvector &result, bool auto_unwrap) const; + virtual bool write_file(const unsigned char *data, size_t data_size, bool auto_wrap); static bool simple_read_file(istream *stream, pvector &result); static bool simple_read_file(istream *stream, pvector &result, size_t max_bytes); - protected: virtual bool scan_local_directory(VirtualFileList *file_list, const ov_set &mount_points) const; diff --git a/panda/src/express/virtualFileMount.cxx b/panda/src/express/virtualFileMount.cxx index 8b0c882f3a..fa7be7874b 100644 --- a/panda/src/express/virtualFileMount.cxx +++ b/panda/src/express/virtualFileMount.cxx @@ -42,18 +42,62 @@ VirtualFileMount:: PT(VirtualFile) VirtualFileMount:: make_virtual_file(const Filename &local_filename, const Filename &original_filename, bool implicit_pz_file, - bool) { + int open_flags) { Filename local(local_filename); if (original_filename.is_text()) { local.set_text(); } PT(VirtualFileSimple) file = - new VirtualFileSimple(this, local, implicit_pz_file); + new VirtualFileSimple(this, local, implicit_pz_file, open_flags); file->set_original_filename(original_filename); + if ((open_flags & VirtualFileSystem::OF_create_file) != 0) { + create_file(local); + } else if ((open_flags & VirtualFileSystem::OF_make_directory) != 0) { + make_directory(local); + } + return file.p(); } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMount::create_file +// Access: Public, Virtual +// Description: Attempts to create the indicated file within the +// mount, if it does not already exist. Returns true on +// success (or if the file already exists), or false if +// it cannot be created. +//////////////////////////////////////////////////////////////////// +bool VirtualFileMount:: +create_file(const Filename &file) { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMount::make_directory +// Access: Public, Virtual +// Description: Attempts to create the indicated file within the +// mount, if it does not already exist. Returns true on +// success, or false if it cannot be created. If the +// directory already existed prior to this call, may +// return either true or false. +//////////////////////////////////////////////////////////////////// +bool VirtualFileMount:: +make_directory(const Filename &file) { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMount::is_writable +// Access: Public, Virtual +// Description: Returns true if the named file or directory may be +// written to, false otherwise. +//////////////////////////////////////////////////////////////////// +bool VirtualFileMount:: +is_writable(const Filename &file) const { + return false; +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileMount::read_file // Access: Public, Virtual @@ -89,6 +133,34 @@ read_file(const Filename &file, bool do_uncompress, return okflag; } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMount::write_file +// Access: Public, Virtual +// Description: Writes the indicated data to the file, if it is a +// writable file. Returns true on success, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool VirtualFileMount:: +write_file(const Filename &file, bool do_compress, + const unsigned char *data, size_t data_size) { + ostream *out = open_write_file(file, do_compress); + if (out == (ostream *)NULL) { + express_cat.info() + << "Unable to write " << file << "\n"; + return false; + } + + out->write((const char *)data, data_size); + bool okflag = (!out->fail()); + close_write_file(out); + + if (!okflag) { + express_cat.info() + << "Error while writing " << file << "\n"; + } + return okflag; +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileMount::open_read_file // Access: Published, Virtual @@ -117,7 +189,7 @@ open_read_file(const Filename &file, bool do_uncompress) const { //////////////////////////////////////////////////////////////////// // Function: VirtualFileMount::close_read_file -// Access: Public +// Access: Public, Virtual // Description: Closes a file opened by a previous call to // open_read_file(). This really just deletes the // istream pointer, but it is recommended to use this @@ -126,18 +198,60 @@ open_read_file(const Filename &file, bool do_uncompress) const { //////////////////////////////////////////////////////////////////// void VirtualFileMount:: close_read_file(istream *stream) const { - if (stream != (istream *)NULL) { - // For some reason--compiler bug in gcc 3.2?--explicitly deleting - // the stream pointer does not call the appropriate global delete - // function; instead apparently calling the system delete - // function. So we call the delete function by hand instead. -#if !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) - stream->~istream(); - (*global_operator_delete)(stream); -#else - delete stream; -#endif + VirtualFileSystem::close_read_file(stream); +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMount::open_write_file +// Access: Published, Virtual +// Description: Opens the file for writing. Returns a newly +// allocated ostream on success (which you should +// eventually delete when you are done writing). +// Returns NULL on failure. +//////////////////////////////////////////////////////////////////// +ostream *VirtualFileMount:: +open_write_file(const Filename &file) { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMount::open_write_file +// Access: Published +// Description: Opens the file for writing. Returns a newly +// allocated ostream on success (which you should +// eventually delete when you are done writing). +// Returns NULL on failure. +// +// If do_compress is true, the file is also +// compressed on-the-fly using zlib. +//////////////////////////////////////////////////////////////////// +ostream *VirtualFileMount:: +open_write_file(const Filename &file, bool do_compress) { + ostream *result = open_write_file(file); + +#ifdef HAVE_ZLIB + if (result != (ostream *)NULL && do_compress) { + // We have to slip in a layer to compress the file on the fly. + OCompressStream *wrapper = new OCompressStream(result, true); + result = wrapper; } +#endif // HAVE_ZLIB + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMount::close_write_file +// Access: Public, Virtual +// Description: Closes a file opened by a previous call to +// open_write_file(). This really just deletes the +// ostream pointer, but it is recommended to use this +// interface instead of deleting it explicitly, to help +// work around compiler issues. +//////////////////////////////////////////////////////////////////// +void VirtualFileMount:: +close_write_file(ostream *stream) { + VirtualFileSystem::close_write_file(stream); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/express/virtualFileMount.h b/panda/src/express/virtualFileMount.h index cd5789303b..f736a31faa 100644 --- a/panda/src/express/virtualFileMount.h +++ b/panda/src/express/virtualFileMount.h @@ -43,18 +43,28 @@ public: virtual PT(VirtualFile) make_virtual_file(const Filename &local_filename, const Filename &original_filename, bool implicit_pz_file, - bool status_only); + int open_flags); virtual bool has_file(const Filename &file) const=0; + virtual bool create_file(const Filename &file); + virtual bool make_directory(const Filename &file); virtual bool is_directory(const Filename &file) const=0; virtual bool is_regular_file(const Filename &file) const=0; + virtual bool is_writable(const Filename &file) const; virtual bool read_file(const Filename &file, bool do_uncompress, pvector &result) const; + virtual bool write_file(const Filename &file, bool do_compress, + const unsigned char *data, size_t data_size); virtual istream *open_read_file(const Filename &file) const=0; istream *open_read_file(const Filename &file, bool do_uncompress) const; - void close_read_file(istream *stream) const; + virtual void close_read_file(istream *stream) const; + + virtual ostream *open_write_file(const Filename &file); + ostream *open_write_file(const Filename &file, bool do_compress); + virtual void close_write_file(ostream *stream); + 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; diff --git a/panda/src/express/virtualFileMountSystem.cxx b/panda/src/express/virtualFileMountSystem.cxx index 5f524feec8..f54e771853 100644 --- a/panda/src/express/virtualFileMountSystem.cxx +++ b/panda/src/express/virtualFileMountSystem.cxx @@ -43,6 +43,37 @@ has_file(const Filename &file) const { return pathname.exists(); } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMountSystem::create_file +// Access: Public, Virtual +// Description: Attempts to create the indicated file within the +// mount, if it does not already exist. Returns true on +// success (or if the file already exists), or false if +// it cannot be created. +//////////////////////////////////////////////////////////////////// +bool VirtualFileMountSystem:: +create_file(const Filename &file) { + Filename pathname(_physical_filename, file); + pathname.set_binary(); + ofstream stream; + return pathname.open_write(stream, false); +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMountSystem::make_directory +// Access: Public, Virtual +// Description: Attempts to create the indicated file within the +// mount, if it does not already exist. Returns true on +// success, or false if it cannot be created. If the +// directory already existed prior to this call, may +// return either true or false. +//////////////////////////////////////////////////////////////////// +bool VirtualFileMountSystem:: +make_directory(const Filename &file) { + Filename pathname(_physical_filename, file); + return pathname.mkdir(); +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileMountSystem::is_directory // Access: Public, Virtual @@ -83,6 +114,26 @@ is_regular_file(const Filename &file) const { return pathname.is_regular_file(); } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMountSystem::is_writable +// Access: Public, Virtual +// Description: Returns true if the named file or directory may be +// written to, false otherwise. +//////////////////////////////////////////////////////////////////// +bool VirtualFileMountSystem:: +is_writable(const Filename &file) const { +#ifdef WIN32 + // First ensure that the file exists to validate its case. + if (VirtualFileSystem::get_global_ptr()->vfs_case_sensitive) { + if (!has_file(file)) { + return false; + } + } +#endif // WIN32 + Filename pathname(_physical_filename, file); + return pathname.is_writable(); +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileMountSystem::open_read_file // Access: Public, Virtual @@ -117,6 +168,40 @@ open_read_file(const Filename &file) const { return stream; } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileMountSystem::open_write_file +// Access: Published, Virtual +// Description: Opens the file for writing. Returns a newly +// allocated ostream on success (which you should +// eventually delete when you are done writing). +// Returns NULL on failure. +//////////////////////////////////////////////////////////////////// +ostream *VirtualFileMountSystem:: +open_write_file(const Filename &file) { +#ifdef WIN32 + // First ensure that the file exists to validate its case. + if (VirtualFileSystem::get_global_ptr()->vfs_case_sensitive) { + if (!has_file(file)) { + return NULL; + } + } +#endif // WIN32 + Filename pathname(_physical_filename, file); + if (file.is_text()) { + pathname.set_text(); + } else { + pathname.set_binary(); + } + pofstream *stream = new pofstream; + if (!pathname.open_write(*stream)) { + // Couldn't open the file for some reason. + close_write_file(stream); + return NULL; + } + + return stream; +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileMountSystem::get_file_size // Access: Published, Virtual diff --git a/panda/src/express/virtualFileMountSystem.h b/panda/src/express/virtualFileMountSystem.h index baf282856e..b3cd485687 100644 --- a/panda/src/express/virtualFileMountSystem.h +++ b/panda/src/express/virtualFileMountSystem.h @@ -32,10 +32,14 @@ PUBLISHED: public: virtual bool has_file(const Filename &file) const; + virtual bool create_file(const Filename &file); + virtual bool make_directory(const Filename &file); virtual bool is_directory(const Filename &file) const; virtual bool is_regular_file(const Filename &file) const; + virtual bool is_writable(const Filename &file) const; virtual istream *open_read_file(const Filename &file) const; + virtual ostream *open_write_file(const Filename &file); 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; diff --git a/panda/src/express/virtualFileSimple.I b/panda/src/express/virtualFileSimple.I index 8679de57f0..2bcef4ead3 100644 --- a/panda/src/express/virtualFileSimple.I +++ b/panda/src/express/virtualFileSimple.I @@ -20,10 +20,11 @@ //////////////////////////////////////////////////////////////////// INLINE VirtualFileSimple:: VirtualFileSimple(VirtualFileMount *mount, const Filename &local_filename, - bool implicit_pz_file) : + bool implicit_pz_file, int open_flags) : _mount(mount), _local_filename(local_filename), - _implicit_pz_file(implicit_pz_file) + _implicit_pz_file(implicit_pz_file), + _open_flags(open_flags) { } diff --git a/panda/src/express/virtualFileSimple.cxx b/panda/src/express/virtualFileSimple.cxx index 64a6ca3ac9..25c1bef946 100644 --- a/panda/src/express/virtualFileSimple.cxx +++ b/panda/src/express/virtualFileSimple.cxx @@ -87,6 +87,18 @@ is_regular_file() const { return _mount->is_regular_file(_local_filename); } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSimple::is_writable +// Access: Published, Virtual +// Description: Returns true if this file represents a writable +// regular file (and write_file() may be called), false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool VirtualFileSimple:: +is_writable() const { + return _mount->is_writable(_local_filename); +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileSimple::open_read_file // Access: Published, Virtual @@ -116,6 +128,59 @@ open_read_file(bool auto_unwrap) const { return _mount->open_read_file(local_filename, do_uncompress); } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSimple::close_read_file +// Access: Published +// Description: Closes a file opened by a previous call to +// open_read_file(). This really just deletes the +// istream pointer, but it is recommended to use this +// interface instead of deleting it explicitly, to help +// work around compiler issues. +//////////////////////////////////////////////////////////////////// +void VirtualFileSimple:: +close_read_file(istream *stream) const { + _mount->close_read_file(stream); +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSimple::open_write_file +// Access: Published, Virtual +// Description: Opens the file for writing. Returns a newly +// allocated ostream on success (which you should +// eventually delete when you are done writing). +// Returns NULL on failure. +// +// If auto_wrap is true, an explicitly-named .pz file +// is automatically compressed. +//////////////////////////////////////////////////////////////////// +ostream *VirtualFileSimple:: +open_write_file(bool auto_wrap) const { + // Will we be automatically wrapping a .pz file? + bool do_compress = (_implicit_pz_file || (auto_wrap && _local_filename.get_extension() == "pz")); + + Filename local_filename(_local_filename); + if (do_compress) { + // .pz files are always binary, of course. + local_filename.set_binary(); + } + + return _mount->open_write_file(local_filename, do_compress); +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSimple::close_write_file +// Access: Published +// Description: Closes a file opened by a previous call to +// open_write_file(). This really just deletes the +// ostream pointer, but it is recommended to use this +// interface instead of deleting it explicitly, to help +// work around compiler issues. +//////////////////////////////////////////////////////////////////// +void VirtualFileSimple:: +close_write_file(ostream *stream) const { + _mount->close_write_file(stream); +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileSimple::get_file_size // Access: Published, Virtual @@ -197,6 +262,26 @@ read_file(pvector &result, bool auto_unwrap) const { return _mount->read_file(local_filename, do_uncompress, result); } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSimple::write_file +// Access: Public, Virtual +// Description: Writes the indicated data to the file, if it is +// writable. Returns true on success, false otherwise. +//////////////////////////////////////////////////////////////////// +bool VirtualFileSimple:: +write_file(const unsigned char *data, size_t data_size, bool auto_wrap) { + // Will we be automatically wrapping a .pz file? + bool do_compress = (_implicit_pz_file || (auto_wrap && _local_filename.get_extension() == "pz")); + + Filename local_filename(_local_filename); + if (do_compress) { + // .pz files are always binary, of course. + local_filename.set_binary(); + } + + return _mount->write_file(local_filename, do_compress, data, data_size); +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileSimple::scan_local_directory // Access: Protected, Virtual @@ -226,7 +311,7 @@ scan_local_directory(VirtualFileList *file_list, const string &basename = (*ni); if (mount_points.find(basename) == mount_points.end()) { Filename filename(_local_filename, basename); - VirtualFileSimple *file = new VirtualFileSimple(_mount, filename, false); + VirtualFileSimple *file = new VirtualFileSimple(_mount, filename, false, 0); file_list->add_file(file); } } diff --git a/panda/src/express/virtualFileSimple.h b/panda/src/express/virtualFileSimple.h index 138238c6ea..4268a8b130 100644 --- a/panda/src/express/virtualFileSimple.h +++ b/panda/src/express/virtualFileSimple.h @@ -30,7 +30,8 @@ class EXPCL_PANDAEXPRESS VirtualFileSimple : public VirtualFile { public: INLINE VirtualFileSimple(VirtualFileMount *mount, const Filename &local_filename, - bool implicit_pz_file); + bool implicit_pz_file, + int open_flags); PUBLISHED: virtual VirtualFileSystem *get_file_system() const; @@ -40,9 +41,13 @@ PUBLISHED: virtual bool has_file() const; virtual bool is_directory() const; virtual bool is_regular_file() const; + virtual bool is_writable() const; INLINE bool is_implicit_pz_file() const; virtual istream *open_read_file(bool auto_unwrap) const; + virtual void close_read_file(istream *stream) const; + virtual ostream *open_write_file(bool auto_wrap) const; + virtual void close_write_file(ostream *stream) const; virtual off_t get_file_size(istream *stream) const; virtual off_t get_file_size() const; virtual time_t get_timestamp() const; @@ -50,6 +55,7 @@ PUBLISHED: public: virtual bool read_file(pvector &result, bool auto_unwrap) const; + virtual bool write_file(const unsigned char *data, size_t data_size, bool auto_wrap); protected: virtual bool scan_local_directory(VirtualFileList *file_list, @@ -59,6 +65,7 @@ private: VirtualFileMount *_mount; Filename _local_filename; bool _implicit_pz_file; + int _open_flags; public: virtual TypeHandle get_type() const { diff --git a/panda/src/express/virtualFileSystem.I b/panda/src/express/virtualFileSystem.I index cefc0ec352..4ef2d94ca3 100644 --- a/panda/src/express/virtualFileSystem.I +++ b/panda/src/express/virtualFileSystem.I @@ -122,6 +122,20 @@ read_file(const Filename &filename, bool auto_unwrap) const { return result; } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSystem::write_file +// Access: Published +// Description: Convenience function; writes the entire contents of +// the indicated file as a string. +// +// If auto_wrap is true, an explicitly-named .pz file +// is automatically compressed while writing. +//////////////////////////////////////////////////////////////////// +INLINE bool VirtualFileSystem:: +write_file(const Filename &filename, const string &data, bool auto_wrap) { + return write_file(filename, (const unsigned char *)data.data(), data.size(), auto_wrap); +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileSystem::read_file // Access: Public @@ -159,3 +173,18 @@ read_file(const Filename &filename, pvector &result, bool auto_un PT(VirtualFile) file = get_file(filename, false); return (file != (VirtualFile *)NULL && file->read_file(result, auto_unwrap)); } + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSystem::write_file +// Access: Public +// Description: Convenience function; writes the entire contents of +// the indicated file as a block of data. +// +// If auto_wrap is true, an explicitly-named .pz file +// is automatically compressed while writing. +//////////////////////////////////////////////////////////////////// +INLINE bool VirtualFileSystem:: +write_file(const Filename &filename, const unsigned char *data, size_t data_size, bool auto_wrap) { + PT(VirtualFile) file = create_file(filename); + return (file != (VirtualFile *)NULL && file->write_file(data, data_size, auto_wrap)); +} diff --git a/panda/src/express/virtualFileSystem.cxx b/panda/src/express/virtualFileSystem.cxx index afdb368ad0..64893805ff 100644 --- a/panda/src/express/virtualFileSystem.cxx +++ b/panda/src/express/virtualFileSystem.cxx @@ -476,7 +476,7 @@ chdir(const Filename &new_directory) { return true; } - PT(VirtualFile) file = do_get_file(new_directory, true); + PT(VirtualFile) file = do_get_file(new_directory, OF_status_only); if (file != (VirtualFile *)NULL && file->is_directory()) { _cwd = file->get_filename(); _lock.release(); @@ -499,6 +499,23 @@ get_cwd() const { return result; } +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSystem::make_directory +// Access: Published +// Description: Attempts to create a directory within the file +// system. Returns true on success, false on failure +// (for instance, because the parent directory does not +// exist, or is read-only). If the directory already +// existed prior to this call, returns true. +//////////////////////////////////////////////////////////////////// +bool VirtualFileSystem:: +make_directory(const Filename &filename) { + ((VirtualFileSystem *)this)->_lock.acquire(); + PT(VirtualFile) result = do_get_file(filename, OF_make_directory); + ((VirtualFileSystem *)this)->_lock.release(); + return result->is_directory(); +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileSystem::get_file // Access: Published @@ -517,8 +534,27 @@ get_cwd() const { //////////////////////////////////////////////////////////////////// PT(VirtualFile) VirtualFileSystem:: get_file(const Filename &filename, bool status_only) const { + int open_flags = status_only ? 0 : OF_status_only; ((VirtualFileSystem *)this)->_lock.acquire(); - PT(VirtualFile) result = do_get_file(filename, status_only); + PT(VirtualFile) result = do_get_file(filename, open_flags); + ((VirtualFileSystem *)this)->_lock.release(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSystem::create_file +// Access: Published +// Description: Attempts to create a file by the indicated name in +// the filesystem, if possible, and returns it. If a +// file by this name already exists, returns the same +// thing as get_file(). If the filename is located +// within a read-only directory, or the directory +// doesn't exist, returns NULL. +//////////////////////////////////////////////////////////////////// +PT(VirtualFile) VirtualFileSystem:: +create_file(const Filename &filename) { + ((VirtualFileSystem *)this)->_lock.acquire(); + PT(VirtualFile) result = do_get_file(filename, OF_create_file); ((VirtualFileSystem *)this)->_lock.release(); return result; } @@ -843,6 +879,76 @@ close_read_file(istream *stream) { } } +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSystem::__py__write_file +// Access: Published +// Description: Convenience function; writes the entire contents of +// the indicated file as a string. +// +// This variant on write_file() is implemented directly +// for Python, as a small optimization, to avoid the +// double-construction of a string object that would be +// otherwise required. +//////////////////////////////////////////////////////////////////// +PyObject *VirtualFileSystem:: +__py__write_file(const Filename &filename, PyObject *data, bool auto_wrap) { + char *buffer; + Py_ssize_t length; + if (PyString_AsStringAndSize(data, &buffer, &length) == -1) { + return NULL; + } + + bool result = write_file(filename, (const unsigned char *)buffer, length, auto_wrap); + return PyBool_FromLong(result); +} +#endif // HAVE_PYTHON + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSystem::open_write_file +// Access: Published +// Description: Convenience function; returns a newly allocated +// ostream if the file exists and can be written, or +// NULL otherwise. Does not return an invalid ostream. +// +// If auto_wrap is true, an explicitly-named .pz file +// is automatically compressed while writing. +//////////////////////////////////////////////////////////////////// +ostream *VirtualFileSystem:: +open_write_file(const Filename &filename, bool auto_wrap) { + PT(VirtualFile) file = create_file(filename); + if (file == (VirtualFile *)NULL) { + return NULL; + } + ostream *str = file->open_write_file(auto_wrap); + if (str != (ostream *)NULL && str->fail()) { + close_write_file(str); + str = (ostream *)NULL; + } + return str; +} + +//////////////////////////////////////////////////////////////////// +// Function: VirtualFileSystem::close_write_file +// Access: Published, Static +// Description: Closes a file opened by a previous call to +// open_write_file(). This really just deletes the +// ostream pointer, but it is recommended to use this +// interface instead of deleting it explicitly, to help +// work around compiler issues. +//////////////////////////////////////////////////////////////////// +void VirtualFileSystem:: +close_write_file(ostream *stream) { + if (stream != (ostream *)NULL) { +#if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) + stream->~ostream(); + (*global_operator_delete)(stream); +#else + delete stream; +#endif + } +} + //////////////////////////////////////////////////////////////////// // Function: VirtualFileSystem::scan_mount_points // Access: Public @@ -948,11 +1054,12 @@ do_mount(VirtualFileMount *mount, const Filename &mount_point, int flags) { //////////////////////////////////////////////////////////////////// // Function: VirtualFileSystem::do_get_file // Access: Private -// Description: The private implementation of get_file(). Assumes -// the lock is already held. +// Description: The private implementation of get_file(), +// create_file(), and make_directory(). Assumes the +// lock is already held. //////////////////////////////////////////////////////////////////// PT(VirtualFile) VirtualFileSystem:: -do_get_file(const Filename &filename, bool status_only) const { +do_get_file(const Filename &filename, int open_flags) const { nassertr(!filename.empty(), NULL); Filename pathname(filename); if (pathname.is_local()) { @@ -984,19 +1091,19 @@ do_get_file(const Filename &filename, bool status_only) const { // Here's an exact match on the mount point. This filename is // the root directory of this mount object. if (consider_match(found_file, composite_file, mount, "", pathname, - false, status_only)) { + false, open_flags)) { return found_file; } } else if (mount_point.empty()) { // This is the root mount point; all files are in here. if (consider_match(found_file, composite_file, mount, strpath, - pathname, false, status_only)) { + pathname, false, open_flags)) { return found_file; } #ifdef HAVE_ZLIB if (vfs_implicit_pz) { if (consider_match(found_file, composite_file, mount, strpath_pz, - pathname, true, status_only)) { + pathname, true, open_flags)) { return found_file; } } @@ -1009,14 +1116,14 @@ do_get_file(const Filename &filename, bool status_only) const { Filename local_filename = strpath.substr(mount_point.length() + 1); Filename local_filename_pz = strpath_pz.substr(mount_point.length() + 1); if (consider_match(found_file, composite_file, mount, local_filename, - pathname, false, status_only)) { + pathname, false, open_flags)) { return found_file; } #ifdef HAVE_ZLIB if (vfs_implicit_pz) { // Bingo! if (consider_match(found_file, composite_file, mount, local_filename_pz, - pathname, true, status_only)) { + pathname, true, open_flags)) { return found_file; } } @@ -1040,7 +1147,7 @@ do_get_file(const Filename &filename, bool status_only) const { if (start_seq != _mount_seq) { // Yes, it was, or some nested file was. Now that we've // implicitly mounted the .mf file, go back and look again. - return do_get_file(filename, status_only); + return do_get_file(filename, open_flags); } } @@ -1064,9 +1171,9 @@ bool VirtualFileSystem:: consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file, VirtualFileMount *mount, const Filename &local_filename, const Filename &original_filename, bool implicit_pz_file, - bool status_only) const { + int open_flags) const { PT(VirtualFile) vfile = - mount->make_virtual_file(local_filename, original_filename, false, status_only); + mount->make_virtual_file(local_filename, original_filename, false, open_flags); if (!vfile->has_file()) { // Keep looking. return false; @@ -1075,8 +1182,8 @@ consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_fil if (found_file == (VirtualFile *)NULL) { // This was our first match. Save it. found_file = vfile; - if (!found_file->is_directory()) { - // If it's not a directory, we're done. + if (!found_file->is_directory() || ((open_flags & OF_make_directory) != 0)) { + // If it's not a directory (or we wanted to make a directory), we're done. return true; } // It is a directory, so save it for later. diff --git a/panda/src/express/virtualFileSystem.h b/panda/src/express/virtualFileSystem.h index 1168a6de89..4d6446a82d 100644 --- a/panda/src/express/virtualFileSystem.h +++ b/panda/src/express/virtualFileSystem.h @@ -68,11 +68,14 @@ PUBLISHED: BLOCKING bool chdir(const Filename &new_directory); BLOCKING Filename get_cwd() const; + BLOCKING bool make_directory(const Filename &filename); BLOCKING PT(VirtualFile) get_file(const Filename &filename, bool status_only = false) const; + BLOCKING PT(VirtualFile) create_file(const Filename &filename); BLOCKING PT(VirtualFile) find_file(const Filename &filename, const DSearchPath &searchpath, bool status_only = false) const; + BLOCKING bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const string &default_extension = string()) const; BLOCKING int find_all_files(const Filename &filename, const DSearchPath &searchpath, @@ -99,9 +102,17 @@ PUBLISHED: BLOCKING istream *open_read_file(const Filename &filename, bool auto_unwrap) const; BLOCKING static void close_read_file(istream *stream); +#ifdef HAVE_PYTHON + BLOCKING PyObject *__py__write_file(const Filename &filename, PyObject *data, bool auto_wrap); +#endif // HAVE_PYTHON + BLOCKING INLINE bool write_file(const Filename &filename, const string &data, bool auto_wrap); + BLOCKING ostream *open_write_file(const Filename &filename, bool auto_wrap); + BLOCKING static void close_write_file(ostream *stream); + public: INLINE bool read_file(const Filename &filename, string &result, bool auto_unwrap) const; INLINE bool read_file(const Filename &filename, pvector &result, bool auto_unwrap) const; + INLINE bool write_file(const Filename &filename, const unsigned char *data, size_t data_size, bool auto_wrap); void scan_mount_points(vector_string &names, const Filename &path) const; @@ -109,6 +120,15 @@ public: int &flags, string &password); public: + // These flags are passed to do_get_file() and + // VirtualFileMount::make_virtual_file() to quality the kind of + // VirtualFile pointer we want to get. + enum OpenFlags { + OF_status_only = 0x0001, + OF_create_file = 0x0002, + OF_make_directory = 0x0004, + }; + // These are declared as class instances, instead of as globals, to // guarantee they will be initialized by the time the // VirtualFileSystem's constructor runs. @@ -119,11 +139,12 @@ public: private: Filename normalize_mount_point(const Filename &mount_point) const; bool do_mount(VirtualFileMount *mount, const Filename &mount_point, int flags); - PT(VirtualFile) do_get_file(const Filename &filename, bool status_only) const; + PT(VirtualFile) do_get_file(const Filename &filename, int open_flags) const; + bool consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file, VirtualFileMount *mount, const Filename &local_filename, const Filename &original_filename, bool implicit_pz_file, - bool status_only) const; + int open_flags) const; bool consider_mount_mf(const Filename &filename); MutexImpl _lock;