osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthDrivers/zipfs/ReaderWriterZipFS.cpp

Go to the documentation of this file.
00001 /* -*-c++-*- */
00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
00003 * Copyright 2008-2009 Pelican Ventures, Inc.
00004 * http://osgearth.org
00005 *
00006 * osgEarth is free software; you can redistribute it and/or modify
00007 * it under the terms of the GNU Lesser General Public License as published by
00008 * the Free Software Foundation; either version 2 of the License, or
00009 * (at your option) any later version.
00010 *
00011 * This program is distributed in the hope that it will be useful,
00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 * GNU Lesser General Public License for more details.
00015 *
00016 * You should have received a copy of the GNU Lesser General Public License
00017 * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018 */
00019 
00020 #include <osg/Notify>
00021 #include <osgDB/FileNameUtils>
00022 #include <osgDB/FileUtils>
00023 #include <osgDB/Registry>
00024 #include <osgDB/ReadFile>
00025 #include <osgDB/WriteFile>
00026 
00027 #include <OpenThreads/ReentrantMutex>
00028 
00029 #include <sstream>
00030 #include <string.h>
00031 
00032 #include "zip.h"
00033 
00034 using namespace osg;
00035 using namespace osgDB;
00036 
00037 static OpenThreads::ReentrantMutex s_mutex;
00038 
00043 class ReaderWriterZipFS : public osgDB::ReaderWriter
00044 {
00045 public:
00046 
00047     enum ObjectType
00048     {
00049         OBJECT,
00050         IMAGE,
00051         HEIGHTFIELD,
00052         NODE
00053     };
00054 
00055     ReaderWriterZipFS()
00056     {
00057         supportsExtension( "zipfs", "Zip virtual file system" );
00058     }
00059 
00060     virtual const char* className()
00061     {
00062         return "ZIP virtual file system";
00063     }
00064 
00065     virtual ReadResult readNode(const std::string& file_name, const Options* options) const
00066     {
00067         return readFile(NODE, file_name, options);
00068     }
00069 
00070     virtual ReadResult readImage(const std::string& file_name, const Options* options) const
00071     {
00072         return readFile(IMAGE, file_name, options);
00073     }
00074 
00075     virtual ReadResult readObject(const std::string& file_name, const Options* options) const
00076     {
00077         return readFile(OBJECT, file_name, options);
00078     }
00079 
00080     virtual ReadResult readHeightField(const std::string& file_name, const Options* options) const
00081     {
00082         return readFile(HEIGHTFIELD, file_name, options);
00083     }
00084 
00085     virtual WriteResult writeObject(const osg::Object& obj, const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
00086     {
00087         return writeFile(OBJECT, &obj, fileName, options);
00088     }
00089 
00090     virtual WriteResult writeImage(const osg::Image& image, const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
00091     {
00092         return writeFile(IMAGE, &image, fileName, options);
00093     }
00094 
00095     virtual WriteResult writeHeightField(const osg::HeightField& hf, const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
00096     {
00097         return writeFile(HEIGHTFIELD, &hf, fileName, options);
00098     }
00099 
00100     virtual WriteResult writeNode(const osg::Node& node, const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
00101     {
00102         return writeFile(NODE, &node, fileName,options);
00103     }
00104 
00105     WriteResult writeFile(ObjectType objectType, const osg::Object* object, osgDB::ReaderWriter* rw, std::ostream& fout, const osgDB::ReaderWriter::Options* options) const
00106     {
00107         switch (objectType)
00108         {
00109         case(OBJECT): return rw->writeObject(*object, fout, options);
00110         case(IMAGE): return rw->writeImage(*(dynamic_cast<const osg::Image*>(object)), fout, options);
00111         case(HEIGHTFIELD): return rw->writeHeightField(*(dynamic_cast<const osg::HeightField*>(object)), fout, options);
00112         case(NODE): return rw->writeNode(*(dynamic_cast<const osg::Node*>(object)), fout,options);
00113         default: break;
00114         }
00115         return WriteResult::FILE_NOT_HANDLED;
00116     }
00117 
00118 
00119     ReadResult readFile(ObjectType objectType, osgDB::ReaderWriter* rw, std::istream& fin, const osgDB::ReaderWriter::Options* options) const
00120     {
00121         switch (objectType)
00122         {
00123           case(OBJECT): return rw->readObject(fin, options);
00124           case(IMAGE): return rw->readImage(fin, options);
00125           case(HEIGHTFIELD): return rw->readHeightField(fin, options);
00126           case(NODE): return rw->readNode(fin, options);
00127           default: break;
00128         }
00129         return ReadResult::FILE_NOT_HANDLED;
00130     }
00131 
00132     ReadResult readFile(ObjectType objectType, const std::string &fullFileName, const osgDB::ReaderWriter::Options* options) const
00133     {
00134         OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(s_mutex);
00135 
00136         //This plugin allows you to treat zip files almost like virtual directories.  So, the pathname to the file you want in the zip should
00137         //be of the format c:\data\myzip.zip\images\foo.png
00138 
00139         std::string::size_type len = fullFileName.find(".zip");
00140         if (len == std::string::npos)
00141         {
00142             osg::notify(osg::INFO) << "ReaderWriterZipFS: Path does not contain zip file" << std::endl;
00143             return ReadResult::FILE_NOT_HANDLED;
00144         }
00145 
00146         std::string zipFile = fullFileName.substr(0, len + 4);
00147         zipFile = osgDB::findDataFile(zipFile);
00148         zipFile = osgDB::convertFileNameToNativeStyle( zipFile );
00149 
00150         //Return if the file doesn't exist
00151         if (!osgDB::fileExists( zipFile )) return ReadResult::FILE_NOT_FOUND;
00152 
00153         osg::notify(osg::INFO) << "ReaderWriterZipFS::readFile  ZipFile path is " << zipFile << std::endl;
00154 
00155         std::string zipEntry = fullFileName.substr(len+4);
00156 
00157 
00158         //Strip the leading slash from the zip entry
00159         if ((zipEntry.length() > 0) && 
00160             ((zipEntry[0] == '/') || (zipEntry[0] == '\\')))
00161         {
00162             zipEntry = zipEntry.substr(1);
00163         }
00164 
00165 
00166         //Lipzip returns filenames with '/' rather than '\\', even on Windows.  So, convert the zip entry to Unix style
00167         zipEntry = osgDB::convertFileNameToUnixStyle(zipEntry);
00168         osg::notify(osg::INFO) << "Zip Entry " << zipEntry << std::endl;
00169 
00170         //See if we can get a ReaderWriter for the zip entry before we even try to unzip the file
00171          ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension(osgDB::getFileExtension(zipEntry));
00172          if (!rw)
00173          {
00174              osg::notify(osg::NOTICE) << "Could not find ReaderWriter for " << zipEntry << std::endl;
00175              return ReadResult::FILE_NOT_HANDLED;
00176          }
00177 
00178 
00179         int err;
00180 
00181         //Open the zip file
00182         struct zip* pZip = zip_open(zipFile.c_str(), ZIP_CHECKCONS, &err);
00183         if (pZip)
00184         {
00185             //List the files
00186             /*int numFiles = zip_get_num_files(pZip);
00187             osg::notify(osg::NOTICE) << zipFile << " has " << numFiles << " files " << std::endl;
00188             for (int i = 0; i < numFiles; ++i)
00189             {
00190             osg::notify(osg::NOTICE) << i << ": " << zip_get_name(pZip, i, 0) << std::endl;
00191             }*/
00192 
00193             //Find the index within the zip file for the given zip entry
00194             int zipIndex = zip_name_locate(pZip, zipEntry.c_str(), 0);
00195 
00196             osg::notify(osg::INFO) << "ReaderWriterZipFS: ZipFile index " << zipIndex << std::endl;
00197             if (zipIndex < 0)
00198             {
00199                 osg::notify(osg::INFO) << "Could not find zip entry " << zipEntry << " in " << zipFile << std::endl;
00200                 //Make sure to close the zipfile
00201                 zip_close(pZip);
00202                 return ReadResult::FILE_NOT_FOUND;  
00203             }
00204             
00205             //Open the entry for reading
00206             zip_file* pZipFile = zip_fopen_index(pZip, zipIndex, 0);
00207 
00208             if (pZipFile) 
00209             {
00210                 //Read the data from the entry into a std::string
00211                 int dataSize = 0;
00212                 std::string data;
00213                 do{
00214                     char buffer[1024];
00215                     dataSize = zip_fread(pZipFile, buffer, 1024);
00216                     if (dataSize > 0)
00217                     {
00218                        data.append((char*)buffer, dataSize);
00219                     }
00220                 }while (dataSize > 0);
00221 
00222                 //Close the zip entry and the actual zip file itself
00223                 zip_fclose(pZipFile);
00224                 zip_close(pZip);
00225 
00226                 std::stringstream strstream(data);
00227                 return readFile(objectType, rw, strstream, options);
00228             }
00229         }
00230         else
00231         {
00232             osg::notify(osg::NOTICE) << "ReaderWriterZipFS::readFile couldn't open zip " << zipFile << " full filename " << fullFileName << std::endl;
00233         }
00234         return ReadResult::FILE_NOT_HANDLED;
00235     }
00236 
00237     WriteResult writeFile(ObjectType objectType, const osg::Object* object, const std::string& fullFileName, const osgDB::ReaderWriter::Options* options) const
00238     {       
00239         OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(s_mutex);
00240 
00241         std::string::size_type len = fullFileName.find(".zip");
00242         if (len == std::string::npos)
00243         {
00244             osg::notify(osg::INFO) << "ReaderWriterZipFS: Path does not contain zip file" << std::endl;
00245             return WriteResult::FILE_NOT_HANDLED;
00246         }
00247 
00248         //It is possible that the zip file doesn't currently exist, so we just use getRealPath instead of findDataFile as in the readFile method
00249         std::string zipFile = osgDB::getRealPath(fullFileName.substr(0, len + 4));
00250 
00251         std::string path = osgDB::getFilePath(zipFile);
00252         //If the path doesn't currently exist, create it
00253         if (!osgDB::fileExists(path) && !osgDB::makeDirectory(path))
00254         {
00255             osg::notify(osg::WARN) << "Couldn't create path " << path << std::endl;
00256         }
00257 
00258         osg::notify(osg::INFO) << "ReaderWriterZipFS::writeFile ZipFile path is " << zipFile << std::endl;
00259 
00260         std::string zipEntry = fullFileName.substr(len+4);
00261 
00262 
00263         //Strip the leading slash from the zip entry
00264         if ((zipEntry.length() > 0) && 
00265             ((zipEntry[0] == '/') || (zipEntry[0] == '\\')))
00266         {
00267             zipEntry = zipEntry.substr(1);
00268         }
00269 
00270 
00271         //Lipzip returns filenames with '/' rather than '\\', even on Windows.  So, convert the zip entry to Unix style
00272         zipEntry = osgDB::convertFileNameToUnixStyle(zipEntry);
00273         osg::notify(osg::INFO) << "Zip Entry " << zipEntry << std::endl;
00274 
00275         //See if we can get a ReaderWriter for the zip entry before we even try to unzip the file
00276          ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension(osgDB::getFileExtension(zipEntry));
00277          if (!rw)
00278          {
00279              osg::notify(osg::INFO) << "Could not find ReaderWriter for " << zipEntry << std::endl;
00280              return WriteResult::FILE_NOT_HANDLED;
00281          }
00282 
00283         int err;
00284 
00285         //Open the zip file
00286         struct zip* pZip = zip_open(zipFile.c_str(), ZIP_CREATE|ZIP_CHECKCONS, &err);
00287         if (pZip)
00288         {
00289             //Write the data to the stream
00290             std::ostringstream strstream;
00291             writeFile(objectType, object, rw, strstream, options);
00292 
00293             char *data = new char[strstream.str().length()];
00294             memcpy(data, strstream.str().c_str(), strstream.str().size());
00295 
00296             WriteResult wr;
00297             struct zip_source *zs = zip_source_buffer(pZip, data, strstream.str().length(), 0);
00298             if (zs)
00299             {
00300                 if (zip_add(pZip, zipEntry.c_str(), zs) != -1) 
00301                 {
00302                     wr = WriteResult::FILE_SAVED;
00303                 }
00304                 else
00305                 {
00306                   osg::notify(osg::NOTICE) << "Couldn't add zip source " << std::endl;
00307                   wr = WriteResult::ERROR_IN_WRITING_FILE;
00308                 }
00309             }
00310             else
00311             {
00312                 osg::notify(osg::NOTICE) << "Couldn't create zip source " << std::endl;
00313                 wr = WriteResult::ERROR_IN_WRITING_FILE;
00314             }
00315             zip_close(pZip);
00316             delete[] data;
00317             return wr;
00318         }
00319         else
00320         {
00321             osg::notify(osg::NOTICE) << "ReaderWriterZipFS::writeFile couldn't open zip " << zipFile << " full filename " << fullFileName << std::endl;
00322         }
00323         return WriteResult::FILE_NOT_HANDLED;      
00324     }
00325 };
00326 
00327 REGISTER_OSGPLUGIN(zipfs, ReaderWriterZipFS)
00328 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines