Advertisement
aaaaaa123456789

ipspatch.c (IPS patch creator and applier)

May 1st, 2016
231
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 14.70 KB | None | 0 0
  1. /*
  2.    This program is hereby released to the public domain.
  3.    ~aaaaaa123456789, 2016-05-01
  4. */
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <stdarg.h>
  10. #include <ctype.h>
  11.  
  12. #define BLOCK_SIZE 8192
  13. #define MINIMUM_RUN 8
  14. #define MAXIMUM_REDUNDANCY 6
  15. // you cannot write a patch block with an offset of 4,542,278 (0x454F46) because it is the end of file marker
  16. #define EOF_MARKER 4542278
  17.  
  18. struct patch_block {
  19.   unsigned offset;
  20.   unsigned short length;
  21.   char payload[];
  22. };
  23.  
  24. int main(int, char **);
  25. void usage(const char *);
  26. void patch(const char *, const char *, const char *);
  27. void copy_contents(FILE *, FILE *, unsigned);
  28. struct patch_block * read_patch_block(FILE *);
  29. void apply_patch_block(FILE *, struct patch_block *);
  30. unsigned convert_buffer_to_number(const char *, unsigned char);
  31. void create_patch(const char *, const char *, const char *);
  32. FILE * open_file_checked(const char *, unsigned *);
  33. FILE * open_patch_checked(const char *);
  34. char get_special_value(FILE *, unsigned);
  35. void create_patch_for_data(const char *, const char *, unsigned, unsigned, FILE *, char);
  36. unsigned get_segment_length(const char *, const char *, unsigned, int);
  37. void write_patch_block_for_data(const char *, unsigned, unsigned, FILE *, char);
  38. int check_runs(const char *, unsigned);
  39. void write_value(unsigned, unsigned char, FILE *);
  40. void dump_patch(const char *);
  41. char * read_line(void);
  42. char * trim_string(const char *);
  43. void build_patch(const char *);
  44. int convert_hex_string_to_number(const char *, unsigned char);
  45. void parse_patch_code_line(char *, unsigned, unsigned, FILE *);
  46. unsigned char hex_digit_value(char);
  47.  
  48. int main (int argc, char ** argv) {
  49.   if (argc < 2) usage(*argv);
  50.   if (!strcmp(argv[1], "patch")) {
  51.     if (argc != 5) usage(*argv);
  52.     patch(argv[2], argv[3], argv[4]);
  53.   } else if (!strcmp(argv[1], "create")) {
  54.     if (argc != 5) usage(*argv);
  55.     create_patch(argv[2], argv[3], argv[4]);
  56.   } else if (!strcmp(argv[1], "dump")) {
  57.     if (argc != 3) usage(*argv);
  58.     dump_patch(argv[2]);
  59.   } else if (!strcmp(argv[1], "build")) {
  60.     if (argc != 3) usage(*argv);
  61.     build_patch(argv[2]);
  62.   } else
  63.     usage(*argv);
  64.   return 0;
  65. }
  66.  
  67. void usage (const char * program_name) {
  68.   fprintf(stderr, "usage:\n"
  69.                   "  creating a patch: %s create <file1> <file2> <output.ips>\n"
  70.                   "  applying a patch: %s patch <file> <patch.ips> <result>\n"
  71.                   "  dumping  a patch: %s dump <patch.ips> (dumps code to stdout)\n"
  72.                   "  building a patch: %s build <patch.ips> (reads code from stdin)\n",
  73.           program_name, program_name, program_name, program_name);
  74.   exit(1);
  75. }
  76.  
  77. void error_and_exit (const char * error, ...) {
  78.   va_list ap;
  79.   va_start(ap, error);
  80.   vfprintf(stderr, error, ap);
  81.   exit(1);
  82. }
  83.  
  84. void patch (const char * source, const char * patch_file, const char * output) {
  85.   unsigned file_length;
  86.   FILE * source_file = open_file_checked(source, &file_length);
  87.   FILE * patch_fp = open_patch_checked(patch_file);
  88.   FILE * target_file = fopen(output, "wb+");
  89.   copy_contents(target_file, source_file, file_length);
  90.   fclose(source_file);
  91.   struct patch_block * block;
  92.   while (!feof(patch_fp)) {
  93.     block = read_patch_block(patch_fp);
  94.     if (!block) break;
  95.     apply_patch_block(target_file, block);
  96.     free(block);
  97.   }
  98.   fclose(patch_fp);
  99.   fclose(target_file);
  100. }
  101.  
  102. void copy_contents (FILE * destination, FILE * source, unsigned length) {
  103.   char buf[BLOCK_SIZE];
  104.   unsigned next;
  105.   while (length) {
  106.     next = (length > BLOCK_SIZE) ? BLOCK_SIZE : length;
  107.     !fread(buf, 1, next, source);
  108.     fwrite(buf, 1, next, destination);
  109.     length -= next;
  110.   }
  111. }
  112.  
  113. struct patch_block * read_patch_block (FILE * fp) {
  114.   char buf[5];
  115.   unsigned offset, length;
  116.   if (fread(buf, 1, 5, fp) < 5) return NULL;
  117.   offset = convert_buffer_to_number(buf, 3);
  118.   if (offset == EOF_MARKER) return NULL;
  119.   length = convert_buffer_to_number(buf + 3, 2);
  120.   struct patch_block * result = malloc(sizeof(struct patch_block) + (length ? length : 3));
  121.   result -> offset = offset;
  122.   result -> length = length;
  123.   if (!length) length = 3;
  124.   if (fread(result -> payload, 1, length, fp) < length) {
  125.     free(result);
  126.     return NULL;
  127.   }
  128.   return result;
  129. }
  130.  
  131. void apply_patch_block (FILE * file, struct patch_block * block) {
  132.   fseek(file, block -> offset, 0);
  133.   if (block -> length) {
  134.     fwrite(block -> payload, 1, block -> length, file);
  135.     return;
  136.   }
  137.   unsigned length = convert_buffer_to_number(block -> payload, 2);
  138.   char fill = block -> payload[2];
  139.   void * buffer = malloc(length);
  140.   memset(buffer, fill, length);
  141.   fwrite(buffer, 1, length, file);
  142.   free(buffer);
  143. }
  144.  
  145. unsigned convert_buffer_to_number (const char * buffer, unsigned char length) {
  146.   unsigned result;
  147.   unsigned char p;
  148.   for (p = result = 0; p < length; p ++) result = (result << 8) | ((unsigned char) buffer[p]);
  149.   return result;
  150. }
  151.  
  152. void create_patch (const char * from, const char * to, const char * output) {
  153.   unsigned first_length, second_length;
  154.   FILE * first_file = open_file_checked(from, &first_length);
  155.   FILE * second_file = open_file_checked(to, &second_length);
  156.   if (first_length > second_length) error_and_exit("error: first file %s is larger than second file %s\n", from, to);
  157.   FILE * patch_file = fopen(output, "wb");
  158.   if (!patch_file) error_and_exit("error: cannot open %s for writing\n", output);
  159.   fwrite("PATCH", 1, 5, patch_file);
  160.   char buffer1[BLOCK_SIZE];
  161.   char buffer2[BLOCK_SIZE];
  162.   unsigned p, next;
  163.   char special = get_special_value(first_file, first_length);
  164.   for (p = 0; p < first_length; p += BLOCK_SIZE) {
  165.     next = first_length - p;
  166.     if (next > BLOCK_SIZE) next = BLOCK_SIZE;
  167.     !fread(buffer1, 1, next, first_file);
  168.     !fread(buffer2, 1, next, second_file);
  169.     create_patch_for_data(buffer1, buffer2, next, p, patch_file, special);
  170.   }
  171.   fclose(first_file);
  172.   if (first_length < second_length) {
  173.     next = second_length - first_length;
  174.     void * data = malloc(next);
  175.     !fread(data, 1, next, second_file);
  176.     write_patch_block_for_data(data, next, first_length, patch_file, special);
  177.     free(data);
  178.   }
  179.   fclose(second_file);
  180.   fwrite("EOF", 1, 3, patch_file);
  181.   fclose(patch_file);
  182. }
  183.  
  184. FILE * open_file_checked (const char * file, unsigned * file_length) {
  185.   FILE * fp = fopen(file, "rb");
  186.   if (!fp) error_and_exit("error: cannot open %s for reading\n", file);
  187.   if (fseek(fp, 0, 2)) error_and_exit("error: cannot seek on %s\n", file);
  188.   long length = ftell(fp);
  189.   if (length < 0)
  190.     error_and_exit("error: cannot determine length of %s\n", file);
  191.   else if (length > 16777216)
  192.     error_and_exit("error: %s is larger than 16 MB\n", file);
  193.   if (file_length) *file_length = length;
  194.   rewind(fp);
  195.   return fp;
  196. }
  197.  
  198. FILE * open_patch_checked (const char * patch_file) {
  199.   FILE * fp = fopen(patch_file, "rb");
  200.   if (!fp) error_and_exit("error: cannot open %s for reading\n", patch_file);
  201.   char buf[5];
  202.   if ((fread(buf, 1, 5, fp) != 5) || memcmp(buf, "PATCH", 5)) error_and_exit("error: %s is not a valid patch file\n", patch_file);
  203.   return fp;
  204. }
  205.  
  206. char get_special_value (FILE * fp, unsigned length) {
  207.   if (length < EOF_MARKER) return 0;
  208.   long pos = ftell(fp);
  209.   fseek(fp, EOF_MARKER - 1, 0);
  210.   char result;
  211.   !fread(&result, 1, 1, fp);
  212.   fseek(fp, pos, 0);
  213.   return result;
  214. }
  215.  
  216. void create_patch_for_data (const char * first_buffer, const char * second_buffer, unsigned length, unsigned offset, FILE * output, char special) {
  217.   unsigned pos = 0;
  218.   unsigned block, next;
  219.   while (pos < length) {
  220.     if (first_buffer[pos] == second_buffer[pos]) {
  221.       pos += get_segment_length(first_buffer + pos, second_buffer + pos, length - pos, 1);
  222.       continue;
  223.     }
  224.     block = 0;
  225.     next = 0;
  226.     do {
  227.       block += next;
  228.       block += get_segment_length(first_buffer + pos + block, second_buffer + pos + block, length - pos - block, 0);
  229.       next = get_segment_length(first_buffer + pos + block, second_buffer + pos + block, length - pos - block, 1);
  230.     } while (((pos + block) < length) && (next < MAXIMUM_REDUNDANCY));
  231.     write_patch_block_for_data(second_buffer + pos, block, offset + pos, output, special);
  232.     pos += block;
  233.   }
  234. }
  235.  
  236. unsigned get_segment_length (const char * first_buffer, const char * second_buffer, unsigned length, int kind) {
  237.   // 0: differing, 1: equal
  238.   unsigned pos;
  239.   for (pos = 0; (pos < length) && ((first_buffer[pos] == second_buffer[pos]) == kind); pos ++);
  240.   return pos;
  241. }
  242.  
  243. void write_patch_block_for_data (const char * data, unsigned length, unsigned offset, FILE * output, char special) {
  244.   if (!length) return;
  245.   if (offset == EOF_MARKER) {
  246.     char * buf = malloc(length + 1);
  247.     *buf = special;
  248.     memcpy(buf + 1, data, length);
  249.     write_patch_block_for_data(buf, length + 1, EOF_MARKER - 1, output, special);
  250.     free(buf);
  251.     return;
  252.   }
  253.   unsigned run;
  254.   for (run = 1; (run < length) && (data[run] == *data); run ++);
  255.   if ((offset != EOF_MARKER) && (run >= MINIMUM_RUN)) {
  256.     if (run > 32767) run = 32767;
  257.     write_value(offset, 3, output);
  258.     write_value(0, 2, output);
  259.     write_value(run, 2, output);
  260.     write_value(*data, 1, output);
  261.     write_patch_block_for_data(data + run, length - run, offset + run, output, special);
  262.     return;
  263.   }
  264.   unsigned current, remainder;
  265.   if (length < 32768) {
  266.     current = length;
  267.     remainder = 0;
  268.   } else {
  269.     current = 32767;
  270.     remainder = length - 32767;
  271.   }
  272.   int run_pos = check_runs(data, current);
  273.   if (run_pos > 0) {
  274.     remainder += current - run_pos;
  275.     current = run_pos;
  276.     if ((offset + current) == EOF_MARKER) {
  277.       remainder --;
  278.       current ++;
  279.     }
  280.   }
  281.   write_value(offset, 3, output);
  282.   write_value(current, 2, output);
  283.   fwrite(data, 1, current, output);
  284.   if (remainder) write_patch_block_for_data(data + current, remainder, offset + current, output, special);
  285. }
  286.  
  287. int check_runs (const char * data, unsigned length) {
  288.   unsigned pos, cmp;
  289.   if (length < MINIMUM_RUN) return -1;
  290.   for (pos = 0; pos <= (length - MINIMUM_RUN); pos ++) {
  291.     for (cmp = 0; (cmp < MINIMUM_RUN) && (data[pos] == data[pos + cmp]); cmp ++);
  292.     if (cmp >= MINIMUM_RUN) return pos;
  293.   }
  294.   return -1;
  295. }
  296.  
  297. void write_value (unsigned value, unsigned char length, FILE * file) {
  298.   char buf[16];
  299.   unsigned char p;
  300.   for (p = 0; p < length; p ++) buf[p] = (value >> (8 * (length - p - 1))) & 255;
  301.   fwrite(buf, 1, length, file);
  302. }
  303.  
  304. void dump_patch (const char * patch_file) {
  305.   FILE * fp = open_patch_checked(patch_file);
  306.   struct patch_block * block;
  307.   while (!feof(fp)) {
  308.     block = read_patch_block(fp);
  309.     if (!block) break;
  310.     printf("%06x:", block -> offset);
  311.     if (block -> length) {
  312.       unsigned p;
  313.       for (p = 0; p < block -> length; p ++) printf(" %02hhx", block -> payload[p]);
  314.       putchar('\n');
  315.     } else
  316.       printf(" %04x * %02hhx\n", convert_buffer_to_number(block -> payload, 2), block -> payload[2]);
  317.     free(block);
  318.   }
  319.   fclose(fp);
  320. }
  321.  
  322. char * read_line (void) {
  323.   int character;
  324.   char * result = NULL;
  325.   unsigned length = 0;
  326.   while (1) {
  327.     character = getchar();
  328.     if ((character == EOF) || (character == '\n')) break;
  329.     result = realloc(result, length + 1);
  330.     result[length ++] = character;
  331.   }
  332.   result = realloc(result, length + 1);
  333.   result[length] = 0;
  334.   return result;
  335. }
  336.  
  337. char * trim_string (const char * string) {
  338.   if (!string) return NULL;
  339.   char * result = malloc(strlen(string) + 1);
  340.   while (isspace(*string)) string ++;
  341.   if (!*string) {
  342.     *result = 0;
  343.     return result;
  344.   }
  345.   unsigned last = strlen(string);
  346.   while (isspace(string[last - 1])) last --;
  347.   strncpy(result, string, last);
  348.   result[last] = 0;
  349.   return result;
  350. }
  351.  
  352. void build_patch (const char * patch_file) {
  353.   FILE * fp = fopen(patch_file, "wb");
  354.   if (!fp) error_and_exit("error: could not open %s for writing\n", patch_file);
  355.   fwrite("PATCH", 1, 5, fp);
  356.   char * line;
  357.   char * split;
  358.   char * address_line;
  359.   unsigned line_number = 0;
  360.   int address;
  361.   while (!feof(stdin)) {
  362.     line = read_line();
  363.     line_number ++;
  364.     if (!(line && *line)) goto comment;
  365.     if (*line == ':') goto comment;
  366.     split = strchr(line, ':');
  367.     if (!split) goto comment;
  368.     *split = 0;
  369.     address_line = trim_string(line);
  370.     address = convert_hex_string_to_number(address_line, 6);
  371.     if (address < 0)
  372.       fprintf(stderr, "warning: line %u: address %s is not valid\n", line_number, line);
  373.     else if (address == EOF_MARKER)
  374.       fprintf(stderr, "warning: line %u: address matches end-of-file marker 0x%06x\n", line_number, EOF_MARKER);
  375.     else
  376.       parse_patch_code_line(split + 1, address, line_number, fp);
  377.     free(address_line);
  378.     comment:
  379.     free(line);
  380.   }
  381.   fwrite("EOF", 1, 3, fp);
  382.   fclose(fp);
  383. }
  384.  
  385. int convert_hex_string_to_number (const char * string, unsigned char limit) {
  386.   if (!(string && *string)) return -1;
  387.   unsigned long min_overflow = 1UL << (4 * limit);
  388.   char * errp;
  389.   unsigned long converted = strtoul(string, &errp, 16);
  390.   if (*errp) return -1;
  391.   if (converted >= min_overflow) return -1;
  392.   return converted;
  393. }
  394.  
  395. void parse_patch_code_line (char * contents, unsigned address, unsigned line_number, FILE * output) {
  396.   char * split = strchr(contents, '*');
  397.   char * tmp;
  398.   if (split) {
  399.     int length, data;
  400.     *split = 0;
  401.     tmp = trim_string(contents);
  402.     length = convert_hex_string_to_number(tmp, 4);
  403.     free(tmp);
  404.     tmp = trim_string(split + 1);
  405.     data = convert_hex_string_to_number(tmp, 2);
  406.     free(tmp);
  407.     if ((length < 0) || (data < 0)) {
  408.       fprintf(stderr, "warning: line %u: invalid contents for address 0x%06x\n", line_number, address);
  409.       return;
  410.     }
  411.     write_value(address, 3, output);
  412.     write_value(0, 2, output);
  413.     write_value(length, 2, output);
  414.     write_value(data, 1, output);
  415.     return;
  416.   }
  417.   unsigned char * buf = malloc(65536);
  418.   unsigned char last_digit = 255;
  419.   unsigned length = 0;
  420.   for (; *contents; contents ++) {
  421.     if (isspace(*contents)) continue;
  422.     if (!isxdigit(*contents)) {
  423.       fprintf(stderr, "warning: line %u: invalid contents for address 0x%06x\n", line_number, address);
  424.       free(buf);
  425.       return;
  426.     }
  427.     if (last_digit == 255)
  428.       last_digit = hex_digit_value(*contents);
  429.     else {
  430.       buf[length ++] = (last_digit << 4) | hex_digit_value(*contents);
  431.       if (length > 65535) {
  432.         fprintf(stderr, "warning: line %u: invalid contents for address 0x%06x\n", line_number, address);
  433.         free(buf);
  434.         return;
  435.       }
  436.       last_digit = 255;
  437.     }
  438.   }
  439.   write_value(address, 3, output);
  440.   write_value(length, 2, output);
  441.   fwrite(buf, 1, length, output);
  442.   free(buf);
  443. }
  444.  
  445. unsigned char hex_digit_value (char digit) {
  446.   if (digit < 'A') return digit - 48;
  447.   return (digit & ~32) - 55;
  448. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement