summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrei Karas <akaras@inbox.ru>2017-02-24 19:10:28 +0300
committerAndrei Karas <akaras@inbox.ru>2017-02-24 23:31:22 +0300
commit0bb68a88e6dc6a04685825e80b4e3dca1dc097d2 (patch)
tree07a4e3bd321c5199fe4d388a1c6cb9923a147c0c
parentd1b37a4b299809c3eb11b073ab4eafdb773b8a45 (diff)
downloadmanaplus-0bb68a88e6dc6a04685825e80b4e3dca1dc097d2.tar.gz
manaplus-0bb68a88e6dc6a04685825e80b4e3dca1dc097d2.tar.bz2
manaplus-0bb68a88e6dc6a04685825e80b4e3dca1dc097d2.tar.xz
manaplus-0bb68a88e6dc6a04685825e80b4e3dca1dc097d2.zip
Add support for extract files from zip archives.
-rw-r--r--src/fs/virtfile.cpp4
-rw-r--r--src/fs/virtfile.h1
-rw-r--r--src/fs/virtfileprivate.cpp6
-rw-r--r--src/fs/virtfileprivate.h5
-rw-r--r--src/fs/zip.cpp114
-rw-r--r--src/fs/zip.h6
-rw-r--r--src/fs/zip_unittest.cc117
-rw-r--r--src/fs/ziplocalheader.h4
8 files changed, 245 insertions, 12 deletions
diff --git a/src/fs/virtfile.cpp b/src/fs/virtfile.cpp
index 9f9ab6e98..fb719f05c 100644
--- a/src/fs/virtfile.cpp
+++ b/src/fs/virtfile.cpp
@@ -27,11 +27,13 @@
#include "debug.h"
VirtFile::VirtFile() :
- mPrivate(nullptr)
+ mPrivate(nullptr),
+ mBuf(nullptr)
{
}
VirtFile::~VirtFile()
{
delete2(mPrivate);
+ delete [] mBuf;
}
diff --git a/src/fs/virtfile.h b/src/fs/virtfile.h
index cf7ded1a4..cb6211e83 100644
--- a/src/fs/virtfile.h
+++ b/src/fs/virtfile.h
@@ -34,6 +34,7 @@ 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 1dcc6116c..5ebb234cc 100644
--- a/src/fs/virtfileprivate.cpp
+++ b/src/fs/virtfileprivate.cpp
@@ -21,6 +21,7 @@
#include "fs/virtfileprivate.h"
#include <unistd.h>
+#include <zlib.h>
#include "debug.h"
@@ -36,7 +37,7 @@ VirtFilePrivate::VirtFilePrivate(const int fd) :
{
}
-VirtFilePrivate::VirtFilePrivate(PHYSFS_file *const file) :
+VirtFilePrivate::VirtFilePrivate(PHYSFS_file *restrict const file) :
mFile(file),
mFd(-1)
{
@@ -45,10 +46,7 @@ VirtFilePrivate::VirtFilePrivate(PHYSFS_file *const file) :
VirtFilePrivate::~VirtFilePrivate()
{
if (mFile != nullptr)
- {
PHYSFS_close(mFile);
- mFile = nullptr;
- }
if (mFd != -1)
close(mFd);
}
diff --git a/src/fs/virtfileprivate.h b/src/fs/virtfileprivate.h
index 62e510142..ace7b49e0 100644
--- a/src/fs/virtfileprivate.h
+++ b/src/fs/virtfileprivate.h
@@ -32,7 +32,7 @@ struct VirtFilePrivate final
{
VirtFilePrivate();
- explicit VirtFilePrivate(PHYSFS_file *const file);
+ explicit VirtFilePrivate(PHYSFS_file *restrict const file);
explicit VirtFilePrivate(const int fd);
@@ -40,7 +40,10 @@ struct VirtFilePrivate final
~VirtFilePrivate();
+ // physfs fields
PHYSFS_file *mFile;
+
+ // dirfs fields
int mFd;
};
diff --git a/src/fs/zip.cpp b/src/fs/zip.cpp
index 57aa9fc76..cff4a3e6f 100644
--- a/src/fs/zip.cpp
+++ b/src/fs/zip.cpp
@@ -20,6 +20,7 @@
#include "fs/zip.h"
+#include "fs/virtfile.h"
#include "fs/ziplocalheader.h"
#include "utils/checkutils.h"
@@ -27,6 +28,7 @@
#include <iostream>
#include <unistd.h>
+#include <zlib.h>
#include "debug.h"
@@ -58,6 +60,7 @@ namespace Zip
size_t cnt = 0U;
uint8_t *const buf = new uint8_t[65535 + 10];
uint16_t val16 = 0U;
+ uint16_t method = 0U;
ZipLocalHeader *header = nullptr;
logger->log("Read archive: %s", archiveName.c_str());
@@ -77,7 +80,13 @@ namespace Zip
header = new ZipLocalHeader;
header->archiveName = archiveName;
// skip useless fields
- fseek(arcFile, 14, SEEK_CUR); // + 14
+ fseek(arcFile, 4, SEEK_CUR); // + 4
+ // file header pointer on 8
+ // +++ need add endian specific decoding for method
+ readVal(&method, 2, "compression method") // + 2
+ header->compressed = (method != 0);
+ // file header pointer on 10
+ fseek(arcFile, 8, SEEK_CUR); // + 8
// file header pointer on 18
readVal(&header->compressSize, 4,
"zip compressed size") // + 4
@@ -115,6 +124,8 @@ namespace Zip
headers.push_back(header);
logger->log(" file name: %s",
header->fileName.c_str());
+ logger->log(" compression method: %u",
+ CAST_U32(method));
logger->log(" compressed size: %u",
header->compressSize);
logger->log(" uncompressed size: %u",
@@ -160,4 +171,105 @@ namespace Zip
return true;
}
+ void reportZlibError(const std::string &text,
+ const int err)
+ {
+ reportAlways("Zlib error: '%s' in %s",
+ text.c_str(),
+ getZlibError(err).c_str());
+ }
+
+ std::string getZlibError(const int err)
+ {
+ switch (err)
+ {
+ case Z_OK:
+ return std::string();
+ default:
+ return "unknown zlib error";
+ }
+ }
+
+ uint8_t *readCompressedFile(const ZipLocalHeader *restrict const header)
+ {
+ if (header == nullptr)
+ {
+ reportAlways("Zip::readCompressedFile: header is null");
+ return nullptr;
+ }
+ FILE *restrict const arcFile = fopen(header->archiveName.c_str(),
+ "r");
+ if (arcFile == nullptr)
+ {
+ reportAlways("Can't open zip file %s",
+ header->archiveName.c_str());
+ return nullptr;
+ }
+
+ fseek(arcFile, header->dataOffset, SEEK_SET);
+ const uint32_t compressSize = header->compressSize;
+ uint8_t *const buf = new uint8_t[compressSize];
+ if (fread(static_cast<void*>(buf), 1, compressSize, arcFile) !=
+ compressSize)
+ {
+ reportAlways("Read zip compressed file error from archive: %s",
+ header->archiveName.c_str());
+ fclose(arcFile);
+ delete [] buf;
+ return nullptr;
+ }
+ fclose(arcFile);
+ return buf;
+ }
+
+ uint8_t *readFile(const ZipLocalHeader *restrict const header)
+ {
+ if (header == nullptr)
+ {
+ reportAlways("Open zip file error. header is null.");
+ return nullptr;
+ }
+ uint8_t *restrict const in = readCompressedFile(header);
+ if (in == nullptr)
+ return nullptr;
+ if (header->compressed == false)
+ return in; // return as is if data not compressed
+ const size_t outSize = header->uncompressSize;
+ uint8_t *restrict const out = new uint8_t[outSize];
+ if (outSize == 0)
+ return out;
+
+ z_stream strm;
+ strm.zalloc = nullptr;
+ strm.zfree = nullptr;
+ strm.opaque = nullptr;
+ strm.next_in = in;
+ strm.avail_in = header->compressSize;
+ strm.next_out = out;
+ strm.avail_out = outSize;
+
+ int ret = inflateInit2(&strm, -MAX_WBITS);
+ if (ret != Z_OK)
+ {
+ reportZlibError(header->archiveName, ret);
+ delete [] in;
+ delete [] out;
+ return nullptr;
+ }
+ ret = inflate(&strm, Z_FINISH);
+// ret = inflate(&strm, Z_SYNC_FLUSH);
+ if (ret != Z_OK &&
+ ret != Z_STREAM_END)
+ {
+ reportZlibError("file decompression error",
+ ret);
+ inflateEnd(&strm);
+ delete [] in;
+ delete [] out;
+ return nullptr;
+ }
+ inflateEnd(&strm);
+ delete [] in;
+ return out;
+ }
} // namespace Zip
diff --git a/src/fs/zip.h b/src/fs/zip.h
index 145be5c88..a54b08129 100644
--- a/src/fs/zip.h
+++ b/src/fs/zip.h
@@ -26,12 +26,18 @@
#include <string>
#include <vector>
+struct VirtFile;
struct ZipLocalHeader;
namespace Zip
{
bool readArchiveInfo(const std::string &restrict archiveName,
std::vector<ZipLocalHeader*> &restrict headers);
+ std::string getZlibError(const int err);
+ void reportZlibError(const std::string &text,
+ const int err);
+ uint8_t *readCompressedFile(const ZipLocalHeader *restrict const header);
+ uint8_t *readFile(const ZipLocalHeader *restrict const header);
} // namespace Zip
#endif // UTILS_ZIP_H
diff --git a/src/fs/zip_unittest.cc b/src/fs/zip_unittest.cc
index 3f0c49cdf..9661b7d19 100644
--- a/src/fs/zip_unittest.cc
+++ b/src/fs/zip_unittest.cc
@@ -40,7 +40,7 @@ TEST_CASE("Zip readArchiveInfo")
if (Files::existsLocal(name) == false)
prefix = "../";
- SECTION("test 1")
+ SECTION("test.zip")
{
name = prefix + "data/test/test.zip";
@@ -56,7 +56,7 @@ TEST_CASE("Zip readArchiveInfo")
REQUIRE(headers[1]->uncompressSize == 1959);
}
- SECTION("test 2")
+ SECTION("test2.zip")
{
name = prefix + "data/test/test2.zip";
@@ -118,7 +118,7 @@ TEST_CASE("Zip readArchiveInfo")
REQUIRE(headers[10]->uncompressSize == 306);
}
- SECTION("test 3")
+ SECTION("test3.zip")
{
name = prefix + "data/test/test3.zip";
@@ -134,7 +134,7 @@ TEST_CASE("Zip readArchiveInfo")
REQUIRE(headers[1]->uncompressSize == 306);
}
- SECTION("test 4")
+ SECTION("test4.zip")
{
name = prefix + "data/test/test4.zip";
@@ -145,3 +145,112 @@ TEST_CASE("Zip readArchiveInfo")
delete_all(headers);
delete2(logger);
}
+
+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 = "../";
+
+ SECTION("empty")
+ {
+ REQUIRE_THROWS(Zip::readCompressedFile(nullptr));
+ }
+
+ SECTION("test2.zip")
+ {
+ name = prefix + "data/test/test2.zip";
+
+ REQUIRE(Zip::readArchiveInfo(name, headers));
+ REQUIRE(headers.size() == 11);
+ // test.txt
+ uint8_t *const buf = Zip::readCompressedFile(headers[0]);
+ REQUIRE(buf != nullptr);
+ delete [] buf;
+ }
+
+ delete_all(headers);
+ delete2(logger);
+}
+
+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 = "../";
+
+ SECTION("empty")
+ {
+ REQUIRE_THROWS(Zip::readFile(nullptr));
+ }
+
+ SECTION("test.zip")
+ {
+ name = prefix + "data/test/test.zip";
+
+ REQUIRE(Zip::readArchiveInfo(name, headers));
+ REQUIRE(headers.size() == 2);
+ for (int f = 0; f < 2; f ++)
+ {
+ logger->log("test header: %s, %u, %u",
+ headers[f]->fileName.c_str(),
+ headers[f]->compressSize,
+ headers[f]->uncompressSize);
+ uint8_t *const buf = Zip::readFile(headers[f]);
+ REQUIRE(buf != nullptr);
+ delete [] buf;
+ }
+ }
+
+ SECTION("test2.zip")
+ {
+ name = prefix + "data/test/test2.zip";
+
+ REQUIRE(Zip::readArchiveInfo(name, headers));
+ REQUIRE(headers.size() == 11);
+ // test.txt
+ uint8_t *buf = Zip::readFile(headers[0]);
+ REQUIRE(buf != nullptr);
+ const std::string str = std::string(reinterpret_cast<char*>(buf),
+ headers[0]->uncompressSize);
+ REQUIRE(str == "test line 1\ntest line 2");
+ delete [] buf;
+ for (int f = 0; f < 11; f ++)
+ {
+ logger->log("test header: %s, %u, %u",
+ headers[f]->fileName.c_str(),
+ headers[f]->compressSize,
+ headers[f]->uncompressSize);
+ buf = Zip::readFile(headers[f]);
+ REQUIRE(buf != nullptr);
+ delete [] buf;
+ }
+ }
+
+ SECTION("test3.zip")
+ {
+ name = prefix + "data/test/test3.zip";
+
+ REQUIRE(Zip::readArchiveInfo(name, headers));
+ REQUIRE(headers.size() == 2);
+ for (int f = 0; f < 2; f ++)
+ {
+ logger->log("test header: %s, %u, %u",
+ headers[f]->fileName.c_str(),
+ headers[f]->compressSize,
+ headers[f]->uncompressSize);
+ uint8_t *const buf = Zip::readFile(headers[f]);
+ REQUIRE(buf != nullptr);
+ delete [] buf;
+ }
+ }
+
+ delete_all(headers);
+ delete2(logger);
+}
diff --git a/src/fs/ziplocalheader.h b/src/fs/ziplocalheader.h
index 4a71d74c0..8905d4c48 100644
--- a/src/fs/ziplocalheader.h
+++ b/src/fs/ziplocalheader.h
@@ -32,7 +32,8 @@ struct ZipLocalHeader final
fileName(),
dataOffset(0U),
compressSize(0U),
- uncompressSize(0U)
+ uncompressSize(0U),
+ compressed(false)
{
}
@@ -43,6 +44,7 @@ struct ZipLocalHeader final
uint32_t dataOffset;
uint32_t compressSize;
uint32_t uncompressSize;
+ bool compressed;
};
#endif // UTILS_ZIPLOCALHEADER_H