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

Revision 12498, 15.7 kB (checked in by robert, 4 years ago)

From Mathias Froehlich, build fixes for legacy unix systems

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: mZipLoaded(false)
42, mZipRecord(NULL)
43{
44}
45
46ZipArchive::~ZipArchive()
47{
48
49}
50
51/** close the archive.*/
52void ZipArchive::close()
53{
54    if(mZipLoaded)
55    {
56        CloseZip(mZipRecord);
57
58        mZipRecord = NULL;
59        mZipIndex.clear();
60    }
61}
62
63/** return true if file exists in archive.*/
64bool ZipArchive::fileExists(const std::string& filename) const
65{
66    return GetZipEntry(filename) != NULL;
67}
68
69/** Get the file name which represents the master file recorded in the Archive.*/
70std::string ZipArchive::getMasterFileName() const
71{
72    return std::string();
73}
74
75std::string ZipArchive::getArchiveFileName() const
76{
77    std::string result;
78    if(mZipLoaded)
79    {
80        result = mMainRecord.name;
81    }
82    return result;
83}
84
85/** Get the full list of file names available in the archive.*/
86bool ZipArchive::getFileNames(osgDB::Archive::FileNameList& fileNameList) const
87{
88    if(mZipLoaded)
89    {
90        ZipEntryMap::const_iterator iter = mZipIndex.begin();
91        ZipEntryMap::const_iterator iterEnd = mZipIndex.end();
92
93        for(;iter != mZipIndex.end(); ++iter)
94        {
95            fileNameList.push_back((*iter).first);
96        }
97
98        return true;
99    }
100    else
101    {
102        return false;
103    }
104}
105
106
107
108bool ZipArchive::open(const std::string& file, ArchiveStatus status, const osgDB::ReaderWriter::Options* options)
109{
110    std::string ext = osgDB::getLowerCaseFileExtension(file);
111    if (!acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
112
113    std::string fileName = osgDB::findDataFile( file, options );
114    if (fileName.empty()) return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND;
115
116    std::string password = ReadPassword(options);
117
118    HZIP hz = OpenZip(fileName.c_str(), password.c_str());
119
120    if(hz != NULL)
121    {
122        IndexZipFiles(hz);
123        return true;
124    }
125    else
126    {
127        return false;
128    }
129}
130
131bool ZipArchive::open(std::istream& fin, const osgDB::ReaderWriter::Options* options)
132{
133    osgDB::ReaderWriter::ReadResult result = osgDB::ReaderWriter::ReadResult(osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED);
134
135    if (fin.fail()) return false;
136
137    fin.seekg(0,std::ios_base::end);
138    unsigned int ulzipFileLength = fin.tellg();
139    fin.seekg(0,std::ios_base::beg);
140
141    // Need to decouple stream content as I can't see any other way to get access to a byte array
142    // containing the content in the stream. One saving grace here is that we know that the
143    // stream has already been fully read in, hence no need to concern ourselves with asynchronous
144    // reads.
145    char * pMemBuffer = new char [ulzipFileLength];
146    if (!pMemBuffer) return false;
147
148    std::string password = ReadPassword(options);
149
150    HZIP hz = NULL;
151    fin.read(pMemBuffer, ulzipFileLength);
152
153    if ((unsigned int)fin.gcount() == ulzipFileLength)
154    {
155        hz = OpenZip(pMemBuffer, ulzipFileLength, password.c_str());
156    }
157    delete [] pMemBuffer;
158
159    if(hz != NULL)
160    {
161        IndexZipFiles(hz);
162        return true;
163    }
164    else
165    {
166        return false;
167    }
168}
169
170osgDB::ReaderWriter::ReadResult ZipArchive::readObject(const std::string& file, const osgDB::ReaderWriter::Options* options) const
171{
172    osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
173
174    std::string ext = osgDB::getLowerCaseFileExtension(file);
175    if (!mZipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
176
177    const ZIPENTRY* ze = GetZipEntry(file);
178    if(ze != NULL)
179    {
180        std::stringstream buffer;
181
182        osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer);
183        if (rw != NULL)
184        {
185            // Setup appropriate options
186            osg::ref_ptr<osgDB::ReaderWriter::Options> local_opt = options ?
187            static_cast<osgDB::ReaderWriter::Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) :
188            new osgDB::ReaderWriter::Options;
189
190            local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name));
191
192            osgDB::ReaderWriter::ReadResult readResult = rw->readObject(buffer,local_opt.get());
193            if (readResult.success())
194            {
195                return readResult;
196            }
197        }
198    }
199
200    return rresult;
201}
202
203osgDB::ReaderWriter::ReadResult ZipArchive::readImage(const std::string& file,const osgDB::ReaderWriter::Options* options) const
204{
205    osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
206
207    std::string ext = osgDB::getLowerCaseFileExtension(file);
208    if (!mZipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
209
210    const ZIPENTRY* ze = GetZipEntry(file);
211    if(ze != NULL)
212    {
213        std::stringstream buffer;
214
215        osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer);
216        if (rw != NULL)
217        {
218            // Setup appropriate options
219            osg::ref_ptr<osgDB::ReaderWriter::Options> local_opt = options ?
220            static_cast<osgDB::ReaderWriter::Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) :
221            new osgDB::ReaderWriter::Options;
222
223            local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name));
224
225            osgDB::ReaderWriter::ReadResult readResult = rw->readImage(buffer,local_opt.get());
226            if (readResult.success())
227            {
228                return readResult;
229            }
230        }
231    }
232
233   return rresult;
234}
235
236osgDB::ReaderWriter::ReadResult ZipArchive::readHeightField(const std::string& file,const osgDB::ReaderWriter::Options* options) const
237{
238    osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
239
240    std::string ext = osgDB::getLowerCaseFileExtension(file);
241    if (!mZipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
242
243    const ZIPENTRY* ze = GetZipEntry(file);
244    if(ze != NULL)
245    {
246        std::stringstream buffer;
247
248        osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer);
249        if (rw != NULL)
250        {
251            // Setup appropriate options
252            osg::ref_ptr<osgDB::ReaderWriter::Options> local_opt = options ?
253            options->cloneOptions() :
254            new osgDB::ReaderWriter::Options;
255
256            local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name));
257
258            osgDB::ReaderWriter::ReadResult readResult = rw->readObject(buffer,local_opt.get());
259            if (readResult.success())
260            {
261                return readResult;
262            }
263        }
264    }
265
266    return rresult;
267}
268
269osgDB::ReaderWriter::ReadResult ZipArchive::readNode(const std::string& file,const osgDB::ReaderWriter::Options* options) const
270{
271    osgDB::ReaderWriter::ReadResult rresult = osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
272
273    std::string ext = osgDB::getLowerCaseFileExtension(file);
274    if (!mZipLoaded || !acceptsExtension(ext)) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
275
276    const ZIPENTRY* ze = GetZipEntry(file);
277    if(ze != NULL)
278    {
279        std::stringstream buffer;
280
281        osgDB::ReaderWriter* rw = ReadFromZipEntry(ze, options, buffer);
282        if (rw != NULL)
283        {
284            // Setup appropriate options
285            osg::ref_ptr<osgDB::ReaderWriter::Options> local_opt = options ?
286                options->cloneOptions() :
287                new osgDB::ReaderWriter::Options;
288
289            local_opt->setPluginStringData("STREAM_FILENAME", osgDB::getSimpleFileName(ze->name));
290
291            osgDB::ReaderWriter::ReadResult readResult = rw->readNode(buffer,local_opt.get());
292            if (readResult.success())
293            {
294                return readResult;
295            }
296        }
297    }
298
299    return rresult;
300}
301
302osgDB::ReaderWriter::WriteResult ZipArchive::writeObject(const osg::Object& /*obj*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const
303{
304    return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED);
305}
306
307osgDB::ReaderWriter::WriteResult ZipArchive::writeImage(const osg::Image& /*image*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const
308{
309    return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED);
310}
311
312osgDB::ReaderWriter::WriteResult ZipArchive::writeHeightField(const osg::HeightField& /*heightField*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const
313{
314    return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED);
315}
316
317osgDB::ReaderWriter::WriteResult ZipArchive::writeNode(const osg::Node& /*node*/,const std::string& /*fileName*/,const osgDB::ReaderWriter::Options*) const
318{
319    return osgDB::ReaderWriter::WriteResult(osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED);
320}
321
322
323osgDB::ReaderWriter* ZipArchive::ReadFromZipEntry(const ZIPENTRY* ze, const osgDB::ReaderWriter::Options* options, std::stringstream& buffer) const
324{
325    if (ze != 0)
326    {
327        char* ibuf = new (std::nothrow) char[ze->unc_size];
328        if (ibuf)
329        {
330            ZRESULT result = UnzipItem(mZipRecord, ze->index, ibuf, ze->unc_size);
331            bool unzipSuccesful = CheckZipErrorCode(result);
332            if(unzipSuccesful)
333            {
334                buffer.write(ibuf,ze->unc_size);
335            }
336
337            delete[] ibuf;
338
339            std::string file_ext = osgDB::getFileExtension(ze->name);
340
341            osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension(file_ext);
342            if (rw != NULL)
343            {
344                return rw;
345            }
346        }
347        else
348        {
349            //std::cout << "Error- failed to allocate enough memory to unzip file '" << ze->name << ", with size '" << ze->unc_size << std::endl;
350        }
351    }
352
353    return NULL;
354}
355
356void CleanupFileString(std::string& strFileOrDir)
357{
358    if (strFileOrDir.empty())
359    {
360        return;
361    }
362
363    // convert all separators to unix-style for conformity
364    for (unsigned int i = 0; i < strFileOrDir.length(); ++i)
365    {
366        if (strFileOrDir[i] == '\\')
367        {
368            strFileOrDir[i] = '/';
369        }
370    }
371
372    // get rid of trailing separators
373    if (strFileOrDir[strFileOrDir.length()-1] == '/')
374    {
375        strFileOrDir = strFileOrDir.substr(0, strFileOrDir.length()-1);
376    }
377
378    //add a beginning separator
379    if(strFileOrDir[0] != '/')
380    {
381        strFileOrDir.insert(0, "/");
382    }
383}
384
385void ZipArchive::IndexZipFiles(HZIP hz)
386{
387    if(hz != NULL && !mZipLoaded)
388    {
389        mZipRecord = hz;
390
391        GetZipItem(hz,-1,&mMainRecord);
392        int numitems = mMainRecord.index;
393
394        // Now loop through each file in zip
395        for (int i = 0; i < numitems; i++)
396        {
397            ZIPENTRY* ze = new ZIPENTRY();
398
399            GetZipItem(hz, i, ze);
400            std::string name = ze->name;
401
402            CleanupFileString(name);
403
404            if(!name.empty())
405            {
406            mZipIndex.insert(ZipEntryMapping(name, ze));
407            }
408
409        }
410
411        mZipLoaded = true;
412    }
413}
414
415ZIPENTRY* ZipArchive::GetZipEntry(const std::string& filename)
416{
417    ZIPENTRY* ze = NULL;
418    std::string fileToLoad = filename;
419    CleanupFileString(fileToLoad);
420
421    ZipEntryMap::iterator iter = mZipIndex.find(fileToLoad);
422    if(iter != mZipIndex.end())
423    {
424        ze = (*iter).second;
425    }
426
427    return ze;
428}
429
430const ZIPENTRY* ZipArchive::GetZipEntry(const std::string& filename) const
431{
432    ZIPENTRY* ze = NULL;
433    std::string fileToLoad = filename;
434    CleanupFileString(fileToLoad);
435
436    ZipEntryMap::const_iterator iter = mZipIndex.find(fileToLoad);
437    if(iter != mZipIndex.end())
438    {
439        ze = (*iter).second;
440    }
441
442    return ze;
443}
444
445osgDB::FileType ZipArchive::getFileType(const std::string& filename) const
446{
447    const ZIPENTRY* ze = GetZipEntry(filename);
448    if(ze != NULL)
449    {
450    #ifdef ZIP_STD
451        if (ze->attr & S_IFDIR)
452    #else
453        if (ze->attr & FILE_ATTRIBUTE_DIRECTORY)
454    #endif
455        {
456            return osgDB::DIRECTORY;
457        }
458        else
459        {
460            return osgDB::REGULAR_FILE;
461        }
462    }
463
464    return osgDB::FILE_NOT_FOUND;
465}
466
467osgDB::DirectoryContents ZipArchive::getDirectoryContents(const std::string& dirName) const
468{
469    osgDB::DirectoryContents dirContents;
470
471    ZipEntryMap::const_iterator iter = mZipIndex.begin();
472    ZipEntryMap::const_iterator iterEnd = mZipIndex.end();
473
474    for(; iter != iterEnd; ++iter)
475    {
476        std::string searchPath = dirName;
477        CleanupFileString(searchPath);
478
479        if(iter->first.size() > searchPath.size())
480        {
481            size_t endSubElement = iter->first.find(searchPath);
482
483            //we match the whole string in the beginning of the path
484            if(endSubElement == 0)
485            {
486                std::string remainingFile = iter->first.substr(searchPath.size() + 1, std::string::npos);
487                size_t endFileToken = remainingFile.find_first_of('/');
488
489                if(endFileToken == std::string::npos)
490                {
491                    dirContents.push_back(remainingFile);
492                }
493            }
494        }
495    }
496
497    return dirContents;
498}
499
500std::string ZipArchive::ReadPassword(const osgDB::ReaderWriter::Options* options) const
501{
502    //try pulling it off the options first
503    std::string password = "";
504    if(options != NULL)
505    {
506        const osgDB::AuthenticationMap* credentials = options->getAuthenticationMap();
507        if(credentials != NULL)
508        {
509            const osgDB::AuthenticationDetails* details = credentials->getAuthenticationDetails("ZipPlugin");
510            if(details != NULL)
511            {
512                password = details->password;
513            }
514        }
515    }
516
517    //if no password, try the registry
518    if(password.empty())
519    {
520        osgDB::Registry* reg = osgDB::Registry::instance();
521        if(reg != NULL)
522        {
523            const osgDB::AuthenticationMap* credentials = reg->getAuthenticationMap();
524            if(credentials != NULL)
525            {
526                const osgDB::AuthenticationDetails* details = credentials->getAuthenticationDetails("ZipPlugin");
527                if(details != NULL)
528                {
529                    password = details->password;
530                }
531            }
532        }
533    }
534
535    return password;
536}
537
538bool ZipArchive::CheckZipErrorCode(ZRESULT result) const
539{
540    if(result == ZR_OK)
541    {
542        return true;
543    }
544    else
545    {
546        unsigned buf_size  = 1025;
547        char* buf = new (std::nothrow) char[buf_size];
548        buf[buf_size - 1] = 0;
549
550        if (buf)
551        {
552            FormatZipMessage(result, buf, buf_size - 1);
553
554            //print error message
555            //sprintf(buf, "%s");
556            OSG_WARN << "Error loading zip file: " << getArchiveFileName() << ", Zip loader returned error: " << buf << "\n";
557            delete [] buf;
558        }
559
560        return false;
561    }
562}
Note: See TracBrowser for help on using the browser.