From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/opcservices/compress.c | 341 ++++++++++++++++++++++++--------- dlls/opcservices/opc_private.h | 4 +- dlls/opcservices/package.c | 5 +- 3 files changed, 257 insertions(+), 93 deletions(-)
diff --git a/dlls/opcservices/compress.c b/dlls/opcservices/compress.c index b35a51d664f..33858a01c48 100644 --- a/dlls/opcservices/compress.c +++ b/dlls/opcservices/compress.c @@ -55,32 +55,24 @@ struct zip32_data_descriptor uint32_t uncompressed_size; };
-struct zip64_data_descriptor +struct zip32_central_directory_header { uint32_t signature; + uint16_t version; + uint16_t min_version; + uint16_t flags; + uint16_t method; + uint32_t mtime; uint32_t crc32; - uint64_t compressed_size; - uint64_t uncompressed_size; -}; - -struct central_directory_header -{ - DWORD signature; - WORD version; - WORD min_version; - WORD flags; - WORD method; - DWORD mtime; - DWORD crc32; - DWORD compressed_size; - DWORD uncompressed_size; - WORD name_length; - WORD extra_length; - WORD comment_length; - WORD diskid; - WORD internal_attributes; - DWORD external_attributes; - DWORD local_file_offset; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t name_length; + uint16_t extra_length; + uint16_t comment_length; + uint16_t diskid; + uint16_t internal_attributes; + uint32_t external_attributes; + uint32_t local_file_offset; };
struct zip32_end_of_central_directory @@ -94,22 +86,81 @@ struct zip32_end_of_central_directory uint32_t directory_offset; uint16_t comment_length; }; + +struct zip64_data_descriptor +{ + uint32_t signature; + uint32_t crc32; + uint64_t compressed_size; + uint64_t uncompressed_size; +}; + +struct zip64_extra_field +{ + uint16_t id; + uint16_t size; + uint64_t uncompressed_size; + uint64_t compressed_size; + uint64_t offset; + uint32_t diskid; +}; + +struct zip64_end_of_central_directory +{ + uint32_t signature; + uint64_t size; + uint16_t version; + uint16_t min_version; + uint32_t diskid; + uint32_t directory_diskid; + uint64_t records_num; + uint64_t records_total; + uint64_t directory_size; + uint64_t directory_offset; +}; + +struct zip64_end_of_central_directory_locator +{ + uint32_t signature; + uint32_t eocd64_disk; + uint64_t eocd64_offset; + uint32_t disk_num; +}; #pragma pack(pop)
-#define CENTRAL_DIR_SIGNATURE 0x02014b50 -#define LOCAL_HEADER_SIGNATURE 0x04034b50 -#define DIRECTORY_END_SIGNATURE 0x06054b50 -#define DATA_DESCRIPTOR_SIGNATURE 0x08074b50 -#define ZIP32_VERSION 20 +enum zip_signatures +{ + ZIP32_CDFH = 0x02014b50, + LOCAL_HEADER_SIGNATURE = 0x04034b50, + ZIP32_EOCD = 0x06054b50, + ZIP64_EOCD64 = 0x06064b50, + ZIP64_EOCD64_LOCATOR = 0x07064b50, + DATA_DESCRIPTOR_SIGNATURE = 0x08074b50, +}; + +enum zip_versions +{ + ZIP32_VERSION = 20, + ZIP64_VERSION = 45, +};
enum entry_flags { USE_DATA_DESCRIPTOR = 0x8, };
+struct zip_file +{ + uint64_t compressed_size; + uint64_t uncompressed_size; + uint64_t offset; + uint32_t crc32; + uint16_t name_length; +}; + struct zip_archive { - struct central_directory_header **files; + struct zip_file **files; size_t file_count; size_t file_size;
@@ -118,11 +169,13 @@ struct zip_archive uint64_t position; HRESULT write_result;
+ bool zip64; + unsigned char input_buffer[0x8000]; unsigned char output_buffer[0x8000]; };
-HRESULT compress_create_archive(IStream *output, struct zip_archive **out) +HRESULT compress_create_archive(IStream *output, bool zip64, struct zip_archive **out) { struct zip_archive *archive; WORD date, time; @@ -144,6 +197,8 @@ HRESULT compress_create_archive(IStream *output, struct zip_archive **out) FileTimeToDosDateTime(&ft, &date, &time); archive->mtime = date << 16 | time;
+ archive->zip64 = zip64; + *out = archive;
return S_OK; @@ -166,24 +221,125 @@ static void compress_write(struct zip_archive *archive, void *data, ULONG size)
void compress_finalize_archive(struct zip_archive *archive) { - struct zip32_end_of_central_directory dir_end = { 0 }; + struct zip32_end_of_central_directory dir_end; + struct zip32_central_directory_header cdh; + uint64_t cd_offset, cd_size = 0; size_t i;
- dir_end.directory_offset = archive->position; - dir_end.records_num = archive->file_count; - dir_end.records_total = archive->file_count; - /* Directory entries */ - for (i = 0; i < archive->file_count; ++i) + cd_offset = archive->position; + + if (archive->zip64) { - compress_write(archive, archive->files[i], sizeof(*archive->files[i])); - compress_write(archive, archive->files[i] + 1, archive->files[i]->name_length); - dir_end.directory_size += archive->files[i]->name_length + sizeof(*archive->files[i]); + struct zip64_end_of_central_directory_locator locator; + struct zip64_end_of_central_directory eocd64; + uint64_t eocd64_offset = archive->position; + struct zip64_extra_field extra_field; + + for (i = 0; i < archive->file_count; ++i) + { + const struct zip_file *file = archive->files[i]; + + cdh.signature = ZIP32_CDFH; + cdh.version = ZIP64_VERSION; + cdh.min_version = ZIP64_VERSION; + cdh.flags = USE_DATA_DESCRIPTOR; + cdh.method = 8; /* Z_DEFLATED */ + cdh.mtime = archive->mtime; + cdh.crc32 = file->crc32; + cdh.compressed_size = ~0u; + cdh.uncompressed_size = ~0u; + cdh.name_length = file->name_length; + cdh.extra_length = sizeof(extra_field); + cdh.comment_length = 0; + cdh.diskid = 0; + cdh.internal_attributes = 0; + cdh.external_attributes = 0; + cdh.local_file_offset = ~0u; + compress_write(archive, &cdh, sizeof(cdh)); + + /* File name */ + compress_write(archive, archive->files[i] + 1, file->name_length); + + /* Extra field */ + extra_field.id = 1; + extra_field.size = sizeof(extra_field); + extra_field.uncompressed_size = file->uncompressed_size; + extra_field.compressed_size = file->compressed_size; + extra_field.offset = file->offset; + extra_field.diskid = 0; + compress_write(archive, &extra_field, sizeof(extra_field)); + + cd_size += cdh.name_length + cdh.extra_length + sizeof(cdh); + } + + /* ZIP64 end of central directory */ + eocd64.signature = ZIP64_EOCD64; + eocd64.size = sizeof(eocd64) - 12; + eocd64.version = ZIP64_VERSION; + eocd64.min_version = ZIP64_VERSION; + eocd64.diskid = 0; + eocd64.directory_diskid = 0; + eocd64.records_num = archive->file_count; + eocd64.records_total = archive->file_count; + eocd64.directory_size = cd_size; + eocd64.directory_offset = cd_offset; + compress_write(archive, &eocd64, sizeof(eocd64)); + + /* ZIP64 end of central directory locator */ + locator.signature = ZIP64_EOCD64_LOCATOR; + locator.eocd64_disk = 0; + locator.eocd64_offset = eocd64_offset; + locator.disk_num = 0; + compress_write(archive, &locator, sizeof(locator)); + + /* End of central directory */ + memset(&dir_end, 0xff, sizeof(dir_end)); + dir_end.signature = ZIP32_EOCD; + dir_end.comment_length = 0; + compress_write(archive, &dir_end, sizeof(dir_end)); } + else + { + for (i = 0; i < archive->file_count; ++i) + { + const struct zip_file *file = archive->files[i]; + + cdh.signature = ZIP32_CDFH; + cdh.version = ZIP32_VERSION; + cdh.min_version = ZIP32_VERSION; + cdh.flags = USE_DATA_DESCRIPTOR; + cdh.method = 8; /* Z_DEFLATED */ + cdh.mtime = archive->mtime; + cdh.crc32 = file->crc32; + cdh.compressed_size = file->compressed_size; + cdh.uncompressed_size = file->uncompressed_size; + cdh.name_length = file->name_length; + cdh.extra_length = 0; + cdh.comment_length = 0; + cdh.diskid = 0; + cdh.internal_attributes = 0; + cdh.external_attributes = 0; + cdh.local_file_offset = file->offset; + compress_write(archive, &cdh, sizeof(cdh)); + + /* File name */ + compress_write(archive, archive->files[i] + 1, file->name_length); + + cd_size += cdh.name_length + sizeof(cdh); + }
- /* End record */ - dir_end.signature = DIRECTORY_END_SIGNATURE; - compress_write(archive, &dir_end, sizeof(dir_end)); + /* End of central directory */ + dir_end.signature = ZIP32_EOCD; + dir_end.diskid = 0; + dir_end.firstdisk = 0; + dir_end.records_num = archive->file_count; + dir_end.records_total = archive->file_count; + dir_end.directory_size = cd_size; + dir_end.directory_offset = cd_offset; + dir_end.comment_length = 0; + compress_write(archive, &dir_end, sizeof(dir_end)); + }
IStream_Release(archive->output);
@@ -204,7 +360,7 @@ static void zfree(void *opaque, void *ptr) }
static void compress_write_content(struct zip_archive *archive, IStream *content, - OPC_COMPRESSION_OPTIONS options, struct zip64_data_descriptor *data_desc) + OPC_COMPRESSION_OPTIONS options, struct zip_file *file) { int level, flush; z_stream z_str; @@ -213,9 +369,8 @@ static void compress_write_content(struct zip_archive *archive, IStream *content HRESULT hr; int init_ret;
- data_desc->signature = DATA_DESCRIPTOR_SIGNATURE; - data_desc->crc32 = RtlComputeCrc32(0, NULL, 0); - data_desc->compressed_size = data_desc->uncompressed_size = 0; + file->crc32 = RtlComputeCrc32(0, NULL, 0); + file->compressed_size = file->uncompressed_size = 0;
move.QuadPart = 0; IStream_Seek(content, move, STREAM_SEEK_SET, NULL); @@ -260,8 +415,8 @@ static void compress_write_content(struct zip_archive *archive, IStream *content
z_str.avail_in = num_read; z_str.next_in = archive->input_buffer; - data_desc->crc32 = RtlComputeCrc32(data_desc->crc32, archive->input_buffer, num_read); - data_desc->uncompressed_size += num_read; + file->crc32 = RtlComputeCrc32(file->crc32, archive->input_buffer, num_read); + file->uncompressed_size += num_read;
flush = sizeof(archive->input_buffer) > num_read ? Z_FINISH : Z_NO_FLUSH;
@@ -277,7 +432,7 @@ static void compress_write_content(struct zip_archive *archive, IStream *content have = sizeof(archive->output_buffer) - z_str.avail_out; compress_write(archive, archive->output_buffer, have);
- data_desc->compressed_size += have; + file->compressed_size += have; } while (z_str.avail_out == 0); } while (flush != Z_FINISH);
@@ -287,11 +442,8 @@ static void compress_write_content(struct zip_archive *archive, IStream *content HRESULT compress_add_file(struct zip_archive *archive, const WCHAR *path, IStream *content, OPC_COMPRESSION_OPTIONS options) { - struct zip32_data_descriptor zip32_data_desc; - struct zip64_data_descriptor zip64_data_desc; - struct central_directory_header *entry; struct local_file_header local_header; - DWORD local_header_pos; + struct zip_file *file; char *name; DWORD len;
@@ -300,65 +452,78 @@ HRESULT compress_add_file(struct zip_archive *archive, const WCHAR *path, return E_OUTOFMEMORY; WideCharToMultiByte(CP_ACP, 0, path, -1, name, len, NULL, NULL);
- /* Local header */ + if (!(file = calloc(1, sizeof(*file) + len))) + { + free(name); + return E_OUTOFMEMORY; + } + file->offset = archive->position; + file->name_length = len - 1; + memcpy(file + 1, name, file->name_length); + free(name); + local_header.signature = LOCAL_HEADER_SIGNATURE; - local_header.version = ZIP32_VERSION; local_header.flags = USE_DATA_DESCRIPTOR; local_header.method = 8; /* Z_DEFLATED */ local_header.mtime = archive->mtime; local_header.crc32 = 0; - local_header.compressed_size = 0; - local_header.uncompressed_size = 0; local_header.name_length = len - 1; local_header.extra_length = 0; - - local_header_pos = archive->position; + if (archive->zip64) + { + local_header.version = ZIP64_VERSION; + local_header.compressed_size = ~0u; + local_header.uncompressed_size = ~0u; + } + else + { + local_header.version = ZIP32_VERSION; + local_header.compressed_size = 0; + local_header.uncompressed_size = 0; + }
compress_write(archive, &local_header, sizeof(local_header)); - compress_write(archive, name, local_header.name_length); + compress_write(archive, file + 1, file->name_length);
/* Content */ - compress_write_content(archive, content, options, &zip64_data_desc); + compress_write_content(archive, content, options, file);
/* Data descriptor */ - zip32_data_desc.signature = DATA_DESCRIPTOR_SIGNATURE; - zip32_data_desc.crc32 = zip64_data_desc.crc32; - zip32_data_desc.compressed_size = zip64_data_desc.compressed_size; - zip32_data_desc.uncompressed_size = zip64_data_desc.uncompressed_size; - compress_write(archive, &zip32_data_desc, sizeof(zip32_data_desc)); - - if (FAILED(archive->write_result)) - return archive->write_result; + if (archive->zip64) + { + struct zip64_data_descriptor zip64_data_desc;
- /* Set directory entry */ - if (!(entry = calloc(1, sizeof(*entry) + local_header.name_length))) + zip64_data_desc.signature = DATA_DESCRIPTOR_SIGNATURE; + zip64_data_desc.crc32 = file->crc32; + zip64_data_desc.compressed_size = file->compressed_size; + zip64_data_desc.uncompressed_size = file->uncompressed_size; + compress_write(archive, &zip64_data_desc, sizeof(zip64_data_desc)); + } + else { - free(name); - return E_OUTOFMEMORY; + struct zip32_data_descriptor zip32_data_desc; + + zip32_data_desc.signature = DATA_DESCRIPTOR_SIGNATURE; + zip32_data_desc.crc32 = file->crc32; + zip32_data_desc.compressed_size = file->compressed_size; + zip32_data_desc.uncompressed_size = file->uncompressed_size; + compress_write(archive, &zip32_data_desc, sizeof(zip32_data_desc)); }
- entry->signature = CENTRAL_DIR_SIGNATURE; - entry->version = local_header.version; - entry->min_version = local_header.version; - entry->flags = local_header.flags; - entry->method = local_header.method; - entry->mtime = local_header.mtime; - entry->crc32 = zip32_data_desc.crc32; - entry->compressed_size = zip32_data_desc.compressed_size; - entry->uncompressed_size = zip32_data_desc.uncompressed_size; - entry->name_length = local_header.name_length; - entry->local_file_offset = local_header_pos; - memcpy(entry + 1, name, entry->name_length); - free(name); + if (FAILED(archive->write_result)) + { + free(file); + return archive->write_result; + }
if (!opc_array_reserve((void **)&archive->files, &archive->file_size, archive->file_count + 1, sizeof(*archive->files))) { - free(entry); + free(file); return E_OUTOFMEMORY; }
- archive->files[archive->file_count++] = entry; + archive->files[archive->file_count++] = file;
return S_OK; } diff --git a/dlls/opcservices/opc_private.h b/dlls/opcservices/opc_private.h index cfd8ee864b2..afb308b0726 100644 --- a/dlls/opcservices/opc_private.h +++ b/dlls/opcservices/opc_private.h @@ -16,6 +16,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <stdbool.h> + #include "msopc.h"
static inline BOOL opc_array_reserve(void **elements, size_t *capacity, size_t count, size_t size) @@ -62,7 +64,7 @@ extern HRESULT opc_root_uri_create(IOpcUri **opc_uri); extern HRESULT opc_package_write(IOpcPackage *package, OPC_WRITE_FLAGS flags, IStream *stream);
struct zip_archive; -extern HRESULT compress_create_archive(IStream *output, struct zip_archive **archive); +extern HRESULT compress_create_archive(IStream *output, bool zip64, struct zip_archive **archive); extern HRESULT compress_add_file(struct zip_archive *archive, const WCHAR *path, IStream *content, OPC_COMPRESSION_OPTIONS options); extern void compress_finalize_archive(struct zip_archive *archive); diff --git a/dlls/opcservices/package.c b/dlls/opcservices/package.c index 17175006433..deb4af80b1f 100644 --- a/dlls/opcservices/package.c +++ b/dlls/opcservices/package.c @@ -2019,13 +2019,10 @@ HRESULT opc_package_write(IOpcPackage *package, OPC_WRITE_FLAGS flags, IStream * IXmlWriter *writer; HRESULT hr;
- if (flags != OPC_WRITE_FORCE_ZIP32) - FIXME("Unsupported write flags %#x.\n", flags); - if (FAILED(hr = CreateXmlWriter(&IID_IXmlWriter, (void **)&writer, NULL))) return hr;
- if (FAILED(hr = compress_create_archive(stream, &archive))) + if (FAILED(hr = compress_create_archive(stream, flags != OPC_WRITE_FORCE_ZIP32, &archive))) { IXmlWriter_Release(writer); return hr;