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
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.
40 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(mspatcha
);
49 #define PA19_FILE_MAGIC 0x39314150
50 #define PATCH_OPTION_EXTRA_FLAGS 0x80000000
52 static UINT32
compute_zero_crc32(UINT32 crc
, INT_PTR len
)
54 static const BYTE zero_buffer
[1024];
58 crc
= RtlComputeCrc32(crc
, zero_buffer
, min(len
, sizeof(zero_buffer
)));
59 len
-= min(len
, sizeof(zero_buffer
));
64 /***********************************************************************************
65 * PatchAPI PA19 file header
69 * UINT32 options_2; (present if PATCH_OPTION_EXTRA_FLAGS set)
70 * UINT32 timestamp; (if PATCH_OPTION_NO_TIMESTAMP is SET in options)
71 * UVLI rebase; (present if PATCH_OPTION_NO_REBASE is not set; used on 32-bit executables)
72 * UVLI unpatched_size;
73 * UINT32 crc32_patched;
74 * BYTE input_file_count;
76 * For each source file:
77 * SVLI (patched_size - unpatched_size);
78 * UINT32 crc32_unpatched;
79 * BYTE ignore_range_count;
80 * For each ignore range:
81 * SVLI OffsetInOldFile;
83 * BYTE retain_range_count;
84 * For each retain range:
85 * SVLI (OffsetInOldFile - (prevOffsetInOldFile + prevLengthInBytes));
86 * SVLI (OffsetInNewFile - OffsetInOldFile);
88 * UVLI unknown_count; (a count of pairs of values related to the reversal of special
89 * processing done to improve compression of 32-bit executables)
90 * UVLI interleave_count; (present only if PATCH_OPTION_INTERLEAVE_FILES is set in options)
91 * UVLI interleave_values[interleave_count * 3 - 1];
92 * UVLI lzxd_input_size;
94 * For each source file:
95 * UINT16 lzxd_block[lzxd_input_size / 2]; (NOT always 16-bit aligned)
97 * UINT32 crc_hack; (rounds out the entire file crc32 to 0)
101 #define MAX_RANGES 255
103 struct input_file_info
{
106 BYTE ignore_range_count
;
107 BYTE retain_range_count
;
108 PATCH_IGNORE_RANGE ignore_table
[MAX_RANGES
];
109 PATCH_RETAIN_RANGE retain_table
[MAX_RANGES
];
110 size_t unknown_count
;
112 const BYTE
*stream_start
;
117 struct patch_file_header
{
122 unsigned input_file_count
;
123 struct input_file_info
*file_table
;
130 /* Currently supported options. Some such as PATCH_OPTION_FAIL_IF_BIGGER don't
131 * affect decoding but can get recorded in the patch file anyway */
132 #define PATCH_OPTION_SUPPORTED_FLAGS ( \
133 PATCH_OPTION_USE_LZX_A \
134 | PATCH_OPTION_USE_LZX_B \
135 | PATCH_OPTION_USE_LZX_LARGE \
136 | PATCH_OPTION_NO_BINDFIX \
137 | PATCH_OPTION_NO_LOCKFIX \
138 | PATCH_OPTION_NO_REBASE \
139 | PATCH_OPTION_FAIL_IF_SAME_FILE \
140 | PATCH_OPTION_FAIL_IF_BIGGER \
141 | PATCH_OPTION_NO_CHECKSUM \
142 | PATCH_OPTION_NO_RESTIMEFIX \
143 | PATCH_OPTION_NO_TIMESTAMP \
144 | PATCH_OPTION_EXTRA_FLAGS)
147 /* read a byte-aligned little-endian UINT32 from input and set error if eof
149 static inline UINT32
read_raw_uint32(struct patch_file_header
*ph
)
151 const BYTE
*src
= ph
->src
;
154 if (ph
->src
> ph
->end
)
156 ph
->err
= ERROR_PATCH_CORRUPT
;
165 /* Read a variable-length integer from a sequence of bytes terminated by
166 * a value with bit 7 set. Set error if invalid or eof */
167 static UINT64
read_uvli(struct patch_file_header
*ph
)
169 const BYTE
*vli
= ph
->src
;
172 ptrdiff_t limit
= min(ph
->end
- vli
, 9);
174 if (ph
->src
>= ph
->end
)
176 ph
->err
= ERROR_PATCH_CORRUPT
;
181 for (i
= 1; i
< limit
&& vli
[i
- 1] < 0x80; ++i
)
182 n
+= (UINT64
)(vli
[i
] & 0x7F) << (7 * i
);
184 if (vli
[i
- 1] < 0x80)
186 TRACE("exceeded maximum vli size\n");
187 ph
->err
= ERROR_PATCH_CORRUPT
;
196 /* Signed variant of the above. First byte sign flag is 0x40.
198 static INT64
read_svli(struct patch_file_header
*ph
)
200 const BYTE
*vli
= ph
->src
;
203 ptrdiff_t limit
= min(ph
->end
- vli
, 9);
205 if (ph
->src
>= ph
->end
)
207 ph
->err
= ERROR_PATCH_CORRUPT
;
212 for (i
= 1; i
< limit
&& vli
[i
- 1] < 0x80; ++i
)
213 n
+= (INT64
)(vli
[i
] & 0x7F) << (7 * i
- 1);
215 if (vli
[i
- 1] < 0x80)
217 TRACE("exceeded maximum vli size\n");
218 ph
->err
= ERROR_PATCH_CORRUPT
;
230 static int __cdecl
compare_ignored_range(const void *a
, const void *b
)
232 LONG delta
= ((PATCH_IGNORE_RANGE
*)a
)->OffsetInOldFile
- ((PATCH_IGNORE_RANGE
*)b
)->OffsetInOldFile
;
240 static int __cdecl
compare_retained_range_old(const void *a
, const void *b
)
242 LONG delta
= ((PATCH_RETAIN_RANGE
*)a
)->OffsetInOldFile
- ((PATCH_RETAIN_RANGE
*)b
)->OffsetInOldFile
;
250 static int __cdecl
compare_retained_range_new(const void *a
, const void *b
)
252 LONG delta
= ((PATCH_RETAIN_RANGE
*)a
)->OffsetInNewFile
- ((PATCH_RETAIN_RANGE
*)b
)->OffsetInNewFile
;
260 static int read_header(struct patch_file_header
*ph
, const BYTE
*buf
, size_t size
)
265 ph
->end
= buf
+ size
;
267 ph
->file_table
= NULL
;
268 ph
->err
= ERROR_SUCCESS
;
270 if (read_raw_uint32(ph
) != PA19_FILE_MAGIC
)
272 TRACE("no PA19 signature\n");
273 ph
->err
= ERROR_PATCH_CORRUPT
;
277 ph
->flags
= read_raw_uint32(ph
);
278 if ((ph
->flags
& PATCH_OPTION_SUPPORTED_FLAGS
) != ph
->flags
)
280 FIXME("unsupported option flag(s): 0x%08lx\n", ph
->flags
& ~PATCH_OPTION_SUPPORTED_FLAGS
);
281 ph
->err
= ERROR_PATCH_PACKAGE_UNSUPPORTED
;
285 /* additional 32-bit flag field */
286 if (ph
->flags
& PATCH_OPTION_EXTRA_FLAGS
)
288 TRACE("skipping extra flag field\n");
289 (void)read_raw_uint32(ph
);
292 /* the meaning of PATCH_OPTION_NO_TIMESTAMP is inverted for decoding */
293 if(ph
->flags
& PATCH_OPTION_NO_TIMESTAMP
)
294 ph
->timestamp
= read_raw_uint32(ph
);
296 /* not sure what this value is for, but its absence seems to mean only that timestamps
297 * in the decompressed 32-bit exe are not modified */
298 if (!(ph
->flags
& PATCH_OPTION_NO_REBASE
))
300 TRACE("skipping rebase field\n");
304 ph
->patched_size
= (size_t)read_uvli(ph
);
305 TRACE("patched file size will be %u\n", (unsigned)ph
->patched_size
);
306 ph
->patched_crc32
= read_raw_uint32(ph
);
308 ph
->input_file_count
= *ph
->src
;
310 TRACE("patch supports %u old file(s)\n", ph
->input_file_count
);
311 /* if no old file used, input_file_count is still 1 */
312 if (ph
->input_file_count
== 0)
314 ph
->err
= ERROR_PATCH_CORRUPT
;
318 if (ph
->err
!= ERROR_SUCCESS
)
321 ph
->file_table
= calloc(ph
->input_file_count
, sizeof(struct input_file_info
));
322 if (ph
->file_table
== NULL
)
324 ph
->err
= ERROR_OUTOFMEMORY
;
328 for (fileno
= 0; fileno
< ph
->input_file_count
; ++fileno
) {
329 struct input_file_info
*fi
= ph
->file_table
+ fileno
;
333 delta
= (ptrdiff_t)read_svli(ph
);
334 fi
->input_size
= ph
->patched_size
+ delta
;
336 fi
->crc32
= read_raw_uint32(ph
);
338 fi
->ignore_range_count
= *ph
->src
;
340 TRACE("found %u range(s) to ignore\n", fi
->ignore_range_count
);
342 for (i
= 0; i
< fi
->ignore_range_count
; ++i
) {
343 PATCH_IGNORE_RANGE
*ir
= fi
->ignore_table
+ i
;
345 ir
->OffsetInOldFile
= (LONG
)read_svli(ph
);
346 ir
->LengthInBytes
= (ULONG
)read_uvli(ph
);
350 ir
->OffsetInOldFile
+= fi
->ignore_table
[i
- 1].OffsetInOldFile
351 + fi
->ignore_table
[i
- 1].LengthInBytes
;
353 if (ir
->OffsetInOldFile
> fi
->input_size
354 || ir
->OffsetInOldFile
+ ir
->LengthInBytes
> fi
->input_size
355 || ir
->LengthInBytes
> fi
->input_size
)
357 ph
->err
= ERROR_PATCH_CORRUPT
;
362 fi
->retain_range_count
= *ph
->src
;
364 TRACE("found %u range(s) to retain\n", fi
->retain_range_count
);
366 for (i
= 0; i
< fi
->retain_range_count
; ++i
) {
367 PATCH_RETAIN_RANGE
*rr
= fi
->retain_table
+ i
;
369 rr
->OffsetInOldFile
= (LONG
)read_svli(ph
);
371 rr
->OffsetInOldFile
+=
372 fi
->retain_table
[i
- 1].OffsetInOldFile
+ fi
->retain_table
[i
- 1].LengthInBytes
;
374 rr
->OffsetInNewFile
= rr
->OffsetInOldFile
+ (LONG
)read_svli(ph
);
375 rr
->LengthInBytes
= (ULONG
)read_uvli(ph
);
377 if (rr
->OffsetInOldFile
> fi
->input_size
378 || rr
->OffsetInOldFile
+ rr
->LengthInBytes
> fi
->input_size
379 || rr
->OffsetInNewFile
> ph
->patched_size
380 || rr
->OffsetInNewFile
+ rr
->LengthInBytes
> ph
->patched_size
381 || rr
->LengthInBytes
> ph
->patched_size
)
383 ph
->err
= ERROR_PATCH_CORRUPT
;
387 /* ranges in new file must be equal and in the same order for all source files */
390 PATCH_RETAIN_RANGE
*rr_0
= ph
->file_table
[0].retain_table
+ i
;
391 if (rr
->OffsetInNewFile
!= rr_0
->OffsetInNewFile
392 || rr
->LengthInBytes
!= rr_0
->LengthInBytes
)
394 ph
->err
= ERROR_PATCH_CORRUPT
;
400 fi
->unknown_count
= (size_t)read_uvli(ph
);
401 if (fi
->unknown_count
)
403 FIXME("special processing of 32-bit executables not implemented.\n");
404 ph
->err
= ERROR_PATCH_PACKAGE_UNSUPPORTED
;
407 fi
->stream_size
= (size_t)read_uvli(ph
);
410 for (fileno
= 0; fileno
< ph
->input_file_count
; ++fileno
)
412 struct input_file_info
*fi
= ph
->file_table
+ fileno
;
414 qsort(fi
->ignore_table
, fi
->ignore_range_count
, sizeof(fi
->ignore_table
[0]), compare_ignored_range
);
415 qsort(fi
->retain_table
, fi
->retain_range_count
, sizeof(fi
->retain_table
[0]), compare_retained_range_old
);
417 fi
->stream_start
= ph
->src
;
418 ph
->src
+= fi
->stream_size
;
421 /* skip the crc adjustment field */
422 ph
->src
= min(ph
->src
+ 4, ph
->end
);
425 UINT32 crc
= RtlComputeCrc32(0, buf
, ph
->src
- buf
) ^ 0xFFFFFFFF;
428 TRACE("patch file crc32 failed\n");
429 if (ph
->src
< ph
->end
)
430 FIXME("probable header parsing error\n");
431 ph
->err
= ERROR_PATCH_CORRUPT
;
435 return (ph
->err
== ERROR_SUCCESS
) ? 0 : -1;
438 #define TICKS_PER_SEC 10000000
439 #define SEC_TO_UNIX_EPOCH ((369 * 365 + 89) * (ULONGLONG)86400)
441 static void posix_time_to_file_time(ULONG timestamp
, FILETIME
*ft
)
443 UINT64 ticks
= ((UINT64
)timestamp
+ SEC_TO_UNIX_EPOCH
) * TICKS_PER_SEC
;
444 ft
->dwLowDateTime
= (DWORD
)ticks
;
445 ft
->dwHighDateTime
= (DWORD
)(ticks
>> 32);
448 /* Get the next range to ignore in the old file.
449 * fi->next_i must be initialized before use */
450 static ULONG
next_ignored_range(const struct input_file_info
*fi
, size_t index
, ULONG old_file_size
, ULONG
*end
)
452 ULONG start
= old_file_size
;
453 *end
= old_file_size
;
454 /* if patching is unnecessary, the ignored ranges are skipped during crc calc */
455 if (fi
->next_i
< fi
->ignore_range_count
&& fi
->stream_size
!= 0)
457 start
= fi
->ignore_table
[fi
->next_i
].OffsetInOldFile
;
458 *end
= max(start
+ fi
->ignore_table
[fi
->next_i
].LengthInBytes
, index
);
459 start
= max(start
, index
);
464 /* Get the next range to retain from the old file.
465 * fi->next_r must be initialized before use */
466 static ULONG
next_retained_range_old(const struct input_file_info
*fi
, size_t index
, ULONG old_file_size
, ULONG
*end
)
468 ULONG start
= old_file_size
;
469 *end
= old_file_size
;
470 if (fi
->next_r
< fi
->retain_range_count
)
472 start
= fi
->retain_table
[fi
->next_r
].OffsetInOldFile
;
473 *end
= max(start
+ fi
->retain_table
[fi
->next_r
].LengthInBytes
, index
);
474 start
= max(start
, index
);
479 /* Get the next range to retain in the new file.
480 * fi->next_r must be initialized before use */
481 static ULONG
next_retained_range_new(const struct input_file_info
*fi
, size_t index
, ULONG new_file_size
, ULONG
*end
)
483 ULONG start
= new_file_size
;
484 *end
= new_file_size
;
485 if (fi
->next_r
< fi
->retain_range_count
)
487 start
= fi
->retain_table
[fi
->next_r
].OffsetInNewFile
;
488 *end
= max(start
+ fi
->retain_table
[fi
->next_r
].LengthInBytes
, index
);
489 start
= max(start
, index
);
494 /* Find the next range in the old file which must be assumed zero-filled during crc32 calc
496 static ULONG
next_zeroed_range(struct input_file_info
*fi
, size_t index
, ULONG old_file_size
, ULONG
*end
)
498 ULONG start
= old_file_size
;
504 *end
= old_file_size
;
506 start_i
= next_ignored_range(fi
, index
, old_file_size
, &end_i
);
507 start_r
= next_retained_range_old(fi
, index
, old_file_size
, &end_r
);
509 if (start_i
< start_r
)
524 /* Use the crc32 of the input file to match the file with an entry in the patch file table
526 struct input_file_info
*find_matching_old_file(const struct patch_file_header
*ph
, const BYTE
*old_file_view
, ULONG old_file_size
)
530 for (i
= 0; i
< ph
->input_file_count
; ++i
)
535 if (ph
->file_table
[i
].input_size
!= old_file_size
)
538 ph
->file_table
[i
].next_i
= 0;
539 for (index
= 0; index
< old_file_size
; )
542 ULONG start
= next_zeroed_range(ph
->file_table
+ i
, index
, old_file_size
, &end
);
543 crc32
= RtlComputeCrc32(crc32
, old_file_view
+ index
, start
- index
);
544 crc32
= compute_zero_crc32(crc32
, end
- start
);
547 if (ph
->file_table
[i
].crc32
== crc32
)
548 return ph
->file_table
+ i
;
553 /* Zero-fill ignored ranges in the old file data for decoder matching
555 static void zero_fill_ignored_ranges(BYTE
*old_file_buf
, const struct input_file_info
*fi
)
558 for (i
= 0; i
< fi
->ignore_range_count
; ++i
)
560 memset(old_file_buf
+ fi
->ignore_table
[i
].OffsetInOldFile
,
562 fi
->ignore_table
[i
].LengthInBytes
);
566 /* Zero-fill retained ranges in the old file data for decoder matching
568 static void zero_fill_retained_ranges(BYTE
*old_file_buf
, BYTE
*new_file_buf
, const struct input_file_info
*fi
)
571 for (i
= 0; i
< fi
->retain_range_count
; ++i
)
573 memset(old_file_buf
+ fi
->retain_table
[i
].OffsetInOldFile
,
575 fi
->retain_table
[i
].LengthInBytes
);
579 /* Copy the retained ranges to the new file buffer
581 static void apply_retained_ranges(const BYTE
*old_file_buf
, BYTE
*new_file_buf
, const struct input_file_info
*fi
)
585 if (old_file_buf
== NULL
)
588 for (i
= 0; i
< fi
->retain_range_count
; ++i
)
590 memcpy(new_file_buf
+ fi
->retain_table
[i
].OffsetInNewFile
,
591 old_file_buf
+ fi
->retain_table
[i
].OffsetInOldFile
,
592 fi
->retain_table
[i
].LengthInBytes
);
596 /* Compute the crc32 for the new file, assuming zero for the retained ranges
598 static DWORD
compute_target_crc32(struct input_file_info
*fi
, const BYTE
*new_file_buf
, ULONG new_file_size
)
603 qsort(fi
->retain_table
, fi
->retain_range_count
, sizeof(fi
->retain_table
[0]), compare_retained_range_new
);
606 for (index
= 0; index
< new_file_size
; )
609 ULONG start
= next_retained_range_new(fi
, index
, new_file_size
, &end
);
611 crc32
= RtlComputeCrc32(crc32
, new_file_buf
+ index
, start
- index
);
612 crc32
= compute_zero_crc32(crc32
, end
- start
);
618 DWORD
apply_patch_to_file_by_buffers(const BYTE
*patch_file_view
, const ULONG patch_file_size
,
619 const BYTE
*old_file_view
, ULONG old_file_size
,
620 BYTE
**pnew_file_buf
, const ULONG new_file_buf_size
, ULONG
*new_file_size
,
621 FILETIME
*new_file_time
,
622 const ULONG apply_option_flags
,
623 PATCH_PROGRESS_CALLBACK
*progress_fn
, void *progress_ctx
,
624 const BOOL test_header_only
)
626 DWORD err
= ERROR_SUCCESS
;
627 struct input_file_info
*file_info
;
628 struct patch_file_header ph
;
630 BYTE
*new_file_buf
= NULL
;
631 BYTE
*decode_buf
= NULL
;
633 if (pnew_file_buf
== NULL
)
635 if (!test_header_only
&& !(apply_option_flags
& APPLY_OPTION_TEST_ONLY
))
636 return ERROR_INVALID_PARAMETER
;
640 new_file_buf
= *pnew_file_buf
;
643 if (old_file_view
== NULL
)
646 if (read_header(&ph
, patch_file_view
, patch_file_size
))
649 goto free_patch_header
;
652 if (new_file_size
!= NULL
)
653 *new_file_size
= (ULONG
)ph
.patched_size
;
655 if (new_file_buf
!= NULL
&& new_file_buf_size
< ph
.patched_size
)
657 err
= ERROR_INSUFFICIENT_BUFFER
;
658 goto free_patch_header
;
661 file_info
= find_matching_old_file(&ph
, old_file_view
, old_file_size
);
662 if (file_info
== NULL
)
664 err
= ERROR_PATCH_WRONG_FILE
;
665 goto free_patch_header
;
667 if (file_info
->input_size
!= old_file_size
)
669 err
= ERROR_PATCH_CORRUPT
;
670 goto free_patch_header
;
672 if (file_info
->stream_size
== 0 && (apply_option_flags
& APPLY_OPTION_FAIL_IF_EXACT
))
674 err
= ERROR_PATCH_NOT_NECESSARY
;
675 goto free_patch_header
;
677 if (file_info
->stream_size
!= 0
678 && file_info
->input_size
> ((ph
.flags
& PATCH_OPTION_USE_LZX_LARGE
) ? MAX_LARGE_WINDOW
: MAX_NORMAL_WINDOW
))
680 /* interleaved by default but not the same as PATCH_OPTION_INTERLEAVE_FILES */
681 FIXME("interleaved LZXD decompression is not supported.\n");
682 err
= ERROR_PATCH_PACKAGE_UNSUPPORTED
;
683 goto free_patch_header
;
686 if (test_header_only
)
687 goto free_patch_header
;
689 /* missing lzxd stream means it's a header test extract */
690 if (file_info
->stream_start
+ file_info
->stream_size
> ph
.end
)
692 err
= ERROR_PATCH_NOT_AVAILABLE
;
693 goto free_patch_header
;
696 buf_size
= old_file_size
+ ph
.patched_size
;
697 decode_buf
= new_file_buf
;
698 if (new_file_buf
== NULL
|| new_file_buf_size
< buf_size
)
700 /* decode_buf must have room for both files, so allocate a new buffer if
701 * necessary. This will be returned to the caller if new_file_buf == NULL */
702 decode_buf
= VirtualAlloc(NULL
, buf_size
, MEM_COMMIT
, PAGE_READWRITE
);
703 if (decode_buf
== NULL
)
705 err
= GetLastError();
706 goto free_patch_header
;
710 if (old_file_view
!= NULL
)
711 memcpy(decode_buf
, old_file_view
, file_info
->input_size
);
713 zero_fill_ignored_ranges(decode_buf
, file_info
);
714 zero_fill_retained_ranges(decode_buf
, decode_buf
+ file_info
->input_size
, file_info
);
716 if (file_info
->stream_size
!= 0)
718 err
= decode_lzxd_stream(file_info
->stream_start
, file_info
->stream_size
,
719 decode_buf
, ph
.patched_size
, file_info
->input_size
,
720 ph
.flags
& PATCH_OPTION_USE_LZX_LARGE
,
721 progress_fn
, progress_ctx
);
723 else if (file_info
->input_size
== ph
.patched_size
)
725 /* files are identical so copy old to new. copying is avoidable but rare */
726 memcpy(decode_buf
+ file_info
->input_size
, decode_buf
, ph
.patched_size
);
730 err
= ERROR_PATCH_CORRUPT
;
731 goto free_decode_buf
;
734 if(err
!= ERROR_SUCCESS
)
736 if (err
== ERROR_PATCH_DECODE_FAILURE
)
737 FIXME("decode failure: data corruption or bug.\n");
738 goto free_decode_buf
;
741 apply_retained_ranges(old_file_view
, decode_buf
+ file_info
->input_size
, file_info
);
743 if (ph
.patched_crc32
!= compute_target_crc32(file_info
, decode_buf
+ file_info
->input_size
, ph
.patched_size
))
745 err
= ERROR_PATCH_CORRUPT
;
746 goto free_decode_buf
;
749 /* retained ranges must be ignored for this test */
750 if ((apply_option_flags
& APPLY_OPTION_FAIL_IF_EXACT
)
751 && file_info
->input_size
== ph
.patched_size
752 && memcmp(decode_buf
, decode_buf
+ file_info
->input_size
, ph
.patched_size
) == 0)
754 err
= ERROR_PATCH_NOT_NECESSARY
;
755 goto free_decode_buf
;
758 if (!(apply_option_flags
& APPLY_OPTION_TEST_ONLY
))
760 if (new_file_buf
== NULL
)
762 /* caller will VirtualFree the buffer */
763 new_file_buf
= decode_buf
;
764 *pnew_file_buf
= new_file_buf
;
766 memmove(new_file_buf
, decode_buf
+ old_file_size
, ph
.patched_size
);
769 if (new_file_time
!= NULL
)
771 new_file_time
->dwLowDateTime
= 0;
772 new_file_time
->dwHighDateTime
= 0;
774 /* the meaning of PATCH_OPTION_NO_TIMESTAMP is inverted for decoding */
775 if (ph
.flags
& PATCH_OPTION_NO_TIMESTAMP
)
776 posix_time_to_file_time(ph
.timestamp
, new_file_time
);
780 if(decode_buf
!= NULL
&& decode_buf
!= new_file_buf
)
781 VirtualFree(decode_buf
, 0, MEM_RELEASE
);
789 BOOL
apply_patch_to_file_by_handles(HANDLE patch_file_hndl
, HANDLE old_file_hndl
, HANDLE new_file_hndl
,
790 const ULONG apply_option_flags
,
791 PATCH_PROGRESS_CALLBACK
*progress_fn
, void *progress_ctx
,
792 const BOOL test_header_only
)
794 LARGE_INTEGER patch_size
;
795 LARGE_INTEGER old_size
;
797 HANDLE old_map
= NULL
;
799 const BYTE
*old_buf
= NULL
;
800 BYTE
*new_buf
= NULL
;
804 DWORD err
= ERROR_SUCCESS
;
806 /* truncate the output file if required, or set the handle to invalid */
807 if (test_header_only
|| (apply_option_flags
& APPLY_OPTION_TEST_ONLY
))
809 new_file_hndl
= INVALID_HANDLE_VALUE
;
811 else if (SetFilePointer(new_file_hndl
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
812 || !SetEndOfFile(new_file_hndl
))
814 err
= GetLastError();
818 if (patch_file_hndl
== INVALID_HANDLE_VALUE
)
820 SetLastError(ERROR_INVALID_HANDLE
);
824 old_size
.QuadPart
= 0;
825 if (!GetFileSizeEx(patch_file_hndl
, &patch_size
)
826 || (old_file_hndl
!= INVALID_HANDLE_VALUE
&& !GetFileSizeEx(old_file_hndl
, &old_size
)))
828 /* Last error set by API */
832 patch_map
= CreateFileMappingW(patch_file_hndl
, NULL
, PAGE_READONLY
, 0, 0, NULL
);
833 if (patch_map
== NULL
)
835 /* Last error set by API */
839 if (old_file_hndl
!= INVALID_HANDLE_VALUE
)
841 old_map
= CreateFileMappingW(old_file_hndl
, NULL
, PAGE_READONLY
, 0, 0, NULL
);
844 err
= GetLastError();
845 goto close_patch_map
;
849 patch_buf
= MapViewOfFile(patch_map
, FILE_MAP_READ
, 0, 0, (SIZE_T
)patch_size
.QuadPart
);
850 if (patch_buf
== NULL
)
852 err
= GetLastError();
856 if (old_size
.QuadPart
)
858 old_buf
= MapViewOfFile(old_map
, FILE_MAP_READ
, 0, 0, (SIZE_T
)old_size
.QuadPart
);
861 err
= GetLastError();
862 goto unmap_patch_buf
;
866 err
= apply_patch_to_file_by_buffers(patch_buf
, (ULONG
)patch_size
.QuadPart
,
867 old_buf
, (ULONG
)old_size
.QuadPart
,
868 &new_buf
, 0, &new_size
,
870 apply_option_flags
, progress_fn
, progress_ctx
,
878 if(new_file_hndl
!= INVALID_HANDLE_VALUE
)
881 res
= WriteFile(new_file_hndl
, new_buf
, new_size
, &Written
, NULL
);
884 err
= GetLastError();
885 else if (new_time
.dwLowDateTime
|| new_time
.dwHighDateTime
)
886 SetFileTime(new_file_hndl
, &new_time
, NULL
, &new_time
);
891 VirtualFree(new_buf
, 0, MEM_RELEASE
);
894 UnmapViewOfFile(old_buf
);
897 UnmapViewOfFile(patch_buf
);
901 CloseHandle(old_map
);
904 CloseHandle(patch_map
);
911 BOOL
apply_patch_to_file(LPCWSTR patch_file_name
, LPCWSTR old_file_name
, LPCWSTR new_file_name
,
912 const ULONG apply_option_flags
,
913 PATCH_PROGRESS_CALLBACK
*progress_fn
, void *progress_ctx
,
914 const BOOL test_header_only
)
917 HANDLE old_hndl
= INVALID_HANDLE_VALUE
;
918 HANDLE new_hndl
= INVALID_HANDLE_VALUE
;
920 DWORD err
= ERROR_SUCCESS
;
922 patch_hndl
= CreateFileW(patch_file_name
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
923 if (patch_hndl
== INVALID_HANDLE_VALUE
)
925 /* last error set by CreateFileW */
929 if (old_file_name
!= NULL
)
931 old_hndl
= CreateFileW(old_file_name
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
932 if (old_hndl
== INVALID_HANDLE_VALUE
)
934 err
= GetLastError();
935 goto close_patch_file
;
939 if (!test_header_only
&& !(apply_option_flags
& APPLY_OPTION_TEST_ONLY
))
941 new_hndl
= CreateFileW(new_file_name
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
942 if (new_hndl
== INVALID_HANDLE_VALUE
)
944 err
= GetLastError();
949 res
= apply_patch_to_file_by_handles(patch_hndl
, old_hndl
, new_hndl
, apply_option_flags
, progress_fn
, progress_ctx
, test_header_only
);
951 err
= GetLastError();
953 if (new_hndl
!= INVALID_HANDLE_VALUE
)
955 CloseHandle(new_hndl
);
957 DeleteFileW(new_file_name
);
961 if (old_hndl
!= INVALID_HANDLE_VALUE
)
962 CloseHandle(old_hndl
);
965 CloseHandle(patch_hndl
);
967 /* set last error even on success as per windows */