00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <algorithm>
00024 #include <list>
00025
00026
00027 #include "zlib.h"
00028 #include <boost/scoped_array.hpp>
00029
00030
00031
00032
00033
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];
00075 if (entryData.comp == 8) {
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) {
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) {
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) {}
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
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
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 }