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