#define VCMI_DLL #include "../stdafx.h" #include "zlib.h" #include "CLodHandler.h" #include #include #include #include #include #include "boost/filesystem/operations.hpp" #include #include #include #include #include #ifdef max #undef max #endif /* * CLodHandler.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ int readNormalNr (const unsigned char * bufor, int pos, int bytCon, bool cyclic) { int ret=0; int amp=1; for (int ir=0; ir=amp/2) { ret = ret-amp; } return ret; } char readChar(const unsigned char * bufor, int &i) { return bufor[i++]; } std::string readString(const unsigned char * bufor, int &i) { int len = readNormalNr(bufor,i); i+=4; assert(len >= 0 && len <= 500000); //not too long std::string ret; ret.reserve(len); for(int gg=0; gg::const_iterator en_it = entries.find(Entry(fname, type)); if(en_it == entries.end()) //nothing's been found { tlog1 << "Cannot find file: " << fname << std::endl; return NULL; } Entry ourEntry = *en_it; if(length) *length = ourEntry.realSize; mutex->lock(); unsigned char * outp; if (ourEntry.offset<0) //file is in the sprites/ folder; no compression { int result; outp = new unsigned char[ourEntry.realSize]; FILE * f = fopen((myDir + "/" + ourEntry.realName).c_str(), "rb"); if (f) { result = fread(outp,1,ourEntry.realSize,f); fclose(f); } else result = -1; mutex->unlock(); if(result<0) { tlog1<<"Error in file reading: " << myDir << "/" << ourEntry.name << std::endl; delete[] outp; return NULL; } else return outp; } else if (ourEntry.size==0) //file is not compressed { outp = new unsigned char[ourEntry.realSize]; LOD.seekg(ourEntry.offset, std::ios::beg); LOD.read((char*)outp, ourEntry.realSize); mutex->unlock(); return outp; } else //we will decompress file { outp = new unsigned char[ourEntry.size]; LOD.seekg(ourEntry.offset, std::ios::beg); LOD.read((char*)outp, ourEntry.size); unsigned char * decomp = NULL; infs2(outp, ourEntry.size, ourEntry.realSize, decomp); mutex->unlock(); delete[] outp; return decomp; } return NULL; } bool CLodHandler::haveFile(const std::string name, LodFileType type) { std::string fname = name; std::transform(fname.begin(), fname.end(), fname.begin(), (int(*)(int))toupper); int dotPos = fname.find_last_of('.'); if ( dotPos != -1 ) fname.erase(dotPos); return vstd::contains(entries, Entry(fname, type)); } DLL_EXPORT int CLodHandler::infs2(unsigned char * in, int size, int realSize, unsigned char *& out, int wBits) { int ret; unsigned have; z_stream strm; out = new unsigned char [realSize]; int latPosOut = 0; /* allocate inflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit2(&strm, wBits); if (ret != Z_OK) return ret; int chunkNumber = 0; do { if(size < chunkNumber * NLoadHandlerHelp::fCHUNK) break; strm.avail_in = std::min(NLoadHandlerHelp::fCHUNK, size - chunkNumber * NLoadHandlerHelp::fCHUNK); if (strm.avail_in == 0) break; strm.next_in = in + chunkNumber * NLoadHandlerHelp::fCHUNK; /* run inflate() on input until output buffer not full */ do { strm.avail_out = realSize - latPosOut; strm.next_out = out + latPosOut; ret = inflate(&strm, Z_NO_FLUSH); //assert(ret != Z_STREAM_ERROR); /* state not clobbered */ bool breakLoop = false; switch (ret) { case Z_STREAM_END: breakLoop = true; break; case Z_NEED_DICT: ret = Z_DATA_ERROR; /* and fall through */ case Z_DATA_ERROR: case Z_MEM_ERROR: (void)inflateEnd(&strm); return ret; } if(breakLoop) break; have = realSize - latPosOut - strm.avail_out; latPosOut += have; } while (strm.avail_out == 0); ++chunkNumber; /* done when inflate() says it's done */ } while (ret != Z_STREAM_END); /* clean up and return */ (void)inflateEnd(&strm); return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; } void CLodHandler::extractFile(const std::string FName, const std::string name) { int len; //length of file to write unsigned char * outp = giveFile(name, FILE_ANY, &len); std::ofstream out; out.open(FName.c_str(), std::ios::binary); if(!out.is_open()) { tlog1<<"Unable to create "<(outp), len); out.close(); } } void CLodHandler::initEntry(Entry &e, const std::string name) { e.name = name; //file names stored in upper case without extension std::transform(e.name.begin(), e.name.end(), e.name.begin(), toupper); std::string ext; size_t dotPos = e.name.find_last_of('.'); if ( dotPos < e.name.size() ) { ext = e.name.substr(dotPos); e.name.erase(dotPos); } std::map::iterator it = extMap.find(ext); if (it == extMap.end()) e.type = FILE_OTHER; else e.type = it->second; } void CLodHandler::init(const std::string lodFile, const std::string dirName) { #define EXT(NAME, TYPE) extMap.insert(std::pair(NAME, TYPE)); EXT(".TXT", FILE_TEXT); EXT(".DEF", FILE_ANIMATION); EXT(".MSK", FILE_MASK); EXT(".MSG", FILE_MASK); EXT(".H3C", FILE_CAMPAIGN); EXT(".H3M", FILE_MAP); EXT(".FNT", FILE_FONT); EXT(".BMP", FILE_GRAPHICS); EXT(".JPG", FILE_GRAPHICS); EXT(".PCX", FILE_GRAPHICS); EXT(".PNG", FILE_GRAPHICS); #undef EXT myDir = dirName; LOD.open(lodFile.c_str(), std::ios::in | std::ios::binary); if (!LOD.is_open()) { tlog1 << "Cannot open " << lodFile << std::endl; return; } Uint32 temp; LOD.seekg(8); LOD.read((char *)&temp, 4); totalFiles = SDL_SwapLE32(temp); LOD.seekg(0x5c, std::ios::beg); struct LodEntry *lodEntries = new struct LodEntry[totalFiles]; LOD.read((char *)lodEntries, sizeof(struct LodEntry) * totalFiles); for (unsigned int i=0; istatus())) { Entry e; e.realName = dir->path().leaf(); initEntry(e, e.realName); if(vstd::contains(entries, e)) //file present in .lod - overwrite its entry entries.erase(e); e.offset = -1; e.realSize = e.size = boost::filesystem::file_size(dir->path()); entries.insert(e); } } } else { tlog1<<"Warning: No "+dirName+"/ folder!"< mapstr; // Read a map by chunks // We could try to read the map size directly (cf RFC 1952) and then read // directly the whole map, but that would create more problems. do { unsigned char *buf = new unsigned char[bufsize]; int ret = gzread(map, buf, bufsize); if (ret == 0 || ret == -1) { delete [] buf; break; } mapstr.push_back(buf); mapsize += ret; } while(1); gzclose(map); // Now that we know the uncompressed size, reassemble the chunks unsigned char *initTable = new unsigned char[mapsize]; std::vector::iterator it; int offset; int tocopy = mapsize; for (it = mapstr.begin(), offset = 0; it != mapstr.end(); it++, offset+=bufsize ) { memcpy(&initTable[offset], *it, tocopy > bufsize ? bufsize : tocopy); tocopy -= bufsize; delete [] *it; } *sizeOut = mapsize; return initTable; }