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 // TODO: Put back hooks
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 // TODO: Put back hooks
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, parP;
143 switch (set->gsaVersion) {
144 case 0:
145 // Try to detect GameShark version
146 GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
147 gsaP = GBACheatGameSharkProbability(o1, o2);
148 o1 = op1;
149 o2 = op2;
150 GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
151 parP = GBACheatProActionReplayProbability(o1, o2);
152 o1 = op1;
153 o2 = op2;
154 if (gsaP > parP) {
155 GBACheatSetGameSharkVersion(set, 1);
156 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
157 return GBACheatAddGameSharkRaw(set, o1, o2);
158 } else {
159 // If probabilities are equal, assume PARv3
160 GBACheatSetGameSharkVersion(set, 3);
161 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
162 return GBACheatAddProActionReplayRaw(set, o1, o2);
163 }
164 break;
165 case 1:
166 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
167 return GBACheatAddGameSharkRaw(set, o1, o2);
168 case 3:
169 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
170 return GBACheatAddProActionReplayRaw(set, o1, o2);
171 }
172 return false;
173}
174
175bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
176 uint32_t address;
177 uint8_t op;
178 uint32_t value = 0;
179 int width = 0;
180 const char* lineNext = hex32(line, &address);
181 if (!lineNext) {
182 return false;
183 }
184 if (lineNext[0] != ':') {
185 return false;
186 }
187 ++lineNext;
188 while (width < 4) {
189 lineNext = hex8(lineNext, &op);
190 if (!lineNext) {
191 break;
192 }
193 value <<= 8;
194 value |= op;
195 ++width;
196 }
197 if (width == 0 || width == 3) {
198 return false;
199 }
200
201 struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
202 cheat->address = address;
203 cheat->operandOffset = 0;
204 cheat->addressOffset = 0;
205 cheat->repeat = 1;
206 cheat->type = CHEAT_ASSIGN;
207 cheat->width = width;
208 cheat->operand = value;
209 return true;
210}
211
212bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) {
213 struct GBACheatSet* cheats = (struct GBACheatSet*) set;
214 switch (type) {
215 case GBA_CHEAT_AUTODETECT:
216 break;
217 case GBA_CHEAT_CODEBREAKER:
218 return GBACheatAddCodeBreakerLine(cheats, line);
219 case GBA_CHEAT_GAMESHARK:
220 return GBACheatAddGameSharkLine(cheats, line);
221 case GBA_CHEAT_PRO_ACTION_REPLAY:
222 return GBACheatAddProActionReplayLine(cheats, line);
223 case GBA_CHEAT_VBA:
224 return GBACheatAddVBALine(cheats, line);
225 default:
226 return false;
227 }
228
229 uint32_t op1;
230 uint16_t op2;
231 uint16_t op3;
232 const char* lineNext = hex32(line, &op1);
233 if (!lineNext) {
234 return false;
235 }
236 if (lineNext[0] == ':') {
237 return GBACheatAddVBALine(cheats, line);
238 }
239 while (isspace((int) lineNext[0])) {
240 ++lineNext;
241 }
242 lineNext = hex16(lineNext, &op2);
243 if (!lineNext) {
244 return false;
245 }
246 if (!lineNext[0] || isspace((int) lineNext[0])) {
247 return GBACheatAddCodeBreaker(cheats, op1, op2);
248 }
249 lineNext = hex16(lineNext, &op3);
250 if (!lineNext) {
251 return false;
252 }
253 uint32_t realOp2 = op2;
254 realOp2 <<= 16;
255 realOp2 |= op3;
256 return GBACheatAddAutodetect(cheats, op1, realOp2);
257}
258
259static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
260 struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
261 _patchROM(device, gbaset);
262}
263
264static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
265 struct GBACheatSet* newSet = (struct GBACheatSet*) set;
266 struct GBACheatSet* gbaset = (struct GBACheatSet*) oldSet;
267 newSet->gsaVersion = gbaset->gsaVersion;
268 memcpy(newSet->gsaSeeds, gbaset->gsaSeeds, sizeof(newSet->gsaSeeds));
269 newSet->cbRngState = gbaset->cbRngState;
270 newSet->cbMaster = gbaset->cbMaster;
271 memcpy(newSet->cbSeeds, gbaset->cbSeeds, sizeof(newSet->cbSeeds));
272 memcpy(newSet->cbTable, gbaset->cbTable, sizeof(newSet->cbTable));
273 if (gbaset->hook) {
274 if (newSet->hook) {
275 --newSet->hook->refs;
276 if (newSet->hook->refs == 0) {
277 free(newSet->hook);
278 }
279 }
280 newSet->hook = gbaset->hook;
281 ++newSet->hook->refs;
282 }
283}
284
285static void GBACheatParseDirectives(struct mCheatSet* set, const struct StringList* directives) {
286 struct GBACheatSet* cheats = (struct GBACheatSet*) set;
287 size_t d;
288 for (d = 0; d < StringListSize(directives); ++d) {
289 const char* directive = *StringListGetConstPointer(directives, d);
290 if (strcmp(directive, "GSAv1") == 0) {
291 GBACheatSetGameSharkVersion(cheats, 1);
292 continue;
293 }
294 if (strcmp(directive, "PARv3") == 0) {
295 GBACheatSetGameSharkVersion(cheats, 3);
296 continue;
297 }
298 }
299}
300
301static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* directives) {
302 struct GBACheatSet* cheats = (struct GBACheatSet*) set;
303
304 // TODO: Check previous directives
305 size_t d;
306 for (d = 0; d < StringListSize(directives); ++d) {
307 free(*StringListGetPointer(directives, d));
308 }
309 StringListClear(directives);
310
311 char** directive;
312 switch (cheats->gsaVersion) {
313 case 1:
314 case 2:
315 directive = StringListAppend(directives);
316 *directive = strdup("GSAv1");
317 break;
318 case 3:
319 case 4:
320 directive = StringListAppend(directives);
321 *directive = strdup("PARv3");
322 break;
323 }
324}
325
326int GBACheatAddressIsReal(uint32_t address) {
327 switch (address >> BASE_OFFSET) {
328 case REGION_BIOS:
329 return -0x80;
330 break;
331 case REGION_WORKING_RAM:
332 if ((address & OFFSET_MASK) > SIZE_WORKING_RAM) {
333 return -0x40;
334 }
335 return 0x20;
336 case REGION_WORKING_IRAM:
337 if ((address & OFFSET_MASK) > SIZE_WORKING_IRAM) {
338 return -0x40;
339 }
340 return 0x20;
341 case REGION_IO:
342 if ((address & OFFSET_MASK) > SIZE_IO) {
343 return -0x80;
344 }
345 return 0x10;
346 case REGION_OAM:
347 if ((address & OFFSET_MASK) > SIZE_OAM) {
348 return -0x80;
349 }
350 return -0x8;
351 case REGION_VRAM:
352 if ((address & OFFSET_MASK) > SIZE_VRAM) {
353 return -0x80;
354 }
355 return -0x8;
356 case REGION_PALETTE_RAM:
357 if ((address & OFFSET_MASK) > SIZE_PALETTE_RAM) {
358 return -0x80;
359 }
360 return -0x8;
361 case REGION_CART0:
362 case REGION_CART0_EX:
363 case REGION_CART1:
364 case REGION_CART1_EX:
365 case REGION_CART2:
366 case REGION_CART2_EX:
367 return -0x8;
368 case REGION_CART_SRAM:
369 case REGION_CART_SRAM_MIRROR:
370 if ((address & OFFSET_MASK) > SIZE_CART_SRAM) {
371 return -0x80;
372 }
373 return -0x8;
374 default:
375 return -0xC0;
376 }
377}