all repos — mgba @ 20f790bb61dc9808f4f220a6ebb380ac25f848b5

mGBA Game Boy Advance Emulator

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}