minor filename fixes
[drnoksnes] / hacks.cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <zlib.h>
5
6 #include "snes9x.h"
7 #include "hacks.h"
8 #include "memmap.h"
9
10 #define kLineBufferSize 4095
11
12 static inline unsigned long parseCrc32(const char * s)
13 {
14         return strtoul(s, 0, 16);
15 }
16
17 static unsigned long getGameCrc32()
18 {
19         unsigned long crc = crc32(0L, Z_NULL, 0);
20         crc = crc32(crc, ROM, Memory.CalculatedSize);
21         return crc;
22 }
23
24 static int loadHacks(char * line)
25 {
26         int count = 0;
27         char *pos = strchr(line, '|'), *start = line;
28         // Skip: Title[start..pos]
29
30         start = pos + 1;
31         pos = strchr(start, '|');
32         if (!pos) return -1;
33         // Skip: Flags1[start..pos]
34
35         start = pos + 1;
36         pos = strchr(start, '|');
37         if (!pos) return -1;
38         // Skip: Flags2[start..pos]
39
40         start = pos + 1;
41         pos = strchr(start, '|');
42         if (!pos) return -1;
43         // Skip: Autoscroll1[start..pos]
44
45         start = pos + 1;
46         pos = strchr(start, '|');
47         if (!pos) return -1;
48         // Skip: Autoscroll2[start..pos]
49
50         start = pos + 1;
51         pos = strchr(start, '|');
52         if (!pos) return -1;
53         // Skip: Scale[start..pos]
54
55         start = pos + 1;
56         pos = strchr(start, '|');
57         // Skip: Offset[start..pos|end_of_line]
58
59         if (!pos) return 0; // No patches!
60
61         start = pos + 1;
62         bool end_of_line = false;
63         printf("Loading patches: %s", start);
64         do {
65                 char *end;
66                 unsigned long addr;
67                 int len;
68
69                 //start is at the "address to modify" string start
70
71                 pos = strchr(start, '=');
72                 if (!pos) return -1;
73
74                 addr = strtoul(start, &end, 16);
75                 if (end != pos) return -1;
76
77                 start = pos + 1; //start is at the "bytes to modify" string start
78                 pos = strchr(start, ',');
79                 if (pos) {
80                         len = (pos - start) / 2;
81                 } else {
82                         len = (strlen(start) - 1) / 2;
83                         end_of_line = true;
84                 }
85
86                 if (Settings.HacksFilter) {
87                         bool accept = false;
88                         // Only accept patches which contain opcode 42
89                         pos = start;
90                         for (int i = 0; i < len; i++) {
91                                 if (pos[0] == '4' && pos[1] == '2') {
92                                         accept = true;
93                                         break;
94                                 }
95                                 pos += 2;
96                         }
97
98                         if (!accept) {
99                                 start = pos + 1; // Go to end of individual patch
100                                 continue; // Skip this hack.
101                         }
102                 }
103
104                 pos = start;
105                 for (int i = 0; i < len; i++) {
106                         char valStr[3] = { pos[0], pos[1], '\0' };
107                         unsigned char val = strtoul(valStr, 0, 16);
108
109                         printf("ROM[0x%lx..0x%lx]=0x%hhx 0x%hhx 0x%hhx\n", 
110                                 addr + i - 1, addr + i + 1,
111                                 ROM[addr + i - 1], ROM[addr + i], ROM[addr + i + 1]);
112                         printf("--> ROM[0x%lx]=0x%hhx\n", addr + i, val);
113                         ROM[addr + i] = val;
114
115                         count++;
116                         pos += 2;
117                 }
118
119                 start = pos + 1;
120         } while (!end_of_line);
121
122         return count;
123 }
124
125 void S9xHacksLoadFile(const char * file)
126 {
127         if (!Settings.HacksEnabled) goto no_hacks;
128         if (!file) goto no_hacks;
129
130         // At this point, the ROM is already loaded.
131         FILE * fp = fopen(file, "r");
132         if (!fp) {
133                 fprintf(stderr, "Can't open hacks file %s: %s\n", file, strerror(errno));
134                 goto no_hacks;
135         }
136
137         const unsigned long gameCrc = getGameCrc32();
138         char * line = (char*) malloc(kLineBufferSize + 1);
139
140         do {
141                 fgets(line, kLineBufferSize, fp);
142
143                 char *pos = strchr(line, '|');
144                 if (!pos) continue;
145                 *pos = '\0';
146
147                 if (gameCrc == parseCrc32(line)) {
148                         // A hit! :)
149                         int res = loadHacks(pos + 1);
150                         if (res > 0) {
151                                 printf("Hacks: searched %s for crc32 %lx, %d hacks loaded\n",
152                                         file, gameCrc, res);
153                         } else if (res < 0) {
154                                 printf("Hacks: searched %s for crc32 %lx, error parsing line\n",
155                                         file, gameCrc);
156                         } else {
157                                 printf("Hacks: searched %s for crc32 %lx, no hacks\n",
158                                         file, gameCrc);
159                         }
160                         goto hacks_found;
161                 }
162         } while (!feof(fp) && !ferror(fp));
163
164         if (ferror(fp)) {
165                 fprintf(stderr, "Error reading hacks file: %s\n");
166         }
167
168         printf("Hacks: searched %s for crc %lu; nothing found\n", file, gameCrc);
169
170 hacks_found:
171         free(line);
172         fclose(fp);
173
174         return;
175 no_hacks:
176         printf("Hacks: disabled\n");
177 }
178