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:
parent
51a9e63c55
commit
76b6cd6fa3
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
SET(TARGET_SRC
|
||||
unzip.cpp
|
||||
ZipArchive.cpp
|
||||
ReaderWriterZIP.cpp
|
||||
)
|
||||
|
||||
SET(TARGET_H
|
||||
unzip.h
|
||||
ZipArchive.h
|
||||
)
|
||||
|
||||
ADD_DEFINITIONS(-DZIP_STD)
|
||||
|
@ -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)
|
||||
std::string fileName = osgDB::findDataFile(file, options);
|
||||
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;
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user