png screenshot support
[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         bool end_of_line = false;
28         char *pos = strchr(line, '|'), *start = line;
29         // Title[start..pos]
30         *pos = '\0';
31         printf("Hacks: detected \"%s\"\n", start);
32
33         start = pos + 1;
34         pos = strchr(start, '|');
35         if (!pos) {
36                 // If there are no flags, jump directly to hacks
37                 goto parse_hacks;
38         }
39         // Skip: Flags1[start..pos]
40
41         start = pos + 1;
42         pos = strchr(start, '|');
43         if (!pos) return -1;
44         // Skip: Flags2[start..pos]
45
46         start = pos + 1;
47         pos = strchr(start, '|');
48         if (!pos) return -1;
49         // Skip: Autoscroll1[start..pos]
50
51         start = pos + 1;
52         pos = strchr(start, '|');
53         if (!pos) return -1;
54         // Skip: Autoscroll2[start..pos]
55
56         start = pos + 1;
57         pos = strchr(start, '|');
58         if (!pos) return -1;
59         // Skip: Scale[start..pos]
60
61         start = pos + 1;
62         pos = strchr(start, '|');
63         // Skip: Offset[start..pos|end_of_line]
64
65         if (!pos) return 0; // No patches!
66
67         start = pos + 1;
68
69 parse_hacks:
70         do {
71                 char *end;
72                 unsigned long addr;
73                 int len;
74
75                 //start is at the "address to modify" string start
76
77                 pos = strchr(start, '=');
78                 if (!pos) return -1;
79
80                 addr = strtoul(start, &end, 16);
81                 if (end != pos) return -1;
82
83                 start = pos + 1; //start is at the "bytes to modify" string start
84                 pos = strchr(start, ',');
85                 if (pos) {
86                         len = (pos - start) / 2;
87                 } else {
88                         len = (strlen(start) - 1) / 2;
89                         end_of_line = true;
90                 }
91
92                 if (Settings.HacksFilter) {
93                         bool accept = false;
94                         // Only accept patches which contain opcode 42
95                         pos = start;
96                         for (int i = 0; i < len; i++) {
97                                 if (pos[0] == '4' && pos[1] == '2') {
98                                         accept = true;
99                                         break;
100                                 }
101                                 pos += 2;
102                         }
103
104                         if (!accept) {
105                                 start = pos + 1; // Go to end of individual patch
106                                 continue; // Skip this hack.
107                         }
108                 }
109
110                 pos = start;
111                 for (int i = 0; i < len; i++) {
112                         char valStr[3] = { pos[0], pos[1], '\0' };
113                         unsigned char val = strtoul(valStr, 0, 16);
114
115 #ifdef DEBUG
116                         printf("ROM[0x%lx..0x%lx]=0x%hhx 0x%hhx 0x%hhx\n", 
117                                 addr + i - 1, addr + i + 1,
118                                 ROM[addr + i - 1], ROM[addr + i], ROM[addr + i + 1]);
119                         printf("--> ROM[0x%lx]=0x%hhx\n", addr + i, val);
120 #endif
121                         ROM[addr + i] = val;
122
123                         count++;
124                         pos += 2;
125                 }
126
127                 start = pos + 1;
128         } while (!end_of_line);
129
130         return count;
131 }
132
133 void S9xHacksLoadFile(const char * file)
134 {
135         unsigned long gameCrc;
136         char * line;
137         FILE * fp;
138
139         if (!Settings.HacksEnabled) goto no_hacks;
140         if (!file) goto no_hacks;
141
142         // At this point, the ROM is already loaded.
143         fp = fopen(file, "r");
144         if (!fp) {
145                 fprintf(stderr, "Can't open hacks file %s: %s\n", file, strerror(errno));
146                 goto no_hacks;
147         }
148
149         // Get current ROM CRC
150         gameCrc = getGameCrc32();
151
152         line = (char*) malloc(kLineBufferSize + 1);
153         do {
154                 line = fgets(line, kLineBufferSize, fp);
155                 if (!line) break;
156
157                 char *pos = strchr(line, '|');
158                 if (!pos) continue;
159                 *pos = '\0';
160
161                 if (gameCrc == parseCrc32(line)) {
162                         // Hit! This line's CRC matches our current ROM CRC.
163                         int res = loadHacks(pos + 1);
164                         if (res > 0) {
165                                 printf("Hacks: searched %s for crc %lX, %d byte%s patched\n",
166                                         file, gameCrc, res, (res == 1 ? "" : "s"));
167                         } else if (res < 0) {
168                                 printf("Hacks: searched %s for crc %lX, error parsing line\n",
169                                         file, gameCrc);
170                         } else {
171                                 printf("Hacks: searched %s for crc %lX, no hacks\n",
172                                         file, gameCrc);
173                         }
174                         goto hacks_found;
175                 }
176         } while (!feof(fp) && !ferror(fp));
177
178         if (ferror(fp)) {
179                 fprintf(stderr, "Error reading hacks file: %s\n", file);
180         }
181
182         printf("Hacks: searched %s for crc %lX; nothing found\n", file, gameCrc);
183
184 hacks_found:
185         free(line);
186         fclose(fp);
187
188         return;
189 no_hacks:
190         printf("Hacks: disabled\n");
191 }
192