all repos — mgba @ 1ac5726d8a2b4ff8f63d0d0e80445b92726fcf42

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