From 5aca48c5a15041a85af0709cdf1496c61c34b1dd Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Fri, 5 May 2017 17:22:43 +0300 Subject: Rename zip into zipreader. --- src/CMakeLists.txt | 8 +- src/Makefile.am | 4 +- src/fs/virtfs/virtfs.cpp | 4 +- src/fs/virtfs/virtfszip.cpp | 8 +- src/fs/virtfs/zip.cpp | 326 ------------------------------------------ src/fs/virtfs/zip.h | 46 ------ src/fs/virtfs/zip_unittest.cc | 32 ++--- src/fs/virtfs/zipreader.cpp | 326 ++++++++++++++++++++++++++++++++++++++++++ src/fs/virtfs/zipreader.h | 46 ++++++ 9 files changed, 401 insertions(+), 399 deletions(-) delete mode 100644 src/fs/virtfs/zip.cpp delete mode 100644 src/fs/virtfs/zip.h create mode 100644 src/fs/virtfs/zipreader.cpp create mode 100644 src/fs/virtfs/zipreader.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 71ca2aa29..331b10c7a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -914,8 +914,8 @@ SET(SRCS fs/virtfs/virtfsziprwops.h fs/virtfs/virtzipentry.cpp fs/virtfs/virtzipentry.h - fs/virtfs/zip.cpp - fs/virtfs/zip.h + fs/virtfs/zipreader.cpp + fs/virtfs/zipreader.h fs/virtfs/ziplocalheader.cpp fs/virtfs/ziplocalheader.h utils/process.cpp @@ -1823,8 +1823,8 @@ SET(DYE_CMD_SRCS fs/virtfs/virtfsziprwops.h fs/virtfs/virtzipentry.cpp fs/virtfs/virtzipentry.h - fs/virtfs/zip.cpp - fs/virtfs/zip.h + fs/virtfs/zipreader.cpp + fs/virtfs/zipreader.h fs/virtfs/ziplocalheader.cpp fs/virtfs/ziplocalheader.h utils/sdl2helper.cpp diff --git a/src/Makefile.am b/src/Makefile.am index b4c8e3e21..94414f868 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -862,8 +862,8 @@ BASE_SRC += events/actionevent.h \ fs/virtfs/virtfsziprwops.h \ fs/virtfs/virtzipentry.cpp \ fs/virtfs/virtzipentry.h \ - fs/virtfs/zip.cpp \ - fs/virtfs/zip.h \ + fs/virtfs/zipreader.cpp \ + fs/virtfs/zipreader.h \ fs/virtfs/ziplocalheader.cpp \ fs/virtfs/ziplocalheader.h diff --git a/src/fs/virtfs/virtfs.cpp b/src/fs/virtfs/virtfs.cpp index 67b01ff3a..58dec670e 100644 --- a/src/fs/virtfs/virtfs.cpp +++ b/src/fs/virtfs/virtfs.cpp @@ -30,7 +30,7 @@ #include "fs/virtfs/virtfszip.h" #include "fs/virtfs/virtlist.h" #include "fs/virtfs/virtzipentry.h" -#include "fs/virtfs/zip.h" +#include "fs/virtfs/zipreader.h" #include "utils/checkutils.h" #include "utils/stringutils.h" @@ -477,7 +477,7 @@ namespace VirtFs } VirtZipEntry *const entry = new VirtZipEntry(newDir, VirtFsZip::getFuncs()); - if (Zip::readArchiveInfo(entry) == false) + if (ZipReader::readArchiveInfo(entry) == false) { delete entry; return false; diff --git a/src/fs/virtfs/virtfszip.cpp b/src/fs/virtfs/virtfszip.cpp index 5b3d60185..0f58dd5f5 100644 --- a/src/fs/virtfs/virtfszip.cpp +++ b/src/fs/virtfs/virtfszip.cpp @@ -25,7 +25,7 @@ #include "fs/virtfs/virtfsziprwops.h" #include "fs/virtfs/virtlist.h" #include "fs/virtfs/virtzipentry.h" -#include "fs/virtfs/zip.h" +#include "fs/virtfs/zipreader.h" #include "fs/virtfs/ziplocalheader.h" #include "utils/checkutils.h" @@ -518,7 +518,8 @@ namespace VirtFsZip const ZipLocalHeader *restrict const header = *it2; if (header->fileName == filename) { - const uint8_t *restrict const buf = Zip::readFile(header); + const uint8_t *restrict const buf = + ZipReader::readFile(header); if (buf == nullptr) return nullptr; File *restrict const file = new File(&funcs, @@ -647,7 +648,8 @@ namespace VirtFsZip const ZipLocalHeader *restrict const header = *it2; if (header->fileName == filename) { - const uint8_t *restrict const buf = Zip::readFile(header); + const uint8_t *restrict const buf = + ZipReader::readFile(header); if (buf == nullptr) return nullptr; diff --git a/src/fs/virtfs/zip.cpp b/src/fs/virtfs/zip.cpp deleted file mode 100644 index 63cfb59b9..000000000 --- a/src/fs/virtfs/zip.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/* - * 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 . - */ - -#include "fs/virtfs/zip.h" - -#include "fs/paths.h" - -#include "fs/virtfs/virtzipentry.h" -#include "fs/virtfs/ziplocalheader.h" - -#include "utils/checkutils.h" -#include "utils/stringutils.h" - -#include -#include - -#if SDL_BYTEORDER == SDL_BIG_ENDIAN -#include -#endif // SDL_BYTEORDER == SDL_BIG_ENDIAN - -#include "debug.h" - -#ifndef SDL_BIG_ENDIAN -#error missing SDL_endian.h -#endif // SDL_BYTEORDER - -// #define DEBUG_ZIP - -extern const char *dirSeparator; - -#define readVal(val, sz, msg) \ - cnt = fread(static_cast(val), 1, sz, arcFile); \ - if (cnt != sz) \ - { \ - reportAlways("Error reading " msg " in file %s", \ - archiveName.c_str()); \ - delete header; \ - delete [] buf; \ - fclose(arcFile); \ - return false; \ - } - -#if SDL_BYTEORDER == SDL_BIG_ENDIAN -#define swapVal16(val) val = bswap_16(val); -#define swapVal32(val) val = bswap_32(val); -#else // SDL_BYTEORDER == SDL_BIG_ENDIAN -#define swapVal16(val) -#define swapVal32(val) -#endif // SDL_BYTEORDER == SDL_BIG_ENDIAN - -namespace VirtFs -{ - -namespace Zip -{ - bool readArchiveInfo(VirtZipEntry *const entry) - { - if (entry == nullptr) - { - reportAlways("Entry is null."); - return false; - } - const std::string archiveName = entry->root; - std::vector &restrict headers = entry->mHeaders; - std::vector &restrict dirs = entry->mDirs; - FILE *restrict const arcFile = fopen(archiveName.c_str(), - "rb"); - if (arcFile == nullptr) - { - reportAlways("Can't open zip file %s", - archiveName.c_str()); - return false; - } - uint8_t *const buf = new uint8_t[65535 + 10]; - uint16_t val16 = 0U; - uint16_t method = 0U; - ZipLocalHeader *header = nullptr; - -#ifdef DEBUG_ZIP - logger->log("Read archive: %s", archiveName.c_str()); -#endif // DEBUG_ZIP - - // format source https://en.wikipedia.org/wiki/Zip_%28file_format%29 - while (feof(arcFile) == 0) - { - size_t cnt = 0U; - // file header pointer on 0 - // read file header signature - readVal(buf, 4, "zip file header"); // + 4 - // pointer on 4 - - if (buf[0] == 0x50 && - buf[1] == 0x4B && - buf[2] == 0x03 && - buf[3] == 0x04) - { // local file header - header = new ZipLocalHeader; - header->zipEntry = entry; - // skip useless fields - fseek(arcFile, 4, SEEK_CUR); // + 4 - // file header pointer on 8 - readVal(&method, 2, "compression method") // + 2 - swapVal16(method) - 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 - swapVal32(header->compressSize) - // file header pointer on 22 - readVal(&header->uncompressSize, 4, - "zip uncompressed size") // + 4 - swapVal32(header->uncompressSize) - // file header pointer on 26 - readVal(&val16, 2, "file name length") // + 2 - swapVal16(val16) - // file header pointer on 28 - const uint32_t fileNameLen = CAST_U32(val16); - if (fileNameLen > 1000) - { - reportAlways("Error too long file name in file %s", - archiveName.c_str()); - delete header; - delete [] buf; - fclose(arcFile); - return false; - } - readVal(&val16, 2, "extra field length") // + 2 - swapVal16(val16) - // file header pointer on 30 - const uint32_t extraFieldLen = CAST_U32(val16); - readVal(buf, fileNameLen, "file name"); - // file header pointer on 30 + fileNameLen - buf[fileNameLen] = 0; - header->fileName = std::string( - reinterpret_cast(buf)); - prepareFsPath(header->fileName); - header->dataOffset = CAST_S32(ftell(arcFile) + extraFieldLen); - fseek(arcFile, extraFieldLen + header->compressSize, SEEK_CUR); - // pointer on 30 + fileNameLen + extraFieldLen + compressSize - if (findLast(header->fileName, dirSeparator) == false) - { - headers.push_back(header); -#ifdef DEBUG_ZIP - 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", - header->uncompressSize); -#endif // DEBUG_ZIP - } - else - { -#ifdef DEBUG_ZIP - logger->log(" dir name: %s", - header->fileName.c_str()); -#endif // DEBUG_ZIP - dirs.push_back(header->fileName); - delete header; - } - } - else if (buf[0] == 0x50 && - buf[1] == 0x4B && - buf[2] == 0x01 && - buf[3] == 0x02) - { // central directory file header - // !!! This is quick way for read zip archives. !!! - // !!! It ignore modified files in archive. !!! - // ignoring central directory entries - break; - } - else if (buf[0] == 0x50 && - buf[1] == 0x4B && - buf[2] == 0x05 && - buf[3] == 0x06) - { // end of central directory - // !!! This is quick way for read zip archives. !!! - // !!! It ignore modified files in archive. !!! - // ignoring end of central directory - break; - } - else - { - reportAlways("Error in header signature (0x%02x%02x%02x%02x)" - " in file %s", - buf[0], - buf[1], - buf[2], - buf[3], - archiveName.c_str()); - delete [] buf; - fclose(arcFile); - return false; - } - } - delete [] buf; - fclose(arcFile); - 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->zipEntry->root.c_str(), - "rb"); - if (arcFile == nullptr) - { - reportAlways("Can't open zip file %s", - header->zipEntry->root.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(buf), 1, compressSize, arcFile) != - compressSize) - { - reportAlways("Read zip compressed file error from archive: %s", - header->zipEntry->root.c_str()); - fclose(arcFile); - delete [] buf; - return nullptr; - } - fclose(arcFile); - return buf; - } - - const 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 = CAST_U32(outSize); - - int ret = inflateInit2(&strm, -MAX_WBITS); - if (ret != Z_OK) - { - reportZlibError(header->zipEntry->root, 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 - -} // namespace VirtFs diff --git a/src/fs/virtfs/zip.h b/src/fs/virtfs/zip.h deleted file mode 100644 index e74ff09aa..000000000 --- a/src/fs/virtfs/zip.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 . - */ - -#ifndef UTILS_ZIP_H -#define UTILS_ZIP_H - -#include "localconsts.h" - -#include - -namespace VirtFs -{ - -struct VirtZipEntry; -struct ZipLocalHeader; - -namespace Zip -{ - bool readArchiveInfo(VirtZipEntry *const entry); - std::string getZlibError(const int err); - void reportZlibError(const std::string &text, - const int err); - uint8_t *readCompressedFile(const ZipLocalHeader *restrict const header); - const uint8_t *readFile(const ZipLocalHeader *restrict const header); -} // namespace Zip - -} // namespace VirtFs - -#endif // UTILS_ZIP_H diff --git a/src/fs/virtfs/zip_unittest.cc b/src/fs/virtfs/zip_unittest.cc index ad42f6a4a..3853bee79 100644 --- a/src/fs/virtfs/zip_unittest.cc +++ b/src/fs/virtfs/zip_unittest.cc @@ -26,7 +26,7 @@ #include "fs/virtfs/virtfszip.h" #include "fs/virtfs/virtzipentry.h" -#include "fs/virtfs/zip.h" +#include "fs/virtfs/zipreader.h" #include "fs/virtfs/ziplocalheader.h" #include "utils/delete2.h" @@ -52,7 +52,7 @@ TEST_CASE("Zip readArchiveInfo") VirtFs::VirtFsZip::getFuncs()); std::vector &headers = entry->mHeaders; - REQUIRE(VirtFs::Zip::readArchiveInfo(entry)); + REQUIRE(VirtFs::ZipReader::readArchiveInfo(entry)); REQUIRE(headers.size() == 2); REQUIRE(entry->root == name); REQUIRE(headers[0]->fileName == "dir" + sep + "hide.png"); @@ -73,7 +73,7 @@ TEST_CASE("Zip readArchiveInfo") VirtFs::VirtFsZip::getFuncs()); std::vector &headers = entry->mHeaders; - REQUIRE(VirtFs::Zip::readArchiveInfo(entry)); + REQUIRE(VirtFs::ZipReader::readArchiveInfo(entry)); REQUIRE(headers.size() == 11); REQUIRE(entry->root == name); REQUIRE(headers[0]->fileName == "test.txt"); @@ -135,7 +135,7 @@ TEST_CASE("Zip readArchiveInfo") VirtFs::VirtFsZip::getFuncs()); std::vector &headers = entry->mHeaders; - REQUIRE(VirtFs::Zip::readArchiveInfo(entry)); + REQUIRE(VirtFs::ZipReader::readArchiveInfo(entry)); REQUIRE(headers.size() == 2); REQUIRE(entry->root == name); REQUIRE(headers[0]->fileName == "test.txt"); @@ -156,7 +156,7 @@ TEST_CASE("Zip readArchiveInfo") VirtFs::VirtFsZip::getFuncs()); std::vector &headers = entry->mHeaders; - REQUIRE(VirtFs::Zip::readArchiveInfo(entry)); + REQUIRE(VirtFs::ZipReader::readArchiveInfo(entry)); REQUIRE(entry->root == name); REQUIRE(headers.size() == 0); @@ -176,7 +176,7 @@ TEST_CASE("Zip readCompressedFile") SECTION("empty") { - REQUIRE_THROWS(VirtFs::Zip::readCompressedFile(nullptr)); + REQUIRE_THROWS(VirtFs::ZipReader::readCompressedFile(nullptr)); } SECTION("test2.zip") @@ -187,11 +187,11 @@ TEST_CASE("Zip readCompressedFile") VirtFs::VirtFsZip::getFuncs()); std::vector &headers = entry->mHeaders; - REQUIRE(VirtFs::Zip::readArchiveInfo(entry)); + REQUIRE(VirtFs::ZipReader::readArchiveInfo(entry)); REQUIRE(headers.size() == 11); REQUIRE(entry->root == name); // test.txt - uint8_t *const buf = VirtFs::Zip::readCompressedFile(headers[0]); + uint8_t *const buf = VirtFs::ZipReader::readCompressedFile(headers[0]); REQUIRE(buf != nullptr); delete [] buf; delete entry; @@ -210,7 +210,7 @@ TEST_CASE("Zip readFile") SECTION("empty") { - REQUIRE_THROWS(VirtFs::Zip::readFile(nullptr)); + REQUIRE_THROWS(VirtFs::ZipReader::readFile(nullptr)); } SECTION("test.zip") @@ -221,7 +221,7 @@ TEST_CASE("Zip readFile") VirtFs::VirtFsZip::getFuncs()); std::vector &headers = entry->mHeaders; - REQUIRE(VirtFs::Zip::readArchiveInfo(entry)); + REQUIRE(VirtFs::ZipReader::readArchiveInfo(entry)); REQUIRE(headers.size() == 2); REQUIRE(entry->root == name); for (int f = 0; f < 2; f ++) @@ -230,7 +230,7 @@ TEST_CASE("Zip readFile") headers[f]->fileName.c_str(), headers[f]->compressSize, headers[f]->uncompressSize); - const uint8_t *const buf = VirtFs::Zip::readFile(headers[f]); + const uint8_t *const buf = VirtFs::ZipReader::readFile(headers[f]); REQUIRE(buf != nullptr); delete [] buf; } @@ -245,11 +245,11 @@ TEST_CASE("Zip readFile") VirtFs::VirtFsZip::getFuncs()); std::vector &headers = entry->mHeaders; - REQUIRE(VirtFs::Zip::readArchiveInfo(entry)); + REQUIRE(VirtFs::ZipReader::readArchiveInfo(entry)); REQUIRE(headers.size() == 11); REQUIRE(entry->root == name); // test.txt - const uint8_t *buf = VirtFs::Zip::readFile(headers[0]); + const uint8_t *buf = VirtFs::ZipReader::readFile(headers[0]); REQUIRE(buf != nullptr); const std::string str = std::string(reinterpret_cast(buf), headers[0]->uncompressSize); @@ -261,7 +261,7 @@ TEST_CASE("Zip readFile") headers[f]->fileName.c_str(), headers[f]->compressSize, headers[f]->uncompressSize); - buf = VirtFs::Zip::readFile(headers[f]); + buf = VirtFs::ZipReader::readFile(headers[f]); REQUIRE(buf != nullptr); delete [] buf; } @@ -276,7 +276,7 @@ TEST_CASE("Zip readFile") VirtFs::VirtFsZip::getFuncs()); std::vector &headers = entry->mHeaders; - REQUIRE(VirtFs::Zip::readArchiveInfo(entry)); + REQUIRE(VirtFs::ZipReader::readArchiveInfo(entry)); REQUIRE(headers.size() == 2); REQUIRE(entry->root == name); for (int f = 0; f < 2; f ++) @@ -285,7 +285,7 @@ TEST_CASE("Zip readFile") headers[f]->fileName.c_str(), headers[f]->compressSize, headers[f]->uncompressSize); - const uint8_t *const buf = VirtFs::Zip::readFile(headers[f]); + const uint8_t *const buf = VirtFs::ZipReader::readFile(headers[f]); REQUIRE(buf != nullptr); delete [] buf; } diff --git a/src/fs/virtfs/zipreader.cpp b/src/fs/virtfs/zipreader.cpp new file mode 100644 index 000000000..0c8bb9247 --- /dev/null +++ b/src/fs/virtfs/zipreader.cpp @@ -0,0 +1,326 @@ +/* + * 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 . + */ + +#include "fs/virtfs/zipreader.h" + +#include "fs/paths.h" + +#include "fs/virtfs/virtzipentry.h" +#include "fs/virtfs/ziplocalheader.h" + +#include "utils/checkutils.h" +#include "utils/stringutils.h" + +#include +#include + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#include +#endif // SDL_BYTEORDER == SDL_BIG_ENDIAN + +#include "debug.h" + +#ifndef SDL_BIG_ENDIAN +#error missing SDL_endian.h +#endif // SDL_BYTEORDER + +// #define DEBUG_ZIP + +extern const char *dirSeparator; + +#define readVal(val, sz, msg) \ + cnt = fread(static_cast(val), 1, sz, arcFile); \ + if (cnt != sz) \ + { \ + reportAlways("Error reading " msg " in file %s", \ + archiveName.c_str()); \ + delete header; \ + delete [] buf; \ + fclose(arcFile); \ + return false; \ + } + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define swapVal16(val) val = bswap_16(val); +#define swapVal32(val) val = bswap_32(val); +#else // SDL_BYTEORDER == SDL_BIG_ENDIAN +#define swapVal16(val) +#define swapVal32(val) +#endif // SDL_BYTEORDER == SDL_BIG_ENDIAN + +namespace VirtFs +{ + +namespace ZipReader +{ + bool readArchiveInfo(VirtZipEntry *const entry) + { + if (entry == nullptr) + { + reportAlways("Entry is null."); + return false; + } + const std::string archiveName = entry->root; + std::vector &restrict headers = entry->mHeaders; + std::vector &restrict dirs = entry->mDirs; + FILE *restrict const arcFile = fopen(archiveName.c_str(), + "rb"); + if (arcFile == nullptr) + { + reportAlways("Can't open zip file %s", + archiveName.c_str()); + return false; + } + uint8_t *const buf = new uint8_t[65535 + 10]; + uint16_t val16 = 0U; + uint16_t method = 0U; + ZipLocalHeader *header = nullptr; + +#ifdef DEBUG_ZIP + logger->log("Read archive: %s", archiveName.c_str()); +#endif // DEBUG_ZIP + + // format source https://en.wikipedia.org/wiki/Zip_%28file_format%29 + while (feof(arcFile) == 0) + { + size_t cnt = 0U; + // file header pointer on 0 + // read file header signature + readVal(buf, 4, "zip file header"); // + 4 + // pointer on 4 + + if (buf[0] == 0x50 && + buf[1] == 0x4B && + buf[2] == 0x03 && + buf[3] == 0x04) + { // local file header + header = new ZipLocalHeader; + header->zipEntry = entry; + // skip useless fields + fseek(arcFile, 4, SEEK_CUR); // + 4 + // file header pointer on 8 + readVal(&method, 2, "compression method") // + 2 + swapVal16(method) + 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 + swapVal32(header->compressSize) + // file header pointer on 22 + readVal(&header->uncompressSize, 4, + "zip uncompressed size") // + 4 + swapVal32(header->uncompressSize) + // file header pointer on 26 + readVal(&val16, 2, "file name length") // + 2 + swapVal16(val16) + // file header pointer on 28 + const uint32_t fileNameLen = CAST_U32(val16); + if (fileNameLen > 1000) + { + reportAlways("Error too long file name in file %s", + archiveName.c_str()); + delete header; + delete [] buf; + fclose(arcFile); + return false; + } + readVal(&val16, 2, "extra field length") // + 2 + swapVal16(val16) + // file header pointer on 30 + const uint32_t extraFieldLen = CAST_U32(val16); + readVal(buf, fileNameLen, "file name"); + // file header pointer on 30 + fileNameLen + buf[fileNameLen] = 0; + header->fileName = std::string( + reinterpret_cast(buf)); + prepareFsPath(header->fileName); + header->dataOffset = CAST_S32(ftell(arcFile) + extraFieldLen); + fseek(arcFile, extraFieldLen + header->compressSize, SEEK_CUR); + // pointer on 30 + fileNameLen + extraFieldLen + compressSize + if (findLast(header->fileName, dirSeparator) == false) + { + headers.push_back(header); +#ifdef DEBUG_ZIP + 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", + header->uncompressSize); +#endif // DEBUG_ZIP + } + else + { +#ifdef DEBUG_ZIP + logger->log(" dir name: %s", + header->fileName.c_str()); +#endif // DEBUG_ZIP + dirs.push_back(header->fileName); + delete header; + } + } + else if (buf[0] == 0x50 && + buf[1] == 0x4B && + buf[2] == 0x01 && + buf[3] == 0x02) + { // central directory file header + // !!! This is quick way for read zip archives. !!! + // !!! It ignore modified files in archive. !!! + // ignoring central directory entries + break; + } + else if (buf[0] == 0x50 && + buf[1] == 0x4B && + buf[2] == 0x05 && + buf[3] == 0x06) + { // end of central directory + // !!! This is quick way for read zip archives. !!! + // !!! It ignore modified files in archive. !!! + // ignoring end of central directory + break; + } + else + { + reportAlways("Error in header signature (0x%02x%02x%02x%02x)" + " in file %s", + buf[0], + buf[1], + buf[2], + buf[3], + archiveName.c_str()); + delete [] buf; + fclose(arcFile); + return false; + } + } + delete [] buf; + fclose(arcFile); + 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("ZipReader::readCompressedFile: header is null"); + return nullptr; + } + FILE *restrict const arcFile = fopen( + header->zipEntry->root.c_str(), + "rb"); + if (arcFile == nullptr) + { + reportAlways("Can't open zip file %s", + header->zipEntry->root.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(buf), 1, compressSize, arcFile) != + compressSize) + { + reportAlways("Read zip compressed file error from archive: %s", + header->zipEntry->root.c_str()); + fclose(arcFile); + delete [] buf; + return nullptr; + } + fclose(arcFile); + return buf; + } + + const 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 = CAST_U32(outSize); + + int ret = inflateInit2(&strm, -MAX_WBITS); + if (ret != Z_OK) + { + reportZlibError(header->zipEntry->root, 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 ZipReader + +} // namespace VirtFs diff --git a/src/fs/virtfs/zipreader.h b/src/fs/virtfs/zipreader.h new file mode 100644 index 000000000..d227315f1 --- /dev/null +++ b/src/fs/virtfs/zipreader.h @@ -0,0 +1,46 @@ +/* + * 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 . + */ + +#ifndef UTILS_ZIP_H +#define UTILS_ZIP_H + +#include "localconsts.h" + +#include + +namespace VirtFs +{ + +struct VirtZipEntry; +struct ZipLocalHeader; + +namespace ZipReader +{ + bool readArchiveInfo(VirtZipEntry *const entry); + std::string getZlibError(const int err); + void reportZlibError(const std::string &text, + const int err); + uint8_t *readCompressedFile(const ZipLocalHeader *restrict const header); + const uint8_t *readFile(const ZipLocalHeader *restrict const header); +} // namespace ZipReader + +} // namespace VirtFs + +#endif // UTILS_ZIP_H -- cgit v1.2.3-60-g2f50