src/core/cheats.c (view raw)
1/* Copyright (c) 2013-2016 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/core/cheats.h>
7
8#include <mgba/core/core.h>
9#include <mgba-util/string.h>
10#include <mgba-util/vfs.h>
11
12#define MAX_LINE_LENGTH 512
13#define MAX_CHEATS 1000
14
15const uint32_t M_CHEAT_DEVICE_ID = 0xABADC0DE;
16
17mLOG_DEFINE_CATEGORY(CHEATS, "Cheats", "core.cheats");
18
19DEFINE_VECTOR(mCheatList, struct mCheat);
20DEFINE_VECTOR(mCheatSets, struct mCheatSet*);
21DEFINE_VECTOR(mCheatPatchList, struct mCheatPatch);
22
23struct mCheatPatchedMem {
24 uint32_t originalValue;
25 int refs;
26 bool dirty;
27};
28
29static uint32_t _patchMakeKey(struct mCheatPatch* patch) {
30 // NB: This assumes patches have only one valid size per platform
31 uint32_t patchKey = patch->address;
32 switch (patch->width) {
33 case 2:
34 patchKey >>= 1;
35 break;
36 case 4:
37 patchKey >>= 2;
38 break;
39 default:
40 break;
41 }
42 // TODO: More than one segment
43 if (patch->segment > 0) {
44 patchKey |= patch->segment << 16;
45 }
46 return patchKey;
47}
48
49static int32_t _readMem(struct mCore* core, uint32_t address, int width) {
50 switch (width) {
51 case 1:
52 return core->busRead8(core, address);
53 case 2:
54 return core->busRead16(core, address);
55 case 4:
56 return core->busRead32(core, address);
57 }
58 return 0;
59}
60
61static int32_t _readMemSegment(struct mCore* core, uint32_t address, int segment, int width) {
62 switch (width) {
63 case 1:
64 return core->rawRead8(core, address, segment);
65 case 2:
66 return core->rawRead16(core, address, segment);
67 case 4:
68 return core->rawRead32(core, address, segment);
69 }
70 return 0;
71}
72
73static void _writeMem(struct mCore* core, uint32_t address, int width, int32_t value) {
74 switch (width) {
75 case 1:
76 core->busWrite8(core, address, value);
77 break;
78 case 2:
79 core->busWrite16(core, address, value);
80 break;
81 case 4:
82 core->busWrite32(core, address, value);
83 break;
84 }
85}
86
87static void _patchMem(struct mCore* core, uint32_t address, int segment, int width, int32_t value) {
88 switch (width) {
89 case 1:
90 core->rawWrite8(core, address, segment, value);
91 break;
92 case 2:
93 core->rawWrite16(core, address, segment, value);
94 break;
95 case 4:
96 core->rawWrite32(core, address, segment, value);
97 break;
98 }
99}
100
101static void _patchROM(struct mCheatDevice* device, struct mCheatSet* cheats) {
102 if (!device->p) {
103 return;
104 }
105 size_t i;
106 for (i = 0; i < mCheatPatchListSize(&cheats->romPatches); ++i) {
107 struct mCheatPatch* patch = mCheatPatchListGetPointer(&cheats->romPatches, i);
108 int segment = -1;
109 if (patch->check && patch->segment < 0) {
110 int maxSegment = 0;
111 for (segment = 0; segment < maxSegment; ++segment) {
112 uint32_t value = _readMemSegment(device->p, patch->address, segment, patch->width);
113 if (value == patch->checkValue) {
114 break;
115 }
116 }
117 if (segment == maxSegment) {
118 continue;
119 }
120 }
121 patch->segment = segment;
122
123 uint32_t patchKey = _patchMakeKey(patch);
124 struct mCheatPatchedMem* patchData = TableLookup(&device->unpatchedMemory, patchKey);
125 if (!patchData) {
126 patchData = malloc(sizeof(*patchData));
127 patchData->originalValue = _readMemSegment(device->p, patch->address, segment, patch->width);
128 patchData->refs = 1;
129 patchData->dirty = false;
130 TableInsert(&device->unpatchedMemory, patchKey, patchData);
131 } else if (!patch->applied) {
132 ++patchData->refs;
133 patchData->dirty = true;
134 } else if (!patchData->dirty) {
135 continue;
136 }
137 _patchMem(device->p, patch->address, segment, patch->width, patch->value);
138 patch->applied = true;
139 }
140}
141
142static void _unpatchROM(struct mCheatDevice* device, struct mCheatSet* cheats) {
143 if (!device->p) {
144 return;
145 }
146 size_t i;
147 for (i = 0; i < mCheatPatchListSize(&cheats->romPatches); ++i) {
148 struct mCheatPatch* patch = mCheatPatchListGetPointer(&cheats->romPatches, i);
149 if (!patch->applied) {
150 continue;
151 }
152 uint32_t patchKey = _patchMakeKey(patch);
153 struct mCheatPatchedMem* patchData = TableLookup(&device->unpatchedMemory, patchKey);
154 --patchData->refs;
155 patchData->dirty = true;
156 if (patchData->refs <= 0) {
157 _patchMem(device->p, patch->address, patch->segment, patch->width, patchData->originalValue);
158 TableRemove(&device->unpatchedMemory, patchKey);
159 }
160 patch->applied = false;
161 }
162}
163
164static void mCheatDeviceInit(void*, struct mCPUComponent*);
165static void mCheatDeviceDeinit(struct mCPUComponent*);
166
167void mCheatDeviceCreate(struct mCheatDevice* device) {
168 device->d.id = M_CHEAT_DEVICE_ID;
169 device->d.init = mCheatDeviceInit;
170 device->d.deinit = mCheatDeviceDeinit;
171 device->autosave = false;
172 device->buttonDown = false;
173 mCheatSetsInit(&device->cheats, 4);
174 TableInit(&device->unpatchedMemory, 4, free);
175}
176
177void mCheatDeviceDestroy(struct mCheatDevice* device) {
178 mCheatDeviceClear(device);
179 mCheatSetsDeinit(&device->cheats);
180 TableDeinit(&device->unpatchedMemory);
181 free(device);
182}
183
184void mCheatDeviceClear(struct mCheatDevice* device) {
185 size_t i;
186 for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
187 struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
188 mCheatSetDeinit(set);
189 }
190 mCheatSetsClear(&device->cheats);
191}
192
193void mCheatSetInit(struct mCheatSet* set, const char* name) {
194 mCheatListInit(&set->list, 4);
195 StringListInit(&set->lines, 4);
196 mCheatPatchListInit(&set->romPatches, 4);
197 if (name) {
198 set->name = strdup(name);
199 } else {
200 set->name = 0;
201 }
202 set->enabled = true;
203}
204
205void mCheatSetDeinit(struct mCheatSet* set) {
206 size_t i;
207 for (i = 0; i < StringListSize(&set->lines); ++i) {
208 free(*StringListGetPointer(&set->lines, i));
209 }
210 mCheatListDeinit(&set->list);
211 if (set->name) {
212 free(set->name);
213 }
214 StringListDeinit(&set->lines);
215 mCheatPatchListDeinit(&set->romPatches);
216 if (set->deinit) {
217 set->deinit(set);
218 }
219 free(set);
220}
221
222void mCheatSetRename(struct mCheatSet* set, const char* name) {
223 if (set->name) {
224 free(set->name);
225 set->name = NULL;
226 }
227 if (name) {
228 set->name = strdup(name);
229 }
230}
231
232bool mCheatAddLine(struct mCheatSet* set, const char* line, int type) {
233 if (!set->addLine(set, line, type)) {
234 return false;
235 }
236 *StringListAppend(&set->lines) = strdup(line);
237 return true;
238}
239
240void mCheatAddSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
241 *mCheatSetsAppend(&device->cheats) = cheats;
242 if (cheats->add) {
243 cheats->add(cheats, device);
244 }
245}
246
247void mCheatRemoveSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
248 size_t i;
249 for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
250 if (*mCheatSetsGetPointer(&device->cheats, i) == cheats) {
251 break;
252 }
253 }
254 if (i == mCheatSetsSize(&device->cheats)) {
255 return;
256 }
257 mCheatSetsShift(&device->cheats, i, 1);
258 if (cheats->remove) {
259 cheats->remove(cheats, device);
260 }
261}
262
263bool mCheatParseFile(struct mCheatDevice* device, struct VFile* vf) {
264 char cheat[MAX_LINE_LENGTH];
265 struct mCheatSet* set = NULL;
266 struct mCheatSet* newSet;
267 bool nextDisabled = false;
268 struct StringList directives;
269 StringListInit(&directives, 4);
270
271 while (true) {
272 size_t i = 0;
273 ssize_t bytesRead = vf->readline(vf, cheat, sizeof(cheat));
274 rtrim(cheat);
275 if (bytesRead == 0) {
276 break;
277 }
278 if (bytesRead < 0) {
279 StringListDeinit(&directives);
280 return false;
281 }
282 while (isspace((int) cheat[i])) {
283 ++i;
284 }
285 switch (cheat[i]) {
286 case '#':
287 do {
288 ++i;
289 } while (isspace((int) cheat[i]));
290 newSet = device->createSet(device, &cheat[i]);
291 newSet->enabled = !nextDisabled;
292 nextDisabled = false;
293 if (set) {
294 mCheatAddSet(device, set);
295 }
296 if (set) {
297 newSet->copyProperties(newSet, set);
298 }
299 newSet->parseDirectives(newSet, &directives);
300 set = newSet;
301 break;
302 case '!':
303 do {
304 ++i;
305 } while (isspace((int) cheat[i]));
306 if (strcasecmp(&cheat[i], "disabled") == 0) {
307 nextDisabled = true;
308 break;
309 }
310 if (strcasecmp(&cheat[i], "reset") == 0) {
311 size_t d;
312 for (d = 0; d < StringListSize(&directives); ++d) {
313 free(*StringListGetPointer(&directives, d));
314 }
315 StringListClear(&directives);
316 break;
317 }
318 *StringListAppend(&directives) = strdup(&cheat[i]);
319 break;
320 default:
321 if (!set) {
322 if (strncmp(cheat, "cheats = ", 9) == 0) {
323 // This is in libretro format, switch over to that parser
324 vf->seek(vf, 0, SEEK_SET);
325 StringListDeinit(&directives);
326 return mCheatParseLibretroFile(device, vf);
327 }
328 if (cheat[0] == '[') {
329 // This is in EZ Flash CHT format, switch over to that parser
330 vf->seek(vf, 0, SEEK_SET);
331 StringListDeinit(&directives);
332 return mCheatParseEZFChtFile(device, vf);
333 }
334 set = device->createSet(device, NULL);
335 set->enabled = !nextDisabled;
336 nextDisabled = false;
337 }
338 mCheatAddLine(set, cheat, 0);
339 break;
340 }
341 }
342 if (set) {
343 mCheatAddSet(device, set);
344 }
345 size_t d;
346 for (d = 0; d < StringListSize(&directives); ++d) {
347 free(*StringListGetPointer(&directives, d));
348 }
349 StringListClear(&directives);
350 StringListDeinit(&directives);
351 return true;
352}
353
354bool mCheatParseLibretroFile(struct mCheatDevice* device, struct VFile* vf) {
355 char cheat[MAX_LINE_LENGTH];
356 char parsed[MAX_LINE_LENGTH];
357 struct mCheatSet* set = NULL;
358 unsigned long i = 0;
359 bool startFound = false;
360
361 while (true) {
362 ssize_t bytesRead = vf->readline(vf, cheat, sizeof(cheat));
363 if (bytesRead == 0) {
364 break;
365 }
366 if (bytesRead < 0) {
367 return false;
368 }
369 if (cheat[0] == '\n') {
370 continue;
371 }
372 if (strncmp(cheat, "cheat", 5) != 0) {
373 return false;
374 }
375 char* underscore = strchr(&cheat[5], '_');
376 if (!underscore) {
377 if (!startFound && cheat[5] == 's') {
378 startFound = true;
379 char* eq = strchr(&cheat[6], '=');
380 if (!eq) {
381 return false;
382 }
383 ++eq;
384 while (isspace((int) eq[0])) {
385 if (eq[0] == '\0') {
386 return false;
387 }
388 ++eq;
389 }
390
391 char* end;
392 unsigned long nCheats = strtoul(eq, &end, 10);
393 if (end[0] != '\0' && !isspace(end[0])) {
394 return false;
395 }
396
397 if (nCheats > MAX_CHEATS) {
398 return false;
399 }
400
401 while (nCheats > mCheatSetsSize(&device->cheats)) {
402 struct mCheatSet* newSet = device->createSet(device, NULL);
403 if (!newSet) {
404 return false;
405 }
406 mCheatAddSet(device, newSet);
407 }
408 continue;
409 }
410 return false;
411 }
412 char* underscore2;
413 i = strtoul(&cheat[5], &underscore2, 10);
414 if (underscore2 != underscore) {
415 return false;
416 }
417 ++underscore;
418 char* eq = strchr(underscore, '=');
419 if (!eq) {
420 return false;
421 }
422 ++eq;
423 while (isspace((int) eq[0])) {
424 if (eq[0] == '\0') {
425 return false;
426 }
427 ++eq;
428 }
429
430 if (i >= mCheatSetsSize(&device->cheats)) {
431 return false;
432 }
433 set = *mCheatSetsGetPointer(&device->cheats, i);
434
435 if (strncmp(underscore, "desc", 4) == 0) {
436 parseQuotedString(eq, strlen(eq), parsed, sizeof(parsed));
437 mCheatSetRename(set, parsed);
438 } else if (strncmp(underscore, "enable", 6) == 0) {
439 set->enabled = strncmp(eq, "true\n", 5) == 0;
440 } else if (strncmp(underscore, "code", 4) == 0) {
441 parseQuotedString(eq, strlen(eq), parsed, sizeof(parsed));
442 char* cur = parsed;
443 char* next;
444 while ((next = strchr(cur, '+'))) {
445 next[0] = '\0';
446 mCheatAddLine(set, cur, 0);
447 cur = &next[1];
448 }
449 mCheatAddLine(set, cur, 0);
450
451 for (++i; i < mCheatSetsSize(&device->cheats); ++i) {
452 struct mCheatSet* newSet = *mCheatSetsGetPointer(&device->cheats, i);
453 newSet->copyProperties(newSet, set);
454 }
455 }
456 }
457 return true;
458}
459
460bool mCheatParseEZFChtFile(struct mCheatDevice* device, struct VFile* vf) {
461 char cheat[MAX_LINE_LENGTH];
462 char cheatName[MAX_LINE_LENGTH];
463 char miniline[32];
464 size_t cheatNameLength = 0;
465 struct mCheatSet* set = NULL;
466
467 cheatName[MAX_LINE_LENGTH - 1] = '\0';
468 while (true) {
469 ssize_t bytesRead = vf->readline(vf, cheat, sizeof(cheat));
470 if (bytesRead == 0) {
471 break;
472 }
473 if (bytesRead < 0) {
474 return false;
475 }
476 if (cheat[0] == '\n' || (bytesRead >= 2 && cheat[0] == '\r' && cheat[1] == '\n')) {
477 continue;
478 }
479
480 if (cheat[0] == '[') {
481 if (strncmp(cheat, "[GameInfo]", 10) == 0) {
482 break;
483 }
484 char* end = strchr(cheat, ']');
485 if (!end) {
486 return false;
487 }
488 char* name = gbkToUtf8(&cheat[1], end - cheat - 1);
489 strncpy(cheatName, name, sizeof(cheatName) - 1);
490 free(name);
491 cheatNameLength = strlen(cheatName);
492 continue;
493 }
494
495 char* eq = strchr(cheat, '=');
496 if (!eq) {
497 continue;
498 }
499 if (strncmp(cheat, "ON", eq - cheat) != 0) {
500 char* subname = gbkToUtf8(cheat, eq - cheat);
501 snprintf(&cheatName[cheatNameLength], sizeof(cheatName) - cheatNameLength - 1, ": %s", subname);
502 }
503 set = device->createSet(device, cheatName);
504 set->enabled = false;
505 mCheatAddSet(device, set);
506 cheatName[cheatNameLength] = '\0';
507 ++eq;
508
509 uint32_t gameptr = 0;
510 uint32_t hexval = 0;
511 int digit;
512 while (eq[0] != '\r' && eq[1] != '\n') {
513 if (cheat + bytesRead == eq || eq[0] == '\0') {
514 bytesRead = vf->readline(vf, cheat, sizeof(cheat));
515 eq = cheat;
516 if (bytesRead == 0) {
517 break;
518 }
519 if (bytesRead < 0) {
520 return false;
521 }
522 }
523 switch (eq[0]) {
524 case ',':
525 if (!gameptr) {
526 gameptr = hexval;
527 if (hexval < 0x40000) {
528 gameptr += 0x02000000;
529 } else {
530 gameptr += 0x03000000 - 0x40000;
531 }
532 } else {
533 if (hexval > 0xFF) {
534 return false;
535 }
536 snprintf(miniline, sizeof(miniline) - 1, "%08X:%02X", gameptr, hexval);
537 mCheatAddLine(set, miniline, 0);
538 ++gameptr;
539 }
540 hexval = 0;
541 break;
542 case ';':
543 if (hexval > 0xFF) {
544 return false;
545 }
546 snprintf(miniline, sizeof(miniline) - 1, "%08X:%02X", gameptr, hexval);
547 mCheatAddLine(set, miniline, 0);
548 hexval = 0;
549 gameptr = 0;
550 break;
551 default:
552 digit = hexDigit(eq[0]);
553 if (digit < 0) {
554 return false;
555 }
556 hexval <<= 4;
557 hexval |= digit;
558 break;
559 }
560 ++eq;
561 }
562 if (gameptr) {
563 if (hexval > 0xFF) {
564 return false;
565 }
566 snprintf(miniline, sizeof(miniline) - 1, "%08X:%02X", gameptr, hexval);
567 mCheatAddLine(set, miniline, 0);
568 }
569 }
570 return true;
571}
572
573bool mCheatSaveFile(struct mCheatDevice* device, struct VFile* vf) {
574 static const char lineStart[3] = "# ";
575 static const char lineEnd = '\n';
576 struct StringList directives;
577 StringListInit(&directives, 4);
578
579 size_t i;
580 for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
581 struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
582 set->dumpDirectives(set, &directives);
583 if (!set->enabled) {
584 static const char* disabledDirective = "!disabled\n";
585 vf->write(vf, disabledDirective, strlen(disabledDirective));
586 }
587 size_t d;
588 for (d = 0; d < StringListSize(&directives); ++d) {
589 char directive[64];
590 ssize_t len = snprintf(directive, sizeof(directive) - 1, "!%s\n", *StringListGetPointer(&directives, d));
591 if (len > 1) {
592 vf->write(vf, directive, (size_t) len > sizeof(directive) ? sizeof(directive) : (size_t) len);
593 }
594 }
595
596 vf->write(vf, lineStart, 2);
597 if (set->name) {
598 vf->write(vf, set->name, strlen(set->name));
599 }
600 vf->write(vf, &lineEnd, 1);
601 size_t c;
602 for (c = 0; c < StringListSize(&set->lines); ++c) {
603 const char* line = *StringListGetPointer(&set->lines, c);
604 vf->write(vf, line, strlen(line));
605 vf->write(vf, &lineEnd, 1);
606 }
607 }
608 size_t d;
609 for (d = 0; d < StringListSize(&directives); ++d) {
610 free(*StringListGetPointer(&directives, d));
611 }
612 StringListClear(&directives);
613 StringListDeinit(&directives);
614 return true;
615}
616
617#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
618void mCheatAutosave(struct mCheatDevice* device) {
619 if (!device->autosave) {
620 return;
621 }
622 struct VFile* vf = mDirectorySetOpenSuffix(&device->p->dirs, device->p->dirs.cheats, ".cheats", O_WRONLY | O_CREAT | O_TRUNC);
623 if (!vf) {
624 return;
625 }
626 mCheatSaveFile(device, vf);
627 vf->close(vf);
628}
629#endif
630
631void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
632 if (cheats->enabled) {
633 _patchROM(device, cheats);
634 }
635 if (cheats->refresh) {
636 cheats->refresh(cheats, device);
637 }
638 if (!cheats->enabled) {
639 _unpatchROM(device, cheats);
640 return;
641 }
642
643 size_t elseLoc = 0;
644 size_t endLoc = 0;
645 size_t nCodes = mCheatListSize(&cheats->list);
646 size_t i;
647 for (i = 0; i < nCodes; ++i) {
648 struct mCheat* cheat = mCheatListGetPointer(&cheats->list, i);
649 int32_t value = 0;
650 int32_t operand = cheat->operand;
651 uint32_t operationsRemaining = cheat->repeat;
652 uint32_t address = cheat->address;
653 bool performAssignment = false;
654 bool condition = true;
655 int conditionRemaining = 0;
656 int negativeConditionRemaining = 0;
657
658 for (; operationsRemaining; --operationsRemaining) {
659 switch (cheat->type) {
660 case CHEAT_ASSIGN:
661 value = operand;
662 performAssignment = true;
663 break;
664 case CHEAT_ASSIGN_INDIRECT:
665 value = operand;
666 address = _readMem(device->p, address + cheat->addressOffset, 4);
667 performAssignment = true;
668 break;
669 case CHEAT_AND:
670 value = _readMem(device->p, address, cheat->width) & operand;
671 performAssignment = true;
672 break;
673 case CHEAT_ADD:
674 value = _readMem(device->p, address, cheat->width) + operand;
675 performAssignment = true;
676 break;
677 case CHEAT_OR:
678 value = _readMem(device->p, address, cheat->width) | operand;
679 performAssignment = true;
680 break;
681 case CHEAT_IF_EQ:
682 condition = _readMem(device->p, address, cheat->width) == operand;
683 conditionRemaining = cheat->repeat;
684 negativeConditionRemaining = cheat->negativeRepeat;
685 operationsRemaining = 1;
686 break;
687 case CHEAT_IF_NE:
688 condition = _readMem(device->p, address, cheat->width) != operand;
689 conditionRemaining = cheat->repeat;
690 negativeConditionRemaining = cheat->negativeRepeat;
691 operationsRemaining = 1;
692 break;
693 case CHEAT_IF_LT:
694 condition = _readMem(device->p, address, cheat->width) < operand;
695 conditionRemaining = cheat->repeat;
696 negativeConditionRemaining = cheat->negativeRepeat;
697 operationsRemaining = 1;
698 break;
699 case CHEAT_IF_GT:
700 condition = _readMem(device->p, address, cheat->width) > operand;
701 conditionRemaining = cheat->repeat;
702 negativeConditionRemaining = cheat->negativeRepeat;
703 operationsRemaining = 1;
704 break;
705 case CHEAT_IF_ULT:
706 condition = (uint32_t) _readMem(device->p, address, cheat->width) < (uint32_t) operand;
707 conditionRemaining = cheat->repeat;
708 negativeConditionRemaining = cheat->negativeRepeat;
709 operationsRemaining = 1;
710 break;
711 case CHEAT_IF_UGT:
712 condition = (uint32_t) _readMem(device->p, address, cheat->width) > (uint32_t) operand;
713 conditionRemaining = cheat->repeat;
714 negativeConditionRemaining = cheat->negativeRepeat;
715 operationsRemaining = 1;
716 break;
717 case CHEAT_IF_AND:
718 condition = _readMem(device->p, address, cheat->width) & operand;
719 conditionRemaining = cheat->repeat;
720 negativeConditionRemaining = cheat->negativeRepeat;
721 operationsRemaining = 1;
722 break;
723 case CHEAT_IF_LAND:
724 condition = _readMem(device->p, address, cheat->width) && operand;
725 conditionRemaining = cheat->repeat;
726 negativeConditionRemaining = cheat->negativeRepeat;
727 operationsRemaining = 1;
728 break;
729 case CHEAT_IF_NAND:
730 condition = !(_readMem(device->p, address, cheat->width) & operand);
731 conditionRemaining = cheat->repeat;
732 negativeConditionRemaining = cheat->negativeRepeat;
733 operationsRemaining = 1;
734 break;
735 case CHEAT_IF_BUTTON:
736 condition = device->buttonDown;
737 conditionRemaining = cheat->repeat;
738 negativeConditionRemaining = cheat->negativeRepeat;
739 operationsRemaining = 1;
740 break;
741 }
742
743 if (performAssignment) {
744 _writeMem(device->p, address, cheat->width, value);
745 }
746
747 address += cheat->addressOffset;
748 operand += cheat->operandOffset;
749 }
750
751
752 if (elseLoc && i == elseLoc) {
753 i = endLoc;
754 endLoc = 0;
755 }
756 if (conditionRemaining > 0 && !condition) {
757 i += conditionRemaining;
758 } else if (negativeConditionRemaining > 0) {
759 elseLoc = i + conditionRemaining;
760 endLoc = elseLoc + negativeConditionRemaining;
761 }
762 }
763}
764
765void mCheatPressButton(struct mCheatDevice* device, bool down) {
766 device->buttonDown = down;
767}
768
769void mCheatDeviceInit(void* cpu, struct mCPUComponent* component) {
770 UNUSED(cpu);
771 struct mCheatDevice* device = (struct mCheatDevice*) component;
772 size_t i;
773 for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
774 struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
775 cheats->add(cheats, device);
776 }
777}
778
779void mCheatDeviceDeinit(struct mCPUComponent* component) {
780 struct mCheatDevice* device = (struct mCheatDevice*) component;
781 size_t i;
782 for (i = mCheatSetsSize(&device->cheats); i--;) {
783 struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
784 cheats->remove(cheats, device);
785 }
786}