xref: /reactos/dll/win32/mspatcha/mspatcha_main.c (revision 97d7206f)
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