summaryrefslogtreecommitdiff
path: root/src/fs/virtfs/fszip.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/virtfs/fszip.cpp')
-rw-r--r--src/fs/virtfs/fszip.cpp668
1 files changed, 668 insertions, 0 deletions
diff --git a/src/fs/virtfs/fszip.cpp b/src/fs/virtfs/fszip.cpp
new file mode 100644
index 000000000..3781380ae
--- /dev/null
+++ b/src/fs/virtfs/fszip.cpp
@@ -0,0 +1,668 @@
+/*
+ * 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/fszip.h"
+
+#include "fs/virtfs/file.h"
+#include "fs/virtfs/fsfuncs.h"
+#include "fs/virtfs/virtfsziprwops.h"
+#include "fs/virtfs/virtlist.h"
+#include "fs/virtfs/virtzipentry.h"
+#include "fs/virtfs/zipreader.h"
+#include "fs/virtfs/ziplocalheader.h"
+
+#include "utils/checkutils.h"
+#include "utils/stringutils.h"
+
+#include "debug.h"
+
+extern const char *dirSeparator;
+
+namespace
+{
+ VirtFs::FsFuncs funcs;
+} // namespace
+
+namespace VirtFs
+{
+
+namespace FsZip
+{
+ FsFuncs *getFuncs()
+ {
+ return &funcs;
+ }
+
+ void deinit()
+ {
+ }
+
+ void init()
+ {
+ initFuncs(&funcs);
+ }
+
+ void initFuncs(FsFuncs *restrict const ptr)
+ {
+ ptr->close = &FsZip::close;
+ ptr->read = &FsZip::read;
+ ptr->write = &FsZip::write;
+ ptr->fileLength = &FsZip::fileLength;
+ ptr->tell = &FsZip::tell;
+ ptr->seek = &FsZip::seek;
+ ptr->eof = &FsZip::eof;
+ ptr->exists = &FsZip::exists;
+ ptr->getRealDir = &FsZip::getRealDir;
+ ptr->enumerate = &FsZip::enumerate;
+ ptr->isDirectory = &FsZip::isDirectory;
+ ptr->openRead = &FsZip::openRead;
+ ptr->openWrite = &FsZip::openWrite;
+ ptr->openAppend = &FsZip::openAppend;
+ ptr->loadFile = &FsZip::loadFile;
+ ptr->getFiles = &FsZip::getFiles;
+ ptr->getFilesWithDir = &FsZip::getFilesWithDir;
+ ptr->getDirs = &FsZip::getDirs;
+ ptr->rwops_seek = &FsZip::rwops_seek;
+ ptr->rwops_read = &FsZip::rwops_read;
+ ptr->rwops_write = &FsZip::rwops_write;
+ ptr->rwops_close = &FsZip::rwops_close;
+#ifdef USE_SDL2
+ ptr->rwops_size = &FsZip::rwops_size;
+#endif // USE_SDL2
+ }
+
+ bool getRealDir(FsEntry *restrict const entry,
+ const std::string &filename,
+ const std::string &dirName,
+ std::string &realDir)
+ {
+ VirtZipEntry *const zipEntry = static_cast<VirtZipEntry*>(entry);
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->mHeaders)
+ {
+ if ((*it2)->fileName == filename)
+ {
+ realDir = entry->root;
+ return true;
+ }
+ }
+ FOR_EACH (std::vector<std::string>::const_iterator,
+ it2,
+ zipEntry->mDirs)
+ {
+ if (*it2 == dirName)
+ {
+ realDir = entry->root;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool exists(FsEntry *restrict const entry,
+ const std::string &filename,
+ const std::string &dirName)
+ {
+ VirtZipEntry *const zipEntry = static_cast<VirtZipEntry*>(entry);
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->mHeaders)
+ {
+ if ((*it2)->fileName == filename)
+ return true;
+ }
+ FOR_EACH (std::vector<std::string>::const_iterator,
+ it2,
+ zipEntry->mDirs)
+ {
+ if (*it2 == dirName)
+ return true;
+ }
+ return false;
+ }
+
+ void enumerate(FsEntry *restrict const entry,
+ const std::string &dirName,
+ StringVect &names)
+ {
+ VirtZipEntry *const zipEntry = static_cast<VirtZipEntry*>(entry);
+ if (dirName == dirSeparator)
+ {
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->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<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->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);
+ }
+ }
+ }
+ }
+
+ void getFiles(FsEntry *restrict const entry,
+ const std::string &dirName,
+ StringVect &names)
+ {
+ VirtZipEntry *const zipEntry = static_cast<VirtZipEntry*>(entry);
+ if (dirName == dirSeparator)
+ {
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->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)
+ {
+ std::string dirName2 = pathJoin(dirName, fileName);
+ if (findLast(dirName2, std::string(dirSeparator)) == false)
+ dirName2 += dirSeparator;
+ FOR_EACH (std::vector<std::string>::const_iterator,
+ it,
+ zipEntry->mDirs)
+ {
+ if (*it == dirName2)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found == false)
+ names.push_back(fileName);
+ }
+ }
+ }
+ else
+ {
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->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)
+ {
+ std::string dirName2 = pathJoin(dirName, fileName);
+ if (findLast(dirName2, std::string(dirSeparator)) ==
+ false)
+ {
+ dirName2 += dirSeparator;
+ }
+ FOR_EACH (std::vector<std::string>::const_iterator,
+ it,
+ zipEntry->mDirs)
+ {
+ if (*it == dirName2)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found == false)
+ names.push_back(fileName);
+ }
+ }
+ }
+ }
+ }
+
+ void getFilesWithDir(FsEntry *restrict const entry,
+ const std::string &dirName,
+ StringVect &names)
+ {
+ VirtZipEntry *const zipEntry = static_cast<VirtZipEntry*>(entry);
+ if (dirName == dirSeparator)
+ {
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->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)
+ {
+ std::string dirName2 = pathJoin(dirName, fileName);
+ if (findLast(dirName2, std::string(dirSeparator)) == false)
+ dirName2 += dirSeparator;
+ FOR_EACH (std::vector<std::string>::const_iterator,
+ it,
+ zipEntry->mDirs)
+ {
+ if (*it == dirName2)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found == false)
+ names.push_back(pathJoin(dirName, fileName));
+ }
+ }
+ }
+ else
+ {
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->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)
+ {
+ std::string dirName2 = pathJoin(dirName, fileName);
+ if (findLast(dirName2, std::string(dirSeparator)) ==
+ false)
+ {
+ dirName2 += dirSeparator;
+ }
+ FOR_EACH (std::vector<std::string>::const_iterator,
+ it,
+ zipEntry->mDirs)
+ {
+ if (*it == dirName2)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found == false)
+ names.push_back(pathJoin(dirName, fileName));
+ }
+ }
+ }
+ }
+ }
+
+ void getDirs(FsEntry *restrict const entry,
+ const std::string &dirName,
+ StringVect &names)
+ {
+ VirtZipEntry *const zipEntry = static_cast<VirtZipEntry*>(entry);
+ if (dirName == dirSeparator)
+ {
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->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)
+ {
+ std::string dirName2 = pathJoin(dirName, fileName);
+ if (findLast(dirName2, std::string(dirSeparator)) == false)
+ dirName2 += dirSeparator;
+ FOR_EACH (std::vector<std::string>::const_iterator,
+ it,
+ zipEntry->mDirs)
+ {
+ if (*it == dirName2)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found == true)
+ names.push_back(fileName);
+ }
+ }
+ }
+ else
+ {
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->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)
+ {
+ std::string dirName2 = pathJoin(dirName, fileName);
+ if (findLast(dirName2, std::string(dirSeparator)) ==
+ false)
+ {
+ dirName2 += dirSeparator;
+ }
+ FOR_EACH (std::vector<std::string>::const_iterator,
+ it,
+ zipEntry->mDirs)
+ {
+ if (*it == dirName2)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found == true)
+ names.push_back(fileName);
+ }
+ }
+ }
+ }
+ }
+
+ bool isDirectory(FsEntry *restrict const entry,
+ const std::string &dirName,
+ bool &isDirFlag)
+ {
+ VirtZipEntry *const zipEntry = static_cast<VirtZipEntry*>(entry);
+ FOR_EACH (std::vector<std::string>::const_iterator,
+ it2,
+ zipEntry->mDirs)
+ {
+ if (*it2 == dirName)
+ {
+ isDirFlag = true;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void freeList(VirtList *restrict const handle)
+ {
+ delete handle;
+ }
+
+ File *openRead(FsEntry *restrict const entry,
+ const std::string &filename)
+ {
+ VirtZipEntry *const zipEntry = static_cast<VirtZipEntry*>(entry);
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->mHeaders)
+ {
+ const ZipLocalHeader *restrict const header = *it2;
+ if (header->fileName == filename)
+ {
+ const uint8_t *restrict const buf =
+ ZipReader::readFile(header);
+ if (buf == nullptr)
+ return nullptr;
+ File *restrict const file = new File(&funcs,
+ buf,
+ header->uncompressSize);
+ return file;
+ }
+ }
+ return nullptr;
+ }
+
+ File *openWrite(FsEntry *restrict const entry A_UNUSED,
+ const std::string &filename A_UNUSED)
+ {
+ reportAlways("VirtFs::openWrite for zip not implemented.");
+ return nullptr;
+ }
+
+ File *openAppend(FsEntry *restrict const entry A_UNUSED,
+ const std::string &filename A_UNUSED)
+ {
+ reportAlways("VirtFs::openAppend for zip not implemented.");
+ return nullptr;
+ }
+
+ int close(File *restrict const file)
+ {
+ if (file == nullptr)
+ return 0;
+ delete file;
+ return 1;
+ }
+
+ int64_t read(File *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("FsZip::read buffer is null");
+ return 0;
+ }
+ const size_t pos = file->mPos;
+ const size_t sz = file->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 = file->mBuf + pos;
+ // left buffer size from pos to end
+ const uint32_t memSize = CAST_U32(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 size_t memEnd = memCount * objSize;
+ memcpy(buffer, memPtr, memEnd);
+ file->mPos += memEnd;
+ return memCount;
+ }
+
+ int64_t write(File *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(File *restrict const file)
+ {
+ if (file == nullptr)
+ return -1;
+
+ return file->mSize;
+ }
+
+ int64_t tell(File *restrict const file)
+ {
+ if (file == nullptr)
+ return -1;
+
+ return file->mPos;
+ }
+
+ int seek(File *restrict const file,
+ const uint64_t pos)
+ {
+ if (file == nullptr)
+ return 0;
+
+ if (pos > file->mSize)
+ return 0;
+ file->mPos = pos;
+ return 1;
+ }
+
+ int eof(File *restrict const file)
+ {
+ if (file == nullptr)
+ return -1;
+
+ return file->mPos >= file->mSize;
+ }
+
+ const char *loadFile(FsEntry *restrict const entry,
+ const std::string &restrict filename,
+ int &restrict fileSize)
+ {
+ VirtZipEntry *const zipEntry = static_cast<VirtZipEntry*>(entry);
+ FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
+ it2,
+ zipEntry->mHeaders)
+ {
+ const ZipLocalHeader *restrict const header = *it2;
+ if (header->fileName == filename)
+ {
+ const uint8_t *restrict const buf =
+ ZipReader::readFile(header);
+ if (buf == nullptr)
+ return nullptr;
+
+ logger->log("Loaded %s/%s",
+ entry->root.c_str(),
+ filename.c_str());
+
+ fileSize = header->uncompressSize;
+ return reinterpret_cast<const char*>(buf);
+ }
+ }
+ return nullptr;
+ }
+} // namespace FsZip
+
+} // namespace VirtFs