diff options
-rw-r--r-- | Userland/Libraries/LibArchive/Zip.cpp | 75 | ||||
-rw-r--r-- | Userland/Libraries/LibArchive/Zip.h | 14 |
2 files changed, 89 insertions, 0 deletions
diff --git a/Userland/Libraries/LibArchive/Zip.cpp b/Userland/Libraries/LibArchive/Zip.cpp index 4c54622085..69a70169f5 100644 --- a/Userland/Libraries/LibArchive/Zip.cpp +++ b/Userland/Libraries/LibArchive/Zip.cpp @@ -117,4 +117,79 @@ bool Zip::for_each_member(Function<IterationDecision(const ZipMember&)> callback return true; } +ZipOutputStream::ZipOutputStream(OutputStream& stream) + : m_stream(stream) +{ +} + +void ZipOutputStream::add_member(const ZipMember& member) +{ + VERIFY(!m_finished); + VERIFY(member.name.length() <= UINT16_MAX); + VERIFY(member.compressed_data.size() <= UINT32_MAX); + m_members.append(member); + + LocalFileHeader local_file_header {}; + local_file_header.minimum_version = member.compression_method == ZipCompressionMethod::Deflate ? 20 : 10; // Deflate was added in PKZip 2.0 + local_file_header.general_purpose_flags = 0; + local_file_header.compression_method = static_cast<u16>(member.compression_method); + local_file_header.modification_time = 0; // TODO: support modification time + local_file_header.modification_date = 0; + local_file_header.crc32 = member.crc32; + local_file_header.compressed_size = member.compressed_data.size(); + local_file_header.uncompressed_size = member.uncompressed_size; + local_file_header.name_length = member.name.length(); + local_file_header.extra_data_length = 0; + local_file_header.name = (const u8*)(member.name.characters()); + local_file_header.extra_data = nullptr; + local_file_header.compressed_data = member.compressed_data.data(); + local_file_header.write(m_stream); +} + +void ZipOutputStream::finish() +{ + VERIFY(!m_finished); + m_finished = true; + + auto file_header_offset = 0; + auto central_directory_size = 0; + for (const ZipMember& member : m_members) { + CentralDirectoryRecord central_directory_record {}; + auto zip_version = member.compression_method == ZipCompressionMethod::Deflate ? 20 : 10; // Deflate was added in PKZip 2.0 + central_directory_record.made_by_version = zip_version; + central_directory_record.minimum_version = zip_version; + central_directory_record.general_purpose_flags = 0; + central_directory_record.compression_method = static_cast<u16>(member.compression_method); + central_directory_record.modification_time = 0; // TODO: support modification time + central_directory_record.modification_date = 0; + central_directory_record.crc32 = member.crc32; + central_directory_record.compressed_size = member.compressed_data.size(); + central_directory_record.uncompressed_size = member.uncompressed_size; + central_directory_record.name_length = member.name.length(); + central_directory_record.extra_data_length = 0; + central_directory_record.comment_length = 0; + central_directory_record.start_disk = 0; + central_directory_record.internal_attributes = 0; + central_directory_record.external_attributes = member.is_directory ? zip_directory_external_attribute : 0; + central_directory_record.local_file_header_offset = file_header_offset; // FIXME: we assume the wrapped output stream was never written to before us + file_header_offset += sizeof(local_file_header_signature) + (sizeof(LocalFileHeader) - (sizeof(u8*) * 3)) + member.name.length() + member.compressed_data.size(); + central_directory_record.name = (const u8*)(member.name.characters()); + central_directory_record.extra_data = nullptr; + central_directory_record.comment = nullptr; + central_directory_record.write(m_stream); + central_directory_size += central_directory_record.size(); + } + + EndOfCentralDirectory end_of_central_directory {}; + end_of_central_directory.disk_number = 0; + end_of_central_directory.central_directory_start_disk = 0; + end_of_central_directory.disk_records_count = m_members.size(); + end_of_central_directory.total_records_count = m_members.size(); + end_of_central_directory.central_directory_size = central_directory_size; + end_of_central_directory.central_directory_offset = file_header_offset; + end_of_central_directory.comment_length = 0; + end_of_central_directory.comment = nullptr; + end_of_central_directory.write(m_stream); +} + } diff --git a/Userland/Libraries/LibArchive/Zip.h b/Userland/Libraries/LibArchive/Zip.h index dd8c632ec2..ac835b8bfa 100644 --- a/Userland/Libraries/LibArchive/Zip.h +++ b/Userland/Libraries/LibArchive/Zip.h @@ -31,6 +31,7 @@ #include <AK/Span.h> #include <AK/Stream.h> #include <AK/String.h> +#include <AK/Vector.h> #include <string.h> namespace Archive { @@ -231,4 +232,17 @@ private: ReadonlyBytes m_input_data; }; +class ZipOutputStream { +public: + ZipOutputStream(OutputStream&); + void add_member(const ZipMember&); + void finish(); + +private: + OutputStream& m_stream; + Vector<ZipMember> m_members; + + bool m_finished { false }; +}; + } |