root/OpenSceneGraph/trunk/src/osgPlugins/zip/ZipArchive.cpp @ 13041

Revision 13041, 19.3 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2*
3* This library is open source and may be redistributed and/or modified under
4* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5* (at your option) any later version.  The full license is in LICENSE file
6* included with this distribution, and on the openscenegraph.org website.
7*
8* This library is distributed in the hope that it will be useful,
9* but WITHOUT ANY WARRANTY; without even the implied warranty of
10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11* OpenSceneGraph Public License for more details.
12*/
13
14#include "ZipArchive.h"
15
16#include <osgDB/FileUtils>
17#include <osgDB/FileNameUtils>
18#include <osgDB/ReadFile>
19#include <osgDB/Registry>
20
21#include <sys/types.h>
22#include <sys/stat.h>
23
24#include <sstream>
25#include <cstdio>
26#include "unzip.h"
27
28#if !defined(S_ISDIR)
29if defined( _S_IFDIR) && !defined( __S_IFDIR)
30#    define __S_IFDIR _S_IFDIR
31endif
32define S_ISDIR(mode)    (mode&__S_IFDIR)
33#endif
34
35#ifndef S_ISREG
36#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
37#endif
38
39
40ZipArchive::ZipArchive()  :
41_zipLoaded( false )
42{
43}
44
45ZipArchive::~ZipArchive()
46{
47}
48
49/** close the archive (on all threads) */
50void ZipArchive::close()
51{
52    if ( _zipLoaded )
53    {
54        OpenThreads::ScopedLock<OpenThreads::Mutex> exclusive(_zipMutex);
55        if ( _zipLoaded )
56        {
57            // close the file (on one thread since it's a shared file)
58            const PerThreadData& data = getDataNoLock();
59            CloseZip( data._zipHandle );
60
61            // clear out the file handles
62            _perThreadData.clear();
63
64            // clear out the index.
65            _zipIndex.clear();
66
67            _zipLoaded = false;
68        }
69    }
70}
71
72/** return true if file exists in archive.*/
73bool ZipArchive::fileExists(const std::string& filename) const
74{
75    return GetZipEntry(filename) != NULL;
76}
77
78/** Get the file name which represents the master file recorded in the Archive.*/
79std::string ZipArchive::getMasterFileName() const
80{
81    return std::string();
82}
83
84std::string ZipArchive::getArchiveFileName() const
85{
86    std::string result;
87    if( _zipLoaded )
88    {
89        result = _mainRecord.name;
90    }
91    return result;
92}
93
94/** Get the full list of file names available in the archive.*/
95bool ZipArchive::getFileNames(osgDB::Archive::FileNameList& fileNameList) const
96{
97    if(_zipLoaded)
98    {
99        ZipEntryMap::const_iterator iter = _zipIndex.begin();
100
101        for(;iter != _zipIndex.end(); ++iter)
102        {
103            fileNameList.push_back((*iter).first);
104        }
105
106        return true;
107    }
108    else
109    {
110        return false;
111    }
112}
113
114bool ZipArchive::open(const std::string& file, ArchiveStatus status, const osgDB::ReaderWriter::Options* options)
115{
116    if ( !_zipLoaded )
117    {
118        // exclusive lock when we open for the first time:
119        OpenThreads::ScopedLock<OpenThreads::Mutex> exclusiveLock( _zipMutex );
120
121        if ( !_zipLoaded ) // double-check avoids race condition
122        {
123            std::string ext = osgDB::getLowerCaseFileExtension(file);
124            if (!acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
125
126            // save the filename + password so other threads can open the file
127            _filename = osgDB::findDataFile( file, options );
128            if (_filename.empty()) return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND;
129
130            _password = ReadPassword(options);
131
132            // open the zip file in this thread:
133            const PerThreadData& data = getDataNoLock();
134
135            // establish a shared (read-only) index:
136            if ( data._zipHandle != NULL )
137            {
138                IndexZipFiles( data._zipHandle );
139                _zipLoaded = true;
140            }
141        }
142    }
143
144    return _zipLoaded;
145}
146
147bool ZipArchive::open(std::istream& fin, const osgDB::ReaderWriter::Options* options)
148{
149    if ( !_zipLoaded )
150    {
151        // exclusive lock when we open for the first time:
152        OpenThreads::ScopedLock<OpenThreads::Mutex> exclusive(_zipMutex);
153
154        if ( !_zipLoaded ) // double-check avoids race condition
155        {
156            osgDB::ReaderWriter::ReadResult result = osgDB::ReaderWriter::ReadResult(osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED);
157
158            if (fin.fail()) return false;
159
160            // read the stream into a memory buffer that we'll keep around for any other
161            // threads that want to open the file
162            std::stringstream buf;
163            buf << fin.rdbuf();
164            _membuffer = buf.str();
165
166            _password = ReadPassword(options);
167
168            // open on this thread:
169            const PerThreadData& data = getDataNoLock();
170
171            if ( data._zipHandle != NULL )
172            {
173                IndexZipFiles( data._zipHandle );
174                _zipLoaded = true;
175            }
176        }
177    }
178
179    return _zipLoaded;
180}
181
182osgDB::ReaderWriter::ReadResult ZipArchive::readObject(const std::string& file, const osgDB::ReaderWriter::Options* options) const
183{
184    osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
185
186    std::string ext = osgDB::getLowerCaseFileExtension(file);
187    if (!_zipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
188
189    const ZIPENTRY* ze = GetZipEntry(file);
190    if(ze != NULL)
191    {
192        std::stringstream buffer;
193
194        osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer);
195        if (rw != NULL)
196        {
197            // Setup appropriate options
198            osg::ref_ptr<osgDB::ReaderWriter::Options> local_opt = options ?
199            static_cast<osgDB::ReaderWriter::Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) :
200            new osgDB::ReaderWriter::Options;
201
202            local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name));
203
204            osgDB::ReaderWriter::ReadResult readResult = rw->readObject(buffer,local_opt.get());
205            if (readResult.success())
206            {
207                return readResult;
208            }
209        }
210    }
211
212    return rresult;
213}
214
215osgDB::ReaderWriter::ReadResult ZipArchive::readImage(const std::string& file,const osgDB::ReaderWriter::Options* options) const
216{
217    osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
218
219    std::string ext = osgDB::getLowerCaseFileExtension(file);
220    if (!_zipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
221
222    const ZIPENTRY* ze = GetZipEntry(file);
223    if(ze != NULL)
224    {
225        std::stringstream buffer;
226
227        osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer);
228        if (rw != NULL)
229        {
230            // Setup appropriate options
231            osg::ref_ptr<osgDB::ReaderWriter::Options> local_opt = options ?
232            static_cast<osgDB::ReaderWriter::Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) :
233            new osgDB::ReaderWriter::Options;
234
235            local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name));
236
237            osgDB::ReaderWriter::ReadResult readResult = rw->readImage(buffer,local_opt.get());
238            if (readResult.success())
239            {
240                return readResult;
241            }
242        }
243    }
244
245   return rresult;
246}
247
248osgDB::ReaderWriter::ReadResult ZipArchive::readHeightField(const std::string& file,const osgDB::ReaderWriter::Options* options) const
249{
250    osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
251
252    std::string ext = osgDB::getLowerCaseFileExtension(file);
253    if (!_zipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
254
255    const ZIPENTRY* ze = GetZipEntry(file);
256    if(ze != NULL)
257    {
258        std::stringstream buffer;
259
260        osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer);
261        if (rw != NULL)
262        {
263            // Setup appropriate options
264            osg::ref_ptr<osgDB::ReaderWriter::Options> local_opt = options ?
265            options->cloneOptions() :
266            new osgDB::ReaderWriter::Options;
267
268            local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name));
269
270            osgDB::ReaderWriter::ReadResult readResult = rw->readObject(buffer,local_opt.get());
271            if (readResult.success())
272            {
273                return readResult;
274            }
275        }
276    }
277
278    return rresult;
279}
280
281osgDB::ReaderWriter::ReadResult ZipArchive::readNode(const std::string& file,const osgDB::ReaderWriter::Options* options) const
282{
283    osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
284
285    std::string ext = osgDB::getLowerCaseFileExtension(file);
286    if (!_zipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
287
288    const ZIPENTRY* ze = GetZipEntry(file);
289    if(ze != NULL)
290    {
291        std::stringstream buffer;
292
293        osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer);
294        if (rw != NULL)
295        {
296            // Setup appropriate options
297            osg::ref_ptr<osgDB::ReaderWriter::Options> local_opt = options ?
298                options->cloneOptions() :
299                new osgDB::ReaderWriter::Options;
300
301            local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name));
302
303            osgDB::ReaderWriter::ReadResult readResult = rw->readNode(buffer,local_opt.get());
304            if (readResult.success())
305            {
306                return readResult;
307            }
308        }
309    }
310
311    return rresult;
312}
313
314
315osgDB::ReaderWriter::ReadResult ZipArchive::readShader(const std::string& file,const osgDB::ReaderWriter::Options* options) const
316{
317    osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
318
319    std::string ext = osgDB::getLowerCaseFileExtension(file);
320    if (!_zipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
321
322    const ZIPENTRY* ze = GetZipEntry(file);
323    if(ze != NULL)
324    {
325        std::stringstream buffer;
326
327        osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer);
328        if (rw != NULL)
329        {
330            // Setup appropriate options
331            osg::ref_ptr<osgDB::ReaderWriter::Options> local_opt = options ?
332                options->cloneOptions() :
333                new osgDB::ReaderWriter::Options;
334
335            local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name));
336
337            osgDB::ReaderWriter::ReadResult readResult = rw->readShader(buffer,local_opt.get());
338            if (readResult.success())
339            {
340                return readResult;
341            }
342        }
343    }
344
345    return rresult;
346}
347
348osgDB::ReaderWriter::WriteResult ZipArchive::writeObject(const osg::Object& /*obj*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const
349{
350    return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED);
351}
352
353osgDB::ReaderWriter::WriteResult ZipArchive::writeImage(const osg::Image& /*image*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const
354{
355    return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED);
356}
357
358osgDB::ReaderWriter::WriteResult ZipArchive::writeHeightField(const osg::HeightField& /*heightField*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const
359{
360    return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED);
361}
362
363osgDB::ReaderWriter::WriteResult ZipArchive::writeNode(const osg::Node& /*node*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const
364{
365    return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED);
366}
367
368osgDB::ReaderWriter::WriteResult ZipArchive::writeShader(const osg::Shader& /*shader*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const
369{
370    return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED);
371}
372
373
374osgDB::ReaderWriter* ZipArchive::ReadFromZipEntry(const ZIPENTRY* ze, const osgDB::ReaderWriter::Options* options, std::stringstream& buffer) const
375{
376    if (ze != 0)
377    {
378        char* ibuf = new (std::nothrow) char[ze->unc_size];
379        if (ibuf)
380        {
381            // fetch the handle for the current thread:
382            const PerThreadData& data = getData();
383            if ( data._zipHandle != NULL )
384            {
385                ZRESULT result = UnzipItem(data._zipHandle, ze->index, ibuf, ze->unc_size);
386                bool unzipSuccesful = CheckZipErrorCode(result);
387                if(unzipSuccesful)
388                {
389                    buffer.write(ibuf,ze->unc_size);
390                }
391
392                delete[] ibuf;
393
394                std::string file_ext = osgDB::getFileExtension(ze->name);
395
396                osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension(file_ext);
397                if (rw != NULL)
398                {
399                    return rw;
400                }
401            }
402        }
403        else
404        {
405            //std::cout << "Error- failed to allocate enough memory to unzip file '" << ze->name << ", with size '" << ze->unc_size << std::endl;
406        }
407    }
408
409    return NULL;
410}
411
412void CleanupFileString(std::string& strFileOrDir)
413{
414    if (strFileOrDir.empty())
415    {
416        return;
417    }
418
419    // convert all separators to unix-style for conformity
420    for (unsigned int i = 0; i < strFileOrDir.length(); ++i)
421    {
422        if (strFileOrDir[i] == '\\')
423        {
424            strFileOrDir[i] = '/';
425        }
426    }
427
428    // get rid of trailing separators
429    if (strFileOrDir[strFileOrDir.length()-1] == '/')
430    {
431        strFileOrDir = strFileOrDir.substr(0, strFileOrDir.length()-1);
432    }
433
434    //add a beginning separator
435    if(strFileOrDir[0] != '/')
436    {
437        strFileOrDir.insert(0, "/");
438    }
439}
440
441void ZipArchive::IndexZipFiles(HZIP hz)
442{
443    if(hz != NULL && !_zipLoaded)
444    {
445        //mZipRecord = hz;
446
447        GetZipItem(hz, -1, &_mainRecord);
448        int numitems = _mainRecord.index;
449
450        // Now loop through each file in zip
451        for (int i = 0; i < numitems; i++)
452        {
453            ZIPENTRY* ze = new ZIPENTRY();
454
455            GetZipItem(hz, i, ze);
456            std::string name = ze->name;
457
458            CleanupFileString(name);
459
460            if(!name.empty())
461            {
462                _zipIndex.insert(ZipEntryMapping(name, ze));
463            }
464        }
465    }
466}
467
468ZIPENTRY* ZipArchive::GetZipEntry(const std::string& filename)
469{
470    ZIPENTRY* ze = NULL;
471    std::string fileToLoad = filename;
472    CleanupFileString(fileToLoad);
473
474    ZipEntryMap::iterator iter = _zipIndex.find(fileToLoad);
475    if(iter != _zipIndex.end())
476    {
477        ze = (*iter).second;
478    }
479
480    return ze;
481}
482
483const ZIPENTRY* ZipArchive::GetZipEntry(const std::string& filename) const
484{
485    ZIPENTRY* ze = NULL;
486    std::string fileToLoad = filename;
487    CleanupFileString(fileToLoad);
488
489    ZipEntryMap::const_iterator iter = _zipIndex.find(fileToLoad);
490    if(iter != _zipIndex.end())
491    {
492        ze = (*iter).second;
493    }
494
495    return ze;
496}
497
498osgDB::FileType ZipArchive::getFileType(const std::string& filename) const
499{
500    const ZIPENTRY* ze = GetZipEntry(filename);
501    if(ze != NULL)
502    {
503    #ifdef ZIP_STD
504        if (ze->attr & S_IFDIR)
505    #else
506        if (ze->attr & FILE_ATTRIBUTE_DIRECTORY)
507    #endif
508        {
509            return osgDB::DIRECTORY;
510        }
511        else
512        {
513            return osgDB::REGULAR_FILE;
514        }
515    }
516
517    return osgDB::FILE_NOT_FOUND;
518}
519
520osgDB::DirectoryContents ZipArchive::getDirectoryContents(const std::string& dirName) const
521{
522    osgDB::DirectoryContents dirContents;
523
524    ZipEntryMap::const_iterator iter = _zipIndex.begin();
525    ZipEntryMap::const_iterator iterEnd = _zipIndex.end();
526
527    for(; iter != iterEnd; ++iter)
528    {
529        std::string searchPath = dirName;
530        CleanupFileString(searchPath);
531
532        if(iter->first.size() > searchPath.size())
533        {
534            size_t endSubElement = iter->first.find(searchPath);
535
536            //we match the whole string in the beginning of the path
537            if(endSubElement == 0)
538            {
539                std::string remainingFile = iter->first.substr(searchPath.size() + 1, std::string::npos);
540                size_t endFileToken = remainingFile.find_first_of('/');
541
542                if(endFileToken == std::string::npos)
543                {
544                    dirContents.push_back(remainingFile);
545                }
546            }
547        }
548    }
549
550    return dirContents;
551}
552
553std::string ZipArchive::ReadPassword(const osgDB::ReaderWriter::Options* options) const
554{
555    //try pulling it off the options first
556    std::string password = "";
557    if(options != NULL)
558    {
559        const osgDB::AuthenticationMap* credentials = options->getAuthenticationMap();
560        if(credentials != NULL)
561        {
562            const osgDB::AuthenticationDetails* details = credentials->getAuthenticationDetails("ZipPlugin");
563            if(details != NULL)
564            {
565                password = details->password;
566            }
567        }
568    }
569
570    //if no password, try the registry
571    if(password.empty())
572    {
573        osgDB::Registry* reg = osgDB::Registry::instance();
574        if(reg != NULL)
575        {
576            const osgDB::AuthenticationMap* credentials = reg->getAuthenticationMap();
577            if(credentials != NULL)
578            {
579                const osgDB::AuthenticationDetails* details = credentials->getAuthenticationDetails("ZipPlugin");
580                if(details != NULL)
581                {
582                    password = details->password;
583                }
584            }
585        }
586    }
587
588    return password;
589}
590
591bool ZipArchive::CheckZipErrorCode(ZRESULT result) const
592{
593    if(result == ZR_OK)
594    {
595        return true;
596    }
597    else
598    {
599        unsigned buf_size  = 1025;
600        char* buf = new (std::nothrow) char[buf_size];
601        buf[buf_size - 1] = 0;
602
603        if (buf)
604        {
605            FormatZipMessage(result, buf, buf_size - 1);
606
607            //print error message
608            //sprintf(buf, "%s");
609            OSG_WARN << "Error loading zip file: " << getArchiveFileName() << ", Zip loader returned error: " << buf << "\n";
610            delete [] buf;
611        }
612
613        return false;
614    }
615}
616
617const ZipArchive::PerThreadData&
618ZipArchive::getData() const
619{
620    OpenThreads::ScopedLock<OpenThreads::Mutex> exclusive( const_cast<ZipArchive*>(this)->_zipMutex );
621    return getDataNoLock();
622}
623
624
625const ZipArchive::PerThreadData&
626ZipArchive::getDataNoLock() const
627{
628    // get/create data for the currently running thread:
629    OpenThreads::Thread* current = OpenThreads::Thread::CurrentThread();
630
631    PerThreadDataMap::const_iterator i = _perThreadData.find( current );
632
633    if ( i == _perThreadData.end() || i->second._zipHandle == NULL )
634    {
635        // cache pattern: cast to const for caching purposes
636        ZipArchive* ncThis = const_cast<ZipArchive*>(this);
637
638        // data does not already exist, so open the ZIP with a handle exclusively for this thread:
639        PerThreadData& data = ncThis->_perThreadData[current];
640        if ( !_filename.empty() )
641        {
642            data._zipHandle = OpenZip( _filename.c_str(), _password.c_str() );
643        }
644        else if ( _membuffer.empty() )
645        {
646            data._zipHandle = OpenZip( (void*)_membuffer.c_str(), _membuffer.length(), _password.c_str() );
647        }
648        else
649        {
650            data._zipHandle = NULL;
651        }
652        return data;
653    }
654    else
655    {
656        return i->second;
657    }
658}
Note: See TracBrowser for help on using the browser.