/* * The ManaPlus Client * Copyright (C) 2013-2017 The ManaPlus Developers * * This file is part of The ManaPlus Client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef USE_PHYSFS #include "fs/virtfs/virtfszip.h" #include "fs/files.h" #include "fs/paths.h" #include "fs/virtfsfuncs.h" #include "fs/virtfile.h" #include "fs/virtlist.h" #include "fs/virtfs/virtfileprivate.h" #include "fs/virtfs/virtzipentry.h" #include "fs/virtfs/zip.h" #include "fs/virtfs/ziplocalheader.h" #include "utils/checkutils.h" #include "utils/dtor.h" #include "utils/stringutils.h" #include "debug.h" extern const char *dirSeparator; namespace { std::vector mEntries; VirtFsFuncs funcs; } // namespace namespace VirtFsZip { VirtZipEntry *searchEntryByArchive(const std::string &restrict archiveName) { FOR_EACH (std::vector::const_iterator, it, mEntries) { if ((*it)->mArchiveName == archiveName) return *it; } return nullptr; } ZipLocalHeader *searchHeaderByName(const std::string &restrict filename) { FOR_EACH (std::vector::const_iterator, it, mEntries) { VirtZipEntry *const entry = *it; FOR_EACH (std::vector::const_iterator, it2, entry->mHeaders) { if ((*it2)->fileName == filename) return *it2; } } return nullptr; } VirtZipEntry *searchZipEntryByNameWithDir(const std::string &restrict filename) { std::string dirName = filename; if (findLast(dirName, std::string(dirSeparator)) == false) dirName += dirSeparator; FOR_EACH (std::vector::const_iterator, it, mEntries) { VirtZipEntry *const entry = *it; FOR_EACH (std::vector::const_iterator, it2, entry->mHeaders) { if ((*it2)->fileName == filename) return entry; } FOR_EACH (std::vector::const_iterator, it2, entry->mDirs) { if (*it2 == dirName) return entry; } } return nullptr; } bool addToSearchPathSilent(std::string newDir, const Append append) { prepareFsPath(newDir); if (Files::existsLocal(newDir) == false) { logger->log("VirtFsZip::addToSearchPath file not exists: %s", newDir.c_str()); return false; } if (findLast(newDir, ".zip") == false) { reportAlways("Called VirtFsZip::addToSearchPath without " "zip archive"); return false; } VirtZipEntry *entry = VirtFsZip::searchEntryByArchive(newDir); if (entry != nullptr) { reportAlways("VirtFsZip::addToSearchPath already exists: %s", newDir.c_str()); return false; } entry = new VirtZipEntry(newDir); if (Zip::readArchiveInfo(entry) == false) { delete entry; return false; } logger->log("Add virtual zip: " + newDir); if (append == Append_true) mEntries.push_back(entry); else { mEntries.insert(mEntries.begin(), entry); } return true; } bool addToSearchPath(std::string newDir, const Append append) { prepareFsPath(newDir); if (Files::existsLocal(newDir) == false) { reportAlways("VirtFsZip::addToSearchPath directory not exists: %s", newDir.c_str()); return false; } if (findLast(newDir, ".zip") == false) { reportAlways("Called VirtFsZip::addToSearchPath without " "zip archive"); return false; } VirtZipEntry *entry = VirtFsZip::searchEntryByArchive(newDir); if (entry != nullptr) { reportAlways("VirtFsZip::addToSearchPath already exists: %s", newDir.c_str()); return false; } entry = new VirtZipEntry(newDir); if (Zip::readArchiveInfo(entry) == false) { delete entry; return false; } logger->log("Add virtual zip: " + newDir); if (append == Append_true) mEntries.push_back(entry); else { mEntries.insert(mEntries.begin(), entry); } return true; } bool removeFromSearchPathSilent(std::string oldDir) { prepareFsPath(oldDir); if (findLast(oldDir, ".zip") == false) { reportAlways("Called removeFromSearchPath without zip archive"); return false; } FOR_EACH (std::vector::iterator, it, mEntries) { VirtZipEntry *const entry = *it; if (entry->mArchiveName == oldDir) { logger->log("Remove virtual zip: " + oldDir); mEntries.erase(it); delete entry; return true; } } logger->log("VirtFsZip::removeFromSearchPath not exists: %s", oldDir.c_str()); return false; } bool removeFromSearchPath(std::string oldDir) { prepareFsPath(oldDir); if (findLast(oldDir, ".zip") == false) { reportAlways("Called removeFromSearchPath without zip archive"); return false; } FOR_EACH (std::vector::iterator, it, mEntries) { VirtZipEntry *const entry = *it; if (entry->mArchiveName == oldDir) { logger->log("Remove virtual zip: " + oldDir); mEntries.erase(it); delete entry; return true; } } reportAlways("VirtFsZip::removeFromSearchPath not exists: %s", oldDir.c_str()); return false; } std::vector &getEntries() { return mEntries; } void deinit() { delete_all(mEntries); mEntries.clear(); } void init() { initFuncs(&funcs); } void initFuncs(VirtFsFuncs *restrict const ptr) { ptr->close = &VirtFsZip::close; ptr->read = &VirtFsZip::read; ptr->write = &VirtFsZip::write; ptr->fileLength = &VirtFsZip::fileLength; ptr->tell = &VirtFsZip::tell; ptr->seek = &VirtFsZip::seek; ptr->eof = &VirtFsZip::eof; } std::string getRealDir(std::string filename) { prepareFsPath(filename); if (checkPath(filename) == false) { reportAlways("VirtFsZip::exists invalid path: %s", filename.c_str()); return std::string(); } return getRealDirInternal(filename); } std::string getRealDirInternal(const std::string &filename) { VirtZipEntry *restrict const entry = searchZipEntryByNameWithDir( filename); if (entry != nullptr) return entry->mArchiveName; return std::string(); } bool exists(std::string name) { prepareFsPath(name); if (checkPath(name) == false) { reportAlways("VirtFsZip::exists invalid path: %s", name.c_str()); return false; } VirtZipEntry *restrict const entry = searchZipEntryByNameWithDir( name); return entry != nullptr; } VirtList *enumerateFiles(std::string dirName) { VirtList *const list = new VirtList; prepareFsPath(dirName); if (checkPath(dirName) == false) { reportAlways("VirtFsZip::enumerateFiles invalid path: %s", dirName.c_str()); return list; } return enumerateFiles(dirName, list); } VirtList *enumerateFiles(std::string dirName, VirtList *restrict const list) { if (findLast(dirName, std::string(dirSeparator)) == false) dirName += dirSeparator; StringVect &names = list->names; if (dirName == "/") { FOR_EACH (std::vector::const_iterator, it, mEntries) { VirtZipEntry *const entry = *it; FOR_EACH (std::vector::const_iterator, it2, entry->mHeaders) { ZipLocalHeader *const header = *it2; std::string fileName = header->fileName; // skip subdirs from enumeration const size_t idx = fileName.find(dirSeparator); if (idx != std::string::npos) fileName.erase(idx); bool found(false); FOR_EACH (StringVectCIter, itn, names) { if (*itn == fileName) { found = true; break; } } if (found == false) names.push_back(fileName); } } } else { FOR_EACH (std::vector::const_iterator, it, mEntries) { VirtZipEntry *const entry = *it; FOR_EACH (std::vector::const_iterator, it2, entry->mHeaders) { ZipLocalHeader *const header = *it2; std::string fileName = header->fileName; if (findCutFirst(fileName, dirName) == true) { // skip subdirs from enumeration const size_t idx = fileName.find(dirSeparator); if (idx != std::string::npos) fileName.erase(idx); bool found(false); FOR_EACH (StringVectCIter, itn, names) { if (*itn == fileName) { found = true; break; } } if (found == false) names.push_back(fileName); } } } } return list; } bool isDirectory(std::string dirName) { prepareFsPath(dirName); if (checkPath(dirName) == false) { reportAlways("VirtFsZip::isDirectory invalid path: %s", dirName.c_str()); return false; } return isDirectoryInternal(dirName); } bool isDirectoryInternal(std::string dirName) { if (findLast(dirName, std::string(dirSeparator)) == false) dirName += dirSeparator; FOR_EACH (std::vector::const_iterator, it, mEntries) { VirtZipEntry *const entry = *it; FOR_EACH (std::vector::const_iterator, it2, entry->mDirs) { if (*it2 == dirName) return true; } } return false; } bool isSymbolicLink(std::string name) { prepareFsPath(name); if (checkPath(name) == false) { reportAlways("VirtFsZip::isSymbolicLink invalid path: %s", name.c_str()); return false; } // look like in zip files can be symlinks, but here they useless return false; } void freeList(VirtList *restrict const handle) { delete handle; } VirtFile *openRead(std::string filename) { prepareFsPath(filename); if (checkPath(filename) == false) { reportAlways("VirtFsZip::openRead invalid path: %s", filename.c_str()); return nullptr; } return openReadInternal(filename); } VirtFile *openReadInternal(const std::string &filename) { ZipLocalHeader *restrict const header = searchHeaderByName(filename); if (header != nullptr) { uint8_t *restrict const buf = Zip::readFile(header); if (buf == nullptr) return nullptr; VirtFile *restrict const file = new VirtFile(&funcs); file->mPrivate = new VirtFilePrivate(buf, header->uncompressSize); return file; } return nullptr; } VirtFile *openWrite(const std::string &restrict filename A_UNUSED) { return nullptr; } VirtFile *openAppend(const std::string &restrict filename A_UNUSED) { return nullptr; } bool setWriteDir(const std::string &restrict newDir A_UNUSED) { return false; } bool mkdir(const std::string &restrict dirname A_UNUSED) { return false; } bool remove(const std::string &restrict filename A_UNUSED) { return false; } void permitLinks(const bool val A_UNUSED) { } const char *getLastError() { return nullptr; } int close(VirtFile *restrict const file) { if (file == nullptr) return 0; delete file; return 1; } int64_t read(VirtFile *restrict const file, void *restrict const buffer, const uint32_t objSize, const uint32_t objCount) { if (file == nullptr || objSize == 0 || objCount == 0) { return 0; } if (buffer == nullptr) { reportAlways("VirtFsZip::read buffer is null"); return 0; } VirtFilePrivate *restrict const priv = file->mPrivate; const uint32_t pos = priv->mPos; const uint32_t sz = priv->mSize; // if outside of buffer, return if (pos >= sz) return 0; // pointer to start for buffer ready to read const uint8_t *restrict const memPtr = priv->mBuf + pos; // left buffer size from pos to end const uint32_t memSize = sz - pos; // number of objects possible to read uint32_t memCount = memSize / objSize; if (memCount == 0) return 0; // limit number of possible objects to read to objCount if (memCount > objCount) memCount = objCount; // number of bytes to read from buffer const uint32_t memEnd = memCount * objSize; memcpy(buffer, memPtr, memEnd); priv->mPos += memEnd; return memCount; } int64_t write(VirtFile *restrict const file A_UNUSED, const void *restrict const buffer A_UNUSED, const uint32_t objSize A_UNUSED, const uint32_t objCount A_UNUSED) { return 0; } int64_t fileLength(VirtFile *restrict const file) { if (file == nullptr) return -1; return file->mPrivate->mSize; } int64_t tell(VirtFile *restrict const file) { if (file == nullptr) return -1; return file->mPrivate->mPos; } int seek(VirtFile *restrict const file, const uint64_t pos) { if (file == nullptr) return 0; if (pos > file->mPrivate->mSize) return 0; file->mPrivate->mPos = pos; return 1; } int eof(VirtFile *restrict const file) { if (file == nullptr) return -1; return file->mPrivate->mPos >= file->mPrivate->mSize; } } // namespace VirtFsZip #endif // USE_PHYSFS