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 "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];
00068 if (info.comp == 8) {
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) {
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) {
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) {}
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
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
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 }