1 /*
2 * PatchAPI
3 *
4 * Copyright 2011 David Hedberg for CodeWeavers
5 * Copyright 2018 Mark Jansen (mark.jansen@reactos.org)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #define WIN32_NO_STATUS
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winnls.h"
26 #include "ndk/rtlfuncs.h"
27 #include "patchapi.h"
28 #include "lzx.h"
29 #include "wine/debug.h"
30
31 static const char szHexString[] = "0123456789abcdef";
32 #define SIGNATURE_MIN_SIZE 9
33
34 #define UNKNOWN_FLAGS_COMBINATION 0x00c40001
35
36
37 WINE_DEFAULT_DEBUG_CHANNEL(mspatcha);
38
39
40 typedef struct _SAFE_READ
41 {
42 PBYTE Root;
43 DWORD Size;
44 PBYTE Ptr;
45 } SAFE_READ, *PSAFE_READ;
46
47
48 /**
49 * @name ReadByte
50 * Read the next byte available from @param pRead
51 *
52 * @param pRead
53 * The input buffer
54 *
55 * @return The byte, or 0
56 */
ReadByte(PSAFE_READ pRead)57 BYTE ReadByte(PSAFE_READ pRead)
58 {
59 if (pRead->Ptr + sizeof(BYTE) <= (pRead->Root + pRead->Size))
60 {
61 BYTE Value = *(PBYTE)pRead->Ptr;
62 pRead->Ptr += sizeof(BYTE);
63 return Value;
64 }
65 pRead->Ptr = pRead->Root + pRead->Size;
66 return 0;
67 }
68
69 /**
70 * @name ReadUShort
71 * Read the next unsigned short available from @param pRead
72 *
73 * @param pRead
74 * The input buffer
75 *
76 * @return The unsigned short, or 0
77 */
ReadUShort(PSAFE_READ pRead)78 USHORT ReadUShort(PSAFE_READ pRead)
79 {
80 if (pRead->Ptr + sizeof(USHORT) <= (pRead->Root + pRead->Size))
81 {
82 USHORT Value = *(PUSHORT)pRead->Ptr;
83 pRead->Ptr += sizeof(USHORT);
84 return Value;
85 }
86 pRead->Ptr = pRead->Root + pRead->Size;
87 return 0;
88 }
89
90 /**
91 * @name ReadDWord
92 * Read the next dword available from @param pRead
93 *
94 * @param pRead
95 * The input buffer
96 *
97 * @return The dword, or 0
98 */
ReadDWord(PSAFE_READ pRead)99 DWORD ReadDWord(PSAFE_READ pRead)
100 {
101 if (pRead->Ptr + sizeof(DWORD) <= (pRead->Root + pRead->Size))
102 {
103 DWORD Value = *(PDWORD)pRead->Ptr;
104 pRead->Ptr += sizeof(DWORD);
105 return Value;
106 }
107 pRead->Ptr = pRead->Root + pRead->Size;
108 return 0;
109 }
110
111 /**
112 * @name DecodeDWord
113 * Read the next variable length-encoded dword from @param pRead
114 *
115 * @param pRead
116 * The input buffer
117 *
118 * @return The dword, or 0
119 */
DecodeDWord(PSAFE_READ pRead)120 DWORD DecodeDWord(PSAFE_READ pRead)
121 {
122 UINT Result = 0, offset;
123
124 for (offset = 0; offset < 32; offset += 7)
125 {
126 DWORD val = ReadByte(pRead);
127 Result |= ((val & 0x7f) << offset);
128 if (val & 0x80)
129 break;
130 }
131
132 return Result;
133 }
134
135
136 /**
137 * @name DecodeInt
138 * Read the next variable length-encoded int from @param pRead
139 *
140 * @param pRead
141 * The input buffer
142 *
143 * @return The int, or 0
144 */
DecodeInt(PSAFE_READ pRead)145 INT DecodeInt(PSAFE_READ pRead)
146 {
147 INT Result = 0, offset;
148
149 for (offset = 0; offset < 32; offset += 6)
150 {
151 INT val = (INT)(DWORD)ReadByte(pRead);
152 Result |= ((val & 0x3f) << offset);
153 if (val & 0x80)
154 {
155 if (val & 0x40)
156 Result *= -1;
157 break;
158 }
159 }
160
161 return Result;
162 }
163
164
165 typedef struct _PATCH_HEADER
166 {
167 DWORD Flags;
168
169 DWORD ImageBase;
170 DWORD ImageTimeStamp;
171
172 DWORD OutputSize;
173 DWORD OutputCrc;
174
175 DWORD OldSize;
176 DWORD OldCrc;
177 DWORD DataSize; // Payload after the patch header
178
179 } PATCH_HEADER;
180
181
182 /**
183 * @name MapFile
184 * Map a view of a file into readonly memory
185 *
186 * @param hFile
187 * The input file handle, readable
188 *
189 * @param dwSize
190 * Mapped file size (out)
191 *
192 * @return A Pointer to the start of the memory
193 */
MapFile(HANDLE hFile,DWORD * dwSize)194 static PBYTE MapFile(HANDLE hFile, DWORD* dwSize)
195 {
196 HANDLE hMap;
197 PVOID pView;
198
199 *dwSize = GetFileSize(hFile, NULL);
200 hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
201 if (hMap != INVALID_HANDLE_VALUE)
202 {
203 pView = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
204 CloseHandle(hMap);
205 return pView;
206 }
207
208 return NULL;
209 }
210
211
212 /*****************************************************
213 * DllMain (MSPATCHA.@)
214 */
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)215 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
216 {
217 switch (fdwReason)
218 {
219 case DLL_PROCESS_ATTACH:
220 DisableThreadLibraryCalls(hinstDLL);
221 break;
222 }
223
224 return TRUE;
225 }
226
227 /*****************************************************
228 * ApplyPatchToFileA (MSPATCHA.1)
229 */
ApplyPatchToFileA(LPCSTR patch_file,LPCSTR old_file,LPCSTR new_file,ULONG apply_flags)230 BOOL WINAPI ApplyPatchToFileA(LPCSTR patch_file, LPCSTR old_file, LPCSTR new_file, ULONG apply_flags)
231 {
232 BOOL ret = FALSE;
233 HANDLE hPatch, hOld, hNew;
234
235 hPatch = CreateFileA(patch_file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
236 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
237 if (hPatch != INVALID_HANDLE_VALUE)
238 {
239 hOld = CreateFileA(old_file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
240 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
241 if (hOld != INVALID_HANDLE_VALUE)
242 {
243 hNew = CreateFileA(new_file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL,
244 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
245 if (hNew != INVALID_HANDLE_VALUE)
246 {
247 ret = ApplyPatchToFileByHandles(hPatch, hOld, hNew, apply_flags);
248 CloseHandle(hNew);
249 }
250 CloseHandle(hOld);
251 }
252 CloseHandle(hPatch);
253 }
254
255 return ret;
256 }
257
258
259 /**
260 * @name ParseHeader
261 * Parse a Patch file header
262 * @note The current implementation is far from complete!
263 *
264 * @param Patch
265 * Buffer pointing to the raw patch data
266 *
267 * @param Header
268 * The result of the parsed header
269 *
270 * @return STATUS_SUCCESS on success, an error code otherwise
271 */
ParseHeader(SAFE_READ * Patch,PATCH_HEADER * Header)272 DWORD ParseHeader(SAFE_READ* Patch, PATCH_HEADER* Header)
273 {
274 DWORD Crc, Unknown;
275 int Delta;
276
277 ZeroMemory(Header, sizeof(*Header));
278
279 /* Validate the patch */
280 Crc = RtlComputeCrc32(0, Patch->Root, Patch->Size);
281 if (Crc != ~0)
282 return ERROR_PATCH_CORRUPT;
283
284 if (ReadDWord(Patch) != '91AP')
285 return ERROR_PATCH_DECODE_FAILURE;
286
287 /* Read the flags, warn about an unknown combination */
288 Header->Flags = ReadDWord(Patch);
289 if (Header->Flags ^ UNKNOWN_FLAGS_COMBINATION)
290 ERR("Unknown flags: 0x%x, patch will most likely fail\n", Header->Flags ^ UNKNOWN_FLAGS_COMBINATION);
291
292 /* 0x5bb3284e, 0x5bb33562, 0x5bb357b1 */
293 Unknown = ReadDWord(Patch);
294 TRACE("Unknown: 0x%x\n", Unknown);
295
296 Header->OutputSize = DecodeDWord(Patch);
297 Header->OutputCrc = ReadDWord(Patch);
298
299 Unknown = ReadByte(Patch);
300 if (Unknown != 1)
301 ERR("Field after CRC is not 1 but %u\n", Unknown);
302
303 Delta = DecodeInt(Patch);
304 Header->OldSize = Header->OutputSize + Delta;
305 Header->OldCrc = ReadDWord(Patch);
306
307 Unknown = ReadUShort(Patch);
308 if (Unknown != 0)
309 ERR("Field1 after OldCrc is not 0 but %u\n", Unknown);
310
311 Unknown = DecodeDWord(Patch);
312 if (Unknown != 0)
313 ERR("Field2 after OldCrc is not 0 but %u\n", Unknown);
314
315 Header->DataSize = DecodeDWord(Patch);
316 /* Remaining data, minus the CRC appended */
317 if (Header->DataSize != (Patch->Size - (Patch->Ptr - Patch->Root) - sizeof(DWORD)))
318 {
319 ERR("Unable to read header, check previous logging!\n");
320 return ERROR_PATCH_DECODE_FAILURE;
321 }
322 return STATUS_SUCCESS;
323 }
324
325 /**
326 * @name CreateNewFileFromPatch
327 * Using the input @param Header and @param Patch, create a new file on @param new_file
328 *
329 * @param Header
330 * Parsed / preprocessed patch header
331 *
332 * @param Patch
333 * Memory buffer pointing to the patch payload
334 *
335 * @param new_file
336 * A handle to the output file. This file will be resized
337 *
338 * @return STATUS_SUCCESS on success, an error code otherwise
339 */
CreateNewFileFromPatch(PATCH_HEADER * Header,SAFE_READ * Patch,HANDLE new_file)340 DWORD CreateNewFileFromPatch(PATCH_HEADER* Header, SAFE_READ* Patch, HANDLE new_file)
341 {
342 SAFE_READ NewFile;
343 HANDLE hMap;
344 USHORT BlockSize;
345 DWORD dwStatus;
346 struct LZXstate* state;
347 int lzxResult;
348
349 hMap = CreateFileMappingW(new_file, NULL, PAGE_READWRITE, 0, Header->OutputSize, NULL);
350 if (hMap == INVALID_HANDLE_VALUE)
351 return ERROR_PATCH_NOT_AVAILABLE;
352
353 NewFile.Root = NewFile.Ptr = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
354 CloseHandle(hMap);
355 NewFile.Size = Header->OutputSize;
356
357 if (!NewFile.Root)
358 return ERROR_PATCH_NOT_AVAILABLE;
359
360 /* At this point Patch->Ptr should point to the payload */
361 BlockSize = ReadUShort(Patch);
362
363 /* This window size does not work on all files (for example, MS SQL Express 2008 setup) */
364 state = LZXinit(17);
365 if (state)
366 {
367 lzxResult = LZXdecompress(state, Patch->Ptr, NewFile.Ptr, BlockSize, NewFile.Size);
368 LZXteardown(state);
369
370 if (lzxResult == DECR_OK)
371 dwStatus = STATUS_SUCCESS;
372 else
373 dwStatus = ERROR_PATCH_DECODE_FAILURE;
374 }
375 else
376 {
377 dwStatus = ERROR_INSUFFICIENT_BUFFER;
378 }
379
380 UnmapViewOfFile(NewFile.Root);
381 return dwStatus;
382 }
383
384
385 /*****************************************************
386 * ApplyPatchToFileByHandles (MSPATCHA.2)
387 */
ApplyPatchToFileByHandles(HANDLE patch_file,HANDLE old_file,HANDLE new_file,ULONG apply_flags)388 BOOL WINAPI ApplyPatchToFileByHandles(HANDLE patch_file, HANDLE old_file, HANDLE new_file, ULONG apply_flags)
389 {
390 SAFE_READ Patch, OldFile;
391 DWORD dwStatus;
392 PATCH_HEADER Header;
393
394 Patch.Root = Patch.Ptr = MapFile(patch_file, &Patch.Size);
395 if (!Patch.Root)
396 {
397 SetLastError(ERROR_PATCH_CORRUPT);
398 return FALSE;
399 }
400
401 /* Let's decode the header */
402 dwStatus = ParseHeader(&Patch, &Header);
403 if (dwStatus != STATUS_SUCCESS)
404 {
405 UnmapViewOfFile(Patch.Root);
406 SetLastError(dwStatus);
407 return FALSE;
408 }
409
410 OldFile.Root = OldFile.Ptr = MapFile(old_file, &OldFile.Size);
411 if (OldFile.Root)
412 {
413 DWORD dwCrc;
414
415 /* Verify the input file */
416 dwCrc = RtlComputeCrc32(0, OldFile.Root, OldFile.Size);
417 if (OldFile.Size == Header.OldSize && dwCrc == Header.OldCrc)
418 {
419 if (apply_flags & APPLY_OPTION_TEST_ONLY)
420 dwStatus = STATUS_SUCCESS;
421 else
422 dwStatus = CreateNewFileFromPatch(&Header, &Patch, new_file);
423 }
424 else
425 {
426 dwStatus = ERROR_PATCH_WRONG_FILE;
427 }
428 UnmapViewOfFile(OldFile.Root);
429 }
430 else
431 {
432 dwStatus = GetLastError();
433 if (dwStatus == STATUS_SUCCESS)
434 dwStatus = ERROR_PATCH_NOT_AVAILABLE;
435 }
436
437 UnmapViewOfFile(Patch.Root);
438 SetLastError(dwStatus);
439 return dwStatus == STATUS_SUCCESS;
440 }
441
442 /*****************************************************
443 * ApplyPatchToFileW (MSPATCHA.6)
444 */
ApplyPatchToFileW(LPCWSTR patch_file,LPCWSTR old_file,LPCWSTR new_file,ULONG apply_flags)445 BOOL WINAPI ApplyPatchToFileW(LPCWSTR patch_file, LPCWSTR old_file, LPCWSTR new_file, ULONG apply_flags)
446 {
447 BOOL ret = FALSE;
448 HANDLE hPatch, hOld, hNew;
449
450 hPatch = CreateFileW(patch_file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
451 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
452 if (hPatch != INVALID_HANDLE_VALUE)
453 {
454 hOld = CreateFileW(old_file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
455 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
456 if (hOld != INVALID_HANDLE_VALUE)
457 {
458 hNew = CreateFileW(new_file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL,
459 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
460 if (hNew != INVALID_HANDLE_VALUE)
461 {
462 ret = ApplyPatchToFileByHandles(hPatch, hOld, hNew, apply_flags);
463 CloseHandle(hNew);
464 }
465 CloseHandle(hOld);
466 }
467 CloseHandle(hPatch);
468 }
469
470 return ret;
471 }
472
473 /*****************************************************
474 * GetFilePatchSignatureA (MSPATCHA.7)
475 */
GetFilePatchSignatureA(LPCSTR filename,ULONG flags,PVOID data,ULONG ignore_range_count,PPATCH_IGNORE_RANGE ignore_range,ULONG retain_range_count,PPATCH_RETAIN_RANGE retain_range,ULONG bufsize,PVOID buffer)476 BOOL WINAPI GetFilePatchSignatureA(LPCSTR filename, ULONG flags, PVOID data, ULONG ignore_range_count,
477 PPATCH_IGNORE_RANGE ignore_range, ULONG retain_range_count,
478 PPATCH_RETAIN_RANGE retain_range, ULONG bufsize, PVOID buffer)
479 {
480 BOOL ret = FALSE;
481 HANDLE hFile;
482
483 hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
484 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
485 if (hFile != INVALID_HANDLE_VALUE)
486 {
487 ret = GetFilePatchSignatureByHandle(hFile, flags, data, ignore_range_count, ignore_range,
488 retain_range_count, retain_range, bufsize, buffer);
489 CloseHandle(hFile);
490 }
491
492 return ret;
493 }
494
495 /*****************************************************
496 * GetFilePatchSignatureA (MSPATCHA.7)
497 */
GetFilePatchSignatureByHandle(HANDLE hFile,ULONG flags,PVOID data,ULONG ignore_range_count,PPATCH_IGNORE_RANGE ignore_range,ULONG retain_range_count,PPATCH_RETAIN_RANGE retain_range,ULONG bufsize,PVOID buffer)498 BOOL WINAPI GetFilePatchSignatureByHandle(HANDLE hFile, ULONG flags, PVOID data, ULONG ignore_range_count,
499 PPATCH_IGNORE_RANGE ignore_range, ULONG retain_range_count,
500 PPATCH_RETAIN_RANGE retain_range, ULONG bufsize, PVOID buffer)
501 {
502 BOOL ret = FALSE;
503 DWORD dwSize, ulCrc;
504 PBYTE pData;
505
506 if (flags)
507 FIXME("Unhandled flags 0x%x\n", flags);
508 if (ignore_range_count)
509 FIXME("Unhandled ignore_range_count %u\n", ignore_range_count);
510 if (retain_range_count)
511 FIXME("Unhandled ignore_range_count %u\n", retain_range_count);
512
513 if ((pData = MapFile(hFile, &dwSize)))
514 {
515 if (dwSize >= 2 && *(PWORD)pData == IMAGE_DOS_SIGNATURE)
516 {
517 FIXME("Potentially unimplemented case, normalized signature\n");
518 }
519
520 ulCrc = RtlComputeCrc32(0, pData, dwSize);
521 if (bufsize >= SIGNATURE_MIN_SIZE)
522 {
523 char *pBuffer = buffer;
524 pBuffer[8] = '\0';
525 for (dwSize = 0; dwSize < 8; ++dwSize)
526 {
527 pBuffer[7 - dwSize] = szHexString[ulCrc & 0xf];
528 ulCrc >>= 4;
529 }
530 ret = TRUE;
531 }
532 UnmapViewOfFile(pData);
533
534 if (bufsize < SIGNATURE_MIN_SIZE)
535 SetLastError(ERROR_INSUFFICIENT_BUFFER);
536 }
537
538 return ret;
539 }
540
541 /*****************************************************
542 * GetFilePatchSignatureW (MSPATCHA.9)
543 */
GetFilePatchSignatureW(LPCWSTR filename,ULONG flags,PVOID data,ULONG ignore_range_count,PPATCH_IGNORE_RANGE ignore_range,ULONG retain_range_count,PPATCH_RETAIN_RANGE retain_range,ULONG bufsize,PVOID buffer)544 BOOL WINAPI GetFilePatchSignatureW(LPCWSTR filename, ULONG flags, PVOID data, ULONG ignore_range_count,
545 PPATCH_IGNORE_RANGE ignore_range, ULONG retain_range_count,
546 PPATCH_RETAIN_RANGE retain_range, ULONG bufsize, PVOID buffer)
547 {
548 CHAR LocalBuf[SIGNATURE_MIN_SIZE];
549 BOOL ret = FALSE;
550 HANDLE hFile;
551
552 hFile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
553 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
554 if (hFile != INVALID_HANDLE_VALUE)
555 {
556 ret = GetFilePatchSignatureByHandle(hFile, flags, data, ignore_range_count, ignore_range,
557 retain_range_count, retain_range, sizeof(LocalBuf), LocalBuf);
558 CloseHandle(hFile);
559
560 if (bufsize < (SIGNATURE_MIN_SIZE * sizeof(WCHAR)))
561 {
562 SetLastError(ERROR_INSUFFICIENT_BUFFER);
563 return FALSE;
564 }
565 if (ret)
566 {
567 MultiByteToWideChar(CP_ACP, 0, LocalBuf, -1, buffer, bufsize / sizeof(WCHAR));
568 }
569 }
570
571 return ret;
572 }
573
574 /*****************************************************
575 * TestApplyPatchToFileA (MSPATCHA.10)
576 */
TestApplyPatchToFileA(LPCSTR patch_file,LPCSTR old_file,ULONG apply_flags)577 BOOL WINAPI TestApplyPatchToFileA(LPCSTR patch_file, LPCSTR old_file, ULONG apply_flags)
578 {
579 BOOL ret = FALSE;
580 HANDLE hPatch, hOld;
581
582 hPatch = CreateFileA(patch_file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
583 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
584 if (hPatch != INVALID_HANDLE_VALUE)
585 {
586 hOld = CreateFileA(old_file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
587 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
588 if (hOld != INVALID_HANDLE_VALUE)
589 {
590 ret = TestApplyPatchToFileByHandles(hPatch, hOld, apply_flags);
591 CloseHandle(hOld);
592 }
593 CloseHandle(hPatch);
594 }
595
596 return ret;
597 }
598
599 /*****************************************************
600 * TestApplyPatchToFileByHandles (MSPATCHA.11)
601 */
TestApplyPatchToFileByHandles(HANDLE patch_file,HANDLE old_file,ULONG apply_flags)602 BOOL WINAPI TestApplyPatchToFileByHandles(HANDLE patch_file, HANDLE old_file, ULONG apply_flags)
603 {
604 return ApplyPatchToFileByHandles(patch_file, old_file, INVALID_HANDLE_VALUE, apply_flags | APPLY_OPTION_TEST_ONLY);
605 }
606
607 /*****************************************************
608 * TestApplyPatchToFileW (MSPATCHA.12)
609 */
TestApplyPatchToFileW(LPCWSTR patch_file,LPCWSTR old_file,ULONG apply_flags)610 BOOL WINAPI TestApplyPatchToFileW(LPCWSTR patch_file, LPCWSTR old_file, ULONG apply_flags)
611 {
612 BOOL ret = FALSE;
613 HANDLE hPatch, hOld;
614
615 hPatch = CreateFileW(patch_file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
616 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
617 if (hPatch != INVALID_HANDLE_VALUE)
618 {
619 hOld = CreateFileW(old_file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
620 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
621 if (hOld != INVALID_HANDLE_VALUE)
622 {
623 ret = TestApplyPatchToFileByHandles(hPatch, hOld, apply_flags);
624 CloseHandle(hOld);
625 }
626 CloseHandle(hPatch);
627 }
628
629 return ret;
630 }
631