From Bradley Anderegg, "Ok, I am re-submitting this with the changes we discussed. That is, there is a default implementation of osgDB::Archive::getDirectoryContents() that uses getFileNames(), and the osgDB::ArchiveExtended header was removed as it is now unnecessary.

Here is a quick list of the modified files:

Archive - getDirectoryContents() no longer pure virtual
Archive.cpp - default getDirectoryContents() implementation
unzip.cpp - modified to fix a bug where the same file will not load twice in a row
ZipArchive.h / ZipArchive.cpp - extends osgDB::Archive and provides support for random access loading within a .zip file
ReaderWriterZip.cpp - modified to use the ZipArchive class"
This commit is contained in:
Robert Osfield 2011-05-25 09:04:44 +00:00
parent 51a9e63c55
commit 76b6cd6fa3
6 changed files with 206 additions and 236 deletions

View File

@ -59,7 +59,7 @@ class OSGDB_EXPORT Archive : public ReaderWriter
/** return the contents of a directory.
* returns an empty array on any error.*/
virtual DirectoryContents getDirectoryContents(const std::string& dirName) const = 0;
virtual DirectoryContents getDirectoryContents(const std::string& dirName) const;
virtual ReadResult readObject(const std::string& /*fileName*/,const Options* =NULL) const = 0;

View File

@ -50,3 +50,73 @@ Archive::~Archive()
OSG_INFO<<"Archive::~Archive() closed"<<std::endl;
}
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, "/");
}
}
osgDB::DirectoryContents osgDB::Archive::getDirectoryContents(const std::string& dirName) const
{
DirectoryContents filenames;
getFileNames(filenames);
std::string searchPath = dirName;
cleanupFileString(searchPath);
osgDB::DirectoryContents dirContents;
DirectoryContents::const_iterator iter = filenames.begin();
DirectoryContents::const_iterator iterEnd = filenames.end();
for(; iter != iterEnd; ++iter)
{
std::string currentFile = *iter;
cleanupFileString(currentFile);
if(currentFile.size() > searchPath.size())
{
size_t endSubElement = currentFile.find(searchPath);
//we match the whole string in the beginning of the path
if(endSubElement == 0)
{
std::string remainingFile = currentFile.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;
}

View File

@ -1,10 +1,12 @@
SET(TARGET_SRC
unzip.cpp
ZipArchive.cpp
ReaderWriterZIP.cpp
)
SET(TARGET_H
unzip.h
ZipArchive.h
)
ADD_DEFINITIONS(-DZIP_STD)

View File

@ -3,10 +3,9 @@
#include <osgDB/FileNameUtils>
#include <osgDB/ReadFile>
#include <osgDB/Registry>
#include <osgDB/Options>
#include <sstream>
#include "unzip.h"
#include "ZipArchive.h"
class ReaderWriterZIP : public osgDB::ReaderWriter
{
@ -15,251 +14,135 @@ class ReaderWriterZIP : public osgDB::ReaderWriter
ReaderWriterZIP()
{
supportsExtension("zip","Zip archive format");
osgDB::Registry::instance()->addArchiveExtension("zip");
}
virtual const char* className() const { return "ZIP Database Reader/Writer"; }
virtual ReadResult readNode(const std::string& file, const osgDB::Options* options) const
{
ReadResult rresult = ReadResult::FILE_NOT_HANDLED;
//Check to see if option is to load and extract to filesystem
bool bExtractToFileSystem = false;
if (options)
{
std::string optExtractTo = options->getPluginStringData("zipextract");
if (!(optExtractTo.empty()))
{
if (osgDB::convertToLowerCase(optExtractTo)=="filesystem")
{
bExtractToFileSystem = true;
}
}
}
if (bExtractToFileSystem)
{
rresult = original_readNode(file,options);
}
else
{
std::string ext = osgDB::getLowerCaseFileExtension(file);
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
std::string fileName = osgDB::findDataFile( file, options );
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
OSG_INFO<<"ReaderWriterZIP::readNode( "<<fileName.c_str()<<" )\n";
// First open file as stream
osgDB::ifstream srcFileStrm(fileName.c_str(),std::ios::in|std::ios::binary);
if (!srcFileStrm.fail())
{
// Now read entire zip file into stream buffer
std::stringstream tmpStrmBuffer;
srcFileStrm.seekg(0,std::ios_base::beg);
tmpStrmBuffer.operator <<(srcFileStrm.rdbuf());
srcFileStrm.close();
// Setup appropriate options
osg::ref_ptr<Options> local_opt = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) :
new Options;
// minor issue associated with database path list, as in context of zip file it
// doesn't make sense. Need to set to empty path for other plugins to access
local_opt->getDatabasePathList().push_front(osgDB::getFilePath(file));
// Now pass through to memory zip handler
rresult = readNode(tmpStrmBuffer,local_opt.get());
// Clean up options
local_opt->getDatabasePathList().pop_front();
}
}
return rresult;
}
virtual ReadResult readNode(std::istream& fin,const osgDB::Options* options) const
{
ReadResult result = ReadResult(ReadResult::FILE_NOT_HANDLED);
if (fin.fail()) return result;
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 result;
fin.read(pMemBuffer, ulzipFileLength);
if ((unsigned int)fin.gcount() == ulzipFileLength)
{
HZIP hz = OpenZip(pMemBuffer, ulzipFileLength, "");
if (hz)
{
ZIPENTRY ze;
GetZipItem(hz,-1,&ze);
int numitems=ze.index;
// Initialise top level group
osg::ref_ptr<osg::Group> grp = new osg::Group;
if (grp.valid())
{
// Now loop through each file in zip
for (int i = 0; i < numitems; i++)
{
GetZipItem(hz,i,&ze);
std::string StreamName = ze.name;
std::stringstream buffer;
char *ibuf = new char[ze.unc_size];
if (ibuf)
{
UnzipItem(hz,i, ibuf, ze.unc_size);
buffer.write(ibuf,ze.unc_size);
delete[] ibuf;
// Now ready to read node //
std::string file_ext = osgDB::getFileExtension(StreamName);
ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension(file_ext);
if (rw)
{
// Setup appropriate options
osg::ref_ptr<Options> local_opt = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) :
new Options;
local_opt->setPluginStringData("STREAM_FILENAME",osgDB::getSimpleFileName(StreamName));
ReadResult readResult = rw->readNode(buffer,local_opt.get());
if (readResult.validNode())
{
grp->addChild(readResult.takeNode());
}
}
}
}
if (grp->getNumChildren() > 0)
{
result = grp.get();
}
}
CloseZip(hz);
}
}
delete [] pMemBuffer;
return result;
}
virtual ReadResult original_readNode(const std::string& file, const osgDB::Options* options) const
virtual ReadResult openArchive(const std::string& file,ArchiveStatus status, unsigned int indexBlockSize = 4096, const Options* options = NULL) const
{
std::string ext = osgDB::getLowerCaseFileExtension(file);
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
std::string fileName = osgDB::findDataFile(file, options);
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
OSG_INFO<<"ReaderWriterZIP::readNode( "<<fileName.c_str()<<" )\n";
char dirname[128];
char command[1024];
#if defined(WIN32) && !defined(__CYGWIN__)
if ( getenv("TEMP") != NULL ){
strcpy(dirname, getenv("TEMP"));
}else{
//TEMP environment variable not set so pick current directory.
strcpy(dirname, "./");
}
strcat(dirname, "\\.osgdb_zip");
mkdir(dirname);
// Using unzip.exe from http://www.info-zip.org/pub/infozip/UnZip.html
// unzip.exe must be in your path. (PATH environment variable).
// OR - WinRAR
// Checking for WinRAR
std::string winrar = std::string( getenv( "ProgramFiles" ) ) + "/WinRAR/winrar.exe";
if ( osgDB::fileExists(winrar) ) {
sprintf( command,
"%s x -o+ \"%s\" \"%s\"", winrar.c_str(),
fileName.c_str(), dirname);
} else {
sprintf( command,
"unzip -o -qq \"%s\" -d \"%s\"",
fileName.c_str(), dirname);
}
#else
sprintf( dirname, "/tmp/.zip%06d", getpid());
mkdir( dirname, 0700 );
sprintf( command,
"unzip %s -d %s",
fileName.c_str(), dirname);
#endif
OSG_INFO<<"Running command '"<<command<<"'"<<std::endl;
if ( system( command ) ) {
return ReadResult::FILE_NOT_HANDLED;
}
osg::ref_ptr<osg::Group> grp = new osg::Group;
osg::ref_ptr<osgDB::ReaderWriter::Options> local_options = options ? static_cast<osgDB::ReaderWriter::Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new osgDB::ReaderWriter::Options;
local_options->getDatabasePathList().push_front(dirname);
// deactivate the automatic generation of images to geode's.
bool prevCreateNodeFromImage = osgDB::Registry::instance()->getCreateNodeFromImage();
osgDB::Registry::instance()->setCreateNodeFromImage(false);
osgDB::DirectoryContents contents = osgDB::getDirectoryContents(dirname);
for(osgDB::DirectoryContents::iterator itr = contents.begin();
itr != contents.end();
++itr)
if (fileName.empty())
{
std::string file_ext = osgDB::getFileExtension(*itr);
if (!acceptsExtension(file_ext) &&
*itr!=std::string(".") &&
*itr!=std::string(".."))
//we do not support writing so the file must exist
return ReadResult::FILE_NOT_FOUND;
}
// copy the incoming options if possible so that plugin options can be applied to files
// inside the archive
osg::ref_ptr<osgDB::ReaderWriter::Options> local_options = options?
options->cloneOptions() :
new osgDB::ReaderWriter::Options;
osg::ref_ptr<ZipArchive> archive = new ZipArchive;
if (!archive->open(fileName, osgDB::ReaderWriter::READ, local_options.get()))
{
osg::Node *node = osgDB::readNodeFile( *itr, local_options.get() );
grp->addChild( node );
}
return ReadResult(ReadResult::FILE_NOT_HANDLED);
}
osgDB::Registry::instance()->setCreateNodeFromImage(prevCreateNodeFromImage);
return archive.get();
}
#if defined(WIN32) && !defined(__CYGWIN__)
// note, is this the right command for windows?
// is there any way of overiding the Y/N option? RO.
sprintf( command, "erase /S /Q \"%s\"", dirname );
int result = system( command );
#else
sprintf( command, "rm -rf %s", dirname );
int result = system( command );
#endif
if (result!=0) return ReadResult::ERROR_IN_READING_FILE;
if( grp->getNumChildren() == 0 )
/** open an archive for reading.*/
virtual ReadResult openArchive(std::istream& fin, const Options* options) const
{
return ReadResult::FILE_NOT_HANDLED;
osg::ref_ptr<ZipArchive> archive = new ZipArchive;
if (!archive->open(fin, options))
{
return ReadResult(ReadResult::FILE_NOT_HANDLED);
}
return grp.get();
return archive.get();
}
virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
{
osgDB::ReaderWriter::ReadResult result = openArchive(file, osgDB::Archive::READ);
if (!result.validArchive()) return result;
// copy the incoming options if possible so that plugin options can be applied to files
// inside the archive
osg::ref_ptr<osgDB::ReaderWriter::Options> local_options = options?
options->cloneOptions() :
new osgDB::ReaderWriter::Options;
local_options->setDatabasePath(file);
//todo- what should we read here?
osgDB::ReaderWriter::ReadResult result_2 = result.getArchive()->readNode(result.getArchive()->getMasterFileName(),local_options.get());
if (!options || (options->getObjectCacheHint() & osgDB::ReaderWriter::Options::CACHE_ARCHIVES))
{
// register the archive so that it is cached for future use.
osgDB::Registry::instance()->addToArchiveCache(file, result.getArchive());
}
return result_2;
}
virtual ReadResult readNode(std::istream& fin,const osgDB::ReaderWriter::Options* options) const
{
osgDB::ReaderWriter::ReadResult result = openArchive(fin, options);
if (!result.validArchive()) return result;
// copy the incoming options if possible so that plugin options can be applied to files
// inside the archive
osg::ref_ptr<osgDB::ReaderWriter::Options> local_options = options?
options->cloneOptions() :
new osgDB::ReaderWriter::Options;
//todo- what should the database path be?
//local_options->setDatabasePath(file);
//todo- what should we read here?
osgDB::ReaderWriter::ReadResult result_2 = result.getArchive()->readNode(result.getArchive()->getMasterFileName(),local_options.get());
//todo- what to do to cache the archive here?
//if (!options || (options->getObjectCacheHint() & osgDB::ReaderWriter::Options::CACHE_ARCHIVES))
//{
// // register the archive so that it is cached for future use.
// osgDB::Registry::instance()->addToArchiveCache(file, result.getArchive());
//}
return result_2;
}
virtual ReadResult readImage(const std::string& file,const Options* options) const
{
ReadResult result = openArchive(file,osgDB::Archive::READ);
if (!result.validArchive()) return result;
// copy the incoming options if possible so that plugin options can be applied to files
// inside the archive
osg::ref_ptr<osgDB::ReaderWriter::Options> local_options = options?
options->cloneOptions() :
new osgDB::ReaderWriter::Options;
local_options->setDatabasePath(file);
ReadResult result_2 = result.getArchive()->readImage(result.getArchive()->getMasterFileName(),local_options.get());
if (!options || (options->getObjectCacheHint() & osgDB::ReaderWriter::Options::CACHE_ARCHIVES))
{
// register the archive so that it is cached for future use.
osgDB::Registry::instance()->addToArchiveCache(file, result.getArchive());
}
return result_2;
}
};

View File

@ -4203,10 +4203,25 @@ ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags)
}
bool reached_eof;
int res = unzReadCurrentFile(uf,dst,len,&reached_eof);
if (res<=0) {unzCloseCurrentFile(uf); currentfile=-1;}
if (reached_eof) return ZR_OK;
if (res>0) return ZR_MORE;
if (res==UNZ_PASSWORD) return ZR_PASSWORD;
if (res<=0)
{
unzCloseCurrentFile(uf); currentfile=-1;
}
if (reached_eof)
{
unzCloseCurrentFile(uf);
currentfile=-1;
return ZR_OK;
}
if (res>0)
{
return ZR_MORE;
}
if (res==UNZ_PASSWORD)
{
return ZR_PASSWORD;
}
return ZR_FLATE;
}
// otherwise we're writing to a handle or a file