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
32 #include "opc_private.h"
34 #include "wine/debug.h"
35 #include "wine/heap.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(msopc
);
40 struct local_file_header
48 DWORD compressed_size
;
49 DWORD uncompressed_size
;
54 struct data_descriptor
58 DWORD compressed_size
;
59 DWORD uncompressed_size
;
62 struct central_directory_header
71 DWORD compressed_size
;
72 DWORD uncompressed_size
;
77 WORD internal_attributes
;
78 DWORD external_attributes
;
79 DWORD local_file_offset
;
82 struct central_directory_end
90 DWORD directory_offset
;
95 #define CENTRAL_DIR_SIGNATURE 0x02014b50
96 #define LOCAL_HEADER_SIGNATURE 0x04034b50
97 #define DIRECTORY_END_SIGNATURE 0x06054b50
98 #define DATA_DESCRIPTOR_SIGNATURE 0x08074b50
103 USE_DATA_DESCRIPTOR
= 0x8,
108 struct central_directory_header
**files
;
115 HRESULT write_result
;
117 unsigned char input_buffer
[0x8000];
118 unsigned char output_buffer
[0x8000];
121 HRESULT
compress_create_archive(IStream
*output
, struct zip_archive
**out
)
123 struct zip_archive
*archive
;
127 if (!(archive
= heap_alloc(sizeof(*archive
))))
128 return E_OUTOFMEMORY
;
130 archive
->files
= NULL
;
131 archive
->file_size
= 0;
132 archive
->file_count
= 0;
133 archive
->write_result
= S_OK
;
135 archive
->output
= output
;
136 IStream_AddRef(archive
->output
);
137 archive
->position
= 0;
139 GetSystemTimeAsFileTime(&ft
);
140 FileTimeToDosDateTime(&ft
, &date
, &time
);
141 archive
->mtime
= date
<< 16 | time
;
148 static void compress_write(struct zip_archive
*archive
, void *data
, ULONG size
)
152 archive
->write_result
= IStream_Write(archive
->output
, data
, size
, &written
);
154 archive
->write_result
= E_FAIL
;
156 archive
->position
+= written
;
158 if (FAILED(archive
->write_result
))
159 WARN("Failed to write output %p, size %u, written %u, hr %#x.\n", data
, size
, written
, archive
->write_result
);
162 void compress_finalize_archive(struct zip_archive
*archive
)
164 struct central_directory_end dir_end
= { 0 };
167 dir_end
.directory_offset
= archive
->position
;
168 dir_end
.records_num
= archive
->file_count
;
169 dir_end
.records_total
= archive
->file_count
;
171 /* Directory entries */
172 for (i
= 0; i
< archive
->file_count
; ++i
)
174 compress_write(archive
, archive
->files
[i
], sizeof(*archive
->files
[i
]));
175 compress_write(archive
, archive
->files
[i
] + 1, archive
->files
[i
]->name_length
);
176 dir_end
.directory_size
+= archive
->files
[i
]->name_length
+ sizeof(*archive
->files
[i
]);
180 dir_end
.signature
= DIRECTORY_END_SIGNATURE
;
181 compress_write(archive
, &dir_end
, sizeof(dir_end
));
183 IStream_Release(archive
->output
);
185 for (i
= 0; i
< archive
->file_count
; i
++)
186 heap_free(archive
->files
[i
]);
187 heap_free(archive
->files
);
191 static void compress_write_content(struct zip_archive
*archive
, IStream
*content
,
192 OPC_COMPRESSION_OPTIONS options
, struct data_descriptor
*data_desc
)
202 data_desc
->crc32
= RtlComputeCrc32(0, NULL
, 0);
204 IStream_Seek(content
, move
, STREAM_SEEK_SET
, NULL
);
210 case OPC_COMPRESSION_NONE
:
211 level
= Z_NO_COMPRESSION
;
213 case OPC_COMPRESSION_NORMAL
:
214 level
= Z_DEFAULT_COMPRESSION
;
216 case OPC_COMPRESSION_MAXIMUM
:
217 level
= Z_BEST_COMPRESSION
;
219 case OPC_COMPRESSION_FAST
:
222 case OPC_COMPRESSION_SUPERFAST
:
223 level
= Z_BEST_SPEED
;
226 WARN("Unsupported compression options %d.\n", options
);
227 level
= Z_DEFAULT_COMPRESSION
;
230 memset(&z_str
, 0, sizeof(z_str
));
231 deflateInit2(&z_str
, level
, Z_DEFLATED
, -MAX_WBITS
, MAX_MEM_LEVEL
, Z_DEFAULT_STRATEGY
);
237 if (FAILED(hr
= IStream_Read(content
, archive
->input_buffer
, sizeof(archive
->input_buffer
), &num_read
)))
239 archive
->write_result
= hr
;
243 z_str
.avail_in
= num_read
;
244 z_str
.next_in
= archive
->input_buffer
;
245 data_desc
->crc32
= RtlComputeCrc32(data_desc
->crc32
, archive
->input_buffer
, num_read
);
247 flush
= sizeof(archive
->input_buffer
) > num_read
? Z_FINISH
: Z_NO_FLUSH
;
253 z_str
.avail_out
= sizeof(archive
->output_buffer
);
254 z_str
.next_out
= archive
->output_buffer
;
256 if ((ret
= deflate(&z_str
, flush
)))
257 WARN("Failed to deflate, ret %d.\n", ret
);
258 have
= sizeof(archive
->output_buffer
) - z_str
.avail_out
;
259 compress_write(archive
, archive
->output_buffer
, have
);
260 } while (z_str
.avail_out
== 0);
261 } while (flush
!= Z_FINISH
);
265 data_desc
->compressed_size
= z_str
.total_out
;
266 data_desc
->uncompressed_size
= z_str
.total_in
;
270 if (options
!= OPC_COMPRESSION_NONE
)
271 FIXME("Writing without compression.\n");
275 if (FAILED(hr
= IStream_Read(content
, archive
->input_buffer
, sizeof(archive
->input_buffer
), &num_read
)))
277 archive
->write_result
= hr
;
284 data_desc
->uncompressed_size
+= num_read
;
285 data_desc
->crc32
= RtlComputeCrc32(data_desc
->crc32
, archive
->input_buffer
, num_read
);
286 compress_write(archive
, archive
->input_buffer
, num_read
);
287 } while (num_read
!= 0 && archive
->write_result
== S_OK
);
289 data_desc
->compressed_size
= data_desc
->uncompressed_size
;
291 #endif /* HAVE_ZLIB */
294 HRESULT
compress_add_file(struct zip_archive
*archive
, const WCHAR
*path
,
295 IStream
*content
, OPC_COMPRESSION_OPTIONS options
)
297 struct central_directory_header
*entry
;
298 struct local_file_header local_header
;
299 struct data_descriptor data_desc
;
300 DWORD local_header_pos
;
304 len
= WideCharToMultiByte(CP_ACP
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
305 if (!(name
= heap_alloc(len
)))
306 return E_OUTOFMEMORY
;
307 WideCharToMultiByte(CP_ACP
, 0, path
, -1, name
, len
, NULL
, NULL
);
310 local_header
.signature
= LOCAL_HEADER_SIGNATURE
;
311 local_header
.version
= VERSION
;
312 local_header
.flags
= USE_DATA_DESCRIPTOR
;
313 local_header
.method
= 8; /* Z_DEFLATED */
314 local_header
.mtime
= archive
->mtime
;
315 local_header
.crc32
= 0;
316 local_header
.compressed_size
= 0;
317 local_header
.uncompressed_size
= 0;
318 local_header
.name_length
= len
- 1;
319 local_header
.extra_length
= 0;
321 local_header_pos
= archive
->position
;
323 compress_write(archive
, &local_header
, sizeof(local_header
));
324 compress_write(archive
, name
, local_header
.name_length
);
327 compress_write_content(archive
, content
, options
, &data_desc
);
329 /* Data descriptor */
330 data_desc
.signature
= DATA_DESCRIPTOR_SIGNATURE
;
331 compress_write(archive
, &data_desc
, sizeof(data_desc
));
333 if (FAILED(archive
->write_result
))
334 return archive
->write_result
;
336 /* Set directory entry */
337 if (!(entry
= heap_alloc_zero(sizeof(*entry
) + local_header
.name_length
)))
340 return E_OUTOFMEMORY
;
343 entry
->signature
= CENTRAL_DIR_SIGNATURE
;
344 entry
->version
= local_header
.version
;
345 entry
->min_version
= local_header
.version
;
346 entry
->flags
= local_header
.flags
;
347 entry
->method
= local_header
.method
;
348 entry
->mtime
= local_header
.mtime
;
349 entry
->crc32
= data_desc
.crc32
;
350 entry
->compressed_size
= data_desc
.compressed_size
;
351 entry
->uncompressed_size
= data_desc
.uncompressed_size
;
352 entry
->name_length
= local_header
.name_length
;
353 entry
->local_file_offset
= local_header_pos
;
354 memcpy(entry
+ 1, name
, entry
->name_length
);
357 if (!opc_array_reserve((void **)&archive
->files
, &archive
->file_size
, archive
->file_count
+ 1,
358 sizeof(*archive
->files
)))
361 return E_OUTOFMEMORY
;
364 archive
->files
[archive
->file_count
++] = entry
;