osgEarth 2.1.1
|
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