From b5124bd82621701172ce17db245e57d053892557 Mon Sep 17 00:00:00 2001 From: Caoimhe Date: Fri, 2 Jun 2023 17:31:20 +0100 Subject: [PATCH] LibArchive: Add helper functions for adding members to a ZipOutputStream --- Userland/Libraries/LibArchive/CMakeLists.txt | 2 +- Userland/Libraries/LibArchive/Zip.cpp | 58 ++++++++++++++++++++ Userland/Libraries/LibArchive/Zip.h | 12 ++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibArchive/CMakeLists.txt b/Userland/Libraries/LibArchive/CMakeLists.txt index 4bc0335c89..710e4b5733 100644 --- a/Userland/Libraries/LibArchive/CMakeLists.txt +++ b/Userland/Libraries/LibArchive/CMakeLists.txt @@ -5,4 +5,4 @@ set(SOURCES ) serenity_lib(LibArchive archive) -target_link_libraries(LibArchive PRIVATE LibCore) +target_link_libraries(LibArchive PRIVATE LibCompress LibCore LibCrypto) diff --git a/Userland/Libraries/LibArchive/Zip.cpp b/Userland/Libraries/LibArchive/Zip.cpp index 8b71b5ee5f..a9904f4178 100644 --- a/Userland/Libraries/LibArchive/Zip.cpp +++ b/Userland/Libraries/LibArchive/Zip.cpp @@ -6,6 +6,8 @@ */ #include +#include +#include namespace Archive { @@ -138,6 +140,62 @@ ErrorOr ZipOutputStream::add_member(ZipMember const& member) return local_file_header.write(*m_stream); } +ErrorOr ZipOutputStream::add_member_from_stream(StringView path, Stream& stream, Optional const& modification_time) +{ + auto buffer = TRY(stream.read_until_eof()); + + Archive::ZipMember member {}; + member.name = TRY(String::from_utf8(path)); + + if (modification_time.has_value()) { + member.modification_date = to_packed_dos_date(modification_time->year(), modification_time->month(), modification_time->day()); + member.modification_time = to_packed_dos_time(modification_time->hour(), modification_time->minute(), modification_time->second()); + } + + auto deflate_buffer = Compress::DeflateCompressor::compress_all(buffer); + auto compression_ratio = 1.f; + auto compressed_size = buffer.size(); + + if (!deflate_buffer.is_error() && deflate_buffer.value().size() < buffer.size()) { + member.compressed_data = deflate_buffer.value().bytes(); + member.compression_method = Archive::ZipCompressionMethod::Deflate; + + compression_ratio = static_cast(deflate_buffer.value().size()) / static_cast(buffer.size()); + compressed_size = member.compressed_data.size(); + } else { + member.compressed_data = buffer.bytes(); + member.compression_method = Archive::ZipCompressionMethod::Store; + } + + member.uncompressed_size = buffer.size(); + + Crypto::Checksum::CRC32 checksum { buffer.bytes() }; + member.crc32 = checksum.digest(); + member.is_directory = false; + + TRY(add_member(member)); + + return MemberInformation { compression_ratio, compressed_size }; +} + +ErrorOr ZipOutputStream::add_directory(StringView name, Optional const& modification_time) +{ + Archive::ZipMember member {}; + member.name = TRY(String::from_utf8(name)); + member.compressed_data = {}; + member.compression_method = Archive::ZipCompressionMethod::Store; + member.uncompressed_size = 0; + member.crc32 = 0; + member.is_directory = true; + + if (modification_time.has_value()) { + member.modification_date = to_packed_dos_date(modification_time->year(), modification_time->month(), modification_time->day()); + member.modification_time = to_packed_dos_time(modification_time->hour(), modification_time->minute(), modification_time->second()); + } + + return add_member(member); +} + ErrorOr ZipOutputStream::finish() { VERIFY(!m_finished); diff --git a/Userland/Libraries/LibArchive/Zip.h b/Userland/Libraries/LibArchive/Zip.h index f87c6dca8f..9a1e940369 100644 --- a/Userland/Libraries/LibArchive/Zip.h +++ b/Userland/Libraries/LibArchive/Zip.h @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace Archive { @@ -271,9 +272,20 @@ private: class ZipOutputStream { public: + struct MemberInformation { + float compression_ratio; + size_t compressed_size; + }; + ZipOutputStream(NonnullOwnPtr); ErrorOr add_member(ZipMember const&); + ErrorOr add_member_from_stream(StringView, Stream&, Optional const& = {}); + + // NOTE: This does not add any of the files within the directory, + // it just adds an entry for it. + ErrorOr add_directory(StringView, Optional const& = {}); + ErrorOr finish(); private: