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 "util/base/exception.h"
00035 #include "vfs/raw/rawdata.h"
00036 #include "util/log/logger.h"
00037 
00038 #include "zipsource.h"
00039 #include "zipfilesource.h"
00040 
00041 namespace FIFE {
00042     
00043     static const uint32_t LF_HEADER = 0x04034b50;
00044     static const uint32_t DE_HEADER = 0x08064b50;
00045     static const uint32_t CF_HEADER = 0x02014b50;
00046 
00047     static Logger _log(LM_LOADERS);
00048     
00049     ZipSource::ZipSource(VFS* vfs, const std::string& zip_file) : VFSSource(vfs),  m_zipfile(vfs->open(zip_file)) {
00050         readIndex();
00051     }
00052 
00053     ZipSource::~ZipSource() {
00054         delete m_zipfile;
00055     }
00056     
00057     bool ZipSource::fileExists(const std::string& file) const {
00058         return m_files.find(file) != m_files.end();
00059     }
00060 
00061     RawData* ZipSource::open(const std::string& path) const {
00062         type_files::const_iterator i = m_files.find(path);
00063         assert(i != m_files.end());
00064         const s_data& info = i->second;
00065 
00066         m_zipfile->setIndex(info.offset);
00067         uint8_t* data = new uint8_t[info.size_real]; // beware of me - one day i WILL cause memory leaks
00068         if (info.comp == 8) { // compressed using deflate
00069             FL_DBG(_log, LMsg("trying to uncompress file ") <<  path << " (compressed with method " << info.comp << ")");
00070             boost::scoped_array<uint8_t> compdata(new uint8_t[info.size_comp]);
00071             m_zipfile->readInto(compdata.get(), info.size_comp);
00072 
00073             z_stream zstream;
00074             zstream.next_in = compdata.get();
00075             zstream.avail_in = info.size_comp;
00076             zstream.zalloc = Z_NULL;
00077             zstream.zfree = Z_NULL;
00078             zstream.opaque = Z_NULL;
00079             zstream.next_out = data;
00080             zstream.avail_out = info.size_real;
00081             
00082             if (inflateInit2(&zstream, -15) != Z_OK) {
00083                 FL_ERR(_log, LMsg("inflateInit2 failed"));
00084                 delete[] data;
00085                 return 0;
00086             }
00087 
00088             int err = inflate(&zstream, Z_FINISH);
00089             if (err != Z_STREAM_END) {
00090                 if (zstream.msg) {
00091                     FL_ERR(_log, LMsg("inflate failed: ") << zstream.msg);
00092                 } else {
00093                     FL_ERR(_log, LMsg("inflate failed without msg, err: ") << err);
00094                 }
00095                 
00096                 inflateEnd(&zstream);
00097                 delete[] data;
00098                 return 0;
00099             }
00100 
00101             inflateEnd(&zstream);
00102         } else if (info.comp == 0) { // uncompressed
00103             m_zipfile->readInto(data, info.size_real);
00104         } else {
00105             FL_ERR(_log, LMsg("unsupported compression"));
00106             return 0;
00107         }
00108 
00109         return new RawData(new ZipFileSource(data, info.size_real));
00110     }
00111 
00112     void ZipSource::readIndex() {
00113         m_zipfile->setIndex(0);
00114         m_files.clear();
00115 
00116         while (!readFileToIndex()) {}
00117     }
00118 
00119     bool ZipSource::readFileToIndex() {
00120         uint32_t header   = m_zipfile->read32Little();
00121         if (header == DE_HEADER || header == CF_HEADER) { // decryption header or central directory header - we are finished
00122             return true;
00123         }
00124 
00125         uint16_t vneeded  = m_zipfile->read16Little();
00126         uint16_t gflags   = m_zipfile->read16Little();
00127         uint16_t comp     = m_zipfile->read16Little();
00128         uint16_t lmodtime = m_zipfile->read16Little();
00129         uint16_t lmoddate = m_zipfile->read16Little();
00130         uint32_t crc      = m_zipfile->read32Little();
00131         uint32_t compsize = m_zipfile->read32Little();
00132         uint32_t realsize = m_zipfile->read32Little();
00133         uint16_t fnamelen = m_zipfile->read16Little();
00134         uint16_t extralen = m_zipfile->read16Little();
00135 
00136         if (header != LF_HEADER) {
00137             FL_ERR(_log, LMsg("invalid local file header: ") << header);
00138             return true;
00139         }
00140 
00141         if (vneeded > 20) {
00142             FL_ERR(_log, LMsg("only zip version 2 is supported, required: ") << vneeded);
00143             return true;
00144         }
00145 
00146         std::string filename = m_zipfile->readString(fnamelen);
00147         m_zipfile->moveIndex(extralen);
00148         unsigned int offset = m_zipfile->getCurrentIndex();
00149         FL_DBG(_log, LMsg("found file: ") << filename << " (" << compsize << "/" << realsize << ") on offset " << offset);
00150 
00151         m_zipfile->moveIndex(compsize);
00152         if (gflags & (0x01 << 3)) {
00153             crc = m_zipfile->read32Little();
00154             compsize = m_zipfile->read32Little();
00155             realsize = m_zipfile->read32Little();
00156         }
00157 
00158         if (lmodtime || lmoddate) {} // shut up the compiler (warnings of unused variables)
00159 
00160         s_data data;
00161         data.comp = comp;
00162         data.size_real = realsize;
00163         data.size_comp = compsize;
00164         data.offset = offset;
00165         data.path = filename;
00166         data.crc32 = crc;
00167 
00168         m_files[filename] = data;
00169         return false;
00170     }
00171 
00172 
00173     // FIXME: quick&very dirty.. 
00174     std::set<std::string> ZipSource::listFiles(const std::string& path) const {
00175         std::set<std::string> result;
00176         
00177         std::string fixedPath = fixPath(path);
00178         int path_len = path.length();
00179         if (fixedPath[path_len - 1] != '/') {
00180             fixedPath += '/';
00181             path_len++;
00182         }
00183         
00184         type_files::const_iterator end = m_files.end();
00185         for (type_files::const_iterator i = m_files.begin(); i != end; ++i) {
00186             std::string name = i->first;
00187             int len = name.length();
00188             if (len && name.find(fixedPath) == 0 && name[len - 1] != '/') {
00189                 name = name.substr(path_len);
00190                 size_t pos = name.find("/");
00191                 if (pos != std::string::npos)
00192                     continue;
00193                 
00194                 result.insert(name);
00195             }
00196         }
00197 
00198         return result;
00199     }
00200 
00201     // FIXME: quick&very dirty.. 
00202     std::set<std::string> ZipSource::listDirectories(const std::string& path) const {
00203         std::set<std::string> result;
00204 
00205         std::string fixedPath = fixPath(path);
00206         int path_len = path.length();
00207         if (fixedPath[path_len - 1] != '/') {
00208             fixedPath += '/';
00209             path_len++;
00210         }
00211 
00212         type_files::const_iterator end = m_files.end();
00213         for (type_files::const_iterator i = m_files.begin(); i != end; ++i) {
00214             std::string name = i->first;
00215             int len = name.length();
00216             if (len && name.find(fixedPath) == 0 && name[len - 1] == '/' && len > path_len) {
00217                 name = name.substr(path_len);
00218                 size_t pos = name.find("/");
00219                 if (pos != std::string::npos) {
00220                     name = name.substr(0, pos);
00221                 }
00222                 result.insert(name);
00223             }
00224         }
00225 
00226         return result;
00227     }
00228 }
Generated on Wed Nov 23 13:04:43 2011 for FIFE by  doxygen 1.6.3