all repos — mgba @ 2eb5696ff7484a0dff996a96712c55e3606559cf

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