diff --git a/src/osgPlugins/zip/ZipArchive.cpp b/src/osgPlugins/zip/ZipArchive.cpp new file mode 100644 index 000000000..25ff5eb8d --- /dev/null +++ b/src/osgPlugins/zip/ZipArchive.cpp @@ -0,0 +1,565 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +* +* This library is open source and may be redistributed and/or modified under +* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or +* (at your option) any later version. The full license is in LICENSE file +* included with this distribution, and on the openscenegraph.org website. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* OpenSceneGraph Public License for more details. +*/ + +#include "ZipArchive.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "unzip.h" + +#if !defined(S_ISDIR) +# if defined( _S_IFDIR) && !defined( __S_IFDIR) +# define __S_IFDIR _S_IFDIR +# endif +# define S_ISDIR(mode) (mode&__S_IFDIR) +#endif + +#ifndef S_ISREG +#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) +#endif + + +ZipArchive::ZipArchive() +: mZipLoaded(false) +, mZipRecord(NULL) +{ +} + +ZipArchive::~ZipArchive() +{ + +} + +/** close the archive.*/ +void ZipArchive::close() +{ + if(mZipLoaded) + { + CloseZip(mZipRecord); + + mZipRecord = NULL; + mZipIndex.clear(); + } +} + +/** return true if file exists in archive.*/ +bool ZipArchive::fileExists(const std::string& filename) const +{ + return GetZipEntry(filename) != NULL; +} + +/** Get the file name which represents the master file recorded in the Archive.*/ +std::string ZipArchive::getMasterFileName() const +{ + return std::string(); +} + +std::string ZipArchive::getArchiveFileName() const +{ + std::string result; + if(mZipLoaded) + { + result = mMainRecord.name; + } + return result; +} + +/** Get the full list of file names available in the archive.*/ +bool ZipArchive::getFileNames(osgDB::Archive::FileNameList& fileNameList) const +{ + if(mZipLoaded) + { + ZipEntryMap::const_iterator iter = mZipIndex.begin(); + ZipEntryMap::const_iterator iterEnd = mZipIndex.end(); + + for(;iter != mZipIndex.end(); ++iter) + { + fileNameList.push_back((*iter).first); + } + + return true; + } + else + { + return false; + } +} + + + +bool ZipArchive::open(const std::string& file, ArchiveStatus status, const osgDB::ReaderWriter::Options* options) +{ + std::string ext = osgDB::getLowerCaseFileExtension(file); + if (!acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED; + + std::string fileName = osgDB::findDataFile( file, options ); + if (fileName.empty()) return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND; + + std::string password = ReadPassword(options); + + HZIP hz = OpenZip(fileName.c_str(), password.c_str()); + + if(hz != NULL) + { + IndexZipFiles(hz); + return true; + } + else + { + return false; + } +} + +bool ZipArchive::open(std::istream& fin, const osgDB::ReaderWriter::Options* options) +{ + osgDB::ReaderWriter::ReadResult result = osgDB::ReaderWriter::ReadResult(osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED); + + if (fin.fail()) return false; + + fin.seekg(0,std::ios_base::end); + unsigned int ulzipFileLength = fin.tellg(); + fin.seekg(0,std::ios_base::beg); + + // Need to decouple stream content as I can't see any other way to get access to a byte array + // containing the content in the stream. One saving grace here is that we know that the + // stream has already been fully read in, hence no need to concern ourselves with asynchronous + // reads. + char * pMemBuffer = new char [ulzipFileLength]; + if (!pMemBuffer) return false; + + std::string password = ReadPassword(options); + + HZIP hz = NULL; + fin.read(pMemBuffer, ulzipFileLength); + + if ((unsigned int)fin.gcount() == ulzipFileLength) + { + hz = OpenZip(pMemBuffer, ulzipFileLength, password.c_str()); + } + delete [] pMemBuffer; + + if(hz != NULL) + { + IndexZipFiles(hz); + return true; + } + else + { + return false; + } +} + +osgDB::ReaderWriter::ReadResult ZipArchive::readObject(const std::string& file, const osgDB::ReaderWriter::Options* options) const +{ + osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED; + + std::string ext = osgDB::getLowerCaseFileExtension(file); + if (!mZipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED; + + const ZIPENTRY* ze = GetZipEntry(file); + if(ze != NULL) + { + std::stringstream buffer; + + osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer); + if (rw != NULL) + { + // Setup appropriate options + osg::ref_ptr local_opt = options ? + static_cast(options->clone(osg::CopyOp::SHALLOW_COPY)) : + new osgDB::ReaderWriter::Options; + + local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name)); + + osgDB::ReaderWriter::ReadResult readResult = rw->readObject(buffer,local_opt.get()); + if (readResult.success()) + { + return readResult; + } + } + } + + return rresult; +} + +osgDB::ReaderWriter::ReadResult ZipArchive::readImage(const std::string& file,const osgDB::ReaderWriter::Options* options) const +{ + osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED; + + std::string ext = osgDB::getLowerCaseFileExtension(file); + if (!mZipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED; + + const ZIPENTRY* ze = GetZipEntry(file); + if(ze != NULL) + { + std::stringstream buffer; + + osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer); + if (rw != NULL) + { + // Setup appropriate options + osg::ref_ptr local_opt = options ? + static_cast(options->clone(osg::CopyOp::SHALLOW_COPY)) : + new osgDB::ReaderWriter::Options; + + local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name)); + + osgDB::ReaderWriter::ReadResult readResult = rw->readImage(buffer,local_opt.get()); + if (readResult.success()) + { + return readResult; + } + } + } + + return rresult; +} + +osgDB::ReaderWriter::ReadResult ZipArchive::readHeightField(const std::string& file,const osgDB::ReaderWriter::Options* options) const +{ + osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED; + + std::string ext = osgDB::getLowerCaseFileExtension(file); + if (!mZipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED; + + const ZIPENTRY* ze = GetZipEntry(file); + if(ze != NULL) + { + std::stringstream buffer; + + osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer); + if (rw != NULL) + { + // Setup appropriate options + osg::ref_ptr local_opt = options ? + options->cloneOptions() : + new osgDB::ReaderWriter::Options; + + local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name)); + + osgDB::ReaderWriter::ReadResult readResult = rw->readObject(buffer,local_opt.get()); + if (readResult.success()) + { + return readResult; + } + } + } + + return rresult; +} + +osgDB::ReaderWriter::ReadResult ZipArchive::readNode(const std::string& file,const osgDB::ReaderWriter::Options* options) const +{ + osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED; + + std::string ext = osgDB::getLowerCaseFileExtension(file); + if (!mZipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED; + + const ZIPENTRY* ze = GetZipEntry(file); + if(ze != NULL) + { + std::stringstream buffer; + + osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer); + if (rw != NULL) + { + // Setup appropriate options + osg::ref_ptr local_opt = options ? + options->cloneOptions() : + new osgDB::ReaderWriter::Options; + + local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name)); + + osgDB::ReaderWriter::ReadResult readResult = rw->readNode(buffer,local_opt.get()); + if (readResult.success()) + { + return readResult; + } + } + } + + return rresult; +} + +osgDB::ReaderWriter::WriteResult ZipArchive::writeObject(const osg::Object& /*obj*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const +{ + return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED); +} + +osgDB::ReaderWriter::WriteResult ZipArchive::writeImage(const osg::Image& /*image*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const +{ + return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED); +} + +osgDB::ReaderWriter::WriteResult ZipArchive::writeHeightField(const osg::HeightField& /*heightField*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const +{ + return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED); +} + +osgDB::ReaderWriter::WriteResult ZipArchive::writeNode(const osg::Node& /*node*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const +{ + return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED); +} + + +osgDB::ReaderWriter* ZipArchive::ReadFromZipEntry(const ZIPENTRY* ze, const osgDB::ReaderWriter::Options* options, std::stringstream& buffer) const +{ + if (ze != 0) + { + char* ibuf = new (std::nothrow) char[ze->unc_size]; + if (ibuf) + { + ZRESULT result = UnzipItem(mZipRecord, ze->index, ibuf, ze->unc_size); + bool unzipSuccesful = CheckZipErrorCode(result); + if(unzipSuccesful) + { + buffer.write(ibuf,ze->unc_size); + } + + delete[] ibuf; + + std::string file_ext = osgDB::getFileExtension(ze->name); + + osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension(file_ext); + if (rw != NULL) + { + return rw; + } + } + else + { + //std::cout << "Error- failed to allocate enough memory to unzip file '" << ze->name << ", with size '" << ze->unc_size << std::endl; + } + } + + return NULL; +} + +void CleanupFileString(std::string& strFileOrDir) +{ + if (strFileOrDir.empty()) + { + return; + } + + // convert all separators to unix-style for conformity + for (unsigned int i = 0; i < strFileOrDir.length(); ++i) + { + if (strFileOrDir[i] == '\\') + { + strFileOrDir[i] = '/'; + } + } + + // get rid of trailing separators + if (strFileOrDir[strFileOrDir.length()-1] == '/') + { + strFileOrDir = strFileOrDir.substr(0, strFileOrDir.length()-1); + } + + //add a beginning separator + if(strFileOrDir[0] != '/') + { + strFileOrDir.insert(0, "/"); + } +} + +void ZipArchive::IndexZipFiles(HZIP hz) +{ + if(hz != NULL && !mZipLoaded) + { + mZipRecord = hz; + + GetZipItem(hz,-1,&mMainRecord); + int numitems = mMainRecord.index; + + // Now loop through each file in zip + for (int i = 0; i < numitems; i++) + { + ZIPENTRY* ze = new ZIPENTRY(); + + GetZipItem(hz, i, ze); + std::string name = ze->name; + + CleanupFileString(name); + + if(!name.empty()) + { + mZipIndex.insert(ZipEntryMapping(name, ze)); + } + + } + + mZipLoaded = true; + } +} + +ZIPENTRY* ZipArchive::GetZipEntry(const std::string& filename) +{ + ZIPENTRY* ze = NULL; + std::string fileToLoad = filename; + CleanupFileString(fileToLoad); + + ZipEntryMap::iterator iter = mZipIndex.find(fileToLoad); + if(iter != mZipIndex.end()) + { + ze = (*iter).second; + } + + return ze; +} + +const ZIPENTRY* ZipArchive::GetZipEntry(const std::string& filename) const +{ + ZIPENTRY* ze = NULL; + std::string fileToLoad = filename; + CleanupFileString(fileToLoad); + + ZipEntryMap::const_iterator iter = mZipIndex.find(fileToLoad); + if(iter != mZipIndex.end()) + { + ze = (*iter).second; + } + + return ze; +} + +osgDB::FileType ZipArchive::getFileType(const std::string& filename) const +{ + const ZIPENTRY* ze = GetZipEntry(filename); + if(ze != NULL) + { + #ifdef ZIP_STD + if (ze->attr & S_IFDIR) + #else + if (ze->attr & FILE_ATTRIBUTE_DIRECTORY) + #endif + { + return osgDB::DIRECTORY; + } + else + { + return osgDB::REGULAR_FILE; + } + } + + return osgDB::FILE_NOT_FOUND; +} + +osgDB::DirectoryContents ZipArchive::getDirectoryContents(const std::string& dirName) const +{ + osgDB::DirectoryContents dirContents; + + ZipEntryMap::const_iterator iter = mZipIndex.begin(); + ZipEntryMap::const_iterator iterEnd = mZipIndex.end(); + + for(; iter != iterEnd; ++iter) + { + const ZipEntryMapping& ze = *iter; + + std::string searchPath = dirName; + CleanupFileString(searchPath); + + if(ze.first.size() > searchPath.size()) + { + size_t endSubElement = ze.first.find(searchPath); + + //we match the whole string in the beginning of the path + if(endSubElement == 0) + { + std::string remainingFile = ze.first.substr(searchPath.size() + 1, std::string::npos); + size_t endFileToken = remainingFile.find_first_of('/'); + + if(endFileToken == std::string::npos) + { + dirContents.push_back(remainingFile); + } + } + } + } + + return dirContents; +} + +std::string ZipArchive::ReadPassword(const osgDB::ReaderWriter::Options* options) const +{ + //try pulling it off the options first + std::string password = ""; + if(options != NULL) + { + const osgDB::AuthenticationMap* credentials = options->getAuthenticationMap(); + if(credentials != NULL) + { + const osgDB::AuthenticationDetails* details = credentials->getAuthenticationDetails("ZipPlugin"); + if(details != NULL) + { + password = details->password; + } + } + } + + //if no password, try the registry + if(password.empty()) + { + osgDB::Registry* reg = osgDB::Registry::instance(); + if(reg != NULL) + { + const osgDB::AuthenticationMap* credentials = reg->getAuthenticationMap(); + if(credentials != NULL) + { + const osgDB::AuthenticationDetails* details = credentials->getAuthenticationDetails("ZipPlugin"); + if(details != NULL) + { + password = details->password; + } + } + } + } + + return password; +} + +bool ZipArchive::CheckZipErrorCode(ZRESULT result) const +{ + if(result == ZR_OK) + { + return true; + } + else + { + unsigned buf_size = 1025; + char* buf = new (std::nothrow) char[buf_size]; + buf[buf_size - 1] = 0; + + if (buf) + { + FormatZipMessage(result, buf, buf_size - 1); + + //print error message + //sprintf(buf, "%s"); + OSG_WARN << "Error loading zip file: " << getArchiveFileName() << ", Zip loader returned error: " << buf << "\n"; + delete [] buf; + } + + return false; + } +} + diff --git a/src/osgPlugins/zip/ZipArchive.h b/src/osgPlugins/zip/ZipArchive.h new file mode 100644 index 000000000..7a45ec63b --- /dev/null +++ b/src/osgPlugins/zip/ZipArchive.h @@ -0,0 +1,88 @@ +#ifndef OSGDB_ZIPARCHIVE +#define OSGDB_ZIPARCHIVE 1 + +#include +#include + +#include + +#include "unzip.h" + + +class ZipArchive : public osgDB::Archive +{ + public: + ZipArchive(); + virtual ~ZipArchive(); + + virtual const char* libraryName() const { return "osgDB"; } + + virtual const char* className() const { return "ZipArchive"; } + + virtual bool acceptsExtension(const std::string& /*extension*/) const { return true; } + + /** close the archive.*/ + virtual void close(); + + /** open the archive.*/ + virtual bool open(const std::string& filename, ArchiveStatus status, const osgDB::ReaderWriter::Options* options); + + /** open the archive for reading.*/ + virtual bool open(std::istream& fin, const osgDB::ReaderWriter::Options* options); + + /** return true if file exists in archive.*/ + virtual bool fileExists(const std::string& filename) const; + + /** Get the filename that refers to the archived file on disk*/ + virtual std::string getArchiveFileName() const; + + /** Get the file name which represents the master file recorded in the Archive.*/ + virtual std::string getMasterFileName() const; + + /** Get the full list of file names available in the archive.*/ + virtual bool getFileNames(osgDB::Archive::FileNameList& fileNameList) const; + + /** return type of file. */ + virtual osgDB::FileType getFileType(const std::string& filename) const; + + /** return the contents of a directory. + * returns an empty array on any error.*/ + virtual osgDB::DirectoryContents getDirectoryContents(const std::string& dirName) const; + + virtual osgDB::ReaderWriter::ReadResult readObject(const std::string& /*fileName*/, const osgDB::ReaderWriter::Options* =NULL) const; + virtual osgDB::ReaderWriter::ReadResult readImage(const std::string& /*fileName*/,const osgDB::ReaderWriter::Options* =NULL) const; + virtual osgDB::ReaderWriter::ReadResult readHeightField(const std::string& /*fileName*/,const osgDB::ReaderWriter::Options* =NULL) const; + virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& /*fileName*/, const osgDB::ReaderWriter::Options* =NULL) const; + + virtual osgDB::ReaderWriter::WriteResult writeObject(const osg::Object& /*obj*/, const std::string& /*fileName*/,const osgDB::ReaderWriter::Options* =NULL) const; + virtual osgDB::ReaderWriter::WriteResult writeImage(const osg::Image& /*image*/, const std::string& /*fileName*/,const osgDB::ReaderWriter::Options* =NULL) const; + virtual osgDB::ReaderWriter::WriteResult writeHeightField(const osg::HeightField& /*heightField*/, const std::string& /*fileName*/,const osgDB::ReaderWriter::Options* =NULL) const; + virtual osgDB::ReaderWriter::WriteResult writeNode(const osg::Node& /*node*/, const std::string& /*fileName*/,const osgDB::ReaderWriter::Options* =NULL) const; + + protected: + + osgDB::ReaderWriter* ReadFromZipEntry(const ZIPENTRY* ze, const osgDB::ReaderWriter::Options* options, std::stringstream& streamIn) const; + + void IndexZipFiles(HZIP hz); + const ZIPENTRY* GetZipEntry(const std::string& filename) const; + ZIPENTRY* GetZipEntry(const std::string& filename); + + std::string ReadPassword(const osgDB::ReaderWriter::Options* options) const; + bool CheckZipErrorCode(ZRESULT result) const; + + private: + + bool mZipLoaded; + + HZIP mZipRecord; + ZIPENTRY mMainRecord; + + typedef std::pair ZipEntryMapping; + typedef std::map ZipEntryMap; + + ZipEntryMap mZipIndex; +}; + + +#endif //OSGDB_ZIPARCHIVE +