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