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