src/gba/cheats.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "cheats.h"
7
8#include "gba/cheats/gameshark.h"
9#include "gba/cheats/parv3.h"
10#include "gba/gba.h"
11#include "util/string.h"
12
13#define MAX_LINE_LENGTH 128
14
15static void _addBreakpoint(struct mCheatDevice* device, struct GBACheatSet* cheats) {
16 if (!device->p || !cheats->hook) {
17 return;
18 }
19 ++cheats->hook->reentries;
20 if (cheats->hook->reentries > 1) {
21 return;
22 }
23 // TODO: Put back hooks
24}
25
26static void _removeBreakpoint(struct mCheatDevice* device, struct GBACheatSet* cheats) {
27 if (!device->p || !cheats->hook) {
28 return;
29 }
30 --cheats->hook->reentries;
31 if (cheats->hook->reentries > 0) {
32 return;
33 }
34 // TODO: Put back hooks
35}
36
37static void _patchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) {
38 if (!device->p) {
39 return;
40 }
41 int i;
42 for (i = 0; i < MAX_ROM_PATCHES; ++i) {
43 if (!cheats->romPatches[i].exists || cheats->romPatches[i].applied) {
44 continue;
45 }
46 GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].newValue, &cheats->romPatches[i].oldValue);
47 cheats->romPatches[i].applied = true;
48 }
49}
50
51static void _unpatchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) {
52 if (!device->p) {
53 return;
54 }
55 int i;
56 for (i = 0; i < MAX_ROM_PATCHES; ++i) {
57 if (!cheats->romPatches[i].exists || !cheats->romPatches[i].applied) {
58 continue;
59 }
60 GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].oldValue, 0);
61 cheats->romPatches[i].applied = false;
62 }
63}
64
65static void GBACheatSetDeinit(struct mCheatSet* set);
66static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device);
67static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device);
68static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device);
69static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet);
70static void GBACheatParseDirectives(struct mCheatSet*, const struct StringList* directives);
71static void GBACheatDumpDirectives(struct mCheatSet*, struct StringList* directives);
72static bool GBACheatAddLine(struct mCheatSet*, const char* line, int type);
73
74static struct mCheatSet* GBACheatSetCreate(struct mCheatDevice* device, const char* name) {
75 UNUSED(device);
76 struct GBACheatSet* set = malloc(sizeof(*set));
77 mCheatSetInit(&set->d, name);
78 set->incompleteCheat = -1;
79 set->incompletePatch = 0;
80 set->currentBlock = -1;
81 set->gsaVersion = 0;
82 set->cbRngState = 0;
83 set->cbMaster = 0;
84 set->remainingAddresses = 0;
85 set->hook = NULL;
86
87 set->d.deinit = GBACheatSetDeinit;
88 set->d.add = GBACheatAddSet;
89 set->d.remove = GBACheatRemoveSet;
90
91 set->d.addLine = GBACheatAddLine;
92 set->d.copyProperties = GBACheatSetCopyProperties;
93
94 set->d.parseDirectives = GBACheatParseDirectives;
95 set->d.dumpDirectives = GBACheatDumpDirectives;
96
97 set->d.refresh = GBACheatRefresh;
98
99 int i;
100 for (i = 0; i < MAX_ROM_PATCHES; ++i) {
101 set->romPatches[i].exists = false;
102 }
103 return &set->d;
104}
105
106struct mCheatDevice* GBACheatDeviceCreate(void) {
107 struct mCheatDevice* device = malloc(sizeof(*device));
108 mCheatDeviceCreate(device);
109 device->createSet = GBACheatSetCreate;
110 return device;
111}
112
113static void GBACheatSetDeinit(struct mCheatSet* set) {
114 struct GBACheatSet* gbaset = (struct GBACheatSet*) set;
115 if (gbaset->hook) {
116 --gbaset->hook->refs;
117 if (gbaset->hook->refs == 0) {
118 free(gbaset->hook);
119 }
120 }
121}
122
123static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
124 struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
125 _addBreakpoint(device, gbaset);
126 _patchROM(device, gbaset);
127}
128
129static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
130 struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
131 _unpatchROM(device, gbaset);
132 _removeBreakpoint(device, gbaset);
133}
134
135static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
136 uint32_t o1 = op1;
137 uint32_t o2 = op2;
138 char line[18] = "XXXXXXXX XXXXXXXX";
139 snprintf(line, sizeof(line), "%08X %08X", op1, op2);
140
141 int gsaP, parP;
142 switch (set->gsaVersion) {
143 case 0:
144 // Try to detect GameShark version
145 GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
146 gsaP = GBACheatGameSharkProbability(o1, o2);
147 o1 = op1;
148 o2 = op2;
149 GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
150 parP = GBACheatProActionReplayProbability(o1, o2);
151 o1 = op1;
152 o2 = op2;
153 if (gsaP > parP) {
154 GBACheatSetGameSharkVersion(set, 1);
155 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
156 return GBACheatAddGameSharkRaw(set, o1, o2);
157 } else {
158 // If probabilities are equal, assume PARv3
159 GBACheatSetGameSharkVersion(set, 3);
160 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
161 return GBACheatAddProActionReplayRaw(set, o1, o2);
162 }
163 break;
164 case 1:
165 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
166 return GBACheatAddGameSharkRaw(set, o1, o2);
167 case 3:
168 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
169 return GBACheatAddProActionReplayRaw(set, o1, o2);
170 }
171 return false;
172}
173
174bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
175 uint32_t address;
176 uint8_t op;
177 uint32_t value = 0;
178 int width = 0;
179 const char* lineNext = hex32(line, &address);
180 if (!lineNext) {
181 return false;
182 }
183 if (lineNext[0] != ':') {
184 return false;
185 }
186 ++lineNext;
187 while (width < 4) {
188 lineNext = hex8(lineNext, &op);
189 if (!lineNext) {
190 break;
191 }
192 value <<= 8;
193 value |= op;
194 ++width;
195 }
196 if (width == 0 || width == 3) {
197 return false;
198 }
199
200 struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
201 cheat->address = address;
202 cheat->operandOffset = 0;
203 cheat->addressOffset = 0;
204 cheat->repeat = 1;
205 cheat->type = CHEAT_ASSIGN;
206 cheat->width = width;
207 cheat->operand = value;
208 return true;
209}
210
211bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) {
212 struct GBACheatSet* cheats = (struct GBACheatSet*) set;
213 switch (type) {
214 case GBA_CHEAT_AUTODETECT:
215 break;
216 case GBA_CHEAT_CODEBREAKER:
217 return GBACheatAddCodeBreakerLine(cheats, line);
218 case GBA_CHEAT_GAMESHARK:
219 return GBACheatAddGameSharkLine(cheats, line);
220 case GBA_CHEAT_PRO_ACTION_REPLAY:
221 return GBACheatAddProActionReplayLine(cheats, line);
222 case GBA_CHEAT_VBA:
223 return GBACheatAddVBALine(cheats, line);
224 default:
225 return false;
226 }
227
228 uint32_t op1;
229 uint16_t op2;
230 uint16_t op3;
231 const char* lineNext = hex32(line, &op1);
232 if (!lineNext) {
233 return false;
234 }
235 if (lineNext[0] == ':') {
236 return GBACheatAddVBALine(cheats, line);
237 }
238 while (isspace((int) lineNext[0])) {
239 ++lineNext;
240 }
241 lineNext = hex16(lineNext, &op2);
242 if (!lineNext) {
243 return false;
244 }
245 if (!lineNext[0] || isspace((int) lineNext[0])) {
246 return GBACheatAddCodeBreaker(cheats, op1, op2);
247 }
248 lineNext = hex16(lineNext, &op3);
249 if (!lineNext) {
250 return false;
251 }
252 uint32_t realOp2 = op2;
253 realOp2 <<= 16;
254 realOp2 |= op3;
255 return GBACheatAddAutodetect(cheats, op1, realOp2);
256}
257
258static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
259 struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
260 _patchROM(device, gbaset);
261}
262
263static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
264 struct GBACheatSet* newSet = (struct GBACheatSet*) set;
265 struct GBACheatSet* gbaset = (struct GBACheatSet*) oldSet;
266 newSet->gsaVersion = gbaset->gsaVersion;
267 memcpy(newSet->gsaSeeds, gbaset->gsaSeeds, sizeof(newSet->gsaSeeds));
268 newSet->cbRngState = gbaset->cbRngState;
269 newSet->cbMaster = gbaset->cbMaster;
270 memcpy(newSet->cbSeeds, gbaset->cbSeeds, sizeof(newSet->cbSeeds));
271 memcpy(newSet->cbTable, gbaset->cbTable, sizeof(newSet->cbTable));
272 if (gbaset->hook) {
273 if (newSet->hook) {
274 --newSet->hook->refs;
275 if (newSet->hook->refs == 0) {
276 free(newSet->hook);
277 }
278 }
279 newSet->hook = gbaset->hook;
280 ++newSet->hook->refs;
281 }
282}
283
284static void GBACheatParseDirectives(struct mCheatSet* set, const struct StringList* directives) {
285 struct GBACheatSet* cheats = (struct GBACheatSet*) set;
286 size_t d;
287 for (d = 0; d < StringListSize(directives); ++d) {
288 const char* directive = *StringListGetConstPointer(directives, d);
289 if (strcmp(directive, "GSAv1") == 0) {
290 GBACheatSetGameSharkVersion(cheats, 1);
291 continue;
292 }
293 if (strcmp(directive, "PARv3") == 0) {
294 GBACheatSetGameSharkVersion(cheats, 3);
295 continue;
296 }
297 }
298}
299
300static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* directives) {
301 struct GBACheatSet* cheats = (struct GBACheatSet*) set;
302
303 // TODO: Check previous directives
304 size_t d;
305 for (d = 0; d < StringListSize(directives); ++d) {
306 free(*StringListGetPointer(directives, d));
307 }
308 StringListClear(directives);
309
310 char** directive;
311 switch (cheats->gsaVersion) {
312 case 1:
313 case 2:
314 directive = StringListAppend(directives);
315 *directive = strdup("GSAv1");
316 break;
317 case 3:
318 case 4:
319 directive = StringListAppend(directives);
320 *directive = strdup("PARv3");
321 break;
322 }
323}
324
325int GBACheatAddressIsReal(uint32_t address) {
326 switch (address >> BASE_OFFSET) {
327 case REGION_BIOS:
328 return -0x80;
329 break;
330 case REGION_WORKING_RAM:
331 if ((address & OFFSET_MASK) > SIZE_WORKING_RAM) {
332 return -0x40;
333 }
334 return 0x20;
335 case REGION_WORKING_IRAM:
336 if ((address & OFFSET_MASK) > SIZE_WORKING_IRAM) {
337 return -0x40;
338 }
339 return 0x20;
340 case REGION_IO:
341 if ((address & OFFSET_MASK) > SIZE_IO) {
342 return -0x80;
343 }
344 return 0x10;
345 case REGION_OAM:
346 if ((address & OFFSET_MASK) > SIZE_OAM) {
347 return -0x80;
348 }
349 return -0x8;
350 case REGION_VRAM:
351 if ((address & OFFSET_MASK) > SIZE_VRAM) {
352 return -0x80;
353 }
354 return -0x8;
355 case REGION_PALETTE_RAM:
356 if ((address & OFFSET_MASK) > SIZE_PALETTE_RAM) {
357 return -0x80;
358 }
359 return -0x8;
360 case REGION_CART0:
361 case REGION_CART0_EX:
362 case REGION_CART1:
363 case REGION_CART1_EX:
364 case REGION_CART2:
365 case REGION_CART2_EX:
366 return -0x8;
367 case REGION_CART_SRAM:
368 case REGION_CART_SRAM_MIRROR:
369 if ((address & OFFSET_MASK) > SIZE_CART_SRAM) {
370 return -0x80;
371 }
372 return -0x8;
373 default:
374 return -0xC0;
375 }
376}