[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