From 7f4b074d70b3c3869d8d183a5ddca0e076f91d58 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 22 Feb 2025 05:48:48 +0300 Subject: [PATCH] add ZipFileDevice (WIP) --- src/io/devices/ZipFileDevice.cpp | 175 +++++++++++++++++++++++++++++++ src/io/devices/ZipFileDevice.hpp | 48 +++++++++ 2 files changed, 223 insertions(+) create mode 100644 src/io/devices/ZipFileDevice.cpp create mode 100644 src/io/devices/ZipFileDevice.hpp diff --git a/src/io/devices/ZipFileDevice.cpp b/src/io/devices/ZipFileDevice.cpp new file mode 100644 index 00000000..79e3e006 --- /dev/null +++ b/src/io/devices/ZipFileDevice.cpp @@ -0,0 +1,175 @@ +#include "ZipFileDevice.hpp" + +#include +#include "debug/Logger.hpp" +#include "util/data_io.hpp" + +static debug::Logger logger("zip-file"); + +using namespace io; + +static constexpr uint32_t EOCD_SIGNATURE = 0x06054b50; +static constexpr uint32_t CENTRAL_DIR_SIGNATURE = 0x02014b50; +static constexpr uint32_t LOCAL_FILE_SIGNATURE = 0x04034b50; + +template +static T read_int(std::unique_ptr& file) { + T value = 0; + file->read(reinterpret_cast(&value), sizeof(value)); + return dataio::le2h(value); +} + +template +static void read_int(std::unique_ptr& file, T& value) { + file->read(reinterpret_cast(&value), sizeof(value)); + value = dataio::le2h(value); +} + +ZipFileDevice::Entry ZipFileDevice::readEntry() { + // Read entry info + Entry entry {}; + read_int(file, entry.versionMadeBy); + read_int(file, entry.versionNeeded); + read_int(file, entry.flags); + read_int(file, entry.compressionMethod); + read_int(file, entry.modTime); + read_int(file, entry.modDate); + read_int(file, entry.crc32); + read_int(file, entry.compressedSize); + read_int(file, entry.uncompressedSize); + auto fileNameLength = read_int(file); + auto extraFieldLength = read_int(file); + auto fileCommentLength = read_int(file); + read_int(file, entry.diskNumberStart); + read_int(file, entry.internalAttributes); + read_int(file, entry.externalAttributes); + read_int(file, entry.localHeaderOffset); + + entry.fileName.resize(fileNameLength, '\0'); + file->read(entry.fileName.data(), fileNameLength); + + // Skip extra field and file comment + file->seekg(extraFieldLength + fileCommentLength, std::ios::cur); + + if (entry.diskNumberStart == 0xFF) { + throw std::runtime_error("zip64 is not supported"); + } + return entry; +} + +void ZipFileDevice::findBlob(Entry& entry) { + file->seekg(entry.localHeaderOffset); + if (read_int(file) != LOCAL_FILE_SIGNATURE) { + throw std::runtime_error("invalid local file signature"); + } + read_int(file); // version + read_int(file); // flags + read_int(file); // compression method + read_int(file); // last modification time + read_int(file); // last modification date + read_int(file); // crc32 + read_int(file); // compressed size + read_int(file); // uncompressed size + auto nameLength = read_int(file); + auto extraFieldLength = read_int(file); + + // Skip extra field and file comment + file->seekg(nameLength + extraFieldLength, std::ios::cur); + entry.blobOffset = file->tellg(); + + std::cout << entry.fileName << ": " << entry.blobOffset << " " << entry.compressionMethod << std::endl; +} + +ZipFileDevice::ZipFileDevice(std::unique_ptr filePtr) + : file(std::move(filePtr)) { + + // Searching for EOCD + file->seekg(0, std::ios::end); + std::streampos fileSize = file->tellg(); + + bool foundEOCD = false; + for (int pos = static_cast(fileSize)-4; pos >= 0; --pos) { + file->seekg(pos); + if (read_int(file) == EOCD_SIGNATURE) { + foundEOCD = true; + break; + } + } + if (!foundEOCD) { + throw std::runtime_error("EOCD not found, ZIP file is invalid"); + } + + // Reading EOCD + read_int(file); // diskNumber + read_int(file); // centralDirDisk + read_int(file); // numEntriesThisDisk + auto totalEntries = read_int(file); + read_int(file); // centralDirSize + auto centralDirOffset = read_int(file); + read_int(file); // commentLength + + file->seekg(centralDirOffset); + + for (uint16_t i = 0; i < totalEntries; i++) { + if (read_int(file) != CENTRAL_DIR_SIGNATURE) { + logger.error() << "invalid central directory entry"; + break; + } + // Read entry info + Entry entry = readEntry(); + entries[entry.fileName] = std::move(entry); + } + + for (auto& [_, entry] : entries) { + findBlob(entry); + } +} + + +std::filesystem::path ZipFileDevice::resolve(std::string_view path) { + throw std::runtime_error("unable to resolve filesystem path"); +} + +std::unique_ptr ZipFileDevice::write(std::string_view path) { + return nullptr; +} + +std::unique_ptr ZipFileDevice::read(std::string_view path) { + return nullptr; +} + +size_t ZipFileDevice::size(std::string_view path) { + return 0; +} + +bool ZipFileDevice::exists(std::string_view path) { + return false; +} + +bool ZipFileDevice::isdir(std::string_view path) { + return false; +} + +bool ZipFileDevice::isfile(std::string_view path) { + return false; +} + +bool ZipFileDevice::mkdir(std::string_view path) { + return false; +} + +bool ZipFileDevice::mkdirs(std::string_view path) { + return false; +} + +bool ZipFileDevice::remove(std::string_view path) { + return false; +} + +uint64_t ZipFileDevice::removeAll(std::string_view path) { + return 0; +} + +std::unique_ptr ZipFileDevice::list(std::string_view path) { + return nullptr; +} diff --git a/src/io/devices/ZipFileDevice.hpp b/src/io/devices/ZipFileDevice.hpp new file mode 100644 index 00000000..dba02a98 --- /dev/null +++ b/src/io/devices/ZipFileDevice.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include "Device.hpp" + +namespace io { + class ZipFileDevice : public Device { + struct Entry { + uint16_t versionMadeBy; + uint16_t versionNeeded; + uint16_t flags; + uint16_t compressionMethod; + uint16_t modTime; + uint16_t modDate; + uint32_t crc32; + uint32_t compressedSize; + uint32_t uncompressedSize; + uint16_t diskNumberStart; + uint16_t internalAttributes; + uint32_t externalAttributes; + uint32_t localHeaderOffset; + std::string fileName; + size_t blobOffset = 0; + }; + public: + ZipFileDevice(std::unique_ptr file); + + std::filesystem::path resolve(std::string_view path) override; + std::unique_ptr write(std::string_view path) override; + std::unique_ptr read(std::string_view path) override; + size_t size(std::string_view path) override; + bool exists(std::string_view path) override; + bool isdir(std::string_view path) override; + bool isfile(std::string_view path) override; + bool mkdir(std::string_view path) override; + bool mkdirs(std::string_view path) override; + bool remove(std::string_view path) override; + uint64_t removeAll(std::string_view path) override; + std::unique_ptr list(std::string_view path) override; + private: + std::unique_ptr file; + std::unordered_map entries; + + Entry readEntry(); + void findBlob(Entry& entry); + }; +}