void ZipWriter::begin_file(const std::string &filename, bool compress) { if (impl->file_begun) throw Exception("ZipWriter already writing a file"); impl->file_begun = true; impl->uncompressed_length = 0; impl->compressed_length = 0; impl->compress = compress; impl->crc32 = ZIP_CRC_START_VALUE; impl->local_header_offset = impl->output.get_position(); impl->local_header = ZipLocalFileHeader(); impl->local_header.version_needed_to_extract = 20; if (impl->storeFilenamesAsUTF8) impl->local_header.general_purpose_bit_flag = ZIP_USE_UTF8; else impl->local_header.general_purpose_bit_flag = 0; impl->local_header.compression_method = compress ? zip_compress_deflate : zip_compress_store; ZipArchive_Impl::calc_time_and_date( impl->local_header.last_mod_file_date, impl->local_header.last_mod_file_time); impl->local_header.crc32 = 0; impl->local_header.uncompressed_size = 0; impl->local_header.compressed_size = 0; impl->local_header.file_name_length = filename.length(); impl->local_header.filename = filename; if (!impl->storeFilenamesAsUTF8) // Add UTF-8 as extra field if we aren't storing normal UTF-8 filenames { // -Info-ZIP Unicode Path Extra Field (0x7075) std::string filename_cp437 = StringHelp::text_to_cp437(filename); std::string filename_utf8 = StringHelp::text_to_utf8(filename); DataBuffer unicode_path(9 + filename_utf8.length()); uint16_t *extra_id = (uint16_t *)(unicode_path.get_data()); uint16_t *extra_len = (uint16_t *)(unicode_path.get_data() + 2); uint8_t *extra_version = (uint8_t *)(unicode_path.get_data() + 4); uint32_t *extra_crc32 = (uint32_t *)(unicode_path.get_data() + 5); *extra_id = 0x7075; *extra_len = 5 + filename_utf8.length(); *extra_version = 1; *extra_crc32 = ZipArchive_Impl::calc_crc32(filename_cp437.data(), filename_cp437.size()); memcpy(unicode_path.get_data() + 9, filename_utf8.data(), filename_utf8.length()); impl->local_header.extra_field_length = unicode_path.get_size(); impl->local_header.extra_field = unicode_path; } impl->local_header.save(impl->output); if (compress) { memset(&impl->zs, 0, sizeof(mz_stream)); int result = mz_deflateInit2(&impl->zs, MZ_DEFAULT_COMPRESSION, MZ_DEFLATED, -15, 8, MZ_DEFAULT_STRATEGY); // Undocumented: if wbits is negative, zlib skips header check if (result != MZ_OK) throw Exception("Zlib deflateInit failed for zip index!"); } }
DataBuffer ZLibCompression::compress(const DataBuffer &data, bool raw, int compression_level, CompressionMode mode) { const int window_bits = 15; DataBuffer zbuffer(1024*1024); IODevice_Memory output; int strategy = MZ_DEFAULT_STRATEGY; switch (mode) { case default_strategy: strategy = MZ_DEFAULT_STRATEGY; break; case filtered: strategy = MZ_FILTERED; break; case huffman_only: strategy = MZ_HUFFMAN_ONLY; break; case rle: strategy = MZ_RLE; break; case fixed: strategy = MZ_FIXED; break; } mz_stream zs = { nullptr }; int result = mz_deflateInit2(&zs, compression_level, MZ_DEFLATED, raw ? -window_bits : window_bits, 8, strategy); // Undocumented: if wbits is negative, zlib skips header check if (result != MZ_OK) throw Exception("Zlib deflateInit failed"); try { zs.next_in = (unsigned char *) data.get_data(); zs.avail_in = data.get_size(); while (true) { zs.next_out = (unsigned char *) zbuffer.get_data(); zs.avail_out = zbuffer.get_size(); int result = mz_deflate(&zs, MZ_FINISH); if (result == MZ_NEED_DICT) throw Exception("Zlib deflate wants a dictionary!"); if (result == MZ_DATA_ERROR) throw Exception("Zip data stream is corrupted"); if (result == MZ_STREAM_ERROR) throw Exception("Zip stream structure was inconsistent!"); if (result == MZ_MEM_ERROR) throw Exception("Zlib did not have enough memory to compress file!"); if (result == MZ_BUF_ERROR) throw Exception("Not enough data in buffer when Z_FINISH was used"); if (result != MZ_OK && result != MZ_STREAM_END) throw Exception("Zlib deflate failed while compressing zip file!"); int zsize = zbuffer.get_size() - zs.avail_out; if (zsize == 0) break; output.write(zbuffer.get_data(), zsize); if (result == MZ_STREAM_END) break; } mz_deflateEnd(&zs); } catch (...) { mz_deflateEnd(&zs); throw; } return output.get_data(); }