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