9 /** DEFINES *******************************************************************/
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)
15 /** TYPES *********************************************************************/
17 typedef struct _PatchedByte
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. */
24 typedef struct _PatchedFile
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. */
34 const char *name
; /*!< Name of the patch. */
35 int fileCount
; /*!< Number of files in the patch. */
36 PatchedFile
*files
; /*!< Files for the patch. */
39 /** FUNCTION PROTOTYPES *******************************************************/
41 static void printUsage();
43 /** GLOBALS *******************************************************************/
45 static Patch m_patch
= { NULL
, 0, NULL
};
46 static int m_argc
= 0;
47 static char **m_argv
= NULL
;
49 /* patch buffer where we put the patch info into */
50 static char m_patchBuffer
[SIZEOF_PATCH_BUFFER_MAGIC
+ PATCH_BUFFER_SIZE
] =
53 /** HELPER FUNCTIONS **********************************************************/
56 loadFile(const char *fileName
, int *fileSize_
)
64 f
= fopen(fileName
, "rb");
67 printf("Couldn't open file %s for reading!\n", fileName
);
72 if (fstat(fileno(f
), &sb
) < 0)
75 printf("Couldn't get size of file %s!\n", fileName
);
78 fileSize
= sb
.st_size
;
85 printf("Couldn't allocate %d bytes for file %s!\n", fileSize
, fileName
);
89 if (fread(p
, fileSize
, 1, f
) != 1)
93 printf("Couldn't read file %s into memory!\n", fileName
);
100 *fileSize_
= fileSize
;
106 saveFile(const char *fileName
, void *file
, int fileSize
)
111 f
= fopen(fileName
, "wb");
114 printf("Couldn't open file %s for writing!\n", fileName
);
119 if (fwrite(file
, fileSize
, 1, f
) != 1)
122 printf("Couldn't write file %s!\n", fileName
);
134 PatchedFile
*patchedFile
,
135 const char *originalFileName
)
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;
143 /* Load both files */
144 origChunk
= loadFile(originalFileName
, &origSize
);
145 if (origChunk
== NULL
)
147 patchedChunk
= loadFile(patchedFileName
, &patchedSize
);
148 if (patchedChunk
== NULL
)
153 if (origSize
!= patchedSize
)
157 printf("File size of %s and %s differs (%d != %d)\n",
158 originalFileName
, patchedFileName
,
159 origSize
, patchedSize
);
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
++)
167 if (origChunk
[i
] != patchedChunk
[i
])
171 /* Resize patches array if needed */
172 if (patchesArrayCount
< patchCount
)
174 PatchedByte
*newPatches
;
175 newPatches
= realloc(patches
, patchCount
* sizeof (PatchedByte
));
176 if (newPatches
== NULL
)
182 printf("\nOut of memory (tried to allocated %d bytes)\n",
183 patchCount
* sizeof (PatchedByte
));
186 patches
= newPatches
;
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
];
194 if ((i
% (origSize
/ 40)) == 0)
197 printf(" %d changed bytes found.\n", patchCount
);
199 /* Unload the files */
203 /* Save patch info */
204 patchedFile
->fileSize
= patchedSize
;
205 patchedFile
->patchCount
= patchCount
;
206 patchedFile
->patches
= patches
;
213 outputPatch(const char *outputFileName
)
215 char *patchExe
, *patchBuffer
= NULL
;
216 int i
, size
, patchExeSize
, patchSize
, stringSize
, stringOffset
, patchOffset
;
220 printf("Putting patch into %s...\n", outputFileName
);
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
++)
227 stringSize
+= strlen(m_patch
.files
[i
].name
) + 1;
228 patchSize
+= sizeof (PatchedByte
) * m_patch
.files
[i
].patchCount
;
230 if ((stringSize
+ patchSize
) > PATCH_BUFFER_SIZE
)
232 printf("Patch is too big - %d bytes maximum, %d bytes needed\n",
233 PATCH_BUFFER_SIZE
, stringSize
+ patchSize
);
237 /* Load patch.exe file into memory... */
238 patchExe
= loadFile(m_argv
[0], &patchExeSize
);
239 if (patchExe
== NULL
)
244 /* Try to find the magic mark for the patch buffer */
245 for (i
= 0; i
< (patchExeSize
- SIZEOF_PATCH_BUFFER_MAGIC
); i
++)
247 if (memcmp(patchExe
+ i
, m_patchBuffer
, SIZEOF_PATCH_BUFFER_MAGIC
) == 0)
249 patchBuffer
= patchExe
+ i
+ SIZEOF_PATCH_BUFFER_MAGIC
;
254 if (!(i
< (patchExeSize
- SIZEOF_PATCH_BUFFER_MAGIC
)))
257 printf("Couldn't find patch buffer magic in file %s - this shouldn't happen!!!\n", m_argv
[0]);
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
;
267 patch
->fileCount
= m_patch
.fileCount
;
268 patch
->files
= (PatchedFile
*)sizeof (Patch
);
270 patch
->name
= (const char *)stringOffset
;
271 strcpy(patchBuffer
+ stringOffset
, m_patch
.name
);
272 stringOffset
+= strlen(m_patch
.name
) + 1;
274 for (i
= 0; i
< m_patch
.fileCount
; i
++)
276 files
[i
].fileSize
= m_patch
.files
[i
].fileSize
;
277 files
[i
].patchCount
= m_patch
.files
[i
].patchCount
;
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;
283 size
= files
[i
].patchCount
* sizeof (PatchedByte
);
284 files
[i
].patches
= (PatchedByte
*)patchOffset
;
285 memcpy(patchBuffer
+ patchOffset
, m_patch
.files
[i
].patches
, size
);
288 size
= patchSize
+ stringSize
;
289 memset(patchBuffer
+ size
, 0, PATCH_BUFFER_SIZE
- size
);
292 if (saveFile(outputFileName
, patchExe
, patchExeSize
) < 0)
299 printf("Patch saved!\n");
311 p
= m_patchBuffer
+ SIZEOF_PATCH_BUFFER_MAGIC
;
314 if (patch
->name
== NULL
)
319 m_patch
.name
= p
+ (int)patch
->name
;
320 m_patch
.fileCount
= patch
->fileCount
;
321 m_patch
.files
= (PatchedFile
*)(p
+ (int)patch
->files
);
323 for (i
= 0; i
< m_patch
.fileCount
; i
++)
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
);
329 printf("Patch %s loaded...\n", m_patch
.name
);
334 /** MAIN FUNCTIONS ************************************************************/
340 const char *outputFileName
;
342 /* Check argument count */
343 if (m_argc
< 6 || (m_argc
% 2) != 0)
349 outputFileName
= m_argv
[3];
350 m_patch
.name
= m_argv
[2];
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
)
357 printf("Out of memory!\n");
360 memset(m_patch
.files
, 0, m_patch
.fileCount
* sizeof (PatchedFile
));
362 /* Compare original to patched files and fill m_patch.files array */
363 for (i
= 0; i
< m_patch
.fileCount
; i
++)
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]);
369 for (i
= 0; i
< m_patch
.fileCount
; i
++)
371 if (m_patch
.files
[i
].patches
!= NULL
)
372 free(m_patch
.files
[i
].patches
);
375 m_patch
.files
= NULL
;
376 m_patch
.fileCount
= 0;
382 return outputPatch(outputFileName
);
389 int c
, i
, j
, fileSize
, makeBackup
;
392 const char *fileName
;
393 char buffer
[MAX_PATH
];
396 if (m_argc
> 1 && strcmp(m_argv
[1], "-d") != 0)
405 printf("This executable doesn't contain a patch, use -c to create one.\n");
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
++)
416 printf("----------------------\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
++)
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
);
433 printf("Applying patch...\n");
434 for (i
= 0; i
< m_patch
.fileCount
; i
++)
436 /* Load original file */
437 fileName
= m_patch
.files
[i
].name
;
438 applyPatch_retry_file
:
439 file
= loadFile(fileName
, &fileSize
);
442 printf("File %s not found! ", fileName
);
443 applyPatch_file_open_error
:
444 printf("(S)kip, (R)etry, (A)bort, (M)anually enter filename");
449 while (c
!= 's' && c
!= 'r' && c
!= 'a' && c
!= 'm');
457 goto applyPatch_retry_file
;
465 if (fgets(buffer
, sizeof (buffer
), stdin
) == NULL
)
467 printf("fgets() failed!\n");
470 p
= strchr(buffer
, '\r');
473 p
= strchr(buffer
, '\n');
478 goto applyPatch_retry_file
;
482 /* Check file size */
483 if (fileSize
!= m_patch
.files
[i
].fileSize
)
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 */
490 goto applyPatch_file_open_error
;
496 printf("Do you want to make a backup of %s? (Y)es, (N)o, (A)bort", fileName
);
501 while (c
!= 'y' && c
!= 'n' && c
!= 'a');
505 char buffer
[MAX_PATH
];
506 _snprintf(buffer
, MAX_PATH
, "%s.bak", fileName
);
507 buffer
[MAX_PATH
-1] = '\0';
509 if (access(buffer
, 0) >= 0) /* file exists */
511 printf("File %s already exists, overwrite? (Y)es, (N)o, (A)bort", buffer
);
516 while (c
!= 'y' && c
!= 'n' && c
!= 'a');
526 if (makeBackup
&& saveFile(buffer
, file
, fileSize
) < 0)
539 for (j
= 0; j
< m_patch
.files
[i
].patchCount
; j
++)
541 int offset
= m_patch
.files
[i
].patches
[j
].offset
;
542 if (file
[offset
] != m_patch
.files
[i
].patches
[j
].expected
)
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
]);
549 file
[offset
] = m_patch
.files
[i
].patches
[j
].patched
;
553 if (saveFile(fileName
, file
, fileSize
) < 0)
561 printf("Patch applied sucessfully!\n");
572 "%s -c - Create patch\n"
573 "%s -d - Dump patch\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]);
590 if (argc
>= 2 && (strcmp(argv
[1], "-h") == 0 || strcmp(argv
[1], "--help") == 0))
595 else if (argc
>= 2 && argv
[1][0] == '-')
597 if (strcmp(argv
[1], "-c") == 0)
599 return createPatch();
601 else if (strcmp(argv
[1], "-d") == 0)
607 printf("Unknown option: %s\n"
608 "Use -h for help.\n",