#include "archive.hpp" #include #include namespace bookmouse { ZipArchive::ZipArchive(const fud::String& filename) { // qDebug("Open file %s", filename.data()); int err; m_archive = zip_open(filename.c_str(), 0, &err); if (m_archive == nullptr) { zip_error_t error; zip_error_init_with_code(&error, err); // qCritical("%s: cannot open zip archive '%s': %s", AppName, filenameUtf8.data(), zip_error_strerror(&error)); zip_error_fini(&error); throw std::runtime_error("Bad zip file"); } populate(); } void ZipArchive::populate() { auto numEntriesResult = zip_get_num_entries(m_archive, ZIP_FL_UNCHANGED); if (numEntriesResult < 0) { throw std::runtime_error("Invalid number of entries"); } auto numEntries = static_cast(numEntriesResult); // qDebug("%zu pages", numEntries); struct NameIndex { fud::String name; size_t index; size_t filesize; static bool compare(const NameIndex& lhs, const NameIndex& rhs) { return std::lexicographical_compare( // force break lhs.name.begin(), lhs.name.end(), rhs.name.begin(), rhs.name.end()); } }; std::vector nameIndices{}; nameIndices.reserve(numEntries); m_pages.reserve(numEntries); for (size_t idx = 0; idx < numEntries; ++idx) { struct zip_stat stats; auto status = zip_stat_index(m_archive, idx, 0, &stats); if (status != 0) { continue; } static_assert(ZIP_STAT_NAME != 0); static_assert(ZIP_STAT_SIZE != 0); static_assert(ZIP_STAT_INDEX != 0); // auto* nameCString = zip_get_name(m_archive, idx, ZIP_FL_ENC_RAW); auto* nameCString = stats.name; if (nameCString == nullptr) { // zip_error_t* error = zip_get_error(m_archive); // qWarning("cannot get name %s", zip_error_strerror(error)); continue; } fud::String name{nameCString}; if (name.empty() || name.back() == '/') { // qDebug("Directory %s", name.empty() ? "N/A" : nameCString); continue; } nameIndices.emplace_back(NameIndex{name, idx, stats.size}); m_pages.emplace_back(std::nullopt); } std::sort(nameIndices.begin(), nameIndices.end(), [](const auto& lhs, const auto& rhs) { return NameIndex::compare(lhs, rhs); }); m_sortedIndices.reserve(nameIndices.size()); m_filenames.reserve(nameIndices.size()); m_fileSizes.reserve(nameIndices.size()); for (const auto& nameIndex : nameIndices) { m_sortedIndices.push_back(nameIndex.index); m_filenames.push_back(nameIndex.name); m_fileSizes.push_back(nameIndex.filesize); } } ZipArchive::~ZipArchive() { if (m_archive != nullptr) { zip_discard(m_archive); m_archive = nullptr; } } ZipArchive::ZipArchive(ZipArchive&& rhs) : m_archive{rhs.m_archive}, m_sortedIndices{std::move(rhs.m_sortedIndices)}, m_filenames{std::move(rhs.m_filenames)}, m_fileSizes{std::move(rhs.m_fileSizes)}, m_pages{std::move(rhs.m_pages)} { rhs.m_archive = nullptr; } ZipArchive& ZipArchive::operator=(ZipArchive&& rhs) { m_archive = rhs.m_archive; m_sortedIndices = std::move(rhs.m_sortedIndices); m_filenames = std::move(rhs.m_filenames); m_fileSizes = std::move(rhs.m_fileSizes); m_pages = std::move(rhs.m_pages); rhs.m_archive = nullptr; return *this; } ArchiveResult ZipArchive::getPage(size_t page) { // qDebug("Getting page %zu", page); if (page > m_sortedIndices.size()) { return ArchiveResult::error(ArchiveError::BadIndex); } if (m_pages[page] != std::nullopt) { // qDebug("Page found %zu", page); return ArchiveResult::okay(std::cref(*m_pages[page])); } auto* file = zip_fopen_index(m_archive, m_sortedIndices[page], 0); if (file == nullptr) { // zip_error_t* error = zip_get_error(m_archive); // qWarning("cannot get file name %s: %s", m_filenames[page].data(), zip_error_strerror(error)); return ArchiveResult::error(ArchiveError::ZipError); } std::vector data; data.resize(m_fileSizes[page]); // auto index = m_sortedIndices[page]; // qDebug("Reading in page data"); auto bytesReadResult = zip_fread(file, data.data(), data.size()); if (bytesReadResult < 0) { return ArchiveResult::error(ArchiveError::ZipError); } zip_fclose(file); file = nullptr; auto bytesRead = static_cast(bytesReadResult); if (bytesRead != data.size()) { return ArchiveResult::error(ArchiveError::ZipError); } m_pages[page] = data; // qDebug("Loading QImage from page data"); /* auto loaded = m_pages[page]->loadFromData(data); if (!loaded) { // qWarning("Failed to load QImage"); m_pages[page] = std::nullopt; return ArchiveResult::error(ArchiveError::BadData); } */ return ArchiveResult::okay(std::cref(*m_pages[page])); } size_t ZipArchive::numPages() const { return m_pages.size(); } } // namespace bookmouse