opcservices: Implement writing stub compressed package.
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
951741e649
commit
80579d6344
|
@ -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
|
||||
|
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
#include <stdarg.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;
|
||||
ULONG num_read;
|
||||
HRESULT hr;
|
||||
|
||||
data_desc->crc32 = RtlComputeCrc32(0, NULL, 0);
|
||||
move.QuadPart = 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 = 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;
|
||||
|
||||
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;
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue