all repos — mgba @ 7ff5c3a905b0a4e42691a61b8059648c1bd5ec8b

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->remainingAddresses = 0;
137	set->hook = 0;
138	int i;
139	for (i = 0; i < MAX_ROM_PATCHES; ++i) {
140		set->romPatches[i].exists = false;
141	}
142	if (name) {
143		set->name = strdup(name);
144	} else {
145		set->name = 0;
146	}
147	set->enabled = true;
148}
149
150void GBACheatSetDeinit(struct GBACheatSet* set) {
151	GBACheatListDeinit(&set->list);
152	size_t i;
153	for (i = 0; i < StringListSize(&set->lines); ++i) {
154		free(*StringListGetPointer(&set->lines, i));
155	}
156	if (set->name) {
157		free(set->name);
158	}
159	if (set->hook) {
160		--set->hook->refs;
161		if (set->hook->refs == 0) {
162			free(set->hook);
163		}
164	}
165}
166
167void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice* device) {
168	// TODO: Remove this function
169	if (gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]) {
170		ARMHotplugDetach(gba->cpu, CPU_COMPONENT_CHEAT_DEVICE);
171	}
172	gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = &device->d;
173	ARMHotplugAttach(gba->cpu, CPU_COMPONENT_CHEAT_DEVICE);
174}
175
176void GBACheatAddSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
177	*GBACheatSetsAppend(&device->cheats) = cheats;
178	_addBreakpoint(device, cheats);
179	_patchROM(device, cheats);
180}
181
182void GBACheatRemoveSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
183	size_t i;
184	for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
185		if (*GBACheatSetsGetPointer(&device->cheats, i) == cheats) {
186			break;
187		}
188	}
189	if (i == GBACheatSetsSize(&device->cheats)) {
190		return;
191	}
192	GBACheatSetsShift(&device->cheats, i, 1);
193	_unpatchROM(device, cheats);
194	_removeBreakpoint(device, cheats);
195}
196
197bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
198	uint32_t o1 = op1;
199	uint32_t o2 = op2;
200	char line[18] = "XXXXXXXX XXXXXXXX";
201	snprintf(line, sizeof(line), "%08X %08X", op1, op2);
202	GBACheatRegisterLine(set, line);
203
204	switch (set->gsaVersion) {
205	case 0:
206		// Try to detect GameShark version
207		GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
208		if ((o1 & 0xF0000000) == 0xF0000000 && !(o2 & 0xFFFFFCFE)) {
209			GBACheatSetGameSharkVersion(set, 1);
210			return GBACheatAddGameSharkRaw(set, o1, o2);
211		}
212		o1 = op1;
213		o2 = op2;
214		GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
215		if ((o1 & 0xFE000000) == 0xC4000000 && !(o2 & 0xFFFF0000)) {
216			GBACheatSetGameSharkVersion(set, 3);
217			return GBACheatAddProActionReplayRaw(set, o1, o2);
218		}
219		break;
220	case 1:
221		GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
222		return GBACheatAddGameSharkRaw(set, o1, o2);
223	case 3:
224		GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
225		return GBACheatAddProActionReplayRaw(set, o1, o2);
226	}
227	return false;
228}
229
230bool GBACheatAutodetectLine(struct GBACheatSet* cheats, const char* line) {
231	uint32_t op1;
232	uint32_t op2;
233	line = hex32(line, &op1);
234	if (!line) {
235		return false;
236	}
237	while (*line == ' ') {
238		++line;
239	}
240	line = hex32(line, &op2);
241	if (!line) {
242		return false;
243	}
244	return GBACheatAddAutodetect(cheats, op1, op2);
245}
246
247bool GBACheatParseFile(struct GBACheatDevice* device, struct VFile* vf) {
248	char cheat[MAX_LINE_LENGTH];
249	struct GBACheatSet* set = 0;
250	struct GBACheatSet* newSet;
251	int gsaVersion = 0;
252	bool nextDisabled = false;
253	bool reset = false;
254	while (true) {
255		size_t i = 0;
256		ssize_t bytesRead = vf->readline(vf, cheat, sizeof(cheat));
257		if (bytesRead == 0) {
258			break;
259		}
260		if (bytesRead < 0) {
261			return false;
262		}
263		while (isspace((int) cheat[i])) {
264			++i;
265		}
266		switch (cheat[i]) {
267		case '#':
268			do {
269				++i;
270			} while (isspace((int) cheat[i]));
271			cheat[strlen(cheat) - 1] = '\0'; // Remove trailing newline
272			newSet = malloc(sizeof(*set));
273			GBACheatSetInit(newSet, &cheat[i]);
274			newSet->enabled = !nextDisabled;
275			nextDisabled = false;
276			if (set) {
277				GBACheatAddSet(device, set);
278			}
279			if (set && !reset) {
280				GBACheatSetCopyProperties(newSet, set);
281			} else {
282				GBACheatSetGameSharkVersion(newSet, gsaVersion);
283			}
284			reset = false;
285			set = newSet;
286			break;
287		case '!':
288			do {
289				++i;
290			} while (isspace((int) cheat[i]));
291			if (strncasecmp(&cheat[i], "GSAv", 4) == 0 || strncasecmp(&cheat[i], "PARv", 4) == 0) {
292				i += 4;
293				gsaVersion = atoi(&cheat[i]);
294				break;
295			}
296			if (strcasecmp(&cheat[i], "disabled") == 0) {
297				nextDisabled = true;
298				break;
299			}
300			if (strcasecmp(&cheat[i], "reset") == 0) {
301				reset = true;
302				break;
303			}
304			break;
305		default:
306			if (!set) {
307				set = malloc(sizeof(*set));
308				GBACheatSetInit(set, 0);
309				set->enabled = !nextDisabled;
310				nextDisabled = false;
311				GBACheatSetGameSharkVersion(set, gsaVersion);
312			}
313			GBACheatAddLine(set, cheat);
314			break;
315		}
316	}
317	if (set) {
318		GBACheatAddSet(device, set);
319	}
320	return true;
321}
322
323bool GBACheatSaveFile(struct GBACheatDevice* device, struct VFile* vf) {
324	static const char lineStart[3] = "# ";
325	static const char lineEnd = '\n';
326
327	struct GBACheatHook* lastHook = 0;
328
329	size_t i;
330	for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
331		struct GBACheatSet* set = *GBACheatSetsGetPointer(&device->cheats, i);
332		if (lastHook && set->hook != lastHook) {
333			static const char* resetDirective = "!reset\n";
334			vf->write(vf, resetDirective, strlen(resetDirective));
335		}
336		switch (set->gsaVersion) {
337		case 1: {
338			static const char* versionDirective = "!GSAv1\n";
339			vf->write(vf, versionDirective, strlen(versionDirective));
340			break;
341		}
342		case 3: {
343			static const char* versionDirective = "!PARv3\n";
344			vf->write(vf, versionDirective, strlen(versionDirective));
345			break;
346		}
347		default:
348			break;
349		}
350		lastHook = set->hook;
351		if (!set->enabled) {
352			static const char* disabledDirective = "!disabled\n";
353			vf->write(vf, disabledDirective, strlen(disabledDirective));
354		}
355
356		vf->write(vf, lineStart, 2);
357		if (set->name) {
358			vf->write(vf, set->name, strlen(set->name));
359		}
360		vf->write(vf, &lineEnd, 1);
361		size_t c;
362		for (c = 0; c < StringListSize(&set->lines); ++c) {
363			const char* line = *StringListGetPointer(&set->lines, c);
364			vf->write(vf, line, strlen(line));
365			vf->write(vf, &lineEnd, 1);
366		}
367	}
368	return true;
369}
370
371bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
372	uint32_t address;
373	uint8_t op;
374	uint32_t value = 0;
375	int width = 0;
376	const char* lineNext = hex32(line, &address);
377	if (!lineNext) {
378		return false;
379	}
380	if (lineNext[0] != ':') {
381		return false;
382	}
383	++lineNext;
384	while (width < 4) {
385		lineNext = hex8(lineNext, &op);
386		if (!lineNext) {
387			break;
388		}
389		value <<= 8;
390		value |= op;
391		++width;
392	}
393	if (width == 0 || width == 3) {
394		return false;
395	}
396
397	struct GBACheat* cheat = GBACheatListAppend(&cheats->list);
398	cheat->address = address;
399	cheat->operandOffset = 0;
400	cheat->addressOffset = 0;
401	cheat->repeat = 1;
402	cheat->type = CHEAT_ASSIGN;
403	cheat->width = width;
404	cheat->operand = value;
405	GBACheatRegisterLine(cheats, line);
406	return true;
407}
408
409bool GBACheatAddLine(struct GBACheatSet* cheats, const char* line) {
410	uint32_t op1;
411	uint16_t op2;
412	uint16_t op3;
413	const char* lineNext = hex32(line, &op1);
414	if (!lineNext) {
415		return false;
416	}
417	if (lineNext[0] == ':') {
418		return GBACheatAddVBALine(cheats, line);
419	}
420	while (isspace((int) lineNext[0])) {
421		++lineNext;
422	}
423	lineNext = hex16(lineNext, &op2);
424	if (!lineNext) {
425		return false;
426	}
427	if (!lineNext[0] || isspace((int) lineNext[0])) {
428		return GBACheatAddCodeBreaker(cheats, op1, op2);
429	}
430	lineNext = hex16(lineNext, &op3);
431	if (!lineNext) {
432		return false;
433	}
434	uint32_t realOp2 = op2;
435	realOp2 <<= 16;
436	realOp2 |= op3;
437	return GBACheatAddAutodetect(cheats, op1, realOp2);
438}
439
440void GBACheatRefresh(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
441	if (!cheats->enabled) {
442		return;
443	}
444	bool condition = true;
445	int conditionRemaining = 0;
446	int negativeConditionRemaining = 0;
447	_patchROM(device, cheats);
448
449	size_t nCodes = GBACheatListSize(&cheats->list);
450	size_t i;
451	for (i = 0; i < nCodes; ++i) {
452		if (conditionRemaining > 0) {
453			--conditionRemaining;
454			if (!condition) {
455				continue;
456			}
457		} else if (negativeConditionRemaining > 0) {
458			conditionRemaining = negativeConditionRemaining - 1;
459			negativeConditionRemaining = 0;
460			condition = !condition;
461			if (!condition) {
462				continue;
463			}
464		} else {
465			condition = true;
466		}
467		struct GBACheat* cheat = GBACheatListGetPointer(&cheats->list, i);
468		int32_t value = 0;
469		int32_t operand = cheat->operand;
470		uint32_t operationsRemaining = cheat->repeat;
471		uint32_t address = cheat->address;
472		bool performAssignment = false;
473		for (; operationsRemaining; --operationsRemaining) {
474			switch (cheat->type) {
475			case CHEAT_ASSIGN:
476				value = operand;
477				performAssignment = true;
478				break;
479			case CHEAT_ASSIGN_INDIRECT:
480				value = operand;
481				address = _readMem(device->p->cpu, address + cheat->addressOffset, 4);
482				performAssignment = true;
483				break;
484			case CHEAT_AND:
485				value = _readMem(device->p->cpu, address, cheat->width) & operand;
486				performAssignment = true;
487				break;
488			case CHEAT_ADD:
489				value = _readMem(device->p->cpu, address, cheat->width) + operand;
490				performAssignment = true;
491				break;
492			case CHEAT_OR:
493				value = _readMem(device->p->cpu, address, cheat->width) | operand;
494				performAssignment = true;
495				break;
496			case CHEAT_IF_EQ:
497				condition = _readMem(device->p->cpu, address, cheat->width) == operand;
498				conditionRemaining = cheat->repeat;
499				negativeConditionRemaining = cheat->negativeRepeat;
500				break;
501			case CHEAT_IF_NE:
502				condition = _readMem(device->p->cpu, address, cheat->width) != operand;
503				conditionRemaining = cheat->repeat;
504				negativeConditionRemaining = cheat->negativeRepeat;
505				break;
506			case CHEAT_IF_LT:
507				condition = _readMem(device->p->cpu, address, cheat->width) < operand;
508				conditionRemaining = cheat->repeat;
509				negativeConditionRemaining = cheat->negativeRepeat;
510				break;
511			case CHEAT_IF_GT:
512				condition = _readMem(device->p->cpu, address, cheat->width) > operand;
513				conditionRemaining = cheat->repeat;
514				negativeConditionRemaining = cheat->negativeRepeat;
515				break;
516			case CHEAT_IF_ULT:
517				condition = (uint32_t) _readMem(device->p->cpu, address, cheat->width) < (uint32_t) operand;
518				conditionRemaining = cheat->repeat;
519				negativeConditionRemaining = cheat->negativeRepeat;
520				break;
521			case CHEAT_IF_UGT:
522				condition = (uint32_t) _readMem(device->p->cpu, address, cheat->width) > (uint32_t) operand;
523				conditionRemaining = cheat->repeat;
524				negativeConditionRemaining = cheat->negativeRepeat;
525				break;
526			case CHEAT_IF_AND:
527				condition = _readMem(device->p->cpu, address, cheat->width) & operand;
528				conditionRemaining = cheat->repeat;
529				negativeConditionRemaining = cheat->negativeRepeat;
530				break;
531			case CHEAT_IF_LAND:
532				condition = _readMem(device->p->cpu, address, cheat->width) && operand;
533				conditionRemaining = cheat->repeat;
534				negativeConditionRemaining = cheat->negativeRepeat;
535				break;
536			}
537
538			if (performAssignment) {
539				_writeMem(device->p->cpu, address, cheat->width, value);
540			}
541
542			address += cheat->addressOffset;
543			operand += cheat->operandOffset;
544		}
545	}
546}
547
548void GBACheatSetCopyProperties(struct GBACheatSet* newSet, struct GBACheatSet* set) {
549	newSet->gsaVersion = set->gsaVersion;
550	memcpy(newSet->gsaSeeds, set->gsaSeeds, sizeof(newSet->gsaSeeds));
551	if (set->hook) {
552		if (newSet->hook) {
553			--newSet->hook->refs;
554			if (newSet->hook->refs == 0) {
555				free(newSet->hook);
556			}
557		}
558		newSet->hook = set->hook;
559		++newSet->hook->refs;
560	}
561}
562
563void GBACheatDeviceInit(void* cpu, struct mCPUComponent* component) {
564	struct GBACheatDevice* device = (struct GBACheatDevice*) component;
565	device->p = (struct GBA*) ((struct ARMCore*) cpu)->master;
566	size_t i;
567	for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
568		struct GBACheatSet* cheats = *GBACheatSetsGetPointer(&device->cheats, i);
569		_addBreakpoint(device, cheats);
570		_patchROM(device, cheats);
571	}
572}
573
574void GBACheatDeviceDeinit(struct mCPUComponent* component) {
575	struct GBACheatDevice* device = (struct GBACheatDevice*) component;
576	size_t i;
577	for (i = GBACheatSetsSize(&device->cheats); i--;) {
578		struct GBACheatSet* cheats = *GBACheatSetsGetPointer(&device->cheats, i);
579		_unpatchROM(device, cheats);
580		_removeBreakpoint(device, cheats);
581	}
582}