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#include "util/vfs.h"
13
14#define MAX_LINE_LENGTH 128
15
16const uint32_t GBA_CHEAT_DEVICE_ID = 0xABADC0DE;
17
18mLOG_DEFINE_CATEGORY(CHEATS, "Cheats");
19
20DEFINE_VECTOR(GBACheatList, struct GBACheat);
21DEFINE_VECTOR(GBACheatSets, struct GBACheatSet*);
22DEFINE_VECTOR(StringList, char*);
23
24static int32_t _readMem(struct ARMCore* cpu, uint32_t address, int width) {
25 switch (width) {
26 case 1:
27 return cpu->memory.load8(cpu, address, 0);
28 case 2:
29 return cpu->memory.load16(cpu, address, 0);
30 case 4:
31 return cpu->memory.load32(cpu, address, 0);
32 }
33 return 0;
34}
35
36static void _writeMem(struct ARMCore* cpu, uint32_t address, int width, int32_t value) {
37 switch (width) {
38 case 1:
39 cpu->memory.store8(cpu, address, value, 0);
40 break;
41 case 2:
42 cpu->memory.store16(cpu, address, value, 0);
43 break;
44 case 4:
45 cpu->memory.store32(cpu, address, value, 0);
46 break;
47 }
48}
49
50void GBACheatRegisterLine(struct GBACheatSet* cheats, const char* line) {
51 *StringListAppend(&cheats->lines) = strdup(line);
52}
53
54static void _addBreakpoint(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
55 if (!device->p || !cheats->hook) {
56 return;
57 }
58 ++cheats->hook->reentries;
59 if (cheats->hook->reentries > 1) {
60 return;
61 }
62 GBASetBreakpoint(device->p, &device->d, cheats->hook->address, cheats->hook->mode, &cheats->hook->patchedOpcode);
63}
64
65static void _removeBreakpoint(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
66 if (!device->p || !cheats->hook) {
67 return;
68 }
69 --cheats->hook->reentries;
70 if (cheats->hook->reentries > 0) {
71 return;
72 }
73 GBAClearBreakpoint(device->p, cheats->hook->address, cheats->hook->mode, cheats->hook->patchedOpcode);
74}
75
76static void _patchROM(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
77 if (!device->p) {
78 return;
79 }
80 int i;
81 for (i = 0; i < MAX_ROM_PATCHES; ++i) {
82 if (!cheats->romPatches[i].exists || cheats->romPatches[i].applied) {
83 continue;
84 }
85 GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].newValue, &cheats->romPatches[i].oldValue);
86 cheats->romPatches[i].applied = true;
87 }
88}
89
90static void _unpatchROM(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
91 if (!device->p) {
92 return;
93 }
94 int i;
95 for (i = 0; i < MAX_ROM_PATCHES; ++i) {
96 if (!cheats->romPatches[i].exists || !cheats->romPatches[i].applied) {
97 continue;
98 }
99 GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].oldValue, 0);
100 cheats->romPatches[i].applied = false;
101 }
102}
103
104static void GBACheatDeviceInit(void*, struct mCPUComponent*);
105static void GBACheatDeviceDeinit(struct mCPUComponent*);
106
107void GBACheatDeviceCreate(struct GBACheatDevice* device) {
108 device->d.id = GBA_CHEAT_DEVICE_ID;
109 device->d.init = GBACheatDeviceInit;
110 device->d.deinit = GBACheatDeviceDeinit;
111 GBACheatSetsInit(&device->cheats, 4);
112}
113
114void GBACheatDeviceDestroy(struct GBACheatDevice* device) {
115 GBACheatDeviceClear(device);
116 GBACheatSetsDeinit(&device->cheats);
117}
118
119void GBACheatDeviceClear(struct GBACheatDevice* device) {
120 size_t i;
121 for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
122 struct GBACheatSet* set = *GBACheatSetsGetPointer(&device->cheats, i);
123 GBACheatSetDeinit(set);
124 free(set);
125 }
126 GBACheatSetsClear(&device->cheats);
127}
128
129void GBACheatSetInit(struct GBACheatSet* set, const char* name) {
130 GBACheatListInit(&set->list, 4);
131 StringListInit(&set->lines, 4);
132 set->incompleteCheat = 0;
133 set->incompletePatch = 0;
134 set->currentBlock = 0;
135 set->gsaVersion = 0;
136 set->cbRngState = 0;
137 set->cbMaster = 0;
138 set->remainingAddresses = 0;
139 set->hook = 0;
140 int i;
141 for (i = 0; i < MAX_ROM_PATCHES; ++i) {
142 set->romPatches[i].exists = false;
143 }
144 if (name) {
145 set->name = strdup(name);
146 } else {
147 set->name = 0;
148 }
149 set->enabled = true;
150}
151
152void GBACheatSetDeinit(struct GBACheatSet* set) {
153 GBACheatListDeinit(&set->list);
154 size_t i;
155 for (i = 0; i < StringListSize(&set->lines); ++i) {
156 free(*StringListGetPointer(&set->lines, i));
157 }
158 if (set->name) {
159 free(set->name);
160 }
161 if (set->hook) {
162 --set->hook->refs;
163 if (set->hook->refs == 0) {
164 free(set->hook);
165 }
166 }
167}
168
169void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice* device) {
170 // TODO: Remove this function
171 if (gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]) {
172 ARMHotplugDetach(gba->cpu, CPU_COMPONENT_CHEAT_DEVICE);
173 }
174 gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = &device->d;
175 ARMHotplugAttach(gba->cpu, CPU_COMPONENT_CHEAT_DEVICE);
176}
177
178void GBACheatAddSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
179 *GBACheatSetsAppend(&device->cheats) = cheats;
180 _addBreakpoint(device, cheats);
181 _patchROM(device, cheats);
182}
183
184void GBACheatRemoveSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
185 size_t i;
186 for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
187 if (*GBACheatSetsGetPointer(&device->cheats, i) == cheats) {
188 break;
189 }
190 }
191 if (i == GBACheatSetsSize(&device->cheats)) {
192 return;
193 }
194 GBACheatSetsShift(&device->cheats, i, 1);
195 _unpatchROM(device, cheats);
196 _removeBreakpoint(device, cheats);
197}
198
199bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
200 uint32_t o1 = op1;
201 uint32_t o2 = op2;
202 char line[18] = "XXXXXXXX XXXXXXXX";
203 snprintf(line, sizeof(line), "%08X %08X", op1, op2);
204 GBACheatRegisterLine(set, line);
205
206 switch (set->gsaVersion) {
207 case 0:
208 // Try to detect GameShark version
209 GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
210 if ((o1 & 0xF0000000) == 0xF0000000 && !(o2 & 0xFFFFFCFE)) {
211 GBACheatSetGameSharkVersion(set, 1);
212 return GBACheatAddGameSharkRaw(set, o1, o2);
213 }
214 o1 = op1;
215 o2 = op2;
216 GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
217 if ((o1 & 0xFE000000) == 0xC4000000 && !(o2 & 0xFFFF0000)) {
218 GBACheatSetGameSharkVersion(set, 3);
219 return GBACheatAddProActionReplayRaw(set, o1, o2);
220 }
221 break;
222 case 1:
223 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
224 return GBACheatAddGameSharkRaw(set, o1, o2);
225 case 3:
226 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
227 return GBACheatAddProActionReplayRaw(set, o1, o2);
228 }
229 return false;
230}
231
232bool GBACheatAutodetectLine(struct GBACheatSet* cheats, const char* line) {
233 uint32_t op1;
234 uint32_t op2;
235 line = hex32(line, &op1);
236 if (!line) {
237 return false;
238 }
239 while (*line == ' ') {
240 ++line;
241 }
242 line = hex32(line, &op2);
243 if (!line) {
244 return false;
245 }
246 return GBACheatAddAutodetect(cheats, op1, op2);
247}
248
249bool GBACheatParseFile(struct GBACheatDevice* device, struct VFile* vf) {
250 char cheat[MAX_LINE_LENGTH];
251 struct GBACheatSet* set = 0;
252 struct GBACheatSet* newSet;
253 int gsaVersion = 0;
254 bool nextDisabled = false;
255 bool reset = false;
256 while (true) {
257 size_t i = 0;
258 ssize_t bytesRead = vf->readline(vf, cheat, sizeof(cheat));
259 if (bytesRead == 0) {
260 break;
261 }
262 if (bytesRead < 0) {
263 return false;
264 }
265 while (isspace((int) cheat[i])) {
266 ++i;
267 }
268 switch (cheat[i]) {
269 case '#':
270 do {
271 ++i;
272 } while (isspace((int) cheat[i]));
273 cheat[strlen(cheat) - 1] = '\0'; // Remove trailing newline
274 newSet = malloc(sizeof(*set));
275 GBACheatSetInit(newSet, &cheat[i]);
276 newSet->enabled = !nextDisabled;
277 nextDisabled = false;
278 if (set) {
279 GBACheatAddSet(device, set);
280 }
281 if (set && !reset) {
282 GBACheatSetCopyProperties(newSet, set);
283 } else {
284 GBACheatSetGameSharkVersion(newSet, gsaVersion);
285 }
286 reset = false;
287 set = newSet;
288 break;
289 case '!':
290 do {
291 ++i;
292 } while (isspace((int) cheat[i]));
293 if (strncasecmp(&cheat[i], "GSAv", 4) == 0 || strncasecmp(&cheat[i], "PARv", 4) == 0) {
294 i += 4;
295 gsaVersion = atoi(&cheat[i]);
296 break;
297 }
298 if (strcasecmp(&cheat[i], "disabled") == 0) {
299 nextDisabled = true;
300 break;
301 }
302 if (strcasecmp(&cheat[i], "reset") == 0) {
303 reset = true;
304 break;
305 }
306 break;
307 default:
308 if (!set) {
309 set = malloc(sizeof(*set));
310 GBACheatSetInit(set, 0);
311 set->enabled = !nextDisabled;
312 nextDisabled = false;
313 GBACheatSetGameSharkVersion(set, gsaVersion);
314 }
315 GBACheatAddLine(set, cheat);
316 break;
317 }
318 }
319 if (set) {
320 GBACheatAddSet(device, set);
321 }
322 return true;
323}
324
325bool GBACheatSaveFile(struct GBACheatDevice* device, struct VFile* vf) {
326 static const char lineStart[3] = "# ";
327 static const char lineEnd = '\n';
328
329 struct GBACheatHook* lastHook = 0;
330
331 size_t i;
332 for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
333 struct GBACheatSet* set = *GBACheatSetsGetPointer(&device->cheats, i);
334 if (lastHook && set->hook != lastHook) {
335 static const char* resetDirective = "!reset\n";
336 vf->write(vf, resetDirective, strlen(resetDirective));
337 }
338 switch (set->gsaVersion) {
339 case 1: {
340 static const char* versionDirective = "!GSAv1\n";
341 vf->write(vf, versionDirective, strlen(versionDirective));
342 break;
343 }
344 case 3: {
345 static const char* versionDirective = "!PARv3\n";
346 vf->write(vf, versionDirective, strlen(versionDirective));
347 break;
348 }
349 default:
350 break;
351 }
352 lastHook = set->hook;
353 if (!set->enabled) {
354 static const char* disabledDirective = "!disabled\n";
355 vf->write(vf, disabledDirective, strlen(disabledDirective));
356 }
357
358 vf->write(vf, lineStart, 2);
359 if (set->name) {
360 vf->write(vf, set->name, strlen(set->name));
361 }
362 vf->write(vf, &lineEnd, 1);
363 size_t c;
364 for (c = 0; c < StringListSize(&set->lines); ++c) {
365 const char* line = *StringListGetPointer(&set->lines, c);
366 vf->write(vf, line, strlen(line));
367 vf->write(vf, &lineEnd, 1);
368 }
369 }
370 return true;
371}
372
373bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
374 uint32_t address;
375 uint8_t op;
376 uint32_t value = 0;
377 int width = 0;
378 const char* lineNext = hex32(line, &address);
379 if (!lineNext) {
380 return false;
381 }
382 if (lineNext[0] != ':') {
383 return false;
384 }
385 ++lineNext;
386 while (width < 4) {
387 lineNext = hex8(lineNext, &op);
388 if (!lineNext) {
389 break;
390 }
391 value <<= 8;
392 value |= op;
393 ++width;
394 }
395 if (width == 0 || width == 3) {
396 return false;
397 }
398
399 struct GBACheat* cheat = GBACheatListAppend(&cheats->list);
400 cheat->address = address;
401 cheat->operandOffset = 0;
402 cheat->addressOffset = 0;
403 cheat->repeat = 1;
404 cheat->type = CHEAT_ASSIGN;
405 cheat->width = width;
406 cheat->operand = value;
407 GBACheatRegisterLine(cheats, line);
408 return true;
409}
410
411bool GBACheatAddLine(struct GBACheatSet* cheats, const char* line) {
412 uint32_t op1;
413 uint16_t op2;
414 uint16_t op3;
415 const char* lineNext = hex32(line, &op1);
416 if (!lineNext) {
417 return false;
418 }
419 if (lineNext[0] == ':') {
420 return GBACheatAddVBALine(cheats, line);
421 }
422 while (isspace((int) lineNext[0])) {
423 ++lineNext;
424 }
425 lineNext = hex16(lineNext, &op2);
426 if (!lineNext) {
427 return false;
428 }
429 if (!lineNext[0] || isspace((int) lineNext[0])) {
430 return GBACheatAddCodeBreaker(cheats, op1, op2);
431 }
432 lineNext = hex16(lineNext, &op3);
433 if (!lineNext) {
434 return false;
435 }
436 uint32_t realOp2 = op2;
437 realOp2 <<= 16;
438 realOp2 |= op3;
439 return GBACheatAddAutodetect(cheats, op1, realOp2);
440}
441
442void GBACheatRefresh(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
443 if (!cheats->enabled) {
444 return;
445 }
446 bool condition = true;
447 int conditionRemaining = 0;
448 int negativeConditionRemaining = 0;
449 _patchROM(device, cheats);
450
451 size_t nCodes = GBACheatListSize(&cheats->list);
452 size_t i;
453 for (i = 0; i < nCodes; ++i) {
454 if (conditionRemaining > 0) {
455 --conditionRemaining;
456 if (!condition) {
457 continue;
458 }
459 } else if (negativeConditionRemaining > 0) {
460 conditionRemaining = negativeConditionRemaining - 1;
461 negativeConditionRemaining = 0;
462 condition = !condition;
463 if (!condition) {
464 continue;
465 }
466 } else {
467 condition = true;
468 }
469 struct GBACheat* cheat = GBACheatListGetPointer(&cheats->list, i);
470 int32_t value = 0;
471 int32_t operand = cheat->operand;
472 uint32_t operationsRemaining = cheat->repeat;
473 uint32_t address = cheat->address;
474 bool performAssignment = false;
475 for (; operationsRemaining; --operationsRemaining) {
476 switch (cheat->type) {
477 case CHEAT_ASSIGN:
478 value = operand;
479 performAssignment = true;
480 break;
481 case CHEAT_ASSIGN_INDIRECT:
482 value = operand;
483 address = _readMem(device->p->cpu, address + cheat->addressOffset, 4);
484 performAssignment = true;
485 break;
486 case CHEAT_AND:
487 value = _readMem(device->p->cpu, address, cheat->width) & operand;
488 performAssignment = true;
489 break;
490 case CHEAT_ADD:
491 value = _readMem(device->p->cpu, address, cheat->width) + operand;
492 performAssignment = true;
493 break;
494 case CHEAT_OR:
495 value = _readMem(device->p->cpu, address, cheat->width) | operand;
496 performAssignment = true;
497 break;
498 case CHEAT_IF_EQ:
499 condition = _readMem(device->p->cpu, address, cheat->width) == operand;
500 conditionRemaining = cheat->repeat;
501 negativeConditionRemaining = cheat->negativeRepeat;
502 break;
503 case CHEAT_IF_NE:
504 condition = _readMem(device->p->cpu, address, cheat->width) != operand;
505 conditionRemaining = cheat->repeat;
506 negativeConditionRemaining = cheat->negativeRepeat;
507 break;
508 case CHEAT_IF_LT:
509 condition = _readMem(device->p->cpu, address, cheat->width) < operand;
510 conditionRemaining = cheat->repeat;
511 negativeConditionRemaining = cheat->negativeRepeat;
512 break;
513 case CHEAT_IF_GT:
514 condition = _readMem(device->p->cpu, address, cheat->width) > operand;
515 conditionRemaining = cheat->repeat;
516 negativeConditionRemaining = cheat->negativeRepeat;
517 break;
518 case CHEAT_IF_ULT:
519 condition = (uint32_t) _readMem(device->p->cpu, address, cheat->width) < (uint32_t) operand;
520 conditionRemaining = cheat->repeat;
521 negativeConditionRemaining = cheat->negativeRepeat;
522 break;
523 case CHEAT_IF_UGT:
524 condition = (uint32_t) _readMem(device->p->cpu, address, cheat->width) > (uint32_t) operand;
525 conditionRemaining = cheat->repeat;
526 negativeConditionRemaining = cheat->negativeRepeat;
527 break;
528 case CHEAT_IF_AND:
529 condition = _readMem(device->p->cpu, address, cheat->width) & operand;
530 conditionRemaining = cheat->repeat;
531 negativeConditionRemaining = cheat->negativeRepeat;
532 break;
533 case CHEAT_IF_LAND:
534 condition = _readMem(device->p->cpu, address, cheat->width) && operand;
535 conditionRemaining = cheat->repeat;
536 negativeConditionRemaining = cheat->negativeRepeat;
537 break;
538 }
539
540 if (performAssignment) {
541 _writeMem(device->p->cpu, address, cheat->width, value);
542 }
543
544 address += cheat->addressOffset;
545 operand += cheat->operandOffset;
546 }
547 }
548}
549
550void GBACheatSetCopyProperties(struct GBACheatSet* newSet, struct GBACheatSet* set) {
551 newSet->gsaVersion = set->gsaVersion;
552 memcpy(newSet->gsaSeeds, set->gsaSeeds, sizeof(newSet->gsaSeeds));
553 newSet->cbRngState = set->cbRngState;
554 newSet->cbMaster = set->cbMaster;
555 memcpy(newSet->cbSeeds, set->cbSeeds, sizeof(newSet->cbSeeds));
556 memcpy(newSet->cbTable, set->cbTable, sizeof(newSet->cbTable));
557 if (set->hook) {
558 if (newSet->hook) {
559 --newSet->hook->refs;
560 if (newSet->hook->refs == 0) {
561 free(newSet->hook);
562 }
563 }
564 newSet->hook = set->hook;
565 ++newSet->hook->refs;
566 }
567}
568
569void GBACheatDeviceInit(void* cpu, struct mCPUComponent* component) {
570 struct GBACheatDevice* device = (struct GBACheatDevice*) component;
571 device->p = (struct GBA*) ((struct ARMCore*) cpu)->master;
572 size_t i;
573 for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
574 struct GBACheatSet* cheats = *GBACheatSetsGetPointer(&device->cheats, i);
575 _addBreakpoint(device, cheats);
576 _patchROM(device, cheats);
577 }
578}
579
580void GBACheatDeviceDeinit(struct mCPUComponent* component) {
581 struct GBACheatDevice* device = (struct GBACheatDevice*) component;
582 size_t i;
583 for (i = GBACheatSetsSize(&device->cheats); i--;) {
584 struct GBACheatSet* cheats = *GBACheatSetsGetPointer(&device->cheats, i);
585 _unpatchROM(device, cheats);
586 _removeBreakpoint(device, cheats);
587 }
588}