1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2014 Torus Knot Software Ltd
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28 #include "OgreStableHeaders.h"
29 
30 #if OGRE_NO_ZIP_ARCHIVE == 0
31 // workaround for Wundef in zzip/conf.h
32 #ifndef __GNUC_MINOR_
33 #define __GNUC_MINOR_ 0
34 #endif
35 
36 #include <zzip/zzip.h>
37 #include <zzip/plugin.h>
38 
39 namespace Ogre {
40 namespace {
41     class ZipArchive : public Archive
42     {
43     protected:
44         /// Handle to root zip file
45         ZZIP_DIR* mZzipDir;
46         /// File list (since zziplib seems to only allow scanning of dir tree once)
47         FileInfoList mFileList;
48         /// A pointer to file io alternative implementation
49         zzip_plugin_io_handlers* mPluginIo;
50 
51         OGRE_AUTO_MUTEX;
52     public:
53         ZipArchive(const String& name, const String& archType, zzip_plugin_io_handlers* pluginIo = NULL);
54         ~ZipArchive();
55         /// @copydoc Archive::isCaseSensitive
isCaseSensitive(void) const56         bool isCaseSensitive(void) const { return OGRE_RESOURCEMANAGER_STRICT != 0; }
57 
58         /// @copydoc Archive::load
59         void load();
60         /// @copydoc Archive::unload
61         void unload();
62 
63         /// @copydoc Archive::open
64         DataStreamPtr open(const String& filename, bool readOnly = true) const;
65 
66         /// @copydoc Archive::create
67         DataStreamPtr create(const String& filename);
68 
69         /// @copydoc Archive::remove
70         void remove(const String& filename);
71 
72         /// @copydoc Archive::list
73         StringVectorPtr list(bool recursive = true, bool dirs = false) const;
74 
75         /// @copydoc Archive::listFileInfo
76         FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false) const;
77 
78         /// @copydoc Archive::find
79         StringVectorPtr find(const String& pattern, bool recursive = true,
80             bool dirs = false) const;
81 
82         /// @copydoc Archive::findFileInfo
83         FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
84             bool dirs = false) const;
85 
86         /// @copydoc Archive::exists
87         bool exists(const String& filename) const;
88 
89         /// @copydoc Archive::getModifiedTime
90         time_t getModifiedTime(const String& filename) const;
91     };
92 
93     /** Specialisation of DataStream to handle streaming data from zip archives. */
94     class ZipDataStream : public DataStream
95     {
96     protected:
97         ZZIP_FILE* mZzipFile;
98         /// We need caching because sometimes serializers step back in data stream and zziplib behaves slow
99         StaticCache<2 * OGRE_STREAM_TEMP_SIZE> mCache;
100     public:
101         /// Constructor for creating named streams
102         ZipDataStream(const String& name, ZZIP_FILE* zzipFile, size_t uncompressedSize);
103         ~ZipDataStream();
104         /// @copydoc DataStream::read
105         size_t read(void* buf, size_t count);
106         /// @copydoc DataStream::write
107         size_t write(const void* buf, size_t count);
108         /// @copydoc DataStream::skip
109         void skip(long count);
110         /// @copydoc DataStream::seek
111         void seek( size_t pos );
112         /// @copydoc DataStream::seek
113         size_t tell(void) const;
114         /// @copydoc DataStream::eof
115         bool eof(void) const;
116         /// @copydoc DataStream::close
117         void close(void);
118     };
119 
120     /// Utility method to format out zzip errors
getErrorDescription(zzip_error_t zzipError,const String & file)121     static String getErrorDescription(zzip_error_t zzipError, const String& file)
122     {
123         const char* errorMsg = "";
124         switch (zzipError)
125         {
126         case ZZIP_NO_ERROR:
127             break;
128         case ZZIP_OUTOFMEM:
129             errorMsg = "Out of memory";
130             break;
131         case ZZIP_DIR_OPEN:
132             errorMsg = "Unable to open zip file";
133             break;
134         case ZZIP_DIR_STAT:
135         case ZZIP_DIR_SEEK:
136         case ZZIP_DIR_READ:
137             errorMsg = "Unable to read zip file";
138             break;
139         case ZZIP_UNSUPP_COMPR:
140             errorMsg = "Unsupported compression format";
141             break;
142         case ZZIP_CORRUPTED:
143             errorMsg = "Corrupted archive";
144             break;
145         case ZZIP_DIR_TOO_SHORT:
146             errorMsg = "Zip file is too short";
147             break;
148         case ZZIP_DIR_EDH_MISSING:
149             errorMsg = "Zip-file's central directory record missing. Is this a 7z file";
150             break;
151         case ZZIP_ENOENT:
152             errorMsg = "File not in archive";
153             break;
154         default:
155             errorMsg = "Unknown error";
156             break;
157         };
158 
159         return StringUtil::format("%s '%s'", errorMsg, file.c_str());
160     }
161 
162     /// A static pointer to file io alternative implementation for the embedded files
163     zzip_plugin_io_handlers* gPluginIo = NULL;
164 }
165     //-----------------------------------------------------------------------
ZipArchive(const String & name,const String & archType,zzip_plugin_io_handlers * pluginIo)166     ZipArchive::ZipArchive(const String& name, const String& archType, zzip_plugin_io_handlers* pluginIo)
167         : Archive(name, archType), mZzipDir(0), mPluginIo(pluginIo)
168     {
169     }
170     //-----------------------------------------------------------------------
~ZipArchive()171     ZipArchive::~ZipArchive()
172     {
173         unload();
174     }
175     //-----------------------------------------------------------------------
load()176     void ZipArchive::load()
177     {
178         OGRE_LOCK_AUTO_MUTEX;
179         if (!mZzipDir)
180         {
181             zzip_error_t zzipError;
182             mZzipDir = zzip_dir_open_ext_io(mName.c_str(), &zzipError, 0, mPluginIo);
183             if (zzipError)
184                 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, getErrorDescription(zzipError, mName));
185 
186             // Cache names
187             ZZIP_DIRENT zzipEntry;
188             while (zzip_dir_read(mZzipDir, &zzipEntry))
189             {
190                 FileInfo info;
191                 info.archive = this;
192                 // Get basename / path
193                 StringUtil::splitFilename(zzipEntry.d_name, info.basename, info.path);
194                 info.filename = zzipEntry.d_name;
195                 // Get sizes
196                 info.compressedSize = static_cast<size_t>(zzipEntry.d_csize);
197                 info.uncompressedSize = static_cast<size_t>(zzipEntry.st_size);
198                 // folder entries
199                 if (info.basename.empty())
200                 {
201                     info.filename = info.filename.substr (0, info.filename.length () - 1);
202                     StringUtil::splitFilename(info.filename, info.basename, info.path);
203                     // Set compressed size to -1 for folders; anyway nobody will check
204                     // the compressed size of a folder, and if he does, its useless anyway
205                     info.compressedSize = size_t (-1);
206                 }
207 #if !OGRE_RESOURCEMANAGER_STRICT
208                 else
209                 {
210                     info.filename = info.basename;
211                 }
212 #endif
213                 mFileList.push_back(info);
214 
215             }
216 
217         }
218     }
219     //-----------------------------------------------------------------------
unload()220     void ZipArchive::unload()
221     {
222         OGRE_LOCK_AUTO_MUTEX;
223         if (mZzipDir)
224         {
225             zzip_dir_close(mZzipDir);
226             mZzipDir = 0;
227             mFileList.clear();
228         }
229 
230     }
231     //-----------------------------------------------------------------------
open(const String & filename,bool readOnly) const232     DataStreamPtr ZipArchive::open(const String& filename, bool readOnly) const
233     {
234         // zziplib is not threadsafe
235         OGRE_LOCK_AUTO_MUTEX;
236         String lookUpFileName = filename;
237 
238 #if OGRE_RESOURCEMANAGER_STRICT
239         const int flags = 0;
240 #else
241         const int flags = ZZIP_CASELESS;
242 #endif
243 
244         // Format not used here (always binary)
245         ZZIP_FILE* zzipFile =
246             zzip_file_open(mZzipDir, lookUpFileName.c_str(), ZZIP_ONLYZIP | flags);
247 
248 #if !OGRE_RESOURCEMANAGER_STRICT
249         if (!zzipFile) // Try if we find the file
250         {
251             String basename, path;
252             StringUtil::splitFilename(lookUpFileName, basename, path);
253             const FileInfoListPtr fileNfo = findFileInfo(basename, true);
254             if (fileNfo->size() == 1) // If there are more files with the same do not open anyone
255             {
256                 Ogre::FileInfo info = fileNfo->at(0);
257                 lookUpFileName = info.path + info.basename;
258                 zzipFile = zzip_file_open(mZzipDir, lookUpFileName.c_str(), ZZIP_ONLYZIP | flags); // When an error happens here we will catch it below
259             }
260         }
261 #endif
262 
263         if (!zzipFile)
264         {
265             OGRE_EXCEPT(Exception::ERR_FILE_NOT_FOUND, getErrorDescription((zzip_error_t)zzip_error(mZzipDir), mName));
266         }
267 
268         // Get uncompressed size too
269         ZZIP_STAT zstat;
270         zzip_dir_stat(mZzipDir, lookUpFileName.c_str(), &zstat, flags);
271 
272         // Construct & return stream
273         return DataStreamPtr(OGRE_NEW ZipDataStream(lookUpFileName, zzipFile, static_cast<size_t>(zstat.st_size)));
274 
275     }
276     //---------------------------------------------------------------------
create(const String & filename)277     DataStreamPtr ZipArchive::create(const String& filename)
278     {
279         OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,
280             "Modification of zipped archives is not supported",
281             "ZipArchive::create");
282 
283     }
284     //---------------------------------------------------------------------
remove(const String & filename)285     void ZipArchive::remove(const String& filename)
286     {
287         OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,
288             "Modification of zipped archives is not supported",
289             "ZipArchive::remove");
290     }
291     //-----------------------------------------------------------------------
list(bool recursive,bool dirs) const292     StringVectorPtr ZipArchive::list(bool recursive, bool dirs) const
293     {
294         OGRE_LOCK_AUTO_MUTEX;
295         StringVectorPtr ret = StringVectorPtr(OGRE_NEW_T(StringVector, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
296 
297         FileInfoList::const_iterator i, iend;
298         iend = mFileList.end();
299         for (i = mFileList.begin(); i != iend; ++i)
300             if ((dirs == (i->compressedSize == size_t (-1))) &&
301                 (recursive || i->path.empty()))
302                 ret->push_back(i->filename);
303 
304         return ret;
305     }
306     //-----------------------------------------------------------------------
listFileInfo(bool recursive,bool dirs) const307     FileInfoListPtr ZipArchive::listFileInfo(bool recursive, bool dirs) const
308     {
309         OGRE_LOCK_AUTO_MUTEX;
310         FileInfoList* fil = OGRE_NEW_T(FileInfoList, MEMCATEGORY_GENERAL)();
311         FileInfoList::const_iterator i, iend;
312         iend = mFileList.end();
313         for (i = mFileList.begin(); i != iend; ++i)
314             if ((dirs == (i->compressedSize == size_t (-1))) &&
315                 (recursive || i->path.empty()))
316                 fil->push_back(*i);
317 
318         return FileInfoListPtr(fil, SPFM_DELETE_T);
319     }
320     //-----------------------------------------------------------------------
find(const String & pattern,bool recursive,bool dirs) const321     StringVectorPtr ZipArchive::find(const String& pattern, bool recursive, bool dirs) const
322     {
323         OGRE_LOCK_AUTO_MUTEX;
324         StringVectorPtr ret = StringVectorPtr(OGRE_NEW_T(StringVector, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
325         // If pattern contains a directory name, do a full match
326         bool full_match = (pattern.find ('/') != String::npos) ||
327                           (pattern.find ('\\') != String::npos);
328         bool wildCard = pattern.find('*') != String::npos;
329 
330         FileInfoList::const_iterator i, iend;
331         iend = mFileList.end();
332         for (i = mFileList.begin(); i != iend; ++i)
333             if ((dirs == (i->compressedSize == size_t (-1))) &&
334                 (recursive || full_match || wildCard))
335                 // Check basename matches pattern (zip is case insensitive)
336                 if (StringUtil::match(full_match ? i->filename : i->basename, pattern, false))
337                     ret->push_back(i->filename);
338 
339         return ret;
340     }
341     //-----------------------------------------------------------------------
findFileInfo(const String & pattern,bool recursive,bool dirs) const342     FileInfoListPtr ZipArchive::findFileInfo(const String& pattern,
343         bool recursive, bool dirs) const
344     {
345         OGRE_LOCK_AUTO_MUTEX;
346         FileInfoListPtr ret = FileInfoListPtr(OGRE_NEW_T(FileInfoList, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
347         // If pattern contains a directory name, do a full match
348         bool full_match = (pattern.find ('/') != String::npos) ||
349                           (pattern.find ('\\') != String::npos);
350         bool wildCard = pattern.find('*') != String::npos;
351 
352         FileInfoList::const_iterator i, iend;
353         iend = mFileList.end();
354         for (i = mFileList.begin(); i != iend; ++i)
355             if ((dirs == (i->compressedSize == size_t (-1))) &&
356                 (recursive || full_match || wildCard))
357                 // Check name matches pattern (zip is case insensitive)
358                 if (StringUtil::match(full_match ? i->filename : i->basename, pattern, false))
359                     ret->push_back(*i);
360 
361         return ret;
362     }
363     //-----------------------------------------------------------------------
364     struct FileNameCompare : public std::binary_function<FileInfo, String, bool>
365     {
operator ()Ogre::FileNameCompare366         bool operator()(const Ogre::FileInfo& lhs, const String& filename) const
367         {
368             return lhs.filename == filename;
369         }
370     };
371     //-----------------------------------------------------------------------
exists(const String & filename) const372     bool ZipArchive::exists(const String& filename) const
373     {
374         OGRE_LOCK_AUTO_MUTEX;
375         String cleanName = filename;
376 #if !OGRE_RESOURCEMANAGER_STRICT
377         if(filename.rfind('/') != String::npos)
378         {
379             StringVector tokens = StringUtil::split(filename, "/");
380             cleanName = tokens[tokens.size() - 1];
381         }
382 #endif
383 
384         return std::find_if (mFileList.begin(), mFileList.end(), std::bind2nd<FileNameCompare>(FileNameCompare(), cleanName)) != mFileList.end();
385     }
386     //---------------------------------------------------------------------
getModifiedTime(const String & filename) const387     time_t ZipArchive::getModifiedTime(const String& filename) const
388     {
389         // Zziplib doesn't yet support getting the modification time of individual files
390         // so just check the mod time of the zip itself
391         struct stat tagStat;
392         bool ret = (stat(mName.c_str(), &tagStat) == 0);
393 
394         if (ret)
395         {
396             return tagStat.st_mtime;
397         }
398         else
399         {
400             return 0;
401         }
402 
403     }
404     //-----------------------------------------------------------------------
ZipDataStream(const String & name,ZZIP_FILE * zzipFile,size_t uncompressedSize)405     ZipDataStream::ZipDataStream(const String& name, ZZIP_FILE* zzipFile, size_t uncompressedSize)
406         :DataStream(name), mZzipFile(zzipFile)
407     {
408         mSize = uncompressedSize;
409     }
410     //-----------------------------------------------------------------------
~ZipDataStream()411     ZipDataStream::~ZipDataStream()
412     {
413         close();
414     }
415     //-----------------------------------------------------------------------
read(void * buf,size_t count)416     size_t ZipDataStream::read(void* buf, size_t count)
417     {
418         size_t was_avail = mCache.read(buf, count);
419         zzip_ssize_t r = 0;
420         if (was_avail < count)
421         {
422             r = zzip_file_read(mZzipFile, (char*)buf + was_avail, count - was_avail);
423             if (r<0) {
424                 ZZIP_DIR *dir = zzip_dirhandle(mZzipFile);
425                 String msg = zzip_strerror_of(dir);
426                 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
427                     mName+" - error from zziplib: "+msg,
428                     "ZipDataStream::read");
429             }
430             mCache.cacheData((char*)buf + was_avail, (size_t)r);
431         }
432         return was_avail + (size_t)r;
433     }
434     //---------------------------------------------------------------------
write(const void * buf,size_t count)435     size_t ZipDataStream::write(const void* buf, size_t count)
436     {
437         // not supported
438         return 0;
439     }
440     //-----------------------------------------------------------------------
skip(long count)441     void ZipDataStream::skip(long count)
442     {
443         long was_avail = static_cast<long>(mCache.avail());
444         if (count > 0)
445         {
446             if (!mCache.ff(count))
447                 zzip_seek(mZzipFile, static_cast<zzip_off_t>(count - was_avail), SEEK_CUR);
448         }
449         else if (count < 0)
450         {
451             if (!mCache.rewind((size_t)(-count)))
452                 zzip_seek(mZzipFile, static_cast<zzip_off_t>(count + was_avail), SEEK_CUR);
453         }
454     }
455     //-----------------------------------------------------------------------
seek(size_t pos)456     void ZipDataStream::seek( size_t pos )
457     {
458         zzip_off_t newPos = static_cast<zzip_off_t>(pos);
459         zzip_off_t prevPos = static_cast<zzip_off_t>(tell());
460         if (prevPos < 0)
461         {
462             // seek set after invalid pos
463             mCache.clear();
464             zzip_seek(mZzipFile, newPos, SEEK_SET);
465         }
466         else
467         {
468             // everything is going all right, relative seek
469             skip((long)(newPos - prevPos));
470         }
471     }
472     //-----------------------------------------------------------------------
tell(void) const473     size_t ZipDataStream::tell(void) const
474     {
475         zzip_off_t pos = zzip_tell(mZzipFile);
476         if (pos<0)
477             return (size_t)(-1);
478         return static_cast<size_t>(pos) - mCache.avail();
479     }
480     //-----------------------------------------------------------------------
eof(void) const481     bool ZipDataStream::eof(void) const
482     {
483         return (tell() >= mSize);
484     }
485     //-----------------------------------------------------------------------
close(void)486     void ZipDataStream::close(void)
487     {
488         mAccess = 0;
489         if (mZzipFile != 0)
490         {
491             zzip_file_close(mZzipFile);
492             mZzipFile = 0;
493         }
494         mCache.clear();
495     }
496     //-----------------------------------------------------------------------
497     //-----------------------------------------------------------------------
498     //  ZipArchiveFactory
499     //-----------------------------------------------------------------------
createInstance(const String & name,bool readOnly)500     Archive *ZipArchiveFactory::createInstance( const String& name, bool readOnly )
501     {
502         if(!readOnly)
503             return NULL;
504 
505         return OGRE_NEW ZipArchive(name, getType());
506     }
507     //-----------------------------------------------------------------------
getType(void) const508     const String& ZipArchiveFactory::getType(void) const
509     {
510         static String name = "Zip";
511         return name;
512     }
513     //-----------------------------------------------------------------------
514     //-----------------------------------------------------------------------
515     //  EmbeddedZipArchiveFactory
516     //-----------------------------------------------------------------------
517     //-----------------------------------------------------------------------
518     /// a struct to hold embedded file data
519     struct EmbeddedFileData
520     {
521         const uint8 * fileData;
522         zzip_size_t fileSize;
523         zzip_size_t curPos;
524         bool isFileOpened;
525         EmbeddedZipArchiveFactory::DecryptEmbeddedZipFileFunc decryptFunc;
526     };
527     //-----------------------------------------------------------------------
528     /// A type for a map between the file names to file index
529     typedef std::map<String, int> FileNameToIndexMap;
530     typedef FileNameToIndexMap::iterator FileNameToIndexMapIter;
531     /// A type to store the embedded files data
532     typedef std::vector<EmbeddedFileData> EmbbedFileDataList;
533 
534     namespace {
535     /// A static map between the file names to file index
536     FileNameToIndexMap * EmbeddedZipArchiveFactory_mFileNameToIndexMap;
537     /// A static list to store the embedded files data
538     EmbbedFileDataList * EmbeddedZipArchiveFactory_mEmbbedFileDataList;
539     _zzip_plugin_io sEmbeddedZipArchiveFactory_PluginIo;
540     #define EMBED_IO_BAD_FILE_HANDLE (-1)
541     #define EMBED_IO_SUCCESS (0)
542     //-----------------------------------------------------------------------
543     /// functions for embedded zzip_plugin_io_handlers implementation
544     /// The functions are here and not as static members because they
545     /// use types that I don't want to define in the header like zzip_char_t,
546     //  zzip_ssize_t and such.
547     //-----------------------------------------------------------------------
548     // get file date by index
getEmbeddedFileDataByIndex(int fd)549     EmbeddedFileData & getEmbeddedFileDataByIndex(int fd)
550     {
551         return (*EmbeddedZipArchiveFactory_mEmbbedFileDataList)[fd-1];
552     }
553     //-----------------------------------------------------------------------
554     // opens the file
EmbeddedZipArchiveFactory_open(zzip_char_t * name,int flags,...)555     int EmbeddedZipArchiveFactory_open(zzip_char_t* name, int flags, ...)
556     {
557         String nameAsString = name;
558         FileNameToIndexMapIter foundIter = EmbeddedZipArchiveFactory_mFileNameToIndexMap->find(nameAsString);
559         if (foundIter != EmbeddedZipArchiveFactory_mFileNameToIndexMap->end())
560         {
561             int fd = foundIter->second;
562             EmbeddedFileData & curEmbeddedFileData = getEmbeddedFileDataByIndex(fd);
563             if(curEmbeddedFileData.isFileOpened)
564             {
565                // file is opened - return an error handle
566                return EMBED_IO_BAD_FILE_HANDLE;
567             }
568 
569             curEmbeddedFileData.isFileOpened = true;
570             return fd;
571         }
572         else
573         {
574            // not found - return an error handle
575            return EMBED_IO_BAD_FILE_HANDLE;
576         }
577     }
578     //-----------------------------------------------------------------------
579     // Closes a file.
580     // Return Value - On success, close returns 0.
EmbeddedZipArchiveFactory_close(int fd)581     int EmbeddedZipArchiveFactory_close(int fd)
582     {
583         if (fd == EMBED_IO_BAD_FILE_HANDLE)
584         {
585             // bad index - return an error
586             return -1;
587         }
588 
589         EmbeddedFileData & curEmbeddedFileData = getEmbeddedFileDataByIndex(fd);
590 
591         if(curEmbeddedFileData.isFileOpened == false)
592         {
593            // file is opened - return an error handle
594            return -1;
595         }
596         else
597         {
598             // success
599             curEmbeddedFileData.isFileOpened = false;
600             curEmbeddedFileData.curPos = 0;
601             return 0;
602         }
603 
604     }
605 
606     //-----------------------------------------------------------------------
607     // reads data from the file
EmbeddedZipArchiveFactory_read(int fd,void * buf,zzip_size_t len)608     zzip_ssize_t EmbeddedZipArchiveFactory_read(int fd, void* buf, zzip_size_t len)
609     {
610         if (fd == EMBED_IO_BAD_FILE_HANDLE)
611         {
612             // bad index - return an error size - negative
613             return -1;
614         }
615         // get the current buffer in file;
616         EmbeddedFileData & curEmbeddedFileData = getEmbeddedFileDataByIndex(fd);
617         const uint8 * curFileData = curEmbeddedFileData.fileData;
618         if (len + curEmbeddedFileData.curPos > curEmbeddedFileData.fileSize)
619         {
620             len = curEmbeddedFileData.fileSize - curEmbeddedFileData.curPos;
621         }
622         curFileData += curEmbeddedFileData.curPos;
623 
624         // copy to out buffer
625         memcpy(buf, curFileData, len);
626 
627         if( curEmbeddedFileData.decryptFunc != NULL )
628         {
629             if (!curEmbeddedFileData.decryptFunc(curEmbeddedFileData.curPos, buf, len))
630             {
631                 // decrypt failed - return an error size - negative
632                 return -1;
633             }
634         }
635 
636         // move the cursor to the new pos
637         curEmbeddedFileData.curPos += len;
638 
639         return len;
640     }
641     //-----------------------------------------------------------------------
642     // Moves file pointer.
EmbeddedZipArchiveFactory_seeks(int fd,zzip_off_t offset,int whence)643     zzip_off_t EmbeddedZipArchiveFactory_seeks(int fd, zzip_off_t offset, int whence)
644     {
645         if (fd == EMBED_IO_BAD_FILE_HANDLE)
646         {
647             // bad index - return an error - nonzero value.
648             return -1;
649         }
650 
651         zzip_size_t newPos = -1;
652         // get the current buffer in file;
653         EmbeddedFileData & curEmbeddedFileData = getEmbeddedFileDataByIndex(fd);
654         switch(whence)
655         {
656             case SEEK_CUR:
657                 newPos = (zzip_size_t)(curEmbeddedFileData.curPos + offset);
658                 break;
659             case SEEK_END:
660                 newPos = (zzip_size_t)(curEmbeddedFileData.fileSize - offset);
661                 break;
662             case SEEK_SET:
663                 newPos = (zzip_size_t)offset;
664                 break;
665             default:
666                 // bad whence - return an error - nonzero value.
667                 return -1;
668                 break;
669         };
670         if (newPos >= curEmbeddedFileData.fileSize)
671         {
672             // bad whence - return an error - nonzero value.
673             return -1;
674         }
675 
676         curEmbeddedFileData.curPos = newPos;
677         return newPos;
678     }
679     //-----------------------------------------------------------------------
680     // returns the file size
EmbeddedZipArchiveFactory_filesize(int fd)681     zzip_off_t EmbeddedZipArchiveFactory_filesize(int fd)
682     {
683         if (fd == EMBED_IO_BAD_FILE_HANDLE)
684         {
685             // bad index - return an error - nonzero value.
686             return -1;
687         }
688                 // get the current buffer in file;
689         EmbeddedFileData & curEmbeddedFileData = getEmbeddedFileDataByIndex(fd);
690         return curEmbeddedFileData.fileSize;
691     }
692     //-----------------------------------------------------------------------
693     // writes data to the file
EmbeddedZipArchiveFactory_write(int fd,_zzip_const void * buf,zzip_size_t len)694     zzip_ssize_t EmbeddedZipArchiveFactory_write(int fd, _zzip_const void* buf, zzip_size_t len)
695     {
696         // the files in this case are read only - return an error  - nonzero value.
697         return -1;
698     }
699     } // namespace {
700     //-----------------------------------------------------------------------
EmbeddedZipArchiveFactory()701     EmbeddedZipArchiveFactory::EmbeddedZipArchiveFactory()
702     {
703         // init static member
704         if (gPluginIo == NULL)
705         {
706             gPluginIo = &sEmbeddedZipArchiveFactory_PluginIo;
707             gPluginIo->fd.open = EmbeddedZipArchiveFactory_open;
708             gPluginIo->fd.close = EmbeddedZipArchiveFactory_close;
709             gPluginIo->fd.read = EmbeddedZipArchiveFactory_read;
710             gPluginIo->fd.seeks = EmbeddedZipArchiveFactory_seeks;
711             gPluginIo->fd.filesize = EmbeddedZipArchiveFactory_filesize;
712             gPluginIo->fd.write = EmbeddedZipArchiveFactory_write;
713             gPluginIo->fd.sys = 1;
714             gPluginIo->fd.type = 1;
715         }
716     }
717     //-----------------------------------------------------------------------
~EmbeddedZipArchiveFactory()718     EmbeddedZipArchiveFactory::~EmbeddedZipArchiveFactory()
719     {
720     }
721     //-----------------------------------------------------------------------
createInstance(const String & name,bool readOnly)722     Archive *EmbeddedZipArchiveFactory::createInstance( const String& name, bool readOnly )
723     {
724         ZipArchive * resZipArchive = OGRE_NEW ZipArchive(name, getType(), gPluginIo);
725         return resZipArchive;
726     }
727     //-----------------------------------------------------------------------
getType(void) const728     const String& EmbeddedZipArchiveFactory::getType(void) const
729     {
730         static String name = "EmbeddedZip";
731         return name;
732     }
733     //-----------------------------------------------------------------------
addEmbbeddedFile(const String & name,const uint8 * fileData,size_t fileSize,DecryptEmbeddedZipFileFunc decryptFunc)734     void EmbeddedZipArchiveFactory::addEmbbeddedFile(const String& name, const uint8 * fileData,
735                                         size_t fileSize, DecryptEmbeddedZipFileFunc decryptFunc)
736     {
737         static bool needToInit = true;
738         if(needToInit)
739         {
740             needToInit = false;
741 
742             // we can't be sure when global variables get initialized
743             // meaning it is possible our list has not been init when this
744             // function is being called. The solution is to use local
745             // static members in this function an init the pointers for the
746             // global here. We know for use that the local static variables
747             // are create in this stage.
748             static FileNameToIndexMap sFileNameToIndexMap;
749             static EmbbedFileDataList sEmbbedFileDataList;
750             EmbeddedZipArchiveFactory_mFileNameToIndexMap = &sFileNameToIndexMap;
751             EmbeddedZipArchiveFactory_mEmbbedFileDataList = &sEmbbedFileDataList;
752         }
753 
754         EmbeddedFileData newEmbeddedFileData;
755         newEmbeddedFileData.curPos = 0;
756         newEmbeddedFileData.isFileOpened = false;
757         newEmbeddedFileData.fileData = fileData;
758         newEmbeddedFileData.fileSize = fileSize;
759         newEmbeddedFileData.decryptFunc = decryptFunc;
760         EmbeddedZipArchiveFactory_mEmbbedFileDataList->push_back(newEmbeddedFileData);
761         (*EmbeddedZipArchiveFactory_mFileNameToIndexMap)[name] = static_cast<int>(EmbeddedZipArchiveFactory_mEmbbedFileDataList->size());
762     }
763     //-----------------------------------------------------------------------
removeEmbbeddedFile(const String & name)764     void EmbeddedZipArchiveFactory::removeEmbbeddedFile( const String& name )
765     {
766         EmbeddedZipArchiveFactory_mFileNameToIndexMap->erase(name);
767     }
768 }
769 
770 #endif
771