#include "archive.hpp" #include "main_window.hpp" #include namespace getsuyomi { using fud::FudStatus; ZipArchive::ZipArchive(const QString& filename) { auto filenameUtf8 = filename.toUtf8(); qDebug("Open file %s", filenameUtf8.data()); int err; m_archive = zip_open(filenameUtf8.data(), 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); return; } auto status = populate(); if (status != FudStatus::Success) { cleanup(); return; } } 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() { cleanup(); } 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; } bool ZipArchive::valid() const { return m_archive != nullptr; } void ZipArchive::cleanup() { if (m_archive != nullptr) { zip_discard(m_archive); m_archive = nullptr; } } fud::FudStatus ZipArchive::populate() { if (!valid()) { qCritical("Invalid zipArchive"); return FudStatus::ObjectInvalid; } auto numEntriesResult = zip_get_num_entries(m_archive, ZIP_FL_UNCHANGED); if (numEntriesResult < 0) { return FudStatus::Failure; } size_t numEntries = static_cast(numEntriesResult); struct NameIndex { std::string name; size_t index; size_t filesize; }; 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) { return FudStatus::Failure; } static_assert(ZIP_STAT_NAME != 0); static_assert(ZIP_STAT_SIZE != 0); static_assert(ZIP_STAT_INDEX != 0); 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; } std::string name{nameCString}; if (name.empty() || name.back() == '/') { continue; } nameIndices.emplace_back(NameIndex{name, idx, stats.size}); m_pages.emplace_back(std::nullopt); } if (m_pages.empty()) { return FudStatus::Empty; } std::sort(nameIndices.begin(), nameIndices.end(), [](const auto& lhs, const auto& rhs) { return std::lexicographical_compare(lhs.name.begin(), lhs.name.end(), rhs.name.begin(), rhs.name.end()); }); 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); } return FudStatus::Success; } ArchiveResult ZipArchive::getPage(size_t page) { if (page > m_sortedIndices.size()) { return ArchiveResult::error(ArchiveError::BadIndex); } if (m_pages[page] != std::nullopt) { 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); } QByteArray data; data.resize(static_cast(m_fileSizes[page])); auto bytesRead = zip_fread(file, data.data(), static_cast(data.size())); zip_fclose(file); file = nullptr; if (bytesRead != data.size()) { return ArchiveResult::error(ArchiveError::ZipError); } m_pages[page] = QImage(); 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 getsuyomi