Use _getch instead of deprecated getch
[reactos.git] / 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 int 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 int 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, int *fileSize_)
57 {
58 FILE *f;
59 struct stat sb;
60 int 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 %d 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, int 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 int origSize, patchedSize, i, patchCount;
140 PatchedByte *patches = NULL;
141 int patchesArrayCount = 0;
142
143 /* Load both files */
144 origChunk = loadFile(originalFileName, &origSize);
145 if (origChunk == NULL)
146 return -1;
147 patchedChunk = loadFile(patchedFileName, &patchedSize);
148 if (patchedChunk == NULL)
149 {
150 free(origChunk);
151 return -1;
152 }
153 if (origSize != patchedSize)
154 {
155 free(origChunk);
156 free(patchedChunk);
157 printf("File size of %s and %s differs (%d != %d)\n",
158 originalFileName, patchedFileName,
159 origSize, patchedSize);
160 return -1;
161 }
162
163 /* Compare the files and record any differences */
164 printf("Comparing %s to %s", originalFileName, patchedFileName);
165 for (i = 0, patchCount = 0; i < origSize; i++)
166 {
167 if (origChunk[i] != patchedChunk[i])
168 {
169 patchCount++;
170
171 /* Resize patches array if needed */
172 if (patchesArrayCount < patchCount)
173 {
174 PatchedByte *newPatches;
175 newPatches = realloc(patches, patchCount * sizeof (PatchedByte));
176 if (newPatches == NULL)
177 {
178 if (patches != NULL)
179 free(patches);
180 free(origChunk);
181 free(patchedChunk);
182 printf("\nOut of memory (tried to allocated %d bytes)\n",
183 patchCount * sizeof (PatchedByte));
184 return -1;
185 }
186 patches = newPatches;
187 }
188
189 /* Fill in patch info */
190 patches[patchCount - 1].offset = i;
191 patches[patchCount - 1].expected = origChunk[i];
192 patches[patchCount - 1].patched = patchedChunk[i];
193 }
194 if ((i % (origSize / 40)) == 0)
195 printf(".");
196 }
197 printf(" %d changed bytes found.\n", patchCount);
198
199 /* Unload the files */
200 free(origChunk);
201 free(patchedChunk);
202
203 /* Save patch info */
204 patchedFile->fileSize = patchedSize;
205 patchedFile->patchCount = patchCount;
206 patchedFile->patches = patches;
207
208 return 0;
209 }
210
211
212 static int
213 outputPatch(const char *outputFileName)
214 {
215 char *patchExe, *patchBuffer = NULL;
216 int i, size, patchExeSize, patchSize, stringSize, stringOffset, patchOffset;
217 Patch *patch;
218 PatchedFile *files;
219
220 printf("Putting patch into %s...\n", outputFileName);
221
222 /* Calculate size of the patch */
223 patchSize = sizeof (Patch) + sizeof (PatchedFile) * m_patch.fileCount;
224 stringSize = strlen(m_patch.name) + 1;
225 for (i = 0; i < m_patch.fileCount; i++)
226 {
227 stringSize += strlen(m_patch.files[i].name) + 1;
228 patchSize += sizeof (PatchedByte) * m_patch.files[i].patchCount;
229 }
230 if ((stringSize + patchSize) > PATCH_BUFFER_SIZE)
231 {
232 printf("Patch is too big - %d bytes maximum, %d bytes needed\n",
233 PATCH_BUFFER_SIZE, stringSize + patchSize);
234 return -1;
235 }
236
237 /* Load patch.exe file into memory... */
238 patchExe = loadFile(m_argv[0], &patchExeSize);
239 if (patchExe == NULL)
240 {
241 return -1;
242 }
243
244 /* Try to find the magic mark for the patch buffer */
245 for (i = 0; i < (patchExeSize - SIZEOF_PATCH_BUFFER_MAGIC); i++)
246 {
247 if (memcmp(patchExe + i, m_patchBuffer, SIZEOF_PATCH_BUFFER_MAGIC) == 0)
248 {
249 patchBuffer = patchExe + i + SIZEOF_PATCH_BUFFER_MAGIC;
250
251 break;
252 }
253 }
254 if (!(i < (patchExeSize - SIZEOF_PATCH_BUFFER_MAGIC)))
255 {
256 free(patchExe);
257 printf("Couldn't find patch buffer magic in file %s - this shouldn't happen!!!\n", m_argv[0]);
258 return -1;
259 }
260
261 /* Pack patch together and replace string pointers by offsets */
262 patch = (Patch *)patchBuffer;
263 files = (PatchedFile *)(patchBuffer + sizeof (Patch));
264 patchOffset = sizeof (Patch) + sizeof (PatchedFile) * m_patch.fileCount;
265 stringOffset = patchSize;
266
267 patch->fileCount = m_patch.fileCount;
268 patch->files = (PatchedFile *)sizeof (Patch);
269
270 patch->name = (const char *)stringOffset;
271 strcpy(patchBuffer + stringOffset, m_patch.name);
272 stringOffset += strlen(m_patch.name) + 1;
273
274 for (i = 0; i < m_patch.fileCount; i++)
275 {
276 files[i].fileSize = m_patch.files[i].fileSize;
277 files[i].patchCount = m_patch.files[i].patchCount;
278
279 files[i].name = (const char *)stringOffset;
280 strcpy(patchBuffer + stringOffset, m_patch.files[i].name);
281 stringOffset += strlen(m_patch.files[i].name) + 1;
282
283 size = files[i].patchCount * sizeof (PatchedByte);
284 files[i].patches = (PatchedByte *)patchOffset;
285 memcpy(patchBuffer + patchOffset, m_patch.files[i].patches, size);
286 patchOffset += size;
287 }
288 size = patchSize + stringSize;
289 memset(patchBuffer + size, 0, PATCH_BUFFER_SIZE - size);
290
291 /* Save file */
292 if (saveFile(outputFileName, patchExe, patchExeSize) < 0)
293 {
294 free(patchExe);
295 return -1;
296 }
297 free(patchExe);
298
299 printf("Patch saved!\n");
300 return 0;
301 }
302
303
304 static int
305 loadPatch()
306 {
307 char *p;
308 Patch *patch;
309 int i;
310
311 p = m_patchBuffer + SIZEOF_PATCH_BUFFER_MAGIC;
312 patch = (Patch *)p;
313
314 if (patch->name == NULL)
315 {
316 return -1;
317 }
318
319 m_patch.name = p + (int)patch->name;
320 m_patch.fileCount = patch->fileCount;
321 m_patch.files = (PatchedFile *)(p + (int)patch->files);
322
323 for (i = 0; i < m_patch.fileCount; i++)
324 {
325 m_patch.files[i].name = p + (int)m_patch.files[i].name;
326 m_patch.files[i].patches = (PatchedByte *)(p + (int)m_patch.files[i].patches);
327 }
328
329 printf("Patch %s loaded...\n", m_patch.name);
330 return 0;
331 }
332
333
334 /** MAIN FUNCTIONS ************************************************************/
335
336 static int
337 createPatch()
338 {
339 int i, status;
340 const char *outputFileName;
341
342 /* Check argument count */
343 if (m_argc < 6 || (m_argc % 2) != 0)
344 {
345 printUsage();
346 return -1;
347 }
348
349 outputFileName = m_argv[3];
350 m_patch.name = m_argv[2];
351
352 /* Allocate PatchedFiles array */
353 m_patch.fileCount = (m_argc - 4) / 2;
354 m_patch.files = malloc(m_patch.fileCount * sizeof (PatchedFile));
355 if (m_patch.files == NULL)
356 {
357 printf("Out of memory!\n");
358 return -1;
359 }
360 memset(m_patch.files, 0, m_patch.fileCount * sizeof (PatchedFile));
361
362 /* Compare original to patched files and fill m_patch.files array */
363 for (i = 0; i < m_patch.fileCount; i++)
364 {
365 m_patch.files[i].name = m_argv[4 + (i * 2) + 1];
366 status = compareFiles(m_patch.files + i, m_argv[4 + (i * 2) + 0]);
367 if (status < 0)
368 {
369 for (i = 0; i < m_patch.fileCount; i++)
370 {
371 if (m_patch.files[i].patches != NULL)
372 free(m_patch.files[i].patches);
373 }
374 free(m_patch.files);
375 m_patch.files = NULL;
376 m_patch.fileCount = 0;
377 return status;
378 }
379 }
380
381 /* Output patch */
382 return outputPatch(outputFileName);
383 }
384
385
386 static int
387 applyPatch()
388 {
389 int c, i, j, fileSize, makeBackup;
390 unsigned char *file;
391 char *p;
392 const char *fileName;
393 char buffer[MAX_PATH];
394
395
396 if (m_argc > 1 && strcmp(m_argv[1], "-d") != 0)
397 {
398 printUsage();
399 return -1;
400 }
401
402 /* Load patch */
403 if (loadPatch() < 0)
404 {
405 printf("This executable doesn't contain a patch, use -c to create one.\n");
406 return -1;
407 }
408
409 if (m_argc > 1)
410 {
411 /* Dump patch */
412 printf("Patch name: %s\n", m_patch.name);
413 printf("File count: %d\n", m_patch.fileCount);
414 for (i = 0; i < m_patch.fileCount; i++)
415 {
416 printf("----------------------\n"
417 "File name: %s\n"
418 "File size: %d bytes\n",
419 m_patch.files[i].name, m_patch.files[i].fileSize);
420 printf("Patch count: %d\n", m_patch.files[i].patchCount);
421 for (j = 0; j < m_patch.files[i].patchCount; j++)
422 {
423 printf(" Offset 0x%x 0x%02x -> 0x%02x\n",
424 m_patch.files[i].patches[j].offset,
425 m_patch.files[i].patches[j].expected,
426 m_patch.files[i].patches[j].patched);
427 }
428 }
429 }
430 else
431 {
432 /* Apply patch */
433 printf("Applying patch...\n");
434 for (i = 0; i < m_patch.fileCount; i++)
435 {
436 /* Load original file */
437 fileName = m_patch.files[i].name;
438 applyPatch_retry_file:
439 file = loadFile(fileName, &fileSize);
440 if (file == NULL)
441 {
442 printf("File %s not found! ", fileName);
443 applyPatch_file_open_error:
444 printf("(S)kip, (R)etry, (A)bort, (M)anually enter filename");
445 do
446 {
447 c = _getch();
448 }
449 while (c != 's' && c != 'r' && c != 'a' && c != 'm');
450 printf("\n");
451 if (c == 's')
452 {
453 continue;
454 }
455 else if (c == 'r')
456 {
457 goto applyPatch_retry_file;
458 }
459 else if (c == 'a')
460 {
461 return 0;
462 }
463 else if (c == 'm')
464 {
465 if (fgets(buffer, sizeof (buffer), stdin) == NULL)
466 {
467 printf("fgets() failed!\n");
468 return -1;
469 }
470 p = strchr(buffer, '\r');
471 if (p != NULL)
472 *p = '\0';
473 p = strchr(buffer, '\n');
474 if (p != NULL)
475 *p = '\0';
476
477 fileName = buffer;
478 goto applyPatch_retry_file;
479 }
480 }
481
482 /* Check file size */
483 if (fileSize != m_patch.files[i].fileSize)
484 {
485 free(file);
486 printf("File %s has unexpected filesize of %d bytes (%d bytes expected)\n",
487 fileName, fileSize, m_patch.files[i].fileSize);
488 if (fileName != m_patch.files[i].name) /* manually entered filename */
489 {
490 goto applyPatch_file_open_error;
491 }
492 return -1;
493 }
494
495 /* Ask for backup */
496 printf("Do you want to make a backup of %s? (Y)es, (N)o, (A)bort", fileName);
497 do
498 {
499 c = _getch();
500 }
501 while (c != 'y' && c != 'n' && c != 'a');
502 printf("\n");
503 if (c == 'y')
504 {
505 char buffer[MAX_PATH];
506 _snprintf(buffer, MAX_PATH, "%s.bak", fileName);
507 buffer[MAX_PATH-1] = '\0';
508 makeBackup = 1;
509 if (access(buffer, 0) >= 0) /* file exists */
510 {
511 printf("File %s already exists, overwrite? (Y)es, (N)o, (A)bort", buffer);
512 do
513 {
514 c = _getch();
515 }
516 while (c != 'y' && c != 'n' && c != 'a');
517 printf("\n");
518 if (c == 'n')
519 makeBackup = 0;
520 else if (c == 'a')
521 {
522 free(file);
523 return 0;
524 }
525 }
526 if (makeBackup && saveFile(buffer, file, fileSize) < 0)
527 {
528 free(file);
529 return -1;
530 }
531 }
532 else if (c == 'a')
533 {
534 free(file);
535 return 0;
536 }
537
538 /* Patch file */
539 for (j = 0; j < m_patch.files[i].patchCount; j++)
540 {
541 int offset = m_patch.files[i].patches[j].offset;
542 if (file[offset] != m_patch.files[i].patches[j].expected)
543 {
544 printf("Unexpected value in file %s at offset 0x%x: expected = 0x%02x, found = 0x%02x\n",
545 fileName, offset, m_patch.files[i].patches[j].expected, file[offset]);
546 free(file);
547 return -1;
548 }
549 file[offset] = m_patch.files[i].patches[j].patched;
550 }
551
552 /* Save file */
553 if (saveFile(fileName, file, fileSize) < 0)
554 {
555 free(file);
556 return -1;
557 }
558 free(file);
559 }
560
561 printf("Patch applied sucessfully!\n");
562 }
563
564 return 0;
565 }
566
567
568 static void
569 printUsage()
570 {
571 printf("Usage:\n"
572 "%s -c - Create patch\n"
573 "%s -d - Dump patch\n"
574 "%s - Apply patch\n"
575 "\n"
576 "A patch can be created like this:\n"
577 "%s -c \"patch name\" output.exe file1.orig file1.patched[ file2.orig file2.patched[ ...]]\n",
578 m_argv[0], m_argv[0], m_argv[0], m_argv[0]);
579 }
580
581
582 int
583 main(
584 int argc,
585 char *argv[])
586 {
587 m_argc = argc;
588 m_argv = argv;
589
590 if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0))
591 {
592 printUsage();
593 return 0;
594 }
595 else if (argc >= 2 && argv[1][0] == '-')
596 {
597 if (strcmp(argv[1], "-c") == 0)
598 {
599 return createPatch();
600 }
601 else if (strcmp(argv[1], "-d") == 0)
602 {
603 return applyPatch();
604 }
605 else
606 {
607 printf("Unknown option: %s\n"
608 "Use -h for help.\n",
609 argv[1]);
610 return -1;
611 }
612 }
613
614 return applyPatch();
615 }
616