Files
OpenVulkano/openVulkanoCpp/IO/Archive/LibArchiveHelper.hpp
2024-10-14 12:57:42 +03:00

133 lines
3.8 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/.
*/
#pragma once
#include <archive.h>
#include <archive_entry.h>
#include <streambuf>
#include "Base/Logger.hpp"
namespace OpenVulkano
{
#ifdef _MSC_VER
using mode_t = unsigned short;
using ssize_t = int64_t;
#endif
namespace LibArchiveHelper
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wswitch"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch"
constexpr mode_t GetFileType(std::filesystem::file_type type)
{
switch (type)
{
case std::filesystem::file_type::regular: return AE_IFREG;
case std::filesystem::file_type::directory: return AE_IFDIR;
case std::filesystem::file_type::symlink: return AE_IFLNK;
case std::filesystem::file_type::block: return AE_IFBLK;
case std::filesystem::file_type::character: return AE_IFCHR;
case std::filesystem::file_type::fifo: return AE_IFIFO;
case std::filesystem::file_type::socket: return AE_IFSOCK;
}
return AE_IFMT;
}
constexpr std::filesystem::file_type GetFileType(mode_t type)
{
switch(type & AE_IFMT)
{
case AE_IFREG: return std::filesystem::file_type::regular;
case AE_IFDIR: return std::filesystem::file_type::directory;
case AE_IFLNK: return std::filesystem::file_type::symlink;
case AE_IFBLK: return std::filesystem::file_type::block;
case AE_IFCHR: return std::filesystem::file_type::character;
case AE_IFIFO: return std::filesystem::file_type::fifo;
case AE_IFSOCK: return std::filesystem::file_type::socket;
}
return std::filesystem::file_type::unknown;
}
#pragma GCC diagnostic pop
#pragma clang diagnostic pop
template<bool BLOCK_WRITE = false>
inline ssize_t CopyArchiveData(struct archive* archiveReader, struct archive* archiveWriter)
{
const void* buffer;
size_t size;
int64_t offset, r;
while (true)
{
r = archive_read_data_block(archiveReader, &buffer, &size, &offset);
if (r == ARCHIVE_EOF) return ARCHIVE_OK;
if (r < ARCHIVE_OK) return r;
if constexpr (BLOCK_WRITE)
{
r = archive_write_data_block(archiveWriter, buffer, size, offset);
}
else
{
r = archive_write_data(archiveWriter, buffer, size);
}
if (r < ARCHIVE_OK) return r;
}
}
inline bool CheckError(long archiveResult, struct archive* arch, const Logger::Ptr& logger)
{
if (archiveResult >= ARCHIVE_OK) return true;
spdlog::level::level_enum lvl = spdlog::level::level_enum::warn;
if (archiveResult <= ARCHIVE_FATAL) lvl = spdlog::level::level_enum::critical;
else if (archiveResult <= ARCHIVE_FAILED) lvl = spdlog::level::level_enum::err;
const char* errorString = archive_error_string(arch);
if (logger) logger->log(lvl, errorString ? errorString : "Unknown error while handling archive");
if (archiveResult == ARCHIVE_FAILED || archiveResult == ARCHIVE_FATAL) throw std::runtime_error(errorString);
return false;
}
}
class LibArchiveEntryStreamBuffer final : public std::streambuf
{
private:
struct archive* m_archive;
char* m_buffer;
size_t m_currentBlockSize;
int64_t offset;
bool ReadNextBlock()
{
const void* buffer;
bool ok = archive_read_data_block(m_archive, &buffer, &m_currentBlockSize, &offset) == ARCHIVE_OK;
m_buffer = static_cast<char*>(const_cast<void*>(buffer));
return ok;
}
public:
LibArchiveEntryStreamBuffer(struct archive* arch) : m_archive(arch), m_currentBlockSize(0)
{}
int underflow() override
{
if (gptr() == egptr())
{
if (ReadNextBlock())
{
setg(m_buffer, m_buffer, m_buffer + m_currentBlockSize);
}
else
{
return std::char_traits<char>::eof();
}
}
return std::char_traits<char>::to_int_type(*this->gptr());
}
};
}