all repos — mgba @ 82c4d93dc451af784444a968fee8edc65b0b471a

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
 18DEFINE_VECTOR(GBACheatList, struct GBACheat);
 19DEFINE_VECTOR(GBACheatSets, struct GBACheatSet*);
 20DEFINE_VECTOR(StringList, char*);
 21
 22static int32_t _readMem(struct ARMCore* cpu, uint32_t address, int width) {
 23	switch (width) {
 24	case 1:
 25		return cpu->memory.load8(cpu, address, 0);
 26	case 2:
 27		return cpu->memory.load16(cpu, address, 0);
 28	case 4:
 29		return cpu->memory.load32(cpu, address, 0);
 30	}
 31	return 0;
 32}
 33
 34static void _writeMem(struct ARMCore* cpu, uint32_t address, int width, int32_t value) {
 35	switch (width) {
 36	case 1:
 37		cpu->memory.store8(cpu, address, value, 0);
 38		break;
 39	case 2:
 40		cpu->memory.store16(cpu, address, value, 0);
 41		break;
 42	case 4:
 43		cpu->memory.store32(cpu, address, value, 0);
 44		break;
 45	}
 46}
 47
 48void GBACheatRegisterLine(struct GBACheatSet* cheats, const char* line) {
 49	*StringListAppend(&cheats->lines) = strdup(line);
 50}
 51
 52static void _addBreakpoint(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
 53	if (!device->p || !cheats->hook) {
 54		return;
 55	}
 56	++cheats->hook->reentries;
 57	if (cheats->hook->reentries > 1) {
 58		return;
 59	}
 60	GBASetBreakpoint(device->p, &device->d, cheats->hook->address, cheats->hook->mode, &cheats->hook->patchedOpcode);
 61}
 62
 63static void _removeBreakpoint(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
 64	if (!device->p || !cheats->hook) {
 65		return;
 66	}
 67	--cheats->hook->reentries;
 68	if (cheats->hook->reentries > 0) {
 69		return;
 70	}
 71	GBAClearBreakpoint(device->p, cheats->hook->address, cheats->hook->mode, cheats->hook->patchedOpcode);
 72}
 73
 74static void _patchROM(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
 75	if (!device->p) {
 76		return;
 77	}
 78	int i;
 79	for (i = 0; i < MAX_ROM_PATCHES; ++i) {
 80		if (!cheats->romPatches[i].exists || cheats->romPatches[i].applied) {
 81			continue;
 82		}
 83		GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].newValue, &cheats->romPatches[i].oldValue);
 84		cheats->romPatches[i].applied = true;
 85	}
 86}
 87
 88static void _unpatchROM(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
 89	if (!device->p) {
 90		return;
 91	}
 92	int i;
 93	for (i = 0; i < MAX_ROM_PATCHES; ++i) {
 94		if (!cheats->romPatches[i].exists || !cheats->romPatches[i].applied) {
 95			continue;
 96		}
 97		GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].oldValue, 0);
 98		cheats->romPatches[i].applied = false;
 99	}
100}
101
102static void GBACheatDeviceInit(struct ARMCore*, struct ARMComponent*);
103static void GBACheatDeviceDeinit(struct ARMComponent*);
104
105void GBACheatDeviceCreate(struct GBACheatDevice* device) {
106	device->d.id = GBA_CHEAT_DEVICE_ID;
107	device->d.init = GBACheatDeviceInit;
108	device->d.deinit = GBACheatDeviceDeinit;
109	GBACheatSetsInit(&device->cheats, 4);
110}
111
112void GBACheatDeviceDestroy(struct GBACheatDevice* device) {
113	GBACheatDeviceClear(device);
114	GBACheatSetsDeinit(&device->cheats);
115}
116
117void GBACheatDeviceClear(struct GBACheatDevice* device) {
118	size_t i;
119	for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
120		struct GBACheatSet* set = *GBACheatSetsGetPointer(&device->cheats, i);
121		GBACheatSetDeinit(set);
122		free(set);
123	}
124	GBACheatSetsClear(&device->cheats);
125}
126
127void GBACheatSetInit(struct GBACheatSet* set, const char* name) {
128	GBACheatListInit(&set->list, 4);
129	StringListInit(&set->lines, 4);
130	set->incompleteCheat = 0;
131	set->incompletePatch = 0;
132	set->currentBlock = 0;
133	set->gsaVersion = 0;
134	set->remainingAddresses = 0;
135	set->hook = 0;
136	int i;
137	for (i = 0; i < MAX_ROM_PATCHES; ++i) {
138		set->romPatches[i].exists = false;
139	}
140	if (name) {
141		set->name = strdup(name);
142	} else {
143		set->name = 0;
144	}
145	set->enabled = true;
146}
147
148void GBACheatSetDeinit(struct GBACheatSet* set) {
149	GBACheatListDeinit(&set->list);
150	size_t i;
151	for (i = 0; i < StringListSize(&set->lines); ++i) {
152		free(*StringListGetPointer(&set->lines, i));
153	}
154	if (set->name) {
155		free(set->name);
156	}
157	if (set->hook) {
158		--set->hook->refs;
159		if (set->hook->refs == 0) {
160			free(set->hook);
161		}
162	}
163}
164
165void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice* device) {
166	if (gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE]) {
167		ARMHotplugDetach(gba->cpu, GBA_COMPONENT_CHEAT_DEVICE);
168	}
169	gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE] = &device->d;
170	ARMHotplugAttach(gba->cpu, GBA_COMPONENT_CHEAT_DEVICE);
171}
172
173void GBACheatAddSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
174	*GBACheatSetsAppend(&device->cheats) = cheats;
175	_addBreakpoint(device, cheats);
176	_patchROM(device, cheats);
177}
178
179void GBACheatRemoveSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
180	size_t i;
181	for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
182		if (*GBACheatSetsGetPointer(&device->cheats, i) == cheats) {
183			break;
184		}
185	}
186	if (i == GBACheatSetsSize(&device->cheats)) {
187		return;
188	}
189	GBACheatSetsShift(&device->cheats, i, 1);
190	_unpatchROM(device, cheats);
191	_removeBreakpoint(device, cheats);
192}
193
194bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
195	uint32_t o1 = op1;
196	uint32_t o2 = op2;
197	char line[18] = "XXXXXXXX XXXXXXXX";
198	snprintf(line, sizeof(line), "%08X %08X", op1, op2);
199	GBACheatRegisterLine(set, line);
200
201	switch (set->gsaVersion) {
202	case 0:
203		// Try to detect GameShark version
204		GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
205		if ((o1 & 0xF0000000) == 0xF0000000 && !(o2 & 0xFFFFFCFE)) {
206			GBACheatSetGameSharkVersion(set, 1);
207			return GBACheatAddGameSharkRaw(set, o1, o2);
208		}
209		o1 = op1;
210		o2 = op2;
211		GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
212		if ((o1 & 0xFE000000) == 0xC4000000 && !(o2 & 0xFFFF0000)) {
213			GBACheatSetGameSharkVersion(set, 3);
214			return GBACheatAddProActionReplayRaw(set, o1, o2);
215		}
216		break;
217	case 1:
218		GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
219		return GBACheatAddGameSharkRaw(set, o1, o2);
220	case 3:
221		GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
222		return GBACheatAddProActionReplayRaw(set, o1, o2);
223	}
224	return false;
225}
226
227bool GBACheatAutodetectLine(struct GBACheatSet* cheats, const char* line) {
228	uint32_t op1;
229	uint32_t op2;
230	line = hex32(line, &op1);
231	if (!line) {
232		return false;
233	}
234	while (*line == ' ') {
235		++line;
236	}
237	line = hex32(line, &op2);
238	if (!line) {
239		return false;
240	}
241	return GBACheatAddAutodetect(cheats, op1, op2);
242}
243
244bool GBACheatParseFile(struct GBACheatDevice* device, struct VFile* vf) {
245	char cheat[MAX_LINE_LENGTH];
246	struct GBACheatSet* set = 0;
247	struct GBACheatSet* newSet;
248	int gsaVersion = 0;
249	bool nextDisabled = false;
250	bool reset = false;
251	while (true) {
252		size_t i = 0;
253		ssize_t bytesRead = vf->readline(vf, cheat, sizeof(cheat));
254		if (bytesRead == 0) {
255			break;
256		}
257		if (bytesRead < 0) {
258			return false;
259		}
260		while (isspace((int) cheat[i])) {
261			++i;
262		}
263		switch (cheat[i]) {
264		case '#':
265			do {
266				++i;
267			} while (isspace((int) cheat[i]));
268			cheat[strlen(cheat) - 1] = '\0'; // Remove trailing newline
269			newSet = malloc(sizeof(*set));
270			GBACheatSetInit(newSet, &cheat[i]);
271			newSet->enabled = !nextDisabled;
272			nextDisabled = false;
273			if (set) {
274				GBACheatAddSet(device, set);
275			}
276			if (set && !reset) {
277				GBACheatSetCopyProperties(newSet, set);
278			} else {
279				GBACheatSetGameSharkVersion(newSet, gsaVersion);
280			}
281			reset = false;
282			set = newSet;
283			break;
284		case '!':
285			do {
286				++i;
287			} while (isspace((int) cheat[i]));
288			if (strncasecmp(&cheat[i], "GSAv", 4) == 0 || strncasecmp(&cheat[i], "PARv", 4) == 0) {
289				i += 4;
290				gsaVersion = atoi(&cheat[i]);
291				break;
292			}
293			if (strcasecmp(&cheat[i], "disabled") == 0) {
294				nextDisabled = true;
295				break;
296			}
297			if (strcasecmp(&cheat[i], "reset") == 0) {
298				reset = true;
299				break;
300			}
301			break;
302		default:
303			if (!set) {
304				set = malloc(sizeof(*set));
305				GBACheatSetInit(set, 0);
306				set->enabled = !nextDisabled;
307				nextDisabled = false;
308				GBACheatSetGameSharkVersion(set, gsaVersion);
309			}
310			GBACheatAddLine(set, cheat);
311			break;
312		}
313	}
314	if (set) {
315		GBACheatAddSet(device, set);
316	}
317	return true;
318}
319
320bool GBACheatSaveFile(struct GBACheatDevice* device, struct VFile* vf) {
321	static const char lineStart[3] = "# ";
322	static const char lineEnd = '\n';
323
324	struct GBACheatHook* lastHook = 0;
325
326	size_t i;
327	for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
328		struct GBACheatSet* set = *GBACheatSetsGetPointer(&device->cheats, i);
329		if (lastHook && set->hook != lastHook) {
330			static const char* resetDirective = "!reset\n";
331			vf->write(vf, resetDirective, strlen(resetDirective));
332		}
333		switch (set->gsaVersion) {
334		case 1: {
335			static const char* versionDirective = "!GSAv1\n";
336			vf->write(vf, versionDirective, strlen(versionDirective));
337			break;
338		}
339		case 3: {
340			static const char* versionDirective = "!PARv3\n";
341			vf->write(vf, versionDirective, strlen(versionDirective));
342			break;
343		}
344		default:
345			break;
346		}
347		lastHook = set->hook;
348		if (!set->enabled) {
349			static const char* disabledDirective = "!disabled\n";
350			vf->write(vf, disabledDirective, strlen(disabledDirective));
351		}
352
353		vf->write(vf, lineStart, 2);
354		if (set->name) {
355			vf->write(vf, set->name, strlen(set->name));
356		}
357		vf->write(vf, &lineEnd, 1);
358		size_t c;
359		for (c = 0; c < StringListSize(&set->lines); ++c) {
360			const char* line = *StringListGetPointer(&set->lines, c);
361			vf->write(vf, line, strlen(line));
362			vf->write(vf, &lineEnd, 1);
363		}
364	}
365	return true;
366}
367
368bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
369	uint32_t address;
370	uint8_t op;
371	uint32_t value = 0;
372	int width = 0;
373	const char* lineNext = hex32(line, &address);
374	if (!lineNext) {
375		return false;
376	}
377	if (lineNext[0] != ':') {
378		return false;
379	}
380	++lineNext;
381	while (width < 4) {
382		lineNext = hex8(lineNext, &op);
383		if (!lineNext) {
384			break;
385		}
386		value <<= 8;
387		value |= op;
388		++width;
389	}
390	if (width == 0 || width == 3) {
391		return false;
392	}
393
394	struct GBACheat* cheat = GBACheatListAppend(&cheats->list);
395	cheat->address = address;
396	cheat->operandOffset = 0;
397	cheat->addressOffset = 0;
398	cheat->repeat = 1;
399	cheat->type = CHEAT_ASSIGN;
400	cheat->width = width;
401	cheat->operand = value;
402	GBACheatRegisterLine(cheats, line);
403	return true;
404}
405
406bool GBACheatAddLine(struct GBACheatSet* cheats, const char* line) {
407	uint32_t op1;
408	uint16_t op2;
409	uint16_t op3;
410	const char* lineNext = hex32(line, &op1);
411	if (!lineNext) {
412		return false;
413	}
414	if (lineNext[0] == ':') {
415		return GBACheatAddVBALine(cheats, line);
416	}
417	while (isspace((int) lineNext[0])) {
418		++lineNext;
419	}
420	lineNext = hex16(lineNext, &op2);
421	if (!lineNext) {
422		return false;
423	}
424	if (!lineNext[0] || isspace((int) lineNext[0])) {
425		return GBACheatAddCodeBreaker(cheats, op1, op2);
426	}
427	lineNext = hex16(lineNext, &op3);
428	if (!lineNext) {
429		return false;
430	}
431	uint32_t realOp2 = op2;
432	realOp2 <<= 16;
433	realOp2 |= op3;
434	return GBACheatAddAutodetect(cheats, op1, realOp2);
435}
436
437void GBACheatRefresh(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
438	if (!cheats->enabled) {
439		return;
440	}
441	bool condition = true;
442	int conditionRemaining = 0;
443	int negativeConditionRemaining = 0;
444	_patchROM(device, cheats);
445
446	size_t nCodes = GBACheatListSize(&cheats->list);
447	size_t i;
448	for (i = 0; i < nCodes; ++i) {
449		if (conditionRemaining > 0) {
450			--conditionRemaining;
451			if (!condition) {
452				continue;
453			}
454		} else if (negativeConditionRemaining > 0) {
455			conditionRemaining = negativeConditionRemaining - 1;
456			negativeConditionRemaining = 0;
457			condition = !condition;
458			if (!condition) {
459				continue;
460			}
461		} else {
462			condition = true;
463		}
464		struct GBACheat* cheat = GBACheatListGetPointer(&cheats->list, i);
465		int32_t value = 0;
466		int32_t operand = cheat->operand;
467		uint32_t operationsRemaining = cheat->repeat;
468		uint32_t address = cheat->address;
469		bool performAssignment = false;
470		for (; operationsRemaining; --operationsRemaining) {
471			switch (cheat->type) {
472			case CHEAT_ASSIGN:
473				value = operand;
474				performAssignment = true;
475				break;
476			case CHEAT_ASSIGN_INDIRECT:
477				value = operand;
478				address = _readMem(device->p->cpu, address + cheat->addressOffset, 4);
479				performAssignment = true;
480				break;
481			case CHEAT_AND:
482				value = _readMem(device->p->cpu, address, cheat->width) & operand;
483				performAssignment = true;
484				break;
485			case CHEAT_ADD:
486				value = _readMem(device->p->cpu, address, cheat->width) + operand;
487				performAssignment = true;
488				break;
489			case CHEAT_OR:
490				value = _readMem(device->p->cpu, address, cheat->width) | operand;
491				performAssignment = true;
492				break;
493			case CHEAT_IF_EQ:
494				condition = _readMem(device->p->cpu, address, cheat->width) == operand;
495				conditionRemaining = cheat->repeat;
496				negativeConditionRemaining = cheat->negativeRepeat;
497				break;
498			case CHEAT_IF_NE:
499				condition = _readMem(device->p->cpu, address, cheat->width) != operand;
500				conditionRemaining = cheat->repeat;
501				negativeConditionRemaining = cheat->negativeRepeat;
502				break;
503			case CHEAT_IF_LT:
504				condition = _readMem(device->p->cpu, address, cheat->width) < operand;
505				conditionRemaining = cheat->repeat;
506				negativeConditionRemaining = cheat->negativeRepeat;
507				break;
508			case CHEAT_IF_GT:
509				condition = _readMem(device->p->cpu, address, cheat->width) > operand;
510				conditionRemaining = cheat->repeat;
511				negativeConditionRemaining = cheat->negativeRepeat;
512				break;
513			case CHEAT_IF_ULT:
514				condition = (uint32_t) _readMem(device->p->cpu, address, cheat->width) < (uint32_t) operand;
515				conditionRemaining = cheat->repeat;
516				negativeConditionRemaining = cheat->negativeRepeat;
517				break;
518			case CHEAT_IF_UGT:
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_AND:
524				condition = _readMem(device->p->cpu, address, cheat->width) & operand;
525				conditionRemaining = cheat->repeat;
526				negativeConditionRemaining = cheat->negativeRepeat;
527				break;
528			case CHEAT_IF_LAND:
529				condition = _readMem(device->p->cpu, address, cheat->width) && operand;
530				conditionRemaining = cheat->repeat;
531				negativeConditionRemaining = cheat->negativeRepeat;
532				break;
533			}
534
535			if (performAssignment) {
536				_writeMem(device->p->cpu, address, cheat->width, value);
537			}
538
539			address += cheat->addressOffset;
540			operand += cheat->operandOffset;
541		}
542	}
543}
544
545void GBACheatSetCopyProperties(struct GBACheatSet* newSet, struct GBACheatSet* set) {
546	newSet->gsaVersion = set->gsaVersion;
547	memcpy(newSet->gsaSeeds, set->gsaSeeds, sizeof(newSet->gsaSeeds));
548	if (set->hook) {
549		if (newSet->hook) {
550			--newSet->hook->refs;
551			if (newSet->hook->refs == 0) {
552				free(newSet->hook);
553			}
554		}
555		newSet->hook = set->hook;
556		++newSet->hook->refs;
557	}
558}
559
560void GBACheatDeviceInit(struct ARMCore* cpu, struct ARMComponent* component) {
561	struct GBACheatDevice* device = (struct GBACheatDevice*) component;
562	device->p = (struct GBA*) cpu->master;
563	size_t i;
564	for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
565		struct GBACheatSet* cheats = *GBACheatSetsGetPointer(&device->cheats, i);
566		_addBreakpoint(device, cheats);
567		_patchROM(device, cheats);
568	}
569}
570
571void GBACheatDeviceDeinit(struct ARMComponent* component) {
572	struct GBACheatDevice* device = (struct GBACheatDevice*) component;
573	size_t i;
574	for (i = GBACheatSetsSize(&device->cheats); i--;) {
575		struct GBACheatSet* cheats = *GBACheatSetsGetPointer(&device->cheats, i);
576		_unpatchROM(device, cheats);
577		_removeBreakpoint(device, cheats);
578	}
579}