zipsource.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005-2008 by the FIFE team                              *
00003  *   http://www.fifengine.de                                               *
00004  *   This file is part of FIFE.                                            *
00005  *                                                                         *
00006  *   FIFE is free software; you can redistribute it and/or                 *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the                 *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
00020  ***************************************************************************/
00021 
00022 // Standard C++ library includes
00023 #include <algorithm>
00024 #include <list>
00025 
00026 // 3rd party library includes
00027 #include "zlib.h"
00028 #include <boost/scoped_array.hpp>
00029 
00030 // FIFE includes
00031 // These includes are split up in two parts, separated by one empty line
00032 // First block: files included from the FIFE root src directory
00033 // Second block: files included from the same folder
00034 #include "vfs/raw/rawdata.h"
00035 #include "vfs/fife_boost_filesystem.h"
00036 #include "util/base/exception.h"
00037 #include "util/log/logger.h"
00038 
00039 #include "zipsource.h"
00040 #include "zipfilesource.h"
00041 #include "zipnode.h"
00042 
00043 namespace FIFE {
00044 
00045     static const uint32_t LF_HEADER = 0x04034b50;
00046     static const uint32_t DE_HEADER = 0x08064b50;
00047     static const uint32_t CF_HEADER = 0x02014b50;
00048 
00049     static Logger _log(LM_LOADERS);
00050 
00051     ZipSource::ZipSource(VFS* vfs, const std::string& zip_file) : VFSSource(vfs), m_zipfile(vfs->open(zip_file)) {
00052         readIndex();
00053     }
00054 
00055     ZipSource::~ZipSource() {
00056         delete m_zipfile;
00057     }
00058 
00059     bool ZipSource::fileExists(const std::string& file) const {
00060         bfs::path path(file);
00061         return (m_zipTree.getNode(path.string()) != 0);
00062     }
00063 
00064     RawData* ZipSource::open(const std::string& path) const {
00065         bfs::path filePath(path);
00066         ZipNode* node = m_zipTree.getNode(filePath.string());
00067 
00068         assert(node != 0);
00069 
00070         if (node) {
00071             const ZipEntryData& entryData = node->getZipEntryData();
00072 
00073             m_zipfile->setIndex(entryData.offset);
00074             uint8_t* data = new uint8_t[entryData.size_real]; // beware of me - one day i WILL cause memory leaks
00075             if (entryData.comp == 8) { // compressed using deflate
00076                 FL_DBG(_log, LMsg("trying to uncompress file ") <<  path << " (compressed with method " << entryData.comp << ")");
00077                 boost::scoped_array<uint8_t> compdata(new uint8_t[entryData.size_comp]);
00078                 m_zipfile->readInto(compdata.get(), entryData.size_comp);
00079 
00080                 z_stream zstream;
00081                 zstream.next_in = compdata.get();
00082                 zstream.avail_in = entryData.size_comp;
00083                 zstream.zalloc = Z_NULL;
00084                 zstream.zfree = Z_NULL;
00085                 zstream.opaque = Z_NULL;
00086                 zstream.next_out = data;
00087                 zstream.avail_out = entryData.size_real;
00088 
00089                 if (inflateInit2(&zstream, -15) != Z_OK) {
00090                     FL_ERR(_log, LMsg("inflateInit2 failed"));
00091                     delete[] data;
00092                     return 0;
00093                 }
00094 
00095                 int32_t err = inflate(&zstream, Z_FINISH);
00096                 if (err != Z_STREAM_END) {
00097                     if (zstream.msg) {
00098                         FL_ERR(_log, LMsg("inflate failed: ") << zstream.msg);
00099                     } else {
00100                         FL_ERR(_log, LMsg("inflate failed without msg, err: ") << err);
00101                     }
00102 
00103                     inflateEnd(&zstream);
00104                     delete[] data;
00105                     return 0;
00106                 }
00107 
00108                 inflateEnd(&zstream);
00109             } else if (entryData.comp == 0) { // uncompressed
00110                 m_zipfile->readInto(data, entryData.size_real);
00111             } else {
00112                 FL_ERR(_log, LMsg("unsupported compression"));
00113                 return 0;
00114             }
00115 
00116             return new RawData(new ZipFileSource(data, entryData.size_real));
00117         }
00118 
00119         return 0;
00120     }
00121 
00122     void ZipSource::readIndex() {
00123         m_zipfile->setIndex(0);
00124 
00125         while (!readFileToIndex()) {}
00126     }
00127 
00128     bool ZipSource::readFileToIndex() {
00129         uint32_t header   = m_zipfile->read32Little();
00130         if (header == DE_HEADER || header == CF_HEADER) { // decryption header or central directory header - we are finished
00131             return true;
00132         }
00133 
00134         uint16_t vneeded  = m_zipfile->read16Little();
00135         uint16_t gflags   = m_zipfile->read16Little();
00136         uint16_t comp     = m_zipfile->read16Little();
00137         uint16_t lmodtime = m_zipfile->read16Little();
00138         uint16_t lmoddate = m_zipfile->read16Little();
00139         uint32_t crc      = m_zipfile->read32Little();
00140         uint32_t compsize = m_zipfile->read32Little();
00141         uint32_t realsize = m_zipfile->read32Little();
00142         uint16_t fnamelen = m_zipfile->read16Little();
00143         uint16_t extralen = m_zipfile->read16Little();
00144 
00145         if (header != LF_HEADER) {
00146             FL_ERR(_log, LMsg("invalid local file header: ") << header);
00147             return true;
00148         }
00149 
00150         if (vneeded > 20) {
00151             FL_ERR(_log, LMsg("only zip version 2 is supported, required: ") << vneeded);
00152             return true;
00153         }
00154 
00155         bfs::path filePath = bfs::path(m_zipfile->readString(fnamelen));
00156 
00157         m_zipfile->moveIndex(extralen);
00158         uint32_t offset = m_zipfile->getCurrentIndex();
00159         FL_DBG(_log, LMsg("found file: ") << filePath.string() << " (" << compsize << "/" << realsize << ") on offset " << offset);
00160 
00161         m_zipfile->moveIndex(compsize);
00162         if (gflags & (0x01 << 3)) {
00163             crc = m_zipfile->read32Little();
00164             compsize = m_zipfile->read32Little();
00165             realsize = m_zipfile->read32Little();
00166         }
00167 
00168         if (lmodtime || lmoddate) {} // shut up the compiler (warnings of unused variables)
00169         
00170         ZipEntryData data;
00171         data.comp = comp;
00172         data.size_real = realsize;
00173         data.size_comp = compsize;
00174         data.offset = offset;
00175         data.crc32 = crc;
00176 
00177         std::string filename = filePath.string();
00178         ZipNode* node = m_zipTree.addNode(filename);
00179 
00180         if (node) {
00181             // store the zip entry information in the node
00182             node->setZipEntryData(data);
00183         }
00184 
00185         return false;
00186     }
00187 
00188 
00189     std::set<std::string> ZipSource::listFiles(const std::string& path) const {
00190         std::set<std::string> result;
00191 
00192         bfs::path fixedPath(path);
00193 
00194         ZipNode* node = m_zipTree.getNode(fixedPath.string());
00195         
00196         if (node) {
00197             ZipNodeContainer files = node->getChildren(ZipContentType::File);
00198             ZipNodeContainer::iterator iter;
00199             for (iter = files.begin(); iter != files.end(); ++iter) {
00200                 result.insert((*iter)->getFullName());
00201             }
00202         }
00203 
00204         return result;
00205     }
00206 
00207     // FIXME: quick&very dirty..
00208     std::set<std::string> ZipSource::listDirectories(const std::string& path) const {
00209         std::set<std::string> result;
00210 
00211         bfs::path fixedPath(path);
00212 
00213         ZipNode* node = m_zipTree.getNode(fixedPath.string());
00214 
00215         if (node) {
00216             ZipNodeContainer files = node->getChildren(ZipContentType::Directory);
00217             ZipNodeContainer::iterator iter;
00218             for (iter = files.begin(); iter != files.end(); ++iter) {
00219                 result.insert((*iter)->getFullName());
00220             }
00221         }
00222 
00223         return result;
00224     }
00225 }