wined3d: Drop support for WINED3DFMT_D32_UNORM.
[wine.git] / dlls / mspatcha / pa19.c
blob633915eec37368d3789c3da7c9f9e36357ffb526
1 /*
2 * PatchAPI PA19 file handlers
4 * Copyright 2019 Conor McCarthy
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * TODO
21 * - Normalization of 32-bit PE executable files and reversal of special
22 * processing of these executables is not implemented.
23 * Without normalization, old files cannot be validated for patching. The
24 * function NormalizeFileForPatchSignature() in Windows could be used to work
25 * out exactly how normalization works.
26 * Most/all of the special processing seems to be relocation of targets for
27 * some jump/call instructions to match more of the old file and improve
28 * compression. Patching of 64-bit exes works because mspatchc.dll does not
29 * implement special processing of them. In 32-bit patches, the variable
30 * named here 'unknown_count' seems to indicate presence of data related to
31 * reversing the processing. The changes that must be reversed occur at some,
32 * but not all, of the positions listed in the PE .reloc table.
35 #include <stdarg.h>
36 #include <stdlib.h>
38 #include "windef.h"
39 #include "wine/heap.h"
40 #include "wine/debug.h"
42 #include "patchapi.h"
44 #include "pa19.h"
45 #include "lzxd_dec.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(mspatcha);
49 #define PA19_FILE_MAGIC 0x39314150
50 #define PATCH_OPTION_EXTRA_FLAGS 0x80000000
52 #define my_max(a, b) ((a) > (b) ? (a) : (b))
53 #define my_min(a, b) ((a) < (b) ? (a) : (b))
56 /* The CRC32 code is copyright (C) 1986 Gary S. Brown and was placed in the
57 * public domain.
58 * CRC polynomial 0xedb88320
60 static const UINT32 CRC_table[256] =
62 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
63 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
64 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
65 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
66 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
67 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
68 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
69 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
70 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
71 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
72 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
73 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
74 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
75 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
76 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
77 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
78 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
79 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
80 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
81 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
82 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
83 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
84 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
85 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
86 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
87 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
88 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
89 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
90 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
91 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
92 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
93 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
94 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
95 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
96 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
97 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
98 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
99 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
100 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
101 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
102 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
103 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
104 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
107 static UINT32 compute_crc32(UINT32 crc, const BYTE *pData, INT_PTR iLen)
109 crc ^= 0xFFFFFFFF;
111 while (iLen > 0) {
112 crc = CRC_table[(crc ^ *pData) & 0xFF] ^ (crc >> 8);
113 pData++;
114 iLen--;
116 return crc ^ 0xFFFFFFFF;
119 static UINT32 compute_zero_crc32(UINT32 crc, INT_PTR iLen)
121 crc ^= 0xFFFFFFFF;
123 while (iLen > 0)
125 crc = CRC_table[crc & 0xFF] ^ (crc >> 8);
126 iLen--;
128 return crc ^ 0xFFFFFFFF;
132 /***********************************************************************************
133 * PatchAPI PA19 file header
135 * BYTE magic[4];
136 * UINT32 options;
137 * UINT32 options_2; (present if PATCH_OPTION_EXTRA_FLAGS set)
138 * UINT32 timestamp; (if PATCH_OPTION_NO_TIMESTAMP is SET in options)
139 * UVLI rebase; (present if PATCH_OPTION_NO_REBASE is not set; used on 32-bit executables)
140 * UVLI unpatched_size;
141 * UINT32 crc32_patched;
142 * BYTE input_file_count;
144 * For each source file:
145 * SVLI (patched_size - unpatched_size);
146 * UINT32 crc32_unpatched;
147 * BYTE ignore_range_count;
148 * For each ignore range:
149 * SVLI OffsetInOldFile;
150 * UVLI LengthInBytes;
151 * BYTE retain_range_count;
152 * For each retain range:
153 * SVLI (OffsetInOldFile - (prevOffsetInOldFile + prevLengthInBytes));
154 * SVLI (OffsetInNewFile - OffsetInOldFile);
155 * UVLI LengthInBytes;
156 * UVLI unknown_count; (a count of pairs of values related to the reversal of special
157 * processing done to improve compression of 32-bit executables)
158 * UVLI interleave_count; (present only if PATCH_OPTION_INTERLEAVE_FILES is set in options)
159 * UVLI interleave_values[interleave_count * 3 - 1];
160 * UVLI lzxd_input_size;
162 * For each source file:
163 * UINT16 lzxd_block[lzxd_input_size / 2]; (NOT always 16-bit aligned)
165 * UINT32 crc_hack; (rounds out the entire file crc32 to 0)
169 #define MAX_RANGES 255
171 struct input_file_info {
172 size_t input_size;
173 DWORD crc32;
174 BYTE ignore_range_count;
175 BYTE retain_range_count;
176 PATCH_IGNORE_RANGE ignore_table[MAX_RANGES];
177 PATCH_RETAIN_RANGE retain_table[MAX_RANGES];
178 size_t unknown_count;
179 size_t stream_size;
180 const BYTE *stream_start;
181 int next_i;
182 int next_r;
185 struct patch_file_header {
186 DWORD flags;
187 DWORD timestamp;
188 size_t patched_size;
189 DWORD patched_crc32;
190 unsigned input_file_count;
191 struct input_file_info *file_table;
192 const BYTE *src;
193 const BYTE *end;
194 DWORD err;
198 /* Currently supported options. Some such as PATCH_OPTION_FAIL_IF_BIGGER don't
199 * affect decoding but can get recorded in the patch file anyway */
200 #define PATCH_OPTION_SUPPORTED_FLAGS ( \
201 PATCH_OPTION_USE_LZX_A \
202 | PATCH_OPTION_USE_LZX_B \
203 | PATCH_OPTION_USE_LZX_LARGE \
204 | PATCH_OPTION_NO_BINDFIX \
205 | PATCH_OPTION_NO_LOCKFIX \
206 | PATCH_OPTION_NO_REBASE \
207 | PATCH_OPTION_FAIL_IF_SAME_FILE \
208 | PATCH_OPTION_FAIL_IF_BIGGER \
209 | PATCH_OPTION_NO_CHECKSUM \
210 | PATCH_OPTION_NO_RESTIMEFIX \
211 | PATCH_OPTION_NO_TIMESTAMP \
212 | PATCH_OPTION_EXTRA_FLAGS)
215 /* read a byte-aligned little-endian UINT32 from input and set error if eof
217 static inline UINT32 read_raw_uint32(struct patch_file_header *ph)
219 const BYTE *src = ph->src;
221 ph->src += 4;
222 if (ph->src > ph->end)
224 ph->err = ERROR_PATCH_CORRUPT;
225 return 0;
227 return src[0]
228 | (src[1] << 8)
229 | (src[2] << 16)
230 | (src[3] << 24);
233 /* Read a variable-length integer from a sequence of bytes terminated by
234 * a value with bit 7 set. Set error if invalid or eof */
235 static UINT64 read_uvli(struct patch_file_header *ph)
237 const BYTE *vli = ph->src;
238 UINT64 n;
239 ptrdiff_t i;
240 ptrdiff_t limit = my_min(ph->end - vli, 9);
242 if (ph->src >= ph->end)
244 ph->err = ERROR_PATCH_CORRUPT;
245 return 0;
248 n = vli[0] & 0x7F;
249 for (i = 1; i < limit && vli[i - 1] < 0x80; ++i)
250 n += (UINT64)(vli[i] & 0x7F) << (7 * i);
252 if (vli[i - 1] < 0x80)
254 TRACE("exceeded maximum vli size\n");
255 ph->err = ERROR_PATCH_CORRUPT;
256 return 0;
259 ph->src += i;
261 return n;
264 /* Signed variant of the above. First byte sign flag is 0x40.
266 static INT64 read_svli(struct patch_file_header *ph)
268 const BYTE *vli = ph->src;
269 INT64 n;
270 ptrdiff_t i;
271 ptrdiff_t limit = my_min(ph->end - vli, 9);
273 if (ph->src >= ph->end)
275 ph->err = ERROR_PATCH_CORRUPT;
276 return 0;
279 n = vli[0] & 0x3F;
280 for (i = 1; i < limit && vli[i - 1] < 0x80; ++i)
281 n += (INT64)(vli[i] & 0x7F) << (7 * i - 1);
283 if (vli[i - 1] < 0x80)
285 TRACE("exceeded maximum vli size\n");
286 ph->err = ERROR_PATCH_CORRUPT;
287 return 0;
290 if (vli[0] & 0x40)
291 n = -n;
293 ph->src += i;
295 return n;
298 static int __cdecl compare_ignored_range(const void *a, const void *b)
300 LONG delta = ((PATCH_IGNORE_RANGE*)a)->OffsetInOldFile - ((PATCH_IGNORE_RANGE*)b)->OffsetInOldFile;
301 if (delta > 0)
302 return 1;
303 if (delta < 0)
304 return -1;
305 return 0;
308 static int __cdecl compare_retained_range_old(const void *a, const void *b)
310 LONG delta = ((PATCH_RETAIN_RANGE*)a)->OffsetInOldFile - ((PATCH_RETAIN_RANGE*)b)->OffsetInOldFile;
311 if (delta > 0)
312 return 1;
313 if (delta < 0)
314 return -1;
315 return 0;
318 static int __cdecl compare_retained_range_new(const void *a, const void *b)
320 LONG delta = ((PATCH_RETAIN_RANGE*)a)->OffsetInNewFile - ((PATCH_RETAIN_RANGE*)b)->OffsetInNewFile;
321 if (delta > 0)
322 return 1;
323 if (delta < 0)
324 return -1;
325 return 0;
328 static int read_header(struct patch_file_header *ph, const BYTE *buf, size_t size)
330 unsigned fileno;
332 ph->src = buf;
333 ph->end = buf + size;
335 ph->file_table = NULL;
336 ph->err = ERROR_SUCCESS;
338 if (read_raw_uint32(ph) != PA19_FILE_MAGIC)
340 TRACE("no PA19 signature\n");
341 ph->err = ERROR_PATCH_CORRUPT;
342 return -1;
345 ph->flags = read_raw_uint32(ph);
346 if ((ph->flags & PATCH_OPTION_SUPPORTED_FLAGS) != ph->flags)
348 FIXME("unsupported option flag(s): 0x%08x\n", ph->flags & ~PATCH_OPTION_SUPPORTED_FLAGS);
349 ph->err = ERROR_PATCH_PACKAGE_UNSUPPORTED;
350 return -1;
353 /* additional 32-bit flag field */
354 if (ph->flags & PATCH_OPTION_EXTRA_FLAGS)
356 TRACE("skipping extra flag field\n");
357 (void)read_raw_uint32(ph);
360 /* the meaning of PATCH_OPTION_NO_TIMESTAMP is inverted for decoding */
361 if(ph->flags & PATCH_OPTION_NO_TIMESTAMP)
362 ph->timestamp = read_raw_uint32(ph);
364 /* not sure what this value is for, but its absence seems to mean only that timestamps
365 * in the decompressed 32-bit exe are not modified */
366 if (!(ph->flags & PATCH_OPTION_NO_REBASE))
368 TRACE("skipping rebase field\n");
369 (void)read_uvli(ph);
372 ph->patched_size = (size_t)read_uvli(ph);
373 TRACE("patched file size will be %u\n", (unsigned)ph->patched_size);
374 ph->patched_crc32 = read_raw_uint32(ph);
376 ph->input_file_count = *ph->src;
377 ++ph->src;
378 TRACE("patch supports %u old file(s)\n", ph->input_file_count);
379 /* if no old file used, input_file_count is still 1 */
380 if (ph->input_file_count == 0)
382 ph->err = ERROR_PATCH_CORRUPT;
383 return -1;
386 if (ph->err != ERROR_SUCCESS)
387 return -1;
389 ph->file_table = heap_calloc(ph->input_file_count, sizeof(struct input_file_info));
390 if (ph->file_table == NULL)
392 ph->err = ERROR_OUTOFMEMORY;
393 return -1;
396 for (fileno = 0; fileno < ph->input_file_count; ++fileno) {
397 struct input_file_info *fi = ph->file_table + fileno;
398 ptrdiff_t delta;
399 unsigned i;
401 delta = (ptrdiff_t)read_svli(ph);
402 fi->input_size = ph->patched_size + delta;
404 fi->crc32 = read_raw_uint32(ph);
406 fi->ignore_range_count = *ph->src;
407 ++ph->src;
408 TRACE("found %u range(s) to ignore\n", fi->ignore_range_count);
410 for (i = 0; i < fi->ignore_range_count; ++i) {
411 PATCH_IGNORE_RANGE *ir = fi->ignore_table + i;
413 ir->OffsetInOldFile = (LONG)read_svli(ph);
414 ir->LengthInBytes = (ULONG)read_uvli(ph);
416 if (i != 0)
418 ir->OffsetInOldFile += fi->ignore_table[i - 1].OffsetInOldFile
419 + fi->ignore_table[i - 1].LengthInBytes;
421 if (ir->OffsetInOldFile > fi->input_size
422 || ir->OffsetInOldFile + ir->LengthInBytes > fi->input_size
423 || ir->LengthInBytes > fi->input_size)
425 ph->err = ERROR_PATCH_CORRUPT;
426 return -1;
430 fi->retain_range_count = *ph->src;
431 ++ph->src;
432 TRACE("found %u range(s) to retain\n", fi->retain_range_count);
434 for (i = 0; i < fi->retain_range_count; ++i) {
435 PATCH_RETAIN_RANGE *rr = fi->retain_table + i;
437 rr->OffsetInOldFile = (LONG)read_svli(ph);
438 if (i != 0)
439 rr->OffsetInOldFile +=
440 fi->retain_table[i - 1].OffsetInOldFile + fi->retain_table[i - 1].LengthInBytes;
442 rr->OffsetInNewFile = rr->OffsetInOldFile + (LONG)read_svli(ph);
443 rr->LengthInBytes = (ULONG)read_uvli(ph);
445 if (rr->OffsetInOldFile > fi->input_size
446 || rr->OffsetInOldFile + rr->LengthInBytes > fi->input_size
447 || rr->OffsetInNewFile > ph->patched_size
448 || rr->OffsetInNewFile + rr->LengthInBytes > ph->patched_size
449 || rr->LengthInBytes > ph->patched_size)
451 ph->err = ERROR_PATCH_CORRUPT;
452 return -1;
455 /* ranges in new file must be equal and in the same order for all source files */
456 if (fileno != 0)
458 PATCH_RETAIN_RANGE *rr_0 = ph->file_table[0].retain_table + i;
459 if (rr->OffsetInNewFile != rr_0->OffsetInNewFile
460 || rr->LengthInBytes != rr_0->LengthInBytes)
462 ph->err = ERROR_PATCH_CORRUPT;
463 return -1;
468 fi->unknown_count = (size_t)read_uvli(ph);
469 if (fi->unknown_count)
471 FIXME("special processing of 32-bit executables not implemented.\n");
472 ph->err = ERROR_PATCH_PACKAGE_UNSUPPORTED;
473 return -1;
475 fi->stream_size = (size_t)read_uvli(ph);
478 for (fileno = 0; fileno < ph->input_file_count; ++fileno)
480 struct input_file_info *fi = ph->file_table + fileno;
482 qsort(fi->ignore_table, fi->ignore_range_count, sizeof(fi->ignore_table[0]), compare_ignored_range);
483 qsort(fi->retain_table, fi->retain_range_count, sizeof(fi->retain_table[0]), compare_retained_range_old);
485 fi->stream_start = ph->src;
486 ph->src += fi->stream_size;
489 /* skip the crc adjustment field */
490 ph->src = my_min(ph->src + 4, ph->end);
493 UINT32 crc = compute_crc32(0, buf, ph->src - buf) ^ 0xFFFFFFFF;
494 if (crc != 0)
496 TRACE("patch file crc32 failed\n");
497 if (ph->src < ph->end)
498 FIXME("probable header parsing error\n");
499 ph->err = ERROR_PATCH_CORRUPT;
503 return (ph->err == ERROR_SUCCESS) ? 0 : -1;
506 static void free_header(struct patch_file_header *ph)
508 heap_free(ph->file_table);
511 #define TICKS_PER_SEC 10000000
512 #define SEC_TO_UNIX_EPOCH ((369 * 365 + 89) * (ULONGLONG)86400)
514 static void posix_time_to_file_time(ULONG timestamp, FILETIME *ft)
516 UINT64 ticks = ((UINT64)timestamp + SEC_TO_UNIX_EPOCH) * TICKS_PER_SEC;
517 ft->dwLowDateTime = (DWORD)ticks;
518 ft->dwHighDateTime = (DWORD)(ticks >> 32);
521 /* Get the next range to ignore in the old file.
522 * fi->next_i must be initialized before use */
523 static ULONG next_ignored_range(const struct input_file_info *fi, size_t index, ULONG old_file_size, ULONG *end)
525 ULONG start = old_file_size;
526 *end = old_file_size;
527 /* if patching is unnecessary, the ignored ranges are skipped during crc calc */
528 if (fi->next_i < fi->ignore_range_count && fi->stream_size != 0)
530 start = fi->ignore_table[fi->next_i].OffsetInOldFile;
531 *end = my_max(start + fi->ignore_table[fi->next_i].LengthInBytes, index);
532 start = my_max(start, index);
534 return start;
537 /* Get the next range to retain from the old file.
538 * fi->next_r must be initialized before use */
539 static ULONG next_retained_range_old(const struct input_file_info *fi, size_t index, ULONG old_file_size, ULONG *end)
541 ULONG start = old_file_size;
542 *end = old_file_size;
543 if (fi->next_r < fi->retain_range_count)
545 start = fi->retain_table[fi->next_r].OffsetInOldFile;
546 *end = my_max(start + fi->retain_table[fi->next_r].LengthInBytes, index);
547 start = my_max(start, index);
549 return start;
552 /* Get the next range to retain in the new file.
553 * fi->next_r must be initialized before use */
554 static ULONG next_retained_range_new(const struct input_file_info *fi, size_t index, ULONG new_file_size, ULONG *end)
556 ULONG start = new_file_size;
557 *end = new_file_size;
558 if (fi->next_r < fi->retain_range_count)
560 start = fi->retain_table[fi->next_r].OffsetInNewFile;
561 *end = my_max(start + fi->retain_table[fi->next_r].LengthInBytes, index);
562 start = my_max(start, index);
564 return start;
567 /* Find the next range in the old file which must be assumed zero-filled during crc32 calc
569 static ULONG next_zeroed_range(struct input_file_info *fi, size_t index, ULONG old_file_size, ULONG *end)
571 ULONG start = old_file_size;
572 ULONG end_i;
573 ULONG start_i;
574 ULONG end_r;
575 ULONG start_r;
577 *end = old_file_size;
579 start_i = next_ignored_range(fi, index, old_file_size, &end_i);
580 start_r = next_retained_range_old(fi, index, old_file_size, &end_r);
582 if (start_i < start_r)
584 start = start_i;
585 *end = end_i;
586 ++fi->next_i;
588 else
590 start = start_r;
591 *end = end_r;
592 ++fi->next_r;
594 return start;
597 /* Use the crc32 of the input file to match the file with an entry in the patch file table
599 struct input_file_info *find_matching_old_file(const struct patch_file_header *ph, const BYTE *old_file_view, ULONG old_file_size)
601 unsigned i;
603 for (i = 0; i < ph->input_file_count; ++i)
605 DWORD crc32 = 0;
606 ULONG index;
608 if (ph->file_table[i].input_size != old_file_size)
609 continue;
611 ph->file_table[i].next_i = 0;
612 for (index = 0; index < old_file_size; )
614 ULONG end;
615 ULONG start = next_zeroed_range(ph->file_table + i, index, old_file_size, &end);
616 crc32 = compute_crc32(crc32, old_file_view + index, start - index);
617 crc32 = compute_zero_crc32(crc32, end - start);
618 index = end;
620 if (ph->file_table[i].crc32 == crc32)
621 return ph->file_table + i;
623 return NULL;
626 /* Zero-fill ignored ranges in the old file data for decoder matching
628 static void zero_fill_ignored_ranges(BYTE *old_file_buf, const struct input_file_info *fi)
630 size_t i;
631 for (i = 0; i < fi->ignore_range_count; ++i)
633 memset(old_file_buf + fi->ignore_table[i].OffsetInOldFile,
635 fi->ignore_table[i].LengthInBytes);
639 /* Zero-fill retained ranges in the old file data for decoder matching
641 static void zero_fill_retained_ranges(BYTE *old_file_buf, BYTE *new_file_buf, const struct input_file_info *fi)
643 size_t i;
644 for (i = 0; i < fi->retain_range_count; ++i)
646 memset(old_file_buf + fi->retain_table[i].OffsetInOldFile,
648 fi->retain_table[i].LengthInBytes);
652 /* Copy the retained ranges to the new file buffer
654 static void apply_retained_ranges(const BYTE *old_file_buf, BYTE *new_file_buf, const struct input_file_info *fi)
656 size_t i;
658 if (old_file_buf == NULL)
659 return;
661 for (i = 0; i < fi->retain_range_count; ++i)
663 memcpy(new_file_buf + fi->retain_table[i].OffsetInNewFile,
664 old_file_buf + fi->retain_table[i].OffsetInOldFile,
665 fi->retain_table[i].LengthInBytes);
669 /* Compute the crc32 for the new file, assuming zero for the retained ranges
671 static DWORD compute_target_crc32(struct input_file_info *fi, const BYTE *new_file_buf, ULONG new_file_size)
673 DWORD crc32 = 0;
674 ULONG index;
676 qsort(fi->retain_table, fi->retain_range_count, sizeof(fi->retain_table[0]), compare_retained_range_new);
677 fi->next_r = 0;
679 for (index = 0; index < new_file_size; )
681 ULONG end;
682 ULONG start = next_retained_range_new(fi, index, new_file_size, &end);
683 ++fi->next_r;
684 crc32 = compute_crc32(crc32, new_file_buf + index, start - index);
685 crc32 = compute_zero_crc32(crc32, end - start);
686 index = end;
688 return crc32;
691 DWORD apply_patch_to_file_by_buffers(const BYTE *patch_file_view, const ULONG patch_file_size,
692 const BYTE *old_file_view, ULONG old_file_size,
693 BYTE **pnew_file_buf, const ULONG new_file_buf_size, ULONG *new_file_size,
694 FILETIME *new_file_time,
695 const ULONG apply_option_flags,
696 PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx,
697 const BOOL test_header_only)
699 DWORD err = ERROR_SUCCESS;
700 struct input_file_info *file_info;
701 struct patch_file_header ph;
702 size_t buf_size;
703 BYTE *new_file_buf = NULL;
704 BYTE *decode_buf = NULL;
706 if (pnew_file_buf == NULL)
708 if (!test_header_only && !(apply_option_flags & APPLY_OPTION_TEST_ONLY))
709 return ERROR_INVALID_PARAMETER;
711 else
713 new_file_buf = *pnew_file_buf;
716 if (old_file_view == NULL)
717 old_file_size = 0;
719 if (read_header(&ph, patch_file_view, patch_file_size))
721 err = ph.err;
722 goto free_patch_header;
725 if (new_file_size != NULL)
726 *new_file_size = (ULONG)ph.patched_size;
728 if (new_file_buf != NULL && new_file_buf_size < ph.patched_size)
730 err = ERROR_INSUFFICIENT_BUFFER;
731 goto free_patch_header;
734 file_info = find_matching_old_file(&ph, old_file_view, old_file_size);
735 if (file_info == NULL)
737 err = ERROR_PATCH_WRONG_FILE;
738 goto free_patch_header;
740 if (file_info->input_size != old_file_size)
742 err = ERROR_PATCH_CORRUPT;
743 goto free_patch_header;
745 if (file_info->stream_size == 0 && (apply_option_flags & APPLY_OPTION_FAIL_IF_EXACT))
747 err = ERROR_PATCH_NOT_NECESSARY;
748 goto free_patch_header;
750 if (file_info->stream_size != 0
751 && file_info->input_size > ((ph.flags & PATCH_OPTION_USE_LZX_LARGE) ? MAX_LARGE_WINDOW : MAX_NORMAL_WINDOW))
753 /* interleaved by default but not the same as PATCH_OPTION_INTERLEAVE_FILES */
754 FIXME("interleaved LZXD decompression is not supported.\n");
755 err = ERROR_PATCH_PACKAGE_UNSUPPORTED;
756 goto free_patch_header;
759 if (test_header_only)
760 goto free_patch_header;
762 /* missing lzxd stream means it's a header test extract */
763 if (file_info->stream_start + file_info->stream_size > ph.end)
765 err = ERROR_PATCH_NOT_AVAILABLE;
766 goto free_patch_header;
769 buf_size = old_file_size + ph.patched_size;
770 decode_buf = new_file_buf;
771 if (new_file_buf == NULL || new_file_buf_size < buf_size)
773 /* decode_buf must have room for both files, so allocate a new buffer if
774 * necessary. This will be returned to the caller if new_file_buf == NULL */
775 decode_buf = VirtualAlloc(NULL, buf_size, MEM_COMMIT, PAGE_READWRITE);
776 if (decode_buf == NULL)
778 err = GetLastError();
779 goto free_patch_header;
783 if (old_file_view != NULL)
784 memcpy(decode_buf, old_file_view, file_info->input_size);
786 zero_fill_ignored_ranges(decode_buf, file_info);
787 zero_fill_retained_ranges(decode_buf, decode_buf + file_info->input_size, file_info);
789 if (file_info->stream_size != 0)
791 err = decode_lzxd_stream(file_info->stream_start, file_info->stream_size,
792 decode_buf, ph.patched_size, file_info->input_size,
793 ph.flags & PATCH_OPTION_USE_LZX_LARGE,
794 progress_fn, progress_ctx);
796 else if (file_info->input_size == ph.patched_size)
798 /* files are identical so copy old to new. copying is avoidable but rare */
799 memcpy(decode_buf + file_info->input_size, decode_buf, ph.patched_size);
801 else
803 err = ERROR_PATCH_CORRUPT;
804 goto free_decode_buf;
807 if(err != ERROR_SUCCESS)
809 if (err == ERROR_PATCH_DECODE_FAILURE)
810 FIXME("decode failure: data corruption or bug.\n");
811 goto free_decode_buf;
814 apply_retained_ranges(old_file_view, decode_buf + file_info->input_size, file_info);
816 if (ph.patched_crc32 != compute_target_crc32(file_info, decode_buf + file_info->input_size, ph.patched_size))
818 err = ERROR_PATCH_CORRUPT;
819 goto free_decode_buf;
822 /* retained ranges must be ignored for this test */
823 if ((apply_option_flags & APPLY_OPTION_FAIL_IF_EXACT)
824 && file_info->input_size == ph.patched_size
825 && memcmp(decode_buf, decode_buf + file_info->input_size, ph.patched_size) == 0)
827 err = ERROR_PATCH_NOT_NECESSARY;
828 goto free_decode_buf;
831 if (!(apply_option_flags & APPLY_OPTION_TEST_ONLY))
833 if (new_file_buf == NULL)
835 /* caller will VirtualFree the buffer */
836 new_file_buf = decode_buf;
837 *pnew_file_buf = new_file_buf;
839 memmove(new_file_buf, decode_buf + old_file_size, ph.patched_size);
842 if (new_file_time != NULL)
844 new_file_time->dwLowDateTime = 0;
845 new_file_time->dwHighDateTime = 0;
847 /* the meaning of PATCH_OPTION_NO_TIMESTAMP is inverted for decoding */
848 if (ph.flags & PATCH_OPTION_NO_TIMESTAMP)
849 posix_time_to_file_time(ph.timestamp, new_file_time);
852 free_decode_buf:
853 if(decode_buf != NULL && decode_buf != new_file_buf)
854 VirtualFree(decode_buf, 0, MEM_RELEASE);
856 free_patch_header:
857 free_header(&ph);
859 return err;
862 BOOL apply_patch_to_file_by_handles(HANDLE patch_file_hndl, HANDLE old_file_hndl, HANDLE new_file_hndl,
863 const ULONG apply_option_flags,
864 PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx,
865 const BOOL test_header_only)
867 LARGE_INTEGER patch_size;
868 LARGE_INTEGER old_size;
869 HANDLE patch_map;
870 HANDLE old_map = NULL;
871 BYTE *patch_buf;
872 const BYTE *old_buf = NULL;
873 BYTE *new_buf = NULL;
874 ULONG new_size;
875 FILETIME new_time;
876 BOOL res = FALSE;
877 DWORD err = ERROR_SUCCESS;
879 /* truncate the output file if required, or set the handle to invalid */
880 if (test_header_only || (apply_option_flags & APPLY_OPTION_TEST_ONLY))
882 new_file_hndl = INVALID_HANDLE_VALUE;
884 else if (SetFilePointer(new_file_hndl, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER
885 || !SetEndOfFile(new_file_hndl))
887 err = GetLastError();
888 return FALSE;
891 if (patch_file_hndl == INVALID_HANDLE_VALUE)
893 SetLastError(ERROR_INVALID_HANDLE);
894 return FALSE;
897 old_size.QuadPart = 0;
898 if (!GetFileSizeEx(patch_file_hndl, &patch_size)
899 || (old_file_hndl != INVALID_HANDLE_VALUE && !GetFileSizeEx(old_file_hndl, &old_size)))
901 /* Last error set by API */
902 return FALSE;
905 patch_map = CreateFileMappingW(patch_file_hndl, NULL, PAGE_READONLY, 0, 0, NULL);
906 if (patch_map == NULL)
908 /* Last error set by API */
909 return FALSE;
912 if (old_file_hndl != INVALID_HANDLE_VALUE)
914 old_map = CreateFileMappingW(old_file_hndl, NULL, PAGE_READONLY, 0, 0, NULL);
915 if (old_map == NULL)
917 err = GetLastError();
918 goto close_patch_map;
922 patch_buf = MapViewOfFile(patch_map, FILE_MAP_READ, 0, 0, (SIZE_T)patch_size.QuadPart);
923 if (patch_buf == NULL)
925 err = GetLastError();
926 goto close_old_map;
929 if (old_size.QuadPart)
931 old_buf = MapViewOfFile(old_map, FILE_MAP_READ, 0, 0, (SIZE_T)old_size.QuadPart);
932 if (old_buf == NULL)
934 err = GetLastError();
935 goto unmap_patch_buf;
939 err = apply_patch_to_file_by_buffers(patch_buf, (ULONG)patch_size.QuadPart,
940 old_buf, (ULONG)old_size.QuadPart,
941 &new_buf, 0, &new_size,
942 &new_time,
943 apply_option_flags, progress_fn, progress_ctx,
944 test_header_only);
946 if(err)
947 goto free_new_buf;
949 res = TRUE;
951 if(new_file_hndl != INVALID_HANDLE_VALUE)
953 DWORD Written = 0;
954 res = WriteFile(new_file_hndl, new_buf, new_size, &Written, NULL);
956 if (!res)
957 err = GetLastError();
958 else if (new_time.dwLowDateTime || new_time.dwHighDateTime)
959 SetFileTime(new_file_hndl, &new_time, NULL, &new_time);
962 free_new_buf:
963 if (new_buf != NULL)
964 VirtualFree(new_buf, 0, MEM_RELEASE);
966 if (old_buf != NULL)
967 UnmapViewOfFile(old_buf);
969 unmap_patch_buf:
970 UnmapViewOfFile(patch_buf);
972 close_old_map:
973 if (old_map != NULL)
974 CloseHandle(old_map);
976 close_patch_map:
977 CloseHandle(patch_map);
979 SetLastError(err);
981 return res;
984 BOOL apply_patch_to_file(LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR new_file_name,
985 const ULONG apply_option_flags,
986 PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx,
987 const BOOL test_header_only)
989 HANDLE patch_hndl;
990 HANDLE old_hndl = INVALID_HANDLE_VALUE;
991 HANDLE new_hndl = INVALID_HANDLE_VALUE;
992 BOOL res = FALSE;
993 DWORD err = ERROR_SUCCESS;
995 patch_hndl = CreateFileW(patch_file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
996 if (patch_hndl == INVALID_HANDLE_VALUE)
998 /* last error set by CreateFileW */
999 return FALSE;
1002 if (old_file_name != NULL)
1004 old_hndl = CreateFileW(old_file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1005 if (old_hndl == INVALID_HANDLE_VALUE)
1007 err = GetLastError();
1008 goto close_patch_file;
1012 if (!test_header_only && !(apply_option_flags & APPLY_OPTION_TEST_ONLY))
1014 new_hndl = CreateFileW(new_file_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1015 if (new_hndl == INVALID_HANDLE_VALUE)
1017 err = GetLastError();
1018 goto close_old_file;
1022 res = apply_patch_to_file_by_handles(patch_hndl, old_hndl, new_hndl, apply_option_flags, progress_fn, progress_ctx, test_header_only);
1023 if(!res)
1024 err = GetLastError();
1026 if (new_hndl != INVALID_HANDLE_VALUE)
1028 CloseHandle(new_hndl);
1029 if (!res)
1030 DeleteFileW(new_file_name);
1033 close_old_file:
1034 if (old_hndl != INVALID_HANDLE_VALUE)
1035 CloseHandle(old_hndl);
1037 close_patch_file:
1038 CloseHandle(patch_hndl);
1040 /* set last error even on success as per windows */
1041 SetLastError(err);
1043 return res;