summaryrefslogtreecommitdiff
path: root/src/fs/virtfs/virtfszip.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/virtfs/virtfszip.cpp')
-rw-r--r--src/fs/virtfs/virtfszip.cpp555
1 files changed, 555 insertions, 0 deletions
diff --git a/src/fs/virtfs/virtfszip.cpp b/src/fs/virtfs/virtfszip.cpp
new file mode 100644
index 000000000..c4ce6f9e8
--- /dev/null
+++ b/src/fs/virtfs/virtfszip.cpp
@@ -0,0 +1,555 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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<VirtZipEntry*> mEntries;
+ VirtFsFuncs funcs;
+} // namespace
+
+namespace VirtFsZip
+{
+ VirtZipEntry *searchEntryByArchive(const std::string &restrict archiveName)
+ {
+ FOR_EACH (std::vector<VirtZipEntry*>::const_iterator, it, mEntries)
+ {
+ if ((*it)->mArchiveName == archiveName)
+ return *it;
+ }
+ return nullptr;
+ }
+
+ ZipLocalHeader *searchHeaderByName(const std::string &restrict filename)
+ {
+ FOR_EACH (std::vector<VirtZipEntry*>::const_iterator, it, mEntries)
+ {
+ VirtZipEntry *const entry = *it;
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ entry->mHeaders)
+ {
+ if ((*it2)->fileName == filename)
+ return *it2;;
+ }
+ }
+ 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<VirtZipEntry*>::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<VirtZipEntry*>::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<VirtZipEntry*> &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)
+ {
+ ZipLocalHeader *restrict const header = searchHeaderByName(filename);
+ if (header != nullptr)
+ return header->zipEntry->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;
+ }
+ ZipLocalHeader *restrict const header = searchHeaderByName(name);
+ if (header != nullptr)
+ return true;
+ return false;
+ }
+
+ 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<VirtZipEntry*>::const_iterator, it, mEntries)
+ {
+ VirtZipEntry *const entry = *it;
+ FOR_EACH (std::vector<ZipLocalHeader*>::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<VirtZipEntry*>::const_iterator, it, mEntries)
+ {
+ VirtZipEntry *const entry = *it;
+ FOR_EACH (std::vector<ZipLocalHeader*>::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<VirtZipEntry*>::const_iterator, it, mEntries)
+ {
+ VirtZipEntry *const entry = *it;
+ FOR_EACH (std::vector<std::string>::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