[REACTOS] Fix 64 bit build (#465)
[reactos.git] / modules / rosapps / applications / sysutils / utils / binpatch / patch.c
1 #include <windows.h>
2 #include <conio.h>
3 #include <io.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/stat.h>
8
9 /** DEFINES *******************************************************************/
10
11 #define PATCH_BUFFER_SIZE 4096 /* Maximum size of a patch */
12 #define PATCH_BUFFER_MAGIC "\xde\xad\xbe\xef MaGiC MaRk "
13 #define SIZEOF_PATCH_BUFFER_MAGIC (sizeof (PATCH_BUFFER_MAGIC) - 1)
14
15 /** TYPES *********************************************************************/
16
17 typedef struct _PatchedByte
18 {
19 uintptr_t offset; /*!< File offset of the patched byte. */
20 unsigned char expected; /*!< Expected (original) value of the byte. */
21 unsigned char patched; /*!< Patched (new) value for the byte. */
22 } PatchedByte;
23
24 typedef struct _PatchedFile
25 {
26 const char *name; /*!< Name of the file to be patched. */
27 size_t fileSize; /*!< Size of the file in bytes. */
28 int patchCount; /*!< Number of patches for the file. */
29 PatchedByte *patches; /*!< Patches for the file. */
30 } PatchedFile;
31
32 typedef struct _Patch
33 {
34 const char *name; /*!< Name of the patch. */
35 int fileCount; /*!< Number of files in the patch. */
36 PatchedFile *files; /*!< Files for the patch. */
37 } Patch;
38
39 /** FUNCTION PROTOTYPES *******************************************************/
40
41 static void printUsage();
42
43 /** GLOBALS *******************************************************************/
44
45 static Patch m_patch = { NULL, 0, NULL };
46 static int m_argc = 0;
47 static char **m_argv = NULL;
48
49 /* patch buffer where we put the patch info into */
50 static char m_patchBuffer[SIZEOF_PATCH_BUFFER_MAGIC + PATCH_BUFFER_SIZE] =
51 PATCH_BUFFER_MAGIC;
52
53 /** HELPER FUNCTIONS **********************************************************/
54
55 static void *
56 loadFile(const char *fileName, size_t *fileSize_)
57 {
58 FILE *f;
59 struct stat sb;
60 size_t fileSize;
61 void *p;
62
63 /* Open the file */
64 f = fopen(fileName, "rb");
65 if (f == NULL)
66 {
67 printf("Couldn't open file %s for reading!\n", fileName);
68 return NULL;
69 }
70
71 /* Get file size */
72 if (fstat(_fileno(f), &sb) < 0)
73 {
74 fclose(f);
75 printf("Couldn't get size of file %s!\n", fileName);
76 return NULL;
77 }
78 fileSize = sb.st_size;
79
80 /* Load file */
81 p = malloc(fileSize);
82 if (p == NULL)
83 {
84 fclose(f);
85 printf("Couldn't allocate %Id bytes for file %s!\n", fileSize, fileName);
86 return NULL;
87 }
88
89 if (fread(p, fileSize, 1, f) != 1)
90 {
91 fclose(f);
92 free(p);
93 printf("Couldn't read file %s into memory!\n", fileName);
94 return NULL;
95 }
96
97 /* Close file */
98 fclose(f);
99
100 *fileSize_ = fileSize;
101 return p;
102 }
103
104
105 static int
106 saveFile(const char *fileName, void *file, size_t fileSize)
107 {
108 FILE *f;
109
110 /* Open the file */
111 f = fopen(fileName, "wb");
112 if (f == NULL)
113 {
114 printf("Couldn't open file %s for writing!\n", fileName);
115 return -1;
116 }
117
118 /* Write file */
119 if (fwrite(file, fileSize, 1, f) != 1)
120 {
121 fclose(f);
122 printf("Couldn't write file %s!\n", fileName);
123 return -1;
124 }
125
126 /* Close file */
127 fclose(f);
128 return 0;
129 }
130
131
132 static int
133 compareFiles(
134 PatchedFile *patchedFile,
135 const char *originalFileName)
136 {
137 const char *patchedFileName = patchedFile->name;
138 unsigned char *origChunk, *patchedChunk;
139 size_t origSize, patchedSize;
140 int i, patchCount;
141 PatchedByte *patches = NULL;
142 int patchesArrayCount = 0;
143
144 /* Load both files */
145 origChunk = loadFile(originalFileName, &origSize);
146 if (origChunk == NULL)
147 return -1;
148 patchedChunk = loadFile(patchedFileName, &patchedSize);
149 if (patchedChunk == NULL)
150 {
151 free(origChunk);
152 return -1;
153 }
154 if (origSize != patchedSize)
155 {
156 free(origChunk);
157 free(patchedChunk);
158 printf("File size of %s and %s differs (%Iu != %Iu)\n",
159 originalFileName, patchedFileName,
160 origSize, patchedSize);
161 return -1;
162 }
163
164 /* Compare the files and record any differences */
165 printf("Comparing %s to %s", originalFileName, patchedFileName);
166 for (i = 0, patchCount = 0; i < origSize; i++)
167 {
168 if (origChunk[i] != patchedChunk[i])
169 {
170 patchCount++;
171
172 /* Resize patches array if needed */
173 if (patchesArrayCount < patchCount)
174 {
175 PatchedByte *newPatches;
176 newPatches = realloc(patches, patchCount * sizeof (PatchedByte));
177 if (newPatches == NULL)
178 {
179 if (patches != NULL)
180 free(patches);
181 free(origChunk);
182 free(patchedChunk);
183 printf("\nOut of memory (tried to allocated %Id bytes)\n",
184 patchCount * sizeof (PatchedByte));
185 return -1;
186 }
187 patches = newPatches;
188 }
189
190 /* Fill in patch info */
191 patches[patchCount - 1].offset = i;
192 patches[patchCount - 1].expected = origChunk[i];
193 patches[patchCount - 1].patched = patchedChunk[i];
194 }
195 if ((i % (origSize / 40)) == 0)
196 printf(".");
197 }
198 printf(" %d changed bytes found.\n", patchCount);
199
200 /* Unload the files */
201 free(origChunk);
202 free(patchedChunk);
203
204 /* Save patch info */
205 patchedFile->fileSize = patchedSize;
206 patchedFile->patchCount = patchCount;
207 patchedFile->patches = patches;
208
209 return 0;
210 }
211
212
213 static int
214 outputPatch(const char *outputFileName)
215 {
216 char *patchExe, *patchBuffer = NULL;
217 size_t i, size, patchExeSize, patchSize, stringSize, stringOffset, patchOffset;
218 Patch *patch;
219 PatchedFile *files;
220
221 printf("Putting patch into %s...\n", outputFileName);
222
223 /* Calculate size of the patch */
224 patchSize = sizeof (Patch) + sizeof (PatchedFile) * m_patch.fileCount;
225 stringSize = strlen(m_patch.name) + 1;
226 for (i = 0; i < m_patch.fileCount; i++)
227 {
228 stringSize += strlen(m_patch.files[i].name) + 1;
229 patchSize += sizeof (PatchedByte) * m_patch.files[i].patchCount;
230 }
231 if ((stringSize + patchSize) > PATCH_BUFFER_SIZE)
232 {
233 printf("Patch is too big - %u bytes maximum, %Iu bytes needed\n",
234 PATCH_BUFFER_SIZE, stringSize + patchSize);
235 return -1;
236 }
237
238 /* Load patch.exe file into memory... */
239 patchExe = loadFile(m_argv[0], &patchExeSize);
240 if (patchExe == NULL)
241 {
242 return -1;
243 }
244
245 /* Try to find the magic mark for the patch buffer */
246 for (i = 0; i < (patchExeSize - SIZEOF_PATCH_BUFFER_MAGIC); i++)
247 {
248 if (memcmp(patchExe + i, m_patchBuffer, SIZEOF_PATCH_BUFFER_MAGIC) == 0)
249 {
250 patchBuffer = patchExe + i + SIZEOF_PATCH_BUFFER_MAGIC;
251
252 break;
253 }
254 }
255 if (!(i < (patchExeSize - SIZEOF_PATCH_BUFFER_MAGIC)))
256 {
257 free(patchExe);
258 printf("Couldn't find patch buffer magic in file %s - this shouldn't happen!!!\n", m_argv[0]);
259 return -1;
260 }
261
262 /* Pack patch together and replace string pointers by offsets */
263 patch = (Patch *)patchBuffer;
264 files = (PatchedFile *)(patchBuffer + sizeof (Patch));
265 patchOffset = sizeof (Patch) + sizeof (PatchedFile) * m_patch.fileCount;
266 stringOffset = patchSize;
267
268 patch->fileCount = m_patch.fileCount;
269 patch->files = (PatchedFile *)sizeof (Patch);
270
271 patch->name = (const char *)stringOffset;
272 strcpy(patchBuffer + stringOffset, m_patch.name);
273 stringOffset += strlen(m_patch.name) + 1;
274
275 for (i = 0; i < m_patch.fileCount; i++)
276 {
277 files[i].fileSize = m_patch.files[i].fileSize;
278 files[i].patchCount = m_patch.files[i].patchCount;
279
280 files[i].name = (const char *)stringOffset;
281 strcpy(patchBuffer + stringOffset, m_patch.files[i].name);
282 stringOffset += strlen(m_patch.files[i].name) + 1;
283
284 size = files[i].patchCount * sizeof (PatchedByte);
285 files[i].patches = (PatchedByte *)patchOffset;
286 memcpy(patchBuffer + patchOffset, m_patch.files[i].patches, size);
287 patchOffset += size;
288 }
289 size = patchSize + stringSize;
290 memset(patchBuffer + size, 0, PATCH_BUFFER_SIZE - size);
291
292 /* Save file */
293 if (saveFile(outputFileName, patchExe, patchExeSize) < 0)
294 {
295 free(patchExe);
296 return -1;
297 }
298 free(patchExe);
299
300 printf("Patch saved!\n");
301 return 0;
302 }
303
304
305 static int
306 loadPatch()
307 {
308 char *p;
309 Patch *patch;
310 int i;
311
312 p = m_patchBuffer + SIZEOF_PATCH_BUFFER_MAGIC;
313 patch = (Patch *)p;
314
315 if (patch->name == NULL)
316 {
317 return -1;
318 }
319
320 m_patch.name = p + (intptr_t)patch->name;
321 m_patch.fileCount = patch->fileCount;
322 m_patch.files = (PatchedFile *)(p + (intptr_t)patch->files);
323
324 for (i = 0; i < m_patch.fileCount; i++)
325 {
326 m_patch.files[i].name = p + (intptr_t)m_patch.files[i].name;
327 m_patch.files[i].patches = (PatchedByte *)(p + (intptr_t)m_patch.files[i].patches);
328 }
329
330 printf("Patch %s loaded...\n", m_patch.name);
331 return 0;
332 }
333
334
335 /** MAIN FUNCTIONS ************************************************************/
336
337 static int
338 createPatch()
339 {
340 int i, status;
341 const char *outputFileName;
342
343 /* Check argument count */
344 if (m_argc < 6 || (m_argc % 2) != 0)
345 {
346 printUsage();
347 return -1;
348 }
349
350 outputFileName = m_argv[3];
351 m_patch.name = m_argv[2];
352
353 /* Allocate PatchedFiles array */
354 m_patch.fileCount = (m_argc - 4) / 2;
355 m_patch.files = malloc(m_patch.fileCount * sizeof (PatchedFile));
356 if (m_patch.files == NULL)
357 {
358 printf("Out of memory!\n");
359 return -1;
360 }
361 memset(m_patch.files, 0, m_patch.fileCount * sizeof (PatchedFile));
362
363 /* Compare original to patched files and fill m_patch.files array */
364 for (i = 0; i < m_patch.fileCount; i++)
365 {
366 m_patch.files[i].name = m_argv[4 + (i * 2) + 1];
367 status = compareFiles(m_patch.files + i, m_argv[4 + (i * 2) + 0]);
368 if (status < 0)
369 {
370 for (i = 0; i < m_patch.fileCount; i++)
371 {
372 if (m_patch.files[i].patches != NULL)
373 free(m_patch.files[i].patches);
374 }
375 free(m_patch.files);
376 m_patch.files = NULL;
377 m_patch.fileCount = 0;
378 return status;
379 }
380 }
381
382 /* Output patch */
383 return outputPatch(outputFileName);
384 }
385
386
387 static int
388 applyPatch()
389 {
390 int c, i, j, makeBackup;
391 size_t fileSize;
392 unsigned char *file;
393 char *p;
394 const char *fileName;
395 char buffer[MAX_PATH];
396
397
398 if (m_argc > 1 && strcmp(m_argv[1], "-d") != 0)
399 {
400 printUsage();
401 return -1;
402 }
403
404 /* Load patch */
405 if (loadPatch() < 0)
406 {
407 printf("This executable doesn't contain a patch, use -c to create one.\n");
408 return -1;
409 }
410
411 if (m_argc > 1)
412 {
413 /* Dump patch */
414 printf("Patch name: %s\n", m_patch.name);
415 printf("File count: %d\n", m_patch.fileCount);
416 for (i = 0; i < m_patch.fileCount; i++)
417 {
418 printf("----------------------\n"
419 "File name: %s\n"
420 "File size: %Id bytes\n",
421 m_patch.files[i].name, m_patch.files[i].fileSize);
422 printf("Patch count: %d\n", m_patch.files[i].patchCount);
423 for (j = 0; j < m_patch.files[i].patchCount; j++)
424 {
425 printf(" Offset 0x%Ix 0x%02x -> 0x%02x\n",
426 m_patch.files[i].patches[j].offset,
427 m_patch.files[i].patches[j].expected,
428 m_patch.files[i].patches[j].patched);
429 }
430 }
431 }
432 else
433 {
434 /* Apply patch */
435 printf("Applying patch...\n");
436 for (i = 0; i < m_patch.fileCount; i++)
437 {
438 /* Load original file */
439 fileName = m_patch.files[i].name;
440 applyPatch_retry_file:
441 file = loadFile(fileName, &fileSize);
442 if (file == NULL)
443 {
444 printf("File %s not found! ", fileName);
445 applyPatch_file_open_error:
446 printf("(S)kip, (R)etry, (A)bort, (M)anually enter filename");
447 do
448 {
449 c = _getch();
450 }
451 while (c != 's' && c != 'r' && c != 'a' && c != 'm');
452 printf("\n");
453 if (c == 's')
454 {
455 continue;
456 }
457 else if (c == 'r')
458 {
459 goto applyPatch_retry_file;
460 }
461 else if (c == 'a')
462 {
463 return 0;
464 }
465 else if (c == 'm')
466 {
467 if (fgets(buffer, sizeof (buffer), stdin) == NULL)
468 {
469 printf("fgets() failed!\n");
470 return -1;
471 }
472 p = strchr(buffer, '\r');
473 if (p != NULL)
474 *p = '\0';
475 p = strchr(buffer, '\n');
476 if (p != NULL)
477 *p = '\0';
478
479 fileName = buffer;
480 goto applyPatch_retry_file;
481 }
482 }
483
484 /* Check file size */
485 if (fileSize != m_patch.files[i].fileSize)
486 {
487 free(file);
488 printf("File %s has unexpected filesize of %Id bytes (%Id bytes expected)\n",
489 fileName, fileSize, m_patch.files[i].fileSize);
490 if (fileName != m_patch.files[i].name) /* manually entered filename */
491 {
492 goto applyPatch_file_open_error;
493 }
494 return -1;
495 }
496
497 /* Ask for backup */
498 printf("Do you want to make a backup of %s? (Y)es, (N)o, (A)bort", fileName);
499 do
500 {
501 c = _getch();
502 }
503 while (c != 'y' && c != 'n' && c != 'a');
504 printf("\n");
505 if (c == 'y')
506 {
507 char buffer[MAX_PATH];
508 _snprintf(buffer, MAX_PATH, "%s.bak", fileName);
509 buffer[MAX_PATH-1] = '\0';
510 makeBackup = 1;
511 if (_access(buffer, 0) >= 0) /* file exists */
512 {
513 printf("File %s already exists, overwrite? (Y)es, (N)o, (A)bort", buffer);
514 do
515 {
516 c = _getch();
517 }
518 while (c != 'y' && c != 'n' && c != 'a');
519 printf("\n");
520 if (c == 'n')
521 makeBackup = 0;
522 else if (c == 'a')
523 {
524 free(file);
525 return 0;
526 }
527 }
528 if (makeBackup && saveFile(buffer, file, fileSize) < 0)
529 {
530 free(file);
531 return -1;
532 }
533 }
534 else if (c == 'a')
535 {
536 free(file);
537 return 0;
538 }
539
540 /* Patch file */
541 for (j = 0; j < m_patch.files[i].patchCount; j++)
542 {
543 int offset = m_patch.files[i].patches[j].offset;
544 if (file[offset] != m_patch.files[i].patches[j].expected)
545 {
546 printf("Unexpected value in file %s at offset 0x%x: expected = 0x%02x, found = 0x%02x\n",
547 fileName, offset, m_patch.files[i].patches[j].expected, file[offset]);
548 free(file);
549 return -1;
550 }
551 file[offset] = m_patch.files[i].patches[j].patched;
552 }
553
554 /* Save file */
555 if (saveFile(fileName, file, fileSize) < 0)
556 {
557 free(file);
558 return -1;
559 }
560 free(file);
561 }
562
563 printf("Patch applied sucessfully!\n");
564 }
565
566 return 0;
567 }
568
569
570 static void
571 printUsage()
572 {
573 printf("Usage:\n"
574 "%s -c - Create patch\n"
575 "%s -d - Dump patch\n"
576 "%s - Apply patch\n"
577 "\n"
578 "A patch can be created like this:\n"
579 "%s -c \"patch name\" output.exe file1.orig file1.patched[ file2.orig file2.patched[ ...]]\n",
580 m_argv[0], m_argv[0], m_argv[0], m_argv[0]);
581 }
582
583
584 int
585 main(
586 int argc,
587 char *argv[])
588 {
589 m_argc = argc;
590 m_argv = argv;
591
592 if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0))
593 {
594 printUsage();
595 return 0;
596 }
597 else if (argc >= 2 && argv[1][0] == '-')
598 {
599 if (strcmp(argv[1], "-c") == 0)
600 {
601 return createPatch();
602 }
603 else if (strcmp(argv[1], "-d") == 0)
604 {
605 return applyPatch();
606 }
607 else
608 {
609 printf("Unknown option: %s\n"
610 "Use -h for help.\n",
611 argv[1]);
612 return -1;
613 }
614 }
615
616 return applyPatch();
617 }
618