[PATCH 2/5] opcservices: Implement writing stub compressed package.

Nikolay Sivov nsivov at codeweavers.com
Wed Sep 5 22:37:12 CDT 2018


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/opcservices/Makefile.in   |   4 +-
 dlls/opcservices/compress.c    | 364 +++++++++++++++++++++++++++++++++
 dlls/opcservices/factory.c     |   5 +-
 dlls/opcservices/opc_private.h |   8 +
 dlls/opcservices/package.c     |  55 +++++
 5 files changed, 433 insertions(+), 3 deletions(-)
 create mode 100644 dlls/opcservices/compress.c

diff --git a/dlls/opcservices/Makefile.in b/dlls/opcservices/Makefile.in
index f165b5ceb8..bdc4c80ba8 100644
--- a/dlls/opcservices/Makefile.in
+++ b/dlls/opcservices/Makefile.in
@@ -1,7 +1,9 @@
 MODULE  = opcservices.dll
-IMPORTS = uuid ole32 advapi32 urlmon
+IMPORTS = uuid ole32 advapi32 urlmon xmllite
+EXTRALIBS = $(Z_LIBS)
 
 C_SRCS = \
+	compress.c \
 	factory.c \
 	package.c \
 	uri.c
diff --git a/dlls/opcservices/compress.c b/dlls/opcservices/compress.c
new file mode 100644
index 0000000000..cd1cb2d912
--- /dev/null
+++ b/dlls/opcservices/compress.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2018 Nikolay Sivov for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include "config.h"
+
+#ifdef HAVE_ZLIB
+# include <zlib.h>
+#endif
+
+#include "windef.h"
+#include "winternl.h"
+#include "msopc.h"
+
+#include "opc_private.h"
+
+#include "wine/debug.h"
+#include "wine/heap.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msopc);
+
+#include <pshpack2.h>
+struct local_file_header
+{
+    DWORD signature;
+    WORD version;
+    WORD flags;
+    WORD method;
+    DWORD mtime;
+    DWORD crc32;
+    DWORD compressed_size;
+    DWORD uncompressed_size;
+    WORD name_length;
+    WORD extra_length;
+};
+
+struct data_descriptor
+{
+    DWORD signature;
+    DWORD crc32;
+    DWORD compressed_size;
+    DWORD 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;
+};
+
+struct central_directory_end
+{
+    DWORD signature;
+    WORD diskid;
+    WORD firstdisk;
+    WORD records_num;
+    WORD records_total;
+    DWORD directory_size;
+    DWORD directory_offset;
+    WORD comment_length;
+};
+#include <poppack.h>
+
+#define CENTRAL_DIR_SIGNATURE 0x02014b50
+#define LOCAL_HEADER_SIGNATURE 0x04034b50
+#define DIRECTORY_END_SIGNATURE 0x06054b50
+#define DATA_DESCRIPTOR_SIGNATURE 0x08074b50
+#define VERSION 20
+
+enum entry_flags
+{
+    USE_DATA_DESCRIPTOR = 0x8,
+};
+
+struct zip_archive
+{
+    struct central_directory_header **files;
+    size_t file_count;
+    size_t file_size;
+
+    DWORD mtime;
+    IStream *output;
+    DWORD position;
+    HRESULT write_result;
+
+    unsigned char input_buffer[0x8000];
+    unsigned char output_buffer[0x8000];
+};
+
+HRESULT compress_create_archive(IStream *output, struct zip_archive **out)
+{
+    struct zip_archive *archive;
+    WORD date, time;
+    FILETIME ft;
+
+    if (!(archive = heap_alloc(sizeof(*archive))))
+        return E_OUTOFMEMORY;
+
+    archive->files = NULL;
+    archive->file_size = 0;
+    archive->file_count = 0;
+    archive->write_result = S_OK;
+
+    archive->output = output;
+    IStream_AddRef(archive->output);
+
+    GetSystemTimeAsFileTime(&ft);
+    FileTimeToDosDateTime(&ft, &date, &time);
+    archive->mtime = date << 16 | time;
+
+    *out = archive;
+
+    return S_OK;
+}
+
+static void compress_write(struct zip_archive *archive, void *data, ULONG size)
+{
+    ULONG written;
+
+    archive->write_result = IStream_Write(archive->output, data, size, &written);
+    if (written != size)
+        archive->write_result = E_FAIL;
+    else
+        archive->position += written;
+
+    if (FAILED(archive->write_result))
+        WARN("Failed to write output %p, size %u, written %u, hr %#x.\n", data, size, written, archive->write_result);
+}
+
+void compress_finalize_archive(struct zip_archive *archive)
+{
+    struct central_directory_end dir_end = { 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)
+    {
+        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]);
+    }
+
+    /* End record */
+    dir_end.signature = DIRECTORY_END_SIGNATURE;
+    compress_write(archive, &dir_end, sizeof(dir_end));
+
+    IStream_Release(archive->output);
+
+    for (i = 0; i < archive->file_count; i++)
+        heap_free(archive->files[i]);
+    heap_free(archive->files);
+    heap_free(archive);
+}
+
+static void compress_write_content(struct zip_archive *archive, IStream *content,
+        OPC_COMPRESSION_OPTIONS options, struct data_descriptor *data_desc)
+{
+#ifdef HAVE_ZLIB
+    int level, flush;
+    z_stream z_str;
+#endif
+    LARGE_INTEGER move = { 0 };
+    ULONG num_read;
+    HRESULT hr;
+
+    data_desc->crc32 = RtlComputeCrc32(0, NULL, 0);
+    IStream_Seek(content, move, STREAM_SEEK_SET, NULL);
+
+#ifdef HAVE_ZLIB
+
+    switch (options)
+    {
+    case OPC_COMPRESSION_NONE:
+        level = Z_NO_COMPRESSION;
+        break;
+    case OPC_COMPRESSION_NORMAL:
+        level = Z_DEFAULT_COMPRESSION;
+        break;
+    case OPC_COMPRESSION_MAXIMUM:
+        level = Z_BEST_COMPRESSION;
+        break;
+    case OPC_COMPRESSION_FAST:
+        level = 2;
+        break;
+    case OPC_COMPRESSION_SUPERFAST:
+        level = Z_BEST_SPEED;
+        break;
+    default:
+        WARN("Unsupported compression options %d.\n", options);
+        level = Z_DEFAULT_COMPRESSION;
+    }
+
+    memset(&z_str, 0, sizeof(z_str));
+    deflateInit2(&z_str, level, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+
+    do
+    {
+        int ret;
+
+        if (FAILED(hr = IStream_Read(content, archive->input_buffer, sizeof(archive->input_buffer), &num_read)))
+        {
+            archive->write_result = hr;
+            break;
+        }
+
+        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);
+
+        flush = sizeof(archive->input_buffer) > num_read ? Z_FINISH : Z_NO_FLUSH;
+
+        do
+        {
+            ULONG have;
+
+            z_str.avail_out = sizeof(archive->output_buffer);
+            z_str.next_out = archive->output_buffer;
+
+            if ((ret = deflate(&z_str, flush)))
+                WARN("Failed to deflate, ret %d.\n", ret);
+            have = sizeof(archive->output_buffer) - z_str.avail_out;
+            compress_write(archive, archive->output_buffer, have);
+        } while (z_str.avail_out == 0);
+    } while (flush != Z_FINISH);
+
+    deflateEnd(&z_str);
+
+    data_desc->compressed_size = z_str.total_out;
+    data_desc->uncompressed_size = z_str.total_in;
+
+#else
+
+    if (options != OPC_COMPRESSION_NONE)
+        FIXME("Writing without compression.\n")
+
+    do
+    {
+        if (FAILED(hr = IStream_Read(content, archive->input_buffer, sizeof(archive->input_buffer), &num_read)))
+        {
+            archive->write_result = hr;
+            break;
+        }
+
+        if (num_read == 0)
+            break;
+
+        data_desc->uncompressed_size += num_read;
+        data_desc->crc32 = RtlComputeCrc32(data_desc->crc32, archive->input_buffer, num_read);
+        compress_write(archive, archive->input_buffer, num_read);
+    } while (num_read != 0 && archive->write_result == S_OK);
+
+    data_desc->compressed_size = data_desc->uncompressed_size;
+
+#endif /* HAVE_ZLIB */
+}
+
+HRESULT compress_add_file(struct zip_archive *archive, const WCHAR *path,
+        IStream *content, OPC_COMPRESSION_OPTIONS options)
+{
+    struct central_directory_header *entry;
+    struct local_file_header local_header;
+    struct data_descriptor data_desc;
+    DWORD local_header_pos;
+    char *name;
+    DWORD len;
+
+    len = WideCharToMultiByte(CP_ACP, 0, path, -1, NULL, 0, NULL, NULL);
+    if (!(name = heap_alloc(len)))
+        return E_OUTOFMEMORY;
+    WideCharToMultiByte(CP_ACP, 0, path, -1, name, len, NULL, NULL);
+
+    /* Local header */
+    local_header.signature = LOCAL_HEADER_SIGNATURE;
+    local_header.version = VERSION;
+    local_header.flags = USE_DATA_DESCRIPTOR;
+    local_header.method = 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;
+
+    compress_write(archive, &local_header, sizeof(local_header));
+    compress_write(archive, name, local_header.name_length);
+
+    /* Content */
+    compress_write_content(archive, content, options, &data_desc);
+
+    /* Data descriptor */
+    data_desc.signature = DATA_DESCRIPTOR_SIGNATURE;
+    compress_write(archive, &data_desc, sizeof(data_desc));
+
+    if (FAILED(archive->write_result))
+        return archive->write_result;
+
+    /* Set directory entry */
+    if (!(entry = heap_alloc_zero(sizeof(*entry) + local_header.name_length)))
+    {
+        heap_free(name);
+        return E_OUTOFMEMORY;
+    }
+
+    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 = data_desc.crc32;
+    entry->compressed_size = data_desc.compressed_size;
+    entry->uncompressed_size = 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);
+    heap_free(name);
+
+    if (!opc_array_reserve((void **)&archive->files, &archive->file_size, archive->file_count + 1,
+            sizeof(*archive->files)))
+    {
+        heap_free(entry);
+        return E_OUTOFMEMORY;
+    }
+
+    archive->files[archive->file_count++] = entry;
+
+    return S_OK;
+}
diff --git a/dlls/opcservices/factory.c b/dlls/opcservices/factory.c
index 4578f41d7c..13fa702293 100644
--- a/dlls/opcservices/factory.c
+++ b/dlls/opcservices/factory.c
@@ -26,6 +26,7 @@
 #include "ole2.h"
 #include "rpcproxy.h"
 #include "msopc.h"
+#include "xmllite.h"
 
 #include "wine/debug.h"
 
@@ -350,9 +351,9 @@ static HRESULT WINAPI opc_factory_ReadPackageFromStream(IOpcFactory *iface, IStr
 static HRESULT WINAPI opc_factory_WritePackageToStream(IOpcFactory *iface, IOpcPackage *package, OPC_WRITE_FLAGS flags,
         IStream *stream)
 {
-    FIXME("iface %p, package %p, flags %#x, stream %p stub!\n", iface, package, flags, stream);
+    TRACE("iface %p, package %p, flags %#x, stream %p.\n", iface, package, flags, stream);
 
-    return E_NOTIMPL;
+    return opc_package_write(package, flags, stream);
 }
 
 static HRESULT WINAPI opc_factory_CreateDigitalSignatureManager(IOpcFactory *iface, IOpcPackage *package,
diff --git a/dlls/opcservices/opc_private.h b/dlls/opcservices/opc_private.h
index 3c78195242..afe5c68561 100644
--- a/dlls/opcservices/opc_private.h
+++ b/dlls/opcservices/opc_private.h
@@ -48,3 +48,11 @@ static inline BOOL opc_array_reserve(void **elements, size_t *capacity, size_t c
 extern HRESULT opc_package_create(IOpcFactory *factory, IOpcPackage **package) DECLSPEC_HIDDEN;
 extern HRESULT opc_part_uri_create(const WCHAR *uri, IOpcPartUri **part_uri) DECLSPEC_HIDDEN;
 extern HRESULT opc_uri_create(const WCHAR *uri, IOpcUri **opc_uri) DECLSPEC_HIDDEN;
+
+extern HRESULT opc_package_write(IOpcPackage *package, OPC_WRITE_FLAGS flags, IStream *stream) DECLSPEC_HIDDEN;
+
+struct zip_archive;
+extern HRESULT compress_create_archive(IStream *output, struct zip_archive **archive) DECLSPEC_HIDDEN;
+extern HRESULT compress_add_file(struct zip_archive *archive, const WCHAR *path, IStream *content,
+        OPC_COMPRESSION_OPTIONS options) DECLSPEC_HIDDEN;
+extern void compress_finalize_archive(struct zip_archive *archive) DECLSPEC_HIDDEN;
diff --git a/dlls/opcservices/package.c b/dlls/opcservices/package.c
index c69239b84e..20d7b4e5ed 100644
--- a/dlls/opcservices/package.c
+++ b/dlls/opcservices/package.c
@@ -22,6 +22,7 @@
 #include "windef.h"
 #include "winbase.h"
 #include "ntsecapi.h"
+#include "xmllite.h"
 
 #include "wine/debug.h"
 #include "wine/unicode.h"
@@ -786,3 +787,57 @@ HRESULT opc_package_create(IOpcFactory *factory, IOpcPackage **out)
     TRACE("Created package %p.\n", *out);
     return S_OK;
 }
+
+static HRESULT opc_package_write_contenttypes(struct zip_archive *archive, IXmlWriter *writer)
+{
+    static const WCHAR contenttypesW[] = {'[','C','o','n','t','e','n','t','_','T','y','p','e','s',']','.','x','m','l',0};
+    static const WCHAR typesW[] = {'T','y','p','e','s',0};
+    IStream *content;
+    HRESULT hr;
+
+    if (FAILED(hr = CreateStreamOnHGlobal(NULL, TRUE, &content)))
+        return hr;
+
+    IXmlWriter_SetOutput(writer, (IUnknown *)content);
+
+    hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
+    if (SUCCEEDED(hr))
+        hr = IXmlWriter_WriteStartElement(writer, NULL, typesW, NULL);
+    if (SUCCEEDED(hr))
+        hr = IXmlWriter_WriteEndDocument(writer);
+    if (SUCCEEDED(hr))
+        hr = IXmlWriter_Flush(writer);
+
+    if (SUCCEEDED(hr))
+        hr = compress_add_file(archive, contenttypesW, content, OPC_COMPRESSION_NORMAL);
+    IStream_Release(content);
+
+    return hr;
+}
+
+HRESULT opc_package_write(IOpcPackage *input, OPC_WRITE_FLAGS flags, IStream *stream)
+{
+    struct zip_archive *archive;
+    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)))
+    {
+        IXmlWriter_Release(writer);
+        return hr;
+    }
+
+    /* [Content_Types].xml */
+    hr = opc_package_write_contenttypes(archive, writer);
+
+    compress_finalize_archive(archive);
+    IXmlWriter_Release(writer);
+
+    return hr;
+}
-- 
2.18.0




More information about the wine-devel mailing list