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