diff options
Diffstat (limited to 'src/archive.cpp')
-rw-r--r-- | src/archive.cpp | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/archive.cpp b/src/archive.cpp new file mode 100644 index 0000000..0339d85 --- /dev/null +++ b/src/archive.cpp @@ -0,0 +1,136 @@ +#include "archive.hpp" + +#include "main_window.hpp" + +#include <algorithm> + +namespace getsuyomi { + +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); + throw std::runtime_error("Bad zip file"); + } + + populate(); +} + +void ZipArchive::populate() +{ + auto numEntries = zip_get_num_entries(m_archive, ZIP_FL_UNCHANGED); + qDebug("%zu pages", numEntries); + + struct NameIndex { + std::string name; + size_t index; + size_t filesize; + }; + + std::vector<NameIndex> 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); + 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; + } + std::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 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); + } +} + +ZipArchive::~ZipArchive() +{ + if (m_archive != nullptr) { + zip_discard(m_archive); + m_archive = nullptr; + } +} + +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); + } + + QByteArray data; + data.resize(m_fileSizes[page]); + auto index = m_sortedIndices[page]; + + qDebug("Reading in page data"); + auto bytesRead = zip_fread(file, data.data(), data.size()); + zip_fclose(file); + file = nullptr; + + if (bytesRead != data.size()) { + return ArchiveResult::error(ArchiveError::ZipError); + } + + m_pages[page] = QImage(); + 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 getsuyomi |