include: Make sure __int64 is correctly defined on PPC64.
[wine.git] / dlls / opcservices / compress.c
blob0ee876512c1c0b2683b73d8cfdd2599438f3e09c
1 /*
2 * Copyright 2018 Nikolay Sivov for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define COBJMACROS
21 #include <stdarg.h>
23 #include "windef.h"
24 #include "winternl.h"
25 #include "msopc.h"
27 #include "opc_private.h"
28 #include "zlib.h"
30 #include "wine/debug.h"
31 #include "wine/heap.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(msopc);
35 #include <pshpack2.h>
36 struct local_file_header
38 DWORD signature;
39 WORD version;
40 WORD flags;
41 WORD method;
42 DWORD mtime;
43 DWORD crc32;
44 DWORD compressed_size;
45 DWORD uncompressed_size;
46 WORD name_length;
47 WORD extra_length;
50 struct data_descriptor
52 DWORD signature;
53 DWORD crc32;
54 DWORD compressed_size;
55 DWORD uncompressed_size;
58 struct central_directory_header
60 DWORD signature;
61 WORD version;
62 WORD min_version;
63 WORD flags;
64 WORD method;
65 DWORD mtime;
66 DWORD crc32;
67 DWORD compressed_size;
68 DWORD uncompressed_size;
69 WORD name_length;
70 WORD extra_length;
71 WORD comment_length;
72 WORD diskid;
73 WORD internal_attributes;
74 DWORD external_attributes;
75 DWORD local_file_offset;
78 struct central_directory_end
80 DWORD signature;
81 WORD diskid;
82 WORD firstdisk;
83 WORD records_num;
84 WORD records_total;
85 DWORD directory_size;
86 DWORD directory_offset;
87 WORD comment_length;
89 #include <poppack.h>
91 #define CENTRAL_DIR_SIGNATURE 0x02014b50
92 #define LOCAL_HEADER_SIGNATURE 0x04034b50
93 #define DIRECTORY_END_SIGNATURE 0x06054b50
94 #define DATA_DESCRIPTOR_SIGNATURE 0x08074b50
95 #define VERSION 20
97 enum entry_flags
99 USE_DATA_DESCRIPTOR = 0x8,
102 struct zip_archive
104 struct central_directory_header **files;
105 size_t file_count;
106 size_t file_size;
108 DWORD mtime;
109 IStream *output;
110 DWORD position;
111 HRESULT write_result;
113 unsigned char input_buffer[0x8000];
114 unsigned char output_buffer[0x8000];
117 HRESULT compress_create_archive(IStream *output, struct zip_archive **out)
119 struct zip_archive *archive;
120 WORD date, time;
121 FILETIME ft;
123 if (!(archive = heap_alloc(sizeof(*archive))))
124 return E_OUTOFMEMORY;
126 archive->files = NULL;
127 archive->file_size = 0;
128 archive->file_count = 0;
129 archive->write_result = S_OK;
131 archive->output = output;
132 IStream_AddRef(archive->output);
133 archive->position = 0;
135 GetSystemTimeAsFileTime(&ft);
136 FileTimeToDosDateTime(&ft, &date, &time);
137 archive->mtime = date << 16 | time;
139 *out = archive;
141 return S_OK;
144 static void compress_write(struct zip_archive *archive, void *data, ULONG size)
146 ULONG written;
148 archive->write_result = IStream_Write(archive->output, data, size, &written);
149 if (written != size)
150 archive->write_result = E_FAIL;
151 else
152 archive->position += written;
154 if (FAILED(archive->write_result))
155 WARN("Failed to write output %p, size %u, written %u, hr %#x.\n", data, size, written, archive->write_result);
158 void compress_finalize_archive(struct zip_archive *archive)
160 struct central_directory_end dir_end = { 0 };
161 size_t i;
163 dir_end.directory_offset = archive->position;
164 dir_end.records_num = archive->file_count;
165 dir_end.records_total = archive->file_count;
167 /* Directory entries */
168 for (i = 0; i < archive->file_count; ++i)
170 compress_write(archive, archive->files[i], sizeof(*archive->files[i]));
171 compress_write(archive, archive->files[i] + 1, archive->files[i]->name_length);
172 dir_end.directory_size += archive->files[i]->name_length + sizeof(*archive->files[i]);
175 /* End record */
176 dir_end.signature = DIRECTORY_END_SIGNATURE;
177 compress_write(archive, &dir_end, sizeof(dir_end));
179 IStream_Release(archive->output);
181 for (i = 0; i < archive->file_count; i++)
182 heap_free(archive->files[i]);
183 heap_free(archive->files);
184 heap_free(archive);
187 static void compress_write_content(struct zip_archive *archive, IStream *content,
188 OPC_COMPRESSION_OPTIONS options, struct data_descriptor *data_desc)
190 int level, flush;
191 z_stream z_str;
192 LARGE_INTEGER move;
193 ULONG num_read;
194 HRESULT hr;
196 data_desc->crc32 = RtlComputeCrc32(0, NULL, 0);
197 move.QuadPart = 0;
198 IStream_Seek(content, move, STREAM_SEEK_SET, NULL);
200 switch (options)
202 case OPC_COMPRESSION_NONE:
203 level = Z_NO_COMPRESSION;
204 break;
205 case OPC_COMPRESSION_NORMAL:
206 level = Z_DEFAULT_COMPRESSION;
207 break;
208 case OPC_COMPRESSION_MAXIMUM:
209 level = Z_BEST_COMPRESSION;
210 break;
211 case OPC_COMPRESSION_FAST:
212 level = 2;
213 break;
214 case OPC_COMPRESSION_SUPERFAST:
215 level = Z_BEST_SPEED;
216 break;
217 default:
218 WARN("Unsupported compression options %d.\n", options);
219 level = Z_DEFAULT_COMPRESSION;
222 memset(&z_str, 0, sizeof(z_str));
223 deflateInit2(&z_str, level, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
227 int ret;
229 if (FAILED(hr = IStream_Read(content, archive->input_buffer, sizeof(archive->input_buffer), &num_read)))
231 archive->write_result = hr;
232 break;
235 z_str.avail_in = num_read;
236 z_str.next_in = archive->input_buffer;
237 data_desc->crc32 = RtlComputeCrc32(data_desc->crc32, archive->input_buffer, num_read);
239 flush = sizeof(archive->input_buffer) > num_read ? Z_FINISH : Z_NO_FLUSH;
243 ULONG have;
245 z_str.avail_out = sizeof(archive->output_buffer);
246 z_str.next_out = archive->output_buffer;
248 if ((ret = deflate(&z_str, flush)))
249 WARN("Failed to deflate, ret %d.\n", ret);
250 have = sizeof(archive->output_buffer) - z_str.avail_out;
251 compress_write(archive, archive->output_buffer, have);
252 } while (z_str.avail_out == 0);
253 } while (flush != Z_FINISH);
255 deflateEnd(&z_str);
257 data_desc->compressed_size = z_str.total_out;
258 data_desc->uncompressed_size = z_str.total_in;
261 HRESULT compress_add_file(struct zip_archive *archive, const WCHAR *path,
262 IStream *content, OPC_COMPRESSION_OPTIONS options)
264 struct central_directory_header *entry;
265 struct local_file_header local_header;
266 struct data_descriptor data_desc;
267 DWORD local_header_pos;
268 char *name;
269 DWORD len;
271 len = WideCharToMultiByte(CP_ACP, 0, path, -1, NULL, 0, NULL, NULL);
272 if (!(name = heap_alloc(len)))
273 return E_OUTOFMEMORY;
274 WideCharToMultiByte(CP_ACP, 0, path, -1, name, len, NULL, NULL);
276 /* Local header */
277 local_header.signature = LOCAL_HEADER_SIGNATURE;
278 local_header.version = VERSION;
279 local_header.flags = USE_DATA_DESCRIPTOR;
280 local_header.method = 8; /* Z_DEFLATED */
281 local_header.mtime = archive->mtime;
282 local_header.crc32 = 0;
283 local_header.compressed_size = 0;
284 local_header.uncompressed_size = 0;
285 local_header.name_length = len - 1;
286 local_header.extra_length = 0;
288 local_header_pos = archive->position;
290 compress_write(archive, &local_header, sizeof(local_header));
291 compress_write(archive, name, local_header.name_length);
293 /* Content */
294 compress_write_content(archive, content, options, &data_desc);
296 /* Data descriptor */
297 data_desc.signature = DATA_DESCRIPTOR_SIGNATURE;
298 compress_write(archive, &data_desc, sizeof(data_desc));
300 if (FAILED(archive->write_result))
301 return archive->write_result;
303 /* Set directory entry */
304 if (!(entry = heap_alloc_zero(sizeof(*entry) + local_header.name_length)))
306 heap_free(name);
307 return E_OUTOFMEMORY;
310 entry->signature = CENTRAL_DIR_SIGNATURE;
311 entry->version = local_header.version;
312 entry->min_version = local_header.version;
313 entry->flags = local_header.flags;
314 entry->method = local_header.method;
315 entry->mtime = local_header.mtime;
316 entry->crc32 = data_desc.crc32;
317 entry->compressed_size = data_desc.compressed_size;
318 entry->uncompressed_size = data_desc.uncompressed_size;
319 entry->name_length = local_header.name_length;
320 entry->local_file_offset = local_header_pos;
321 memcpy(entry + 1, name, entry->name_length);
322 heap_free(name);
324 if (!opc_array_reserve((void **)&archive->files, &archive->file_size, archive->file_count + 1,
325 sizeof(*archive->files)))
327 heap_free(entry);
328 return E_OUTOFMEMORY;
331 archive->files[archive->file_count++] = entry;
333 return S_OK;