patcher.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /*
  2. patcher - Parse assembly file and apply patches.
  3. Copyright (C) 2002 Romain Liévin
  4. Copyright (C) 2002-2005 Kevin Kofler
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software Foundation,
  15. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  16. */
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <ctype.h>
  21. #include <sys/types.h>
  22. #include <unistd.h>
  23. #include <dirent.h>
  24. #include "patcher.h"
  25. #define EXIT_FAILURE 1
  26. char *output_file = NULL;
  27. int
  28. main (int argc, char **argv)
  29. {
  30. int i, fline_ROM_CALLs=0, reg_relative=0;
  31. FILE *infile, *outfile;
  32. char *temp_file;
  33. unsigned char buffer[32772]; // 4 extra bytes for the patches
  34. /* GNU stuff */
  35. program_name = argv[0];
  36. i = decode_switches (argc, argv);
  37. if ((i>=argc) || strcmp(file_extension(argv[i]),".s")) usage(EXIT_FAILURE);
  38. if (!output_file) output_file=argv[i];
  39. temp_file = malloc(strlen(argv[i])+3);
  40. if (!temp_file) {fprintf(stderr, "Fatal error: malloc failed\n");exit(EXIT_FAILURE);}
  41. strcpy(temp_file,argv[i]);change_extension(temp_file,".tmp");
  42. unlink(temp_file);
  43. outfile = fopen(temp_file, "wt");
  44. if (!outfile)
  45. {
  46. fprintf(stderr, "Unable to open the temporary file <%s>\n", argv[i]);
  47. exit(-1);
  48. }
  49. infile = fopen(argv[i], "rt");
  50. if (!infile)
  51. {
  52. fclose(outfile);
  53. unlink(temp_file);
  54. fprintf(stderr, "Unable to open the file <%s>\n", argv[i]);
  55. exit(-1);
  56. }
  57. // Copy infile line by line, applying patches if needed:
  58. while(1) {
  59. fgets(buffer, 32768, infile);
  60. if (feof(infile)) break;
  61. if (buffer[strlen(buffer)-1]=='\n') buffer[strlen(buffer)-1]=0;
  62. output_line(buffer, outfile, &fline_ROM_CALLs, &reg_relative);
  63. }
  64. fclose(outfile);
  65. fclose(infile);
  66. if (!strcmp(output_file,argv[i])) unlink(output_file);
  67. rename(temp_file,output_file);
  68. free(temp_file);
  69. exit (0);
  70. }
  71. /* Set all the option flags according to the switches specified.
  72. Return the index of the first non-option argument. */
  73. static int
  74. decode_switches (int argc, char **argv)
  75. {
  76. int c;
  77. for (c=1;c<argc;c++) {
  78. if (!strcmp(argv[c],"-V")||!strcmp(argv[c],"--version")) {
  79. printf ("patcher 1.11\n");
  80. exit(0);
  81. } else if (!strcmp(argv[c],"-h")||!strcmp(argv[c],"--help")) {
  82. usage(0);
  83. } else if (!strcmp(argv[c],"-o")) {
  84. if (c+1<argc) output_file=argv[c+1]; else usage(EXIT_FAILURE);
  85. } else break;
  86. }
  87. if (c>=argc) usage(EXIT_FAILURE);
  88. return c;
  89. }
  90. static void
  91. usage (int status)
  92. {
  93. printf ("%s - parse assembly files and apply TIGCC patches\n",
  94. program_name);
  95. printf ("Usage: %s [OPTION]... [FILE]...\n", program_name);
  96. printf ("\
  97. Options:\n\
  98. -h, --help display this help and exit\n\
  99. -V, --version output version information and exit\n\
  100. -o output_file select output file (default: same as input)\n\
  101. ");
  102. exit (status);
  103. }
  104. /* Return the filename extension or NULL */
  105. static const char *file_extension(const char *filename)
  106. {
  107. int i;
  108. const char *p;
  109. for(i=strlen(filename); i > 0; i--)
  110. {
  111. if(filename[i] == '.') break;
  112. }
  113. p=filename+i;
  114. return p;
  115. }
  116. static void output_line(unsigned char *buffer, FILE *outfile, int *pfline_ROM_CALLs, int *preg_relative)
  117. {
  118. // This is a straight C translation of Sebastian's ParseSFile Delphi source code.
  119. #define fline_ROM_CALLs (*pfline_ROM_CALLs)
  120. #define reg_relative (*preg_relative)
  121. if (*buffer) {
  122. char *p1,*p2;
  123. // detect -f-reg-relative
  124. if (!strcmp(buffer,"\t.set __relation,__ld_entry_point_plus_0x8000"))
  125. reg_relative=1;
  126. // detect F-LINE ROM_CALLs
  127. if (strstr(buffer,"_F_LINE"))
  128. fline_ROM_CALLs=1;
  129. // don't patch .ascii or .asciz commands
  130. if (strncmp(buffer,"\t.ascii",7) && strncmp(buffer,"\t.asciz",7))
  131. {
  132. // handle -f-reg-relative
  133. if (reg_relative && (p1=strstr(buffer,"-__relation"))) {
  134. char *p4=buffer;
  135. while (p1) {
  136. if ((p2=strstr(p4,"_CALL_")) && (p2<p1) && (p1-p2<10)) {
  137. char *p3=strchr(p1+12,')');
  138. if (p3) memmove(p1,p3+1,strlen(p3)); // zap "-__relation(%an)"
  139. p4=p1;
  140. } else if ((p2=strstr(p4,"_ER_CODE_")) && (p2<p1) && (p1-p2<14)) {
  141. char *p3=strchr(p1+12,')');
  142. if (p3) memmove(p1,p3+1,strlen(p3)); // zap "-__relation(%an)"
  143. p4=p1;
  144. }
  145. p1=strstr(p1+1,"-__relation");
  146. }
  147. }
  148. // handle A-LINE ER_THROW
  149. if (!strncmp(buffer,"\tjra _ER_CODE_",14) || !strncmp(buffer,"\tjmp _ER_CODE_",14)) {
  150. memmove(buffer+2,buffer+1,strlen(buffer)); // replace "jxx _ER_CODE_" with
  151. strncpy(buffer+1,".word _A_LINE+",14); // ".word _A_LINE+"
  152. } else {
  153. // handle ROM_CALLs and RAM_CALLs (always use jsr so -l works)
  154. if (!strncmp(buffer,"\tjbsr",5)&&strstr(buffer,"_CALL_"))
  155. memmove(buffer+2,buffer+3,strlen(buffer)-1); // zap the 'b'
  156. else if (!strncmp(buffer,"\tjra",4)&&strstr(buffer,"_CALL_")) {
  157. buffer[2]='m'; // jra -> jmp
  158. buffer[3]='p';
  159. }
  160. // handle __ld_calc_const
  161. else if (!strncmp(buffer,"\tmove.l #__ld_calc_const_",25)) {
  162. buffer[6]='w'; // move.l -> move.w
  163. }
  164. // handle F-LINE ROM_CALLs
  165. if (fline_ROM_CALLs && !strncmp(buffer,"\tjsr _ROM_CALL_",15) && strlen(buffer)<=18) {
  166. memmove(buffer+3,buffer+1,strlen(buffer)); // replace "jsr _ROM_CALL_"
  167. strncpy(buffer+1,".word _F_LINE+0x",16); // with ".word _F_LINE+0x"
  168. } else {
  169. // add :l to ROM_CALLs
  170. char *p4=strstr(buffer,"_ROM_CALL_");
  171. while (p4) {
  172. char *p5=p4;
  173. p4+=10;
  174. while (isalnum(*p4)||*p4=='_'||*p4=='+'||*p4=='-'||*p4=='*'||*p4=='/') p4++;
  175. while (*p4==':'||isalpha(*p4)) memmove(p4,p4+1,strlen(p4)); // zap :w if it's there
  176. if (strlen(buffer)>32769) break; // avoid buffer overflow
  177. memmove(p4+2,p4,strlen(p4)+1);
  178. *(p4++)=':'; // add ":l"
  179. *(p4++)='l';
  180. if (!strncmp(p4,"(%pc)",5)||!strncmp(p4,"(%Pc)",5)||!strncmp(p4,"(%pC)",5)||!strncmp(p4,"(%PC)",5)) {
  181. memmove(p4,p4+5,strlen(p4+5)+1);
  182. } else if ((!strncmp(p4,",%pc)",5)||!strncmp(p4,",%Pc)",5)||!strncmp(p4,",%pC)",5)
  183. ||!strncmp(p4,",%PC)",5))
  184. &&(p5>buffer)&&(p5[-1]=='(')) {
  185. memmove(p4,p4+5,strlen(p4+5)+1);
  186. memmove(p5-1,p5,strlen(p5)+1);
  187. }
  188. p4=strstr(p4,"_ROM_CALL_");
  189. }
  190. p4=strstr(buffer,"__ld_calc_const_");
  191. while (p4) {
  192. char *p5=p4;
  193. p4+=16;
  194. while ((*p4>='0'&&*p4<='9')||(*p4>='a'&&*p4<='z')||(*p4>='A'&&*p4<='Z')||(*p4=='_')||(*p4==':')) p4++;
  195. if (!strncmp(p4,"(%pc)",5)||!strncmp(p4,"(%Pc)",5)||!strncmp(p4,"(%pC)",5)||!strncmp(p4,"(%PC)",5)) {
  196. memmove(p4,p4+5,strlen(p4+5)+1);
  197. } else if ((!strncmp(p4,",%pc)",5)||!strncmp(p4,",%Pc)",5)||!strncmp(p4,",%pC)",5)
  198. ||!strncmp(p4,",%PC)",5))
  199. &&(p5>buffer)&&(p5[-1]=='(')) {
  200. memmove(p4,p4+5,strlen(p4+5)+1);
  201. memmove(p5-1,p5,strlen(p5)+1);
  202. }
  203. p4=strstr(p4,"__ld_calc_const_");
  204. }
  205. }
  206. }
  207. }
  208. fputs(buffer, outfile);
  209. fputc('\n',outfile);
  210. }
  211. #undef fline_ROM_CALLs
  212. #undef reg_relative
  213. }
  214. static char *change_extension(char *file, const char *newext)
  215. {
  216. char *start = (char *)strrchr(file, '.');
  217. if(start == NULL) {
  218. start = file; //return file;
  219. } else {
  220. sprintf(start, "%s", newext);
  221. }
  222. return file;
  223. }