diff options
author | Andrei Karas <akaras@inbox.ru> | 2017-02-25 19:10:51 +0300 |
---|---|---|
committer | Andrei Karas <akaras@inbox.ru> | 2017-02-25 19:11:57 +0300 |
commit | e6198149823ee18e4167b0e6dd34cfb7f2e2f496 (patch) | |
tree | 013181d9d8e4f06433838cffaabf6159aa2f775b | |
parent | 0bb68a88e6dc6a04685825e80b4e3dca1dc097d2 (diff) | |
download | mv-e6198149823ee18e4167b0e6dd34cfb7f2e2f496.tar.gz mv-e6198149823ee18e4167b0e6dd34cfb7f2e2f496.tar.bz2 mv-e6198149823ee18e4167b0e6dd34cfb7f2e2f496.tar.xz mv-e6198149823ee18e4167b0e6dd34cfb7f2e2f496.zip |
Implement basic VirtFsZip for virtual fs based on zip archives.
Api similar to VirtFs.
For now unused.
-rw-r--r-- | src/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/fs/virtfile.cpp | 8 | ||||
-rw-r--r-- | src/fs/virtfile.h | 1 | ||||
-rw-r--r-- | src/fs/virtfileprivate.cpp | 21 | ||||
-rw-r--r-- | src/fs/virtfileprivate.h | 10 | ||||
-rw-r--r-- | src/fs/virtfs_unittest.cc | 20 | ||||
-rw-r--r-- | src/fs/virtfsdir.cpp | 8 | ||||
-rw-r--r-- | src/fs/virtfsdir_unittest.cc | 6 | ||||
-rw-r--r-- | src/fs/virtfszip.cpp | 471 | ||||
-rw-r--r-- | src/fs/virtfszip.h | 79 | ||||
-rw-r--r-- | src/fs/virtfszip_unittest.cc | 671 | ||||
-rw-r--r-- | src/fs/virtzipentry.cpp | 38 | ||||
-rw-r--r-- | src/fs/virtzipentry.h | 45 | ||||
-rw-r--r-- | src/fs/zip.cpp | 29 | ||||
-rw-r--r-- | src/fs/zip.h | 5 | ||||
-rw-r--r-- | src/fs/zip_unittest.cc | 82 | ||||
-rw-r--r-- | src/fs/ziplocalheader.cpp | 37 | ||||
-rw-r--r-- | src/fs/ziplocalheader.h | 14 |
19 files changed, 1493 insertions, 68 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 91fc76c9f..d0c0f1a22 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -893,12 +893,17 @@ SET(SRCS fs/virtfsrwops.h fs/virtfstools.cpp fs/virtfstools.h + fs/virtfszip.cpp + fs/virtfszip.h fs/virtlist.cpp fs/virtlist.h + fs/virtzipentry.cpp + fs/virtzipentry.h fs/virtfs.cpp fs/virtfs.h fs/zip.cpp fs/zip.h + fs/ziplocalheader.cpp fs/ziplocalheader.h utils/process.cpp utils/process.h @@ -1786,12 +1791,17 @@ SET(DYE_CMD_SRCS fs/virtfsrwops.h fs/virtfstools.cpp fs/virtfstools.h + fs/virtfszip.cpp + fs/virtfszip.h fs/virtlist.cpp fs/virtlist.h + fs/virtzipentry.cpp + fs/virtzipentry.h fs/virtfs.cpp fs/virtfs.h fs/zip.cpp fs/zip.h + fs/ziplocalheader.cpp fs/ziplocalheader.h utils/sdl2helper.cpp utils/sdl2helper.h diff --git a/src/Makefile.am b/src/Makefile.am index 2b53079f1..0a0250412 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -557,12 +557,17 @@ BASE_SRC += events/actionevent.h \ fs/virtfsrwops.h \ fs/virtfstools.cpp \ fs/virtfstools.h \ + fs/virtfszip.cpp \ + fs/virtfszip.h \ fs/virtlist.cpp \ fs/virtlist.h \ + fs/virtzipentry.cpp \ + fs/virtzipentry.h \ fs/virtfs.cpp \ fs/virtfs.h \ fs/zip.cpp \ fs/zip.h \ + fs/ziplocalheader.cpp \ fs/ziplocalheader.h \ utils/process.cpp \ utils/process.h \ @@ -1929,6 +1934,7 @@ manaplustests_SOURCES = ${SRC} \ fs/virtfs_unittest.cc \ fs/zip_unittest.cc \ fs/virtfsdir_unittest.cc \ + fs/virtfszip_unittest.cc \ utils/xml_unittest.cc \ utils/timer_unittest.cc \ utils/xmlutils_unittest.cc \ diff --git a/src/fs/virtfile.cpp b/src/fs/virtfile.cpp index fb719f05c..3ed99ca2d 100644 --- a/src/fs/virtfile.cpp +++ b/src/fs/virtfile.cpp @@ -22,18 +22,14 @@ #include "fs/virtfileprivate.h" -#include "utils/delete2.h" - #include "debug.h" VirtFile::VirtFile() : - mPrivate(nullptr), - mBuf(nullptr) + mPrivate(nullptr) { } VirtFile::~VirtFile() { - delete2(mPrivate); - delete [] mBuf; + delete mPrivate; } diff --git a/src/fs/virtfile.h b/src/fs/virtfile.h index cb6211e83..cf7ded1a4 100644 --- a/src/fs/virtfile.h +++ b/src/fs/virtfile.h @@ -34,7 +34,6 @@ struct VirtFile final ~VirtFile(); VirtFilePrivate *mPrivate; - uint8_t *mBuf; }; #endif // UTILS_VIRTFILE_H diff --git a/src/fs/virtfileprivate.cpp b/src/fs/virtfileprivate.cpp index 5ebb234cc..4be55fc1b 100644 --- a/src/fs/virtfileprivate.cpp +++ b/src/fs/virtfileprivate.cpp @@ -27,18 +27,37 @@ VirtFilePrivate::VirtFilePrivate() : mFile(nullptr), + mBuf(nullptr), + mPos(0U), + mSize(0U), mFd(-1) { } VirtFilePrivate::VirtFilePrivate(const int fd) : mFile(nullptr), + mBuf(nullptr), + mPos(0U), + mSize(0U), mFd(fd) { } VirtFilePrivate::VirtFilePrivate(PHYSFS_file *restrict const file) : mFile(file), + mBuf(nullptr), + mPos(0U), + mSize(0U), + mFd(-1) +{ +} + +VirtFilePrivate::VirtFilePrivate(uint8_t *restrict const buf, + const size_t sz) : + mFile(nullptr), + mBuf(buf), + mPos(0U), + mSize(sz), mFd(-1) { } @@ -49,4 +68,6 @@ VirtFilePrivate::~VirtFilePrivate() PHYSFS_close(mFile); if (mFd != -1) close(mFd); + if (mBuf) + delete [] mBuf; } diff --git a/src/fs/virtfileprivate.h b/src/fs/virtfileprivate.h index ace7b49e0..758c1b5aa 100644 --- a/src/fs/virtfileprivate.h +++ b/src/fs/virtfileprivate.h @@ -36,6 +36,9 @@ struct VirtFilePrivate final explicit VirtFilePrivate(const int fd); + VirtFilePrivate(uint8_t *restrict const buf, + const size_t sz); + A_DELETE_COPY(VirtFilePrivate) ~VirtFilePrivate(); @@ -43,6 +46,13 @@ struct VirtFilePrivate final // physfs fields PHYSFS_file *mFile; + // zipfs fields + uint8_t *mBuf; + + // zipfs fields + size_t mPos; + size_t mSize; + // dirfs fields int mFd; }; diff --git a/src/fs/virtfs_unittest.cc b/src/fs/virtfs_unittest.cc index a79f01aee..5b9da2394 100644 --- a/src/fs/virtfs_unittest.cc +++ b/src/fs/virtfs_unittest.cc @@ -335,6 +335,21 @@ TEST_CASE("VirtFs getRealDir") } REQUIRE(VirtFs::getRealDir("zzz") == ""); + if (dir1 == true) + { + VirtFs::addZipToSearchPath("data/test/test.zip", Append_false); + REQUIRE(VirtFs::getRealDir("dir/brimmedhat.png") == + "data/test/test.zip"); + REQUIRE(VirtFs::getRealDir("hide.png") == "data/test"); + } + else + { + VirtFs::addZipToSearchPath("../data/test/test.zip", Append_false); + REQUIRE(VirtFs::getRealDir("dir/brimmedhat.png") == + "../data/test/test.zip"); + REQUIRE(VirtFs::getRealDir("hide.png") == "../data/test"); + } + VirtFs::removeDirFromSearchPath("data/test"); VirtFs::removeDirFromSearchPath("../data/test"); @@ -343,17 +358,22 @@ TEST_CASE("VirtFs getRealDir") REQUIRE(VirtFs::getRealDir("test") == "data"); REQUIRE(VirtFs::getRealDir("test/test.txt") == "data"); + REQUIRE(VirtFs::getRealDir("dir/hide.png") == "data/test/test.zip"); } else { REQUIRE(VirtFs::getRealDir("test") == "../data"); REQUIRE(VirtFs::getRealDir("test/test.txt") == "../data"); + REQUIRE(VirtFs::getRealDir("dir/hide.png") == "../data/test/test.zip"); } + REQUIRE(VirtFs::exists("dir/hide.png")); REQUIRE(VirtFs::getRealDir("zzz") == ""); VirtFs::removeDirFromSearchPath("data"); VirtFs::removeDirFromSearchPath("../data"); + VirtFs::removeZipFromSearchPath("data/test/test.zip"); + VirtFs::removeZipFromSearchPath("../data/test/test.zip"); delete2(logger); } diff --git a/src/fs/virtfsdir.cpp b/src/fs/virtfsdir.cpp index c3471b2d7..249317773 100644 --- a/src/fs/virtfsdir.cpp +++ b/src/fs/virtfsdir.cpp @@ -30,6 +30,7 @@ #include "fs/virtlist.h" #include "utils/checkutils.h" +#include "utils/dtor.h" #include "utils/stringutils.h" #include <dirent.h> @@ -40,10 +41,6 @@ #include <sys/types.h> #include <sys/stat.h> -#ifdef ANDROID -#include "fs/paths.h" -#endif // ANDROID - #include "debug.h" extern const char *dirSeparator; @@ -249,6 +246,7 @@ namespace VirtFsDir void deinit() { + delete_all(mEntries); mEntries.clear(); } @@ -525,7 +523,7 @@ namespace VirtFsDir const uint64_t pos) { if (file == nullptr) - return -1; + return 0; const int fd = file->mPrivate->mFd; if (fd == -1) diff --git a/src/fs/virtfsdir_unittest.cc b/src/fs/virtfsdir_unittest.cc index df596712b..aa2b0542b 100644 --- a/src/fs/virtfsdir_unittest.cc +++ b/src/fs/virtfsdir_unittest.cc @@ -555,8 +555,6 @@ TEST_CASE("VirtFsDir openRead") REQUIRE(file == nullptr); file = VirtFsDir::openRead("units.xml"); REQUIRE(file == nullptr); -// file = VirtFsDir::openRead("test"); -// REQUIRE(file == nullptr); file = VirtFsDir::openRead("testQ"); REQUIRE(file == nullptr); @@ -577,8 +575,6 @@ TEST_CASE("VirtFsDir openRead") file = VirtFsDir::openRead("units.xml"); REQUIRE(file != nullptr); VirtFsDir::close(file); -// file = VirtFsDir::openRead("test"); -// REQUIRE(file == nullptr); file = VirtFsDir::openRead("testQ"); REQUIRE(file == nullptr); @@ -594,8 +590,6 @@ TEST_CASE("VirtFsDir openRead") REQUIRE(file == nullptr); file = VirtFsDir::openRead("units.xml"); REQUIRE(file == nullptr); -// file = VirtFsDir::openRead("test"); -// REQUIRE(file == nullptr); file = VirtFsDir::openRead("testQ"); REQUIRE(file == nullptr); diff --git a/src/fs/virtfszip.cpp b/src/fs/virtfszip.cpp new file mode 100644 index 000000000..d980a56b6 --- /dev/null +++ b/src/fs/virtfszip.cpp @@ -0,0 +1,471 @@ +/* + * 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/virtfszip.h" + +#include "fs/files.h" +#include "fs/paths.h" +#include "fs/virtfile.h" +#include "fs/virtfileprivate.h" +#include "fs/virtlist.h" +#include "fs/virtzipentry.h" +#include "fs/zip.h" +#include "fs/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; +} // 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(const std::string &newDir, + const Append append) + { + 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(const std::string &newDir, + const Append append) + { + 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) + { + 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) + { + 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(); + } + + std::string getRealDir(const std::string &restrict filename) + { + if (checkPath(filename) == false) + { + reportAlways("VirtFsZip::exists invalid path: %s", + filename.c_str()); + return std::string(); + } + ZipLocalHeader *restrict const header = searchHeaderByName(filename); + if (header != nullptr) + return header->zipEntry->mArchiveName; + return std::string(); + } + + bool exists(const std::string &restrict 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; + if (checkPath(dirName) == false) + { + reportAlways("VirtFsZip::enumerateFiles invalid path: %s", + dirName.c_str()); + return list; + } + if (findLast(dirName, std::string(dirSeparator)) == false) + dirName += dirSeparator; + StringVect &names = list->names; + 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) + { + if (checkPath(dirName) == false) + { + reportAlways("VirtFsZip::isDirectory invalid path: %s", + dirName.c_str()); + return false; + } + 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(const std::string &restrict 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(const std::string &restrict filename) + { + if (checkPath(filename) == false) + { + reportAlways("VirtFsZip::openRead invalid path: %s", + filename.c_str()); + return nullptr; + } + 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; + 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 diff --git a/src/fs/virtfszip.h b/src/fs/virtfszip.h new file mode 100644 index 000000000..9ccd59ed5 --- /dev/null +++ b/src/fs/virtfszip.h @@ -0,0 +1,79 @@ +/* + * 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/>. + */ + +#ifndef UTILS_VIRTFSZIP_H +#define UTILS_VIRTFSZIP_H + +#include "enums/simpletypes/append.h" +#include "enums/simpletypes/skiperror.h" + +#include "localconsts.h" + +#include <vector> +#include <string> + +struct VirtFile; +struct VirtList; +struct VirtZipEntry; +struct ZipLocalHeader; + +namespace VirtFsZip +{ + VirtZipEntry *searchEntryByArchive(const std::string &restrict archiveName); + ZipLocalHeader *searchHeaderByName(const std::string &restrict filename); + bool addToSearchPath(const std::string &newDir, + const Append append); + bool addToSearchPathSilent(const std::string &newDir, + const Append append); + bool removeFromSearchPath(std::string oldDir); + bool removeFromSearchPathSilent(std::string oldDir); + void deinit(); + std::vector<VirtZipEntry*> &getEntries(); + bool exists(const std::string &restrict name); + VirtList *enumerateFiles(std::string dir) RETURNS_NONNULL; + bool isDirectory(std::string dirName); + bool isSymbolicLink(const std::string &restrict name); + void freeList(VirtList *restrict const handle); + VirtFile *openRead(const std::string &restrict filename); + VirtFile *openWrite(const std::string &restrict filename); + VirtFile *openAppend(const std::string &restrict filename); + bool setWriteDir(const std::string &restrict newDir); + std::string getRealDir(const std::string &restrict filename); + bool mkdir(const std::string &restrict dirName); + bool remove(const std::string &restrict filename); + void permitLinks(const bool val); + const char *getLastError(); + int64_t read(VirtFile *restrict const handle, + void *restrict const buffer, + const uint32_t objSize, + const uint32_t objCount); + int64_t write(VirtFile *restrict const file, + const void *restrict const buffer, + const uint32_t objSize, + const uint32_t objCount); + int close(VirtFile *restrict const file); + int64_t fileLength(VirtFile *restrict const file); + int64_t tell(VirtFile *restrict const file); + int seek(VirtFile *restrict const file, + const uint64_t pos); + int eof(VirtFile *restrict const file); +} // namespace VirtFsZip + +#endif // UTILS_VIRTFSZIP_H diff --git a/src/fs/virtfszip_unittest.cc b/src/fs/virtfszip_unittest.cc new file mode 100644 index 000000000..376f26380 --- /dev/null +++ b/src/fs/virtfszip_unittest.cc @@ -0,0 +1,671 @@ +/* + * The ManaPlus Client + * Copyright (C) 2016-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 "catch.hpp" + +#include "fs/files.h" +#include "fs/virtlist.h" +#include "fs/virtfszip.h" +#include "fs/virtzipentry.h" +#include "fs/ziplocalheader.h" + +#include "utils/checkutils.h" +#include "utils/delete2.h" + +#include "debug.h" + +TEST_CASE("VirtFsZip getEntries") +{ + REQUIRE(VirtFsZip::getEntries().empty()); + REQUIRE(VirtFsZip::searchEntryByArchive("test.zip") == nullptr); +} + +TEST_CASE("VirtFsZip addToSearchPath") +{ + logger = new Logger(); + std::string name("data/test/test.zip"); + std::string prefix("data/test/"); + std::vector<ZipLocalHeader*> headers; + if (Files::existsLocal(name) == false) + prefix = "../" + prefix; + + SECTION("simple 1") + { + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_false)); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "file2.zip") == nullptr); + REQUIRE(VirtFsZip::getEntries().size() == 1); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == + prefix + "test.zip"); + } + + SECTION("simple 2") + { + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_true)); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "file2.zip") == nullptr); + REQUIRE(VirtFsZip::getEntries().size() == 1); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == + prefix + "test.zip"); + } + + SECTION("simple 3") + { + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_false)); + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test2.zip", + Append_false)); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test2.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test3.zip") == nullptr); + REQUIRE(VirtFsZip::getEntries().size() == 2); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == + prefix + "test2.zip"); + REQUIRE(VirtFsZip::getEntries()[1]->mArchiveName == + prefix + "test.zip"); + } + + SECTION("simple 4") + { + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_true)); + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test2.zip", + Append_true)); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test2.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test3.zip") == nullptr); + REQUIRE(VirtFsZip::getEntries().size() == 2); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == + prefix + "test.zip"); + REQUIRE(VirtFsZip::getEntries()[1]->mArchiveName == + prefix + "test2.zip"); + } + + SECTION("simple 5") + { + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_true)); + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test2.zip", + Append_true)); + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test3.zip", + Append_true)); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test2.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test3.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test4.zip") == nullptr); + REQUIRE(VirtFsZip::getEntries().size() == 3); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == + prefix + "test.zip"); + REQUIRE(VirtFsZip::getEntries()[1]->mArchiveName == + prefix + "test2.zip"); + REQUIRE(VirtFsZip::getEntries()[2]->mArchiveName == + prefix + "test3.zip"); + } + + SECTION("simple 6") + { + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_true)); + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test2.zip", + Append_true)); + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test3.zip", + Append_false)); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test2.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test3.zip") != nullptr); + REQUIRE(VirtFsZip::searchEntryByArchive( + prefix + "test4.zip") == nullptr); + REQUIRE(VirtFsZip::getEntries().size() == 3); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == + prefix + "test3.zip"); + REQUIRE(VirtFsZip::getEntries()[1]->mArchiveName == + prefix + "test.zip"); + REQUIRE(VirtFsZip::getEntries()[2]->mArchiveName == + prefix + "test2.zip"); + } + + VirtFsZip::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsZip removeFromSearchPath") +{ + logger = new Logger(); + std::string name("data/test/test.zip"); + std::string prefix("data/test/"); + std::vector<ZipLocalHeader*> headers; + if (Files::existsLocal(name) == false) + prefix = "../" + prefix; + + SECTION("simple 1") + { + REQUIRE_THROWS(VirtFsZip::removeFromSearchPath( + prefix + "test123.zip")); + } + + SECTION("simple 2") + { + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_true)); + REQUIRE_THROWS(VirtFsZip::removeFromSearchPath(prefix + "test2.zip")); + REQUIRE(VirtFsZip::removeFromSearchPath(prefix + "test.zip")); + } + + SECTION("simple 3") + { + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_true)); + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test2.zip", + Append_true)); + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test3.zip", + Append_false)); + REQUIRE(VirtFsZip::getEntries().size() == 3); + REQUIRE_THROWS(VirtFsZip::removeFromSearchPath(prefix + "test4.zip")); + REQUIRE(VirtFsZip::removeFromSearchPath(prefix + "test.zip")); + REQUIRE(VirtFsZip::getEntries().size() == 2); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == + prefix + "test3.zip"); + REQUIRE(VirtFsZip::getEntries()[1]->mArchiveName == + prefix + "test2.zip"); + REQUIRE_THROWS(VirtFsZip::removeFromSearchPath(prefix + "test.zip")); + REQUIRE(VirtFsZip::getEntries().size() == 2); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == + prefix + "test3.zip"); + REQUIRE(VirtFsZip::getEntries()[1]->mArchiveName == + prefix + "test2.zip"); + REQUIRE(VirtFsZip::removeFromSearchPath(prefix + "test2.zip")); + REQUIRE_THROWS(VirtFsZip::removeFromSearchPath(prefix + "test2.zip")); + REQUIRE(VirtFsZip::getEntries().size() == 1); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == + prefix + "test3.zip"); + } + + SECTION("simple 4") + { + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_true)); + REQUIRE(VirtFsZip::getEntries().size() == 1); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == + prefix + "test.zip"); + REQUIRE_THROWS(VirtFsZip::removeFromSearchPath(prefix + "test2.zip")); + REQUIRE(VirtFsZip::removeFromSearchPath(prefix + "test.zip")); + REQUIRE(VirtFsZip::getEntries().size() == 0); + REQUIRE(VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_true)); + REQUIRE(VirtFsZip::getEntries().size() == 1); + REQUIRE(VirtFsZip::getEntries()[0]->mArchiveName == prefix + "test.zip"); + } + + VirtFsZip::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsZip exists") +{ + logger = new Logger(); + VirtFsZip::addToSearchPathSilent("data/test/test2.zip", + Append_false); + VirtFsZip::addToSearchPathSilent("../data/test/test2.zip", + Append_false); + + REQUIRE(VirtFsZip::exists("dir2/units.xml") == true); + REQUIRE(VirtFsZip::exists("test/units123.xml") == false); + REQUIRE(VirtFsZip::exists("tesQ/units.xml") == false); + REQUIRE(VirtFsZip::exists("units1.xml") == false); + REQUIRE(VirtFsZip::exists("dir/hide.png") == true); + REQUIRE(VirtFsZip::exists("dir/brimmedhat.png") == false); + + VirtFsZip::addToSearchPathSilent("data/test/test.zip", + Append_false); + VirtFsZip::addToSearchPathSilent("../data/test/test.zip", + Append_false); + + REQUIRE(VirtFsZip::exists("dir2/units.xml") == true); + REQUIRE(VirtFsZip::exists("test/units123.xml") == false); + REQUIRE(VirtFsZip::exists("tesQ/units.xml") == false); + REQUIRE(VirtFsZip::exists("units1.xml") == false); + REQUIRE(VirtFsZip::exists("dir/hide.png") == true); + REQUIRE(VirtFsZip::exists("dir/brimmedhat.png") == true); + + VirtFsZip::removeFromSearchPathSilent("data/test/test2.zip"); + VirtFsZip::removeFromSearchPathSilent("../data/test/test2.zip"); + + REQUIRE(VirtFsZip::exists("dir2/units.xml") == false); + REQUIRE(VirtFsZip::exists("test/units123.xml") == false); + REQUIRE(VirtFsZip::exists("tesQ/units.xml") == false); + REQUIRE(VirtFsZip::exists("units1.xml") == false); + REQUIRE(VirtFsZip::exists("dir/hide.png") == true); + REQUIRE(VirtFsZip::exists("dir/brimmedhat.png") == true); + + REQUIRE_THROWS(VirtFsZip::exists("test/../units.xml")); + + VirtFsZip::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsZip getRealDir") +{ + logger = new Logger(); + std::string name("data/test/test.zip"); + std::string prefix("data/test/"); + if (Files::existsLocal(name) == false) + prefix = "../" + prefix; + VirtFsZip::addToSearchPathSilent(prefix + "test2.zip", + Append_false); + + REQUIRE(VirtFsZip::getRealDir(".") == ""); + REQUIRE(VirtFsZip::getRealDir("..") == ""); + REQUIRE(VirtFsZip::getRealDir("test.txt") == prefix + "test2.zip"); + REQUIRE(VirtFsZip::getRealDir("dir/dye.png") == + prefix + "test2.zip"); + REQUIRE(VirtFsZip::getRealDir("zzz") == ""); + + VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_false); + REQUIRE(VirtFsZip::getRealDir("dir/dye.png") == + prefix + "test2.zip"); + REQUIRE(VirtFsZip::getRealDir("dir/hide.png") == + prefix + "test.zip"); + REQUIRE(VirtFsZip::getRealDir("dir/brimmedhat.png") == + prefix + "test.zip"); + REQUIRE(VirtFsZip::getRealDir("zzz") == ""); + + VirtFsZip::removeFromSearchPathSilent(prefix + "test.zip"); + + REQUIRE(VirtFsZip::getRealDir("dir/brimmedhat.png") == ""); + REQUIRE(VirtFsZip::getRealDir("test.txt") == prefix + "test2.zip"); + REQUIRE(VirtFsZip::getRealDir("dir/dye.png") == + prefix + "test2.zip"); + REQUIRE(VirtFsZip::getRealDir("zzz") == ""); + + VirtFsZip::removeFromSearchPathSilent(prefix + "test2.zip"); + VirtFsZip::deinit(); + delete2(logger); +} + +static bool inList(VirtList *list, + const std::string &name) +{ + FOR_EACH (StringVectCIter, it, list->names) + { + if (*it == name) + return true; + } + return false; +} + +TEST_CASE("VirtFsZip enumerateFiles1") +{ + logger = new Logger; + std::string name("data/test/test.zip"); + std::string prefix("data/test/"); + if (Files::existsLocal(name) == false) + prefix = "../" + prefix; + + VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_false); + + VirtList *list = nullptr; + + list = VirtFsZip::enumerateFiles("dir"); + REQUIRE(list->names.size() == 2); + REQUIRE(inList(list, "brimmedhat.png")); + REQUIRE(inList(list, "hide.png")); + VirtFsZip::freeList(list); + + VirtFsZip::removeFromSearchPathSilent(prefix + "test.zip"); + VirtFsZip::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsZip enumerateFiles2") +{ + logger = new Logger; + std::string name("data/test/test.zip"); + std::string prefix("data/test/"); + if (Files::existsLocal(name) == false) + prefix = "../" + prefix; + + VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_true); + VirtFsZip::addToSearchPathSilent(prefix + "test2.zip", + Append_true); + + VirtList *list = nullptr; + + list = VirtFsZip::enumerateFiles("dir"); + FOR_EACH (StringVectCIter, it, list->names) + { + logger->log("filename: " + *it); + } + + REQUIRE(list->names.size() == 5); + REQUIRE(inList(list, "brimmedhat.png")); + REQUIRE(inList(list, "hide.png")); + REQUIRE(inList(list, "1")); + REQUIRE(inList(list, "gpl")); + REQUIRE(inList(list, "dye.png")); + VirtFsZip::freeList(list); + + VirtFsZip::removeFromSearchPathSilent(prefix + "test.zip"); + VirtFsZip::removeFromSearchPathSilent(prefix + "test2.zip"); + VirtFsZip::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsZip isDirectory") +{ + logger = new Logger(); + std::string name("data/test/test.zip"); + std::string prefix("data/test/"); + if (Files::existsLocal(name) == false) + prefix = "../" + prefix; + + VirtFsZip::addToSearchPathSilent(prefix + "test2.zip", + Append_false); + +// +++ need uncomment this lines after path sanitization will be added + REQUIRE(VirtFsZip::isDirectory("dir2/units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("dir2/units.xml/") == false); +// REQUIRE(VirtFsZip::isDirectory("dir2//units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("dir2/units123.xml") == false); +// REQUIRE(VirtFsZip::isDirectory("dir2//units123.xml") == false); + REQUIRE(VirtFsZip::isDirectory("tesQ/units.xml") == false); +// REQUIRE(VirtFsZip::isDirectory("tesQ//units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("dir") == true); +// REQUIRE(VirtFsZip::isDirectory("dir2/") == true); +// REQUIRE(VirtFsZip::isDirectory("dir2//") == true); + REQUIRE(VirtFsZip::isDirectory("dir/1") == true); +// REQUIRE(VirtFsZip::isDirectory("dir//1") == true); +// REQUIRE(VirtFsZip::isDirectory("dir//1/") == true); + REQUIRE(VirtFsZip::isDirectory("dir/1") == true); + REQUIRE(VirtFsZip::isDirectory("dir/1/zzz") == false); +// REQUIRE(VirtFsZip::isDirectory("test/dir1//") == false); + REQUIRE(VirtFsZip::isDirectory("testQ") == false); + REQUIRE(VirtFsZip::isDirectory("testQ/") == false); +// REQUIRE(VirtFsZip::isDirectory("testQ//") == false); + + VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_false); + + REQUIRE(VirtFsZip::isDirectory("dir2/units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("dir2/units.xml/") == false); +// REQUIRE(VirtFsZip::isDirectory("dir2//units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("dir2/units123.xml") == false); +// REQUIRE(VirtFsZip::isDirectory("dir2//units123.xml") == false); + REQUIRE(VirtFsZip::isDirectory("tesQ/units.xml") == false); +// REQUIRE(VirtFsZip::isDirectory("tesQ//units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("dir") == true); + REQUIRE(VirtFsZip::isDirectory("dir2/") == true); +// REQUIRE(VirtFsZip::isDirectory("dir2//") == true); + REQUIRE(VirtFsZip::isDirectory("dir/1") == true); +// REQUIRE(VirtFsZip::isDirectory("dir//1") == true); +// REQUIRE(VirtFsZip::isDirectory("dir//1/") == true); + REQUIRE(VirtFsZip::isDirectory("dir/1") == true); + REQUIRE(VirtFsZip::isDirectory("dir/1/zzz") == false); +// REQUIRE(VirtFsZip::isDirectory("test/dir1//") == false); + REQUIRE(VirtFsZip::isDirectory("testQ") == false); + REQUIRE(VirtFsZip::isDirectory("testQ/") == false); +// REQUIRE(VirtFsZip::isDirectory("testQ//") == false); + + VirtFsZip::removeFromSearchPathSilent(prefix + "test2.zip"); + + REQUIRE(VirtFsZip::isDirectory("dir2/units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("dir2/units.xml/") == false); +// REQUIRE(VirtFsZip::isDirectory("dir2//units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("dir2/units123.xml") == false); +// REQUIRE(VirtFsZip::isDirectory("dir2//units123.xml") == false); + REQUIRE(VirtFsZip::isDirectory("tesQ/units.xml") == false); +// REQUIRE(VirtFsZip::isDirectory("tesQ//units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("units.xml") == false); + REQUIRE(VirtFsZip::isDirectory("dir") == true); + REQUIRE(VirtFsZip::isDirectory("dir2/") == false); +// REQUIRE(VirtFsZip::isDirectory("dir2//") == false); + REQUIRE(VirtFsZip::isDirectory("dir/1") == false); +// REQUIRE(VirtFsZip::isDirectory("dir//1") == false); +// REQUIRE(VirtFsZip::isDirectory("dir//1/") == false); + REQUIRE(VirtFsZip::isDirectory("dir/1") == false); + REQUIRE(VirtFsZip::isDirectory("dir/1/zzz") == false); +// REQUIRE(VirtFsZip::isDirectory("test/dir1//") == false); + REQUIRE(VirtFsZip::isDirectory("testQ") == false); + REQUIRE(VirtFsZip::isDirectory("testQ/") == false); +// REQUIRE(VirtFsZip::isDirectory("testQ//") == false); + + VirtFsZip::removeFromSearchPathSilent(prefix + "test.zip"); + VirtFsZip::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsZip openRead") +{ + logger = new Logger(); + std::string name("data/test/test.zip"); + std::string prefix("data/test/"); + if (Files::existsLocal(name) == false) + prefix = "../" + prefix; + + VirtFsZip::addToSearchPathSilent(prefix + "test2.zip", + Append_false); + + VirtFile *file = nullptr; + + file = VirtFsZip::openRead("dir2/units.xml"); + REQUIRE(file != nullptr); + VirtFsZip::close(file); + file = VirtFsZip::openRead("dir2/units123.xml"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("tesQ/units.xml"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("units.xml1"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("testQ"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("dir/brimmedhat.png"); + REQUIRE(file == nullptr); + + VirtFsZip::addToSearchPathSilent(prefix + "test.zip", + Append_false); + + file = VirtFsZip::openRead("dir2/units.xml"); + REQUIRE(file != nullptr); + VirtFsZip::close(file); + file = VirtFsZip::openRead("dir2/units123.xml"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("tesQ/units.xml"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("units.xml1"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("testQ"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("dir/brimmedhat.png"); + REQUIRE(file != nullptr); + VirtFsZip::close(file); + + VirtFsZip::removeFromSearchPathSilent(prefix + "test.zip"); + + file = VirtFsZip::openRead("dir2/units.xml"); + REQUIRE(file != nullptr); + VirtFsZip::close(file); + file = VirtFsZip::openRead("dir2/units123.xml"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("tesQ/units.xml"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("units.xml1"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("testQ"); + REQUIRE(file == nullptr); + file = VirtFsZip::openRead("dir/brimmedhat.png"); + REQUIRE(file == nullptr); + + VirtFsZip::removeFromSearchPathSilent(prefix + "test2.zip"); + + VirtFsZip::deinit(); + delete2(logger); +} + +TEST_CASE("VirtFsZip read") +{ + logger = new Logger(); + std::string name("data/test/test.zip"); + std::string prefix("data/test/"); + if (Files::existsLocal(name) == false) + prefix = "../" + prefix; + + VirtFsZip::addToSearchPathSilent(prefix + "test2.zip", + Append_false); + VirtFile *file = nullptr; + void *restrict buffer = nullptr; + + SECTION("test 1") + { + file = VirtFsZip::openRead("dir2/test.txt"); + REQUIRE(file != nullptr); + REQUIRE(VirtFsZip::fileLength(file) == 23); + const int fileSize = VirtFsZip::fileLength(file); + + buffer = calloc(fileSize + 1, 1); + REQUIRE(VirtFsZip::read(file, buffer, 1, fileSize) == fileSize); + REQUIRE(strcmp(static_cast<char*>(buffer), + "test line 1\ntest line 2") == 0); + REQUIRE(VirtFsZip::tell(file) == fileSize); + REQUIRE(VirtFsZip::eof(file) == true); + } + + SECTION("test 2") + { + file = VirtFsZip::openRead("dir2/test.txt"); + REQUIRE(file != nullptr); + REQUIRE(VirtFsZip::fileLength(file) == 23); + const int fileSize = VirtFsZip::fileLength(file); + + buffer = calloc(fileSize + 1, 1); + REQUIRE(VirtFsZip::seek(file, 12) != 0); + REQUIRE(VirtFsZip::eof(file) == false); + REQUIRE(VirtFsZip::tell(file) == 12); + REQUIRE(VirtFsZip::read(file, buffer, 1, 11) == 11); + REQUIRE(strcmp(static_cast<char*>(buffer), + "test line 2") == 0); + REQUIRE(VirtFsZip::eof(file) == true); + } + + SECTION("test 3") + { + file = VirtFsZip::openRead("dir2/test.txt"); + REQUIRE(file != nullptr); + const int fileSize = VirtFsZip::fileLength(file); + + buffer = calloc(fileSize + 1, 1); + for (int f = 0; f < fileSize; f ++) + { + REQUIRE(VirtFsZip::seek(file, f) != 0); + REQUIRE(VirtFsZip::eof(file) == false); + REQUIRE(VirtFsZip::tell(file) == f); + } + } + + SECTION("test 4") + { + file = VirtFsZip::openRead("dir2/test.txt"); + REQUIRE(file != nullptr); + const int fileSize = VirtFsZip::fileLength(file); + const char *restrict const str = "test line 1\ntest line 2"; + buffer = calloc(fileSize + 1, 1); + for (int f = 0; f < fileSize - 1; f ++) + { + REQUIRE(VirtFsZip::read(file, buffer, 1, 1) == 1); + REQUIRE(static_cast<char*>(buffer)[0] == str[f]); + REQUIRE(VirtFsZip::eof(file) == false); + REQUIRE(VirtFsZip::tell(file) == f + 1); + } + REQUIRE(VirtFsZip::read(file, buffer, 1, 1) == 1); + REQUIRE(static_cast<char*>(buffer)[0] == str[22]); + REQUIRE(VirtFsZip::eof(file) == true); + REQUIRE(VirtFsZip::tell(file) == fileSize); + } + + SECTION("test 5") + { + file = VirtFsZip::openRead("dir2/test.txt"); + REQUIRE(file != nullptr); + const int fileSize = VirtFsZip::fileLength(file); + const char *restrict const str = "test line 1\ntest line 2"; + buffer = calloc(fileSize + 1, 1); + for (int f = 0; f < fileSize - 1; f += 2) + { + REQUIRE(VirtFsZip::read(file, buffer, 2, 1) == 1); + REQUIRE(static_cast<char*>(buffer)[0] == str[f]); + REQUIRE(static_cast<char*>(buffer)[1] == str[f + 1]); + REQUIRE(VirtFsZip::eof(file) == false); + REQUIRE(VirtFsZip::tell(file) == f + 2); + } + REQUIRE(VirtFsZip::eof(file) == false); + REQUIRE(VirtFsZip::tell(file) == 22); + REQUIRE(VirtFsZip::read(file, buffer, 2, 1) == 0); + REQUIRE(VirtFsZip::eof(file) == false); + } + + SECTION("test 6") + { + file = VirtFsZip::openRead("dir2/test.txt"); + REQUIRE(file != nullptr); + const int fileSize = VirtFsZip::fileLength(file); + const char *restrict const str = "test line 1\ntest line 2"; + buffer = calloc(fileSize + 1, 1); + for (int f = 0; f < fileSize - 1; f += 2) + { + REQUIRE(VirtFsZip::read(file, buffer, 1, 2) == 2); + REQUIRE(static_cast<char*>(buffer)[0] == str[f]); + REQUIRE(static_cast<char*>(buffer)[1] == str[f + 1]); + REQUIRE(VirtFsZip::eof(file) == false); + REQUIRE(VirtFsZip::tell(file) == f + 2); + } + REQUIRE(VirtFsZip::eof(file) == false); + REQUIRE(VirtFsZip::tell(file) == 22); + REQUIRE(VirtFsZip::read(file, buffer, 1, 2) == 1); + REQUIRE(static_cast<char*>(buffer)[0] == str[22]); + REQUIRE(VirtFsZip::eof(file) == true); + } + + VirtFsZip::close(file); + free(buffer); + VirtFsZip::removeFromSearchPathSilent(prefix + "test2.zip"); + delete2(logger); +} diff --git a/src/fs/virtzipentry.cpp b/src/fs/virtzipentry.cpp new file mode 100644 index 000000000..e0189fccf --- /dev/null +++ b/src/fs/virtzipentry.cpp @@ -0,0 +1,38 @@ +/* + * The ManaPlus Client + * Copyright (C) 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/virtzipentry.h" + +#include "fs/ziplocalheader.h" + +#include "utils/dtor.h" + +#include "debug.h" + +VirtZipEntry::VirtZipEntry(const std::string &restrict archiveName) : + mArchiveName(archiveName), + mHeaders() +{ +} + +VirtZipEntry::~VirtZipEntry() +{ + delete_all(mHeaders); +} diff --git a/src/fs/virtzipentry.h b/src/fs/virtzipentry.h new file mode 100644 index 000000000..ba3bd2d9f --- /dev/null +++ b/src/fs/virtzipentry.h @@ -0,0 +1,45 @@ +/* + * The ManaPlus Client + * Copyright (C) 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/>. + */ + +#ifndef UTILS_VIRTZIPENTRY_H +#define UTILS_VIRTZIPENTRY_H + +#include <string> +#include <vector> + +#include "localconsts.h" + +struct ZipLocalHeader; + +struct VirtZipEntry final +{ + VirtZipEntry(const std::string &restrict archiveName); + + A_DELETE_COPY(VirtZipEntry) + + ~VirtZipEntry(); + + std::string mArchiveName; + + std::vector<ZipLocalHeader*> mHeaders; + std::vector<std::string> mDirs; +}; + +#endif // UTILS_VIRTZIPENTRY_H diff --git a/src/fs/zip.cpp b/src/fs/zip.cpp index cff4a3e6f..62b60a451 100644 --- a/src/fs/zip.cpp +++ b/src/fs/zip.cpp @@ -20,7 +20,7 @@ #include "fs/zip.h" -#include "fs/virtfile.h" +#include "fs/virtzipentry.h" #include "fs/ziplocalheader.h" #include "utils/checkutils.h" @@ -46,9 +46,16 @@ namespace Zip { - bool readArchiveInfo(const std::string &restrict archiveName, - std::vector<ZipLocalHeader*> &restrict headers) + bool readArchiveInfo(VirtZipEntry *const entry) { + if (entry == nullptr) + { + reportAlways("Entry is null."); + return false; + } + const std::string archiveName = entry->mArchiveName; + std::vector<ZipLocalHeader*> &restrict headers = entry->mHeaders; + std::vector<std::string> &restrict dirs = entry->mDirs; FILE *restrict const arcFile = fopen(archiveName.c_str(), "r"); if (arcFile == nullptr) @@ -78,7 +85,7 @@ namespace Zip buf[3] == 0x04) { // local file header header = new ZipLocalHeader; - header->archiveName = archiveName; + header->zipEntry = entry; // skip useless fields fseek(arcFile, 4, SEEK_CUR); // + 4 // file header pointer on 8 @@ -131,6 +138,11 @@ namespace Zip logger->log(" uncompressed size: %u", header->uncompressSize); } + else + { + dirs.push_back(header->fileName); + delete header; + } } else if (buf[0] == 0x50 && buf[1] == 0x4B && @@ -197,12 +209,13 @@ namespace Zip reportAlways("Zip::readCompressedFile: header is null"); return nullptr; } - FILE *restrict const arcFile = fopen(header->archiveName.c_str(), + FILE *restrict const arcFile = fopen( + header->zipEntry->mArchiveName.c_str(), "r"); if (arcFile == nullptr) { reportAlways("Can't open zip file %s", - header->archiveName.c_str()); + header->zipEntry->mArchiveName.c_str()); return nullptr; } @@ -213,7 +226,7 @@ namespace Zip compressSize) { reportAlways("Read zip compressed file error from archive: %s", - header->archiveName.c_str()); + header->zipEntry->mArchiveName.c_str()); fclose(arcFile); delete [] buf; return nullptr; @@ -251,7 +264,7 @@ namespace Zip int ret = inflateInit2(&strm, -MAX_WBITS); if (ret != Z_OK) { - reportZlibError(header->archiveName, ret); + reportZlibError(header->zipEntry->mArchiveName, ret); delete [] in; delete [] out; return nullptr; diff --git a/src/fs/zip.h b/src/fs/zip.h index a54b08129..412dbcef9 100644 --- a/src/fs/zip.h +++ b/src/fs/zip.h @@ -26,13 +26,12 @@ #include <string> #include <vector> -struct VirtFile; +struct VirtZipEntry; struct ZipLocalHeader; namespace Zip { - bool readArchiveInfo(const std::string &restrict archiveName, - std::vector<ZipLocalHeader*> &restrict headers); + bool readArchiveInfo(VirtZipEntry *const entry); std::string getZlibError(const int err); void reportZlibError(const std::string &text, const int err); diff --git a/src/fs/zip_unittest.cc b/src/fs/zip_unittest.cc index 9661b7d19..72691c47e 100644 --- a/src/fs/zip_unittest.cc +++ b/src/fs/zip_unittest.cc @@ -23,6 +23,7 @@ #include "logger.h" #include "fs/files.h" +#include "fs/virtzipentry.h" #include "fs/zip.h" #include "fs/ziplocalheader.h" @@ -36,7 +37,6 @@ TEST_CASE("Zip readArchiveInfo") logger = new Logger(); std::string name("data/test/test.zip"); std::string prefix; - std::vector<ZipLocalHeader*> headers; if (Files::existsLocal(name) == false) prefix = "../"; @@ -44,105 +44,113 @@ TEST_CASE("Zip readArchiveInfo") { name = prefix + "data/test/test.zip"; - REQUIRE(Zip::readArchiveInfo(name, headers)); + VirtZipEntry *const entry = new VirtZipEntry(name); + std::vector<ZipLocalHeader*> &headers = entry->mHeaders; + + REQUIRE(Zip::readArchiveInfo(entry)); REQUIRE(headers.size() == 2); - REQUIRE(headers[0]->archiveName == name); + REQUIRE(entry->mArchiveName == name); REQUIRE(headers[0]->fileName == "dir/hide.png"); REQUIRE(headers[0]->compressSize == 365); REQUIRE(headers[0]->uncompressSize == 368); - REQUIRE(headers[1]->archiveName == name); REQUIRE(headers[1]->fileName == "dir/brimmedhat.png"); REQUIRE(headers[1]->compressSize == 1959); REQUIRE(headers[1]->uncompressSize == 1959); + + delete entry; } SECTION("test2.zip") { name = prefix + "data/test/test2.zip"; - REQUIRE(Zip::readArchiveInfo(name, headers)); + VirtZipEntry *const entry = new VirtZipEntry(name); + std::vector<ZipLocalHeader*> &headers = entry->mHeaders; + + REQUIRE(Zip::readArchiveInfo(entry)); REQUIRE(headers.size() == 11); - REQUIRE(headers[0]->archiveName == name); + REQUIRE(entry->mArchiveName == name); REQUIRE(headers[0]->fileName == "test.txt"); REQUIRE(headers[0]->compressSize == 17); REQUIRE(headers[0]->uncompressSize == 23); - REQUIRE(headers[1]->archiveName == name); REQUIRE(headers[1]->fileName == "dir2/hide.png"); REQUIRE(headers[1]->compressSize == 365); REQUIRE(headers[1]->uncompressSize == 368); - REQUIRE(headers[2]->archiveName == name); REQUIRE(headers[2]->fileName == "dir2/test.txt"); REQUIRE(headers[2]->compressSize == 17); REQUIRE(headers[2]->uncompressSize == 23); - REQUIRE(headers[3]->archiveName == name); REQUIRE(headers[3]->fileName == "dir2/paths.xml"); REQUIRE(headers[3]->compressSize == 154); REQUIRE(headers[3]->uncompressSize == 185); - REQUIRE(headers[4]->archiveName == name); REQUIRE(headers[4]->fileName == "dir2/units.xml"); REQUIRE(headers[4]->compressSize == 202); REQUIRE(headers[4]->uncompressSize == 306); - REQUIRE(headers[5]->archiveName == name); REQUIRE(headers[5]->fileName == "dir/hide.png"); REQUIRE(headers[5]->compressSize == 365); REQUIRE(headers[5]->uncompressSize == 368); - REQUIRE(headers[6]->archiveName == name); REQUIRE(headers[6]->fileName == "dir/1/test.txt"); REQUIRE(headers[6]->compressSize == 17); REQUIRE(headers[6]->uncompressSize == 23); - REQUIRE(headers[7]->archiveName == name); REQUIRE(headers[7]->fileName == "dir/1/file1.txt"); REQUIRE(headers[7]->compressSize == 17); REQUIRE(headers[7]->uncompressSize == 23); - REQUIRE(headers[8]->archiveName == name); REQUIRE(headers[8]->fileName == "dir/gpl/palette.gpl"); REQUIRE(headers[8]->compressSize == 128); REQUIRE(headers[8]->uncompressSize == 213); - REQUIRE(headers[9]->archiveName == name); REQUIRE(headers[9]->fileName == "dir/dye.png"); REQUIRE(headers[9]->compressSize == 794); REQUIRE(headers[9]->uncompressSize == 794); - REQUIRE(headers[10]->archiveName == name); REQUIRE(headers[10]->fileName == "units.xml"); REQUIRE(headers[10]->compressSize == 202); REQUIRE(headers[10]->uncompressSize == 306); + + delete entry; } SECTION("test3.zip") { name = prefix + "data/test/test3.zip"; - REQUIRE(Zip::readArchiveInfo(name, headers)); + VirtZipEntry *const entry = new VirtZipEntry(name); + std::vector<ZipLocalHeader*> &headers = entry->mHeaders; + + REQUIRE(Zip::readArchiveInfo(entry)); REQUIRE(headers.size() == 2); - REQUIRE(headers[0]->archiveName == name); + REQUIRE(entry->mArchiveName == name); REQUIRE(headers[0]->fileName == "test.txt"); REQUIRE(headers[0]->compressSize == 17); REQUIRE(headers[0]->uncompressSize == 23); - REQUIRE(headers[1]->archiveName == name); REQUIRE(headers[1]->fileName == "units.xml"); REQUIRE(headers[1]->compressSize == 202); REQUIRE(headers[1]->uncompressSize == 306); + + delete entry; } SECTION("test4.zip") { name = prefix + "data/test/test4.zip"; - REQUIRE(Zip::readArchiveInfo(name, headers)); + VirtZipEntry *const entry = new VirtZipEntry(name); + std::vector<ZipLocalHeader*> &headers = entry->mHeaders; + + REQUIRE(Zip::readArchiveInfo(entry)); + REQUIRE(entry->mArchiveName == name); REQUIRE(headers.size() == 0); + + delete entry; } - delete_all(headers); delete2(logger); } @@ -151,7 +159,6 @@ TEST_CASE("Zip readCompressedFile") logger = new Logger(); std::string name("data/test/test.zip"); std::string prefix; - std::vector<ZipLocalHeader*> headers; if (Files::existsLocal(name) == false) prefix = "../"; @@ -164,15 +171,19 @@ TEST_CASE("Zip readCompressedFile") { name = prefix + "data/test/test2.zip"; - REQUIRE(Zip::readArchiveInfo(name, headers)); + VirtZipEntry *const entry = new VirtZipEntry(name); + std::vector<ZipLocalHeader*> &headers = entry->mHeaders; + + REQUIRE(Zip::readArchiveInfo(entry)); REQUIRE(headers.size() == 11); + REQUIRE(entry->mArchiveName == name); // test.txt uint8_t *const buf = Zip::readCompressedFile(headers[0]); REQUIRE(buf != nullptr); delete [] buf; + delete entry; } - delete_all(headers); delete2(logger); } @@ -181,7 +192,6 @@ TEST_CASE("Zip readFile") logger = new Logger(); std::string name("data/test/test.zip"); std::string prefix; - std::vector<ZipLocalHeader*> headers; if (Files::existsLocal(name) == false) prefix = "../"; @@ -194,8 +204,12 @@ TEST_CASE("Zip readFile") { name = prefix + "data/test/test.zip"; - REQUIRE(Zip::readArchiveInfo(name, headers)); + VirtZipEntry *const entry = new VirtZipEntry(name); + std::vector<ZipLocalHeader*> &headers = entry->mHeaders; + + REQUIRE(Zip::readArchiveInfo(entry)); REQUIRE(headers.size() == 2); + REQUIRE(entry->mArchiveName == name); for (int f = 0; f < 2; f ++) { logger->log("test header: %s, %u, %u", @@ -206,14 +220,19 @@ TEST_CASE("Zip readFile") REQUIRE(buf != nullptr); delete [] buf; } + delete entry; } SECTION("test2.zip") { name = prefix + "data/test/test2.zip"; - REQUIRE(Zip::readArchiveInfo(name, headers)); + VirtZipEntry *const entry = new VirtZipEntry(name); + std::vector<ZipLocalHeader*> &headers = entry->mHeaders; + + REQUIRE(Zip::readArchiveInfo(entry)); REQUIRE(headers.size() == 11); + REQUIRE(entry->mArchiveName == name); // test.txt uint8_t *buf = Zip::readFile(headers[0]); REQUIRE(buf != nullptr); @@ -231,14 +250,19 @@ TEST_CASE("Zip readFile") REQUIRE(buf != nullptr); delete [] buf; } + delete entry; } SECTION("test3.zip") { name = prefix + "data/test/test3.zip"; - REQUIRE(Zip::readArchiveInfo(name, headers)); + VirtZipEntry *const entry = new VirtZipEntry(name); + std::vector<ZipLocalHeader*> &headers = entry->mHeaders; + + REQUIRE(Zip::readArchiveInfo(entry)); REQUIRE(headers.size() == 2); + REQUIRE(entry->mArchiveName == name); for (int f = 0; f < 2; f ++) { logger->log("test header: %s, %u, %u", @@ -249,8 +273,8 @@ TEST_CASE("Zip readFile") REQUIRE(buf != nullptr); delete [] buf; } + delete entry; } - delete_all(headers); delete2(logger); } diff --git a/src/fs/ziplocalheader.cpp b/src/fs/ziplocalheader.cpp new file mode 100644 index 000000000..7bb869624 --- /dev/null +++ b/src/fs/ziplocalheader.cpp @@ -0,0 +1,37 @@ +/* + * The ManaPlus Client + * Copyright (C) 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/ziplocalheader.h" + +#include "fs/virtzipentry.h" + +#include "localconsts.h" + +#include <string> + +ZipLocalHeader::ZipLocalHeader() : + fileName(), + zipEntry(nullptr), + dataOffset(0U), + compressSize(0U), + uncompressSize(0U), + compressed(false) +{ +} diff --git a/src/fs/ziplocalheader.h b/src/fs/ziplocalheader.h index 8905d4c48..f3a1894ce 100644 --- a/src/fs/ziplocalheader.h +++ b/src/fs/ziplocalheader.h @@ -25,22 +25,16 @@ #include <string> +struct VirtZipEntry; + struct ZipLocalHeader final { - ZipLocalHeader() : - archiveName(), - fileName(), - dataOffset(0U), - compressSize(0U), - uncompressSize(0U), - compressed(false) - { - } + ZipLocalHeader(); A_DELETE_COPY(ZipLocalHeader) - std::string archiveName; std::string fileName; + VirtZipEntry *zipEntry; uint32_t dataOffset; uint32_t compressSize; uint32_t uncompressSize; |