all repos — mgba @ 1807b66bd60c4dadd91190833ca0a81e8d4d49bd

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 <mgba/internal/gba/cheats.h>
  7
  8#include <mgba/core/core.h>
  9#include <mgba/internal/gba/gba.h>
 10#include <mgba-util/string.h>
 11#include "gba/cheats/gameshark.h"
 12#include "gba/cheats/parv3.h"
 13
 14#define MAX_LINE_LENGTH 128
 15
 16static void _addBreakpoint(struct mCheatDevice* device, struct GBACheatSet* cheats) {
 17	if (!device->p || !cheats->hook) {
 18		return;
 19	}
 20	++cheats->hook->reentries;
 21	if (cheats->hook->reentries > 1) {
 22		return;
 23	}
 24	GBASetBreakpoint(device->p->board, &device->d, cheats->hook->address, cheats->hook->mode, &cheats->hook->patchedOpcode);
 25}
 26
 27static void _removeBreakpoint(struct mCheatDevice* device, struct GBACheatSet* cheats) {
 28	if (!device->p || !cheats->hook) {
 29		return;
 30	}
 31	--cheats->hook->reentries;
 32	if (cheats->hook->reentries > 0) {
 33		return;
 34	}
 35	GBAClearBreakpoint(device->p->board, cheats->hook->address, cheats->hook->mode, cheats->hook->patchedOpcode);
 36}
 37
 38static void _patchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) {
 39	if (!device->p) {
 40		return;
 41	}
 42	int i;
 43	for (i = 0; i < MAX_ROM_PATCHES; ++i) {
 44		if (!cheats->romPatches[i].exists || cheats->romPatches[i].applied) {
 45			continue;
 46		}
 47		GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].newValue, &cheats->romPatches[i].oldValue);
 48		cheats->romPatches[i].applied = true;
 49	}
 50}
 51
 52static void _unpatchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) {
 53	if (!device->p) {
 54		return;
 55	}
 56	int i;
 57	for (i = 0; i < MAX_ROM_PATCHES; ++i) {
 58		if (!cheats->romPatches[i].exists || !cheats->romPatches[i].applied) {
 59			continue;
 60		}
 61		GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].oldValue, 0);
 62		cheats->romPatches[i].applied = false;
 63	}
 64}
 65
 66static void GBACheatSetDeinit(struct mCheatSet* set);
 67static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device);
 68static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device);
 69static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device);
 70static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet);
 71static void GBACheatParseDirectives(struct mCheatSet*, const struct StringList* directives);
 72static void GBACheatDumpDirectives(struct mCheatSet*, struct StringList* directives);
 73static bool GBACheatAddLine(struct mCheatSet*, const char* line, int type);
 74
 75static struct mCheatSet* GBACheatSetCreate(struct mCheatDevice* device, const char* name) {
 76	UNUSED(device);
 77	struct GBACheatSet* set = malloc(sizeof(*set));
 78	mCheatSetInit(&set->d, name);
 79	set->incompleteCheat = -1;
 80	set->incompletePatch = 0;
 81	set->currentBlock = -1;
 82	set->gsaVersion = 0;
 83	set->cbRngState = 0;
 84	set->cbMaster = 0;
 85	set->remainingAddresses = 0;
 86	set->hook = NULL;
 87
 88	set->d.deinit = GBACheatSetDeinit;
 89	set->d.add = GBACheatAddSet;
 90	set->d.remove = GBACheatRemoveSet;
 91
 92	set->d.addLine = GBACheatAddLine;
 93	set->d.copyProperties = GBACheatSetCopyProperties;
 94
 95	set->d.parseDirectives = GBACheatParseDirectives;
 96	set->d.dumpDirectives = GBACheatDumpDirectives;
 97
 98	set->d.refresh = GBACheatRefresh;
 99
100	int i;
101	for (i = 0; i < MAX_ROM_PATCHES; ++i) {
102		set->romPatches[i].exists = false;
103	}
104	return &set->d;
105}
106
107struct mCheatDevice* GBACheatDeviceCreate(void) {
108	struct mCheatDevice* device = malloc(sizeof(*device));
109	mCheatDeviceCreate(device);
110	device->createSet = GBACheatSetCreate;
111	return device;
112}
113
114static void GBACheatSetDeinit(struct mCheatSet* set) {
115	struct GBACheatSet* gbaset = (struct GBACheatSet*) set;
116	if (gbaset->hook) {
117		--gbaset->hook->refs;
118		if (gbaset->hook->refs == 0) {
119			free(gbaset->hook);
120		}
121	}
122}
123
124static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
125	struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
126	_addBreakpoint(device, gbaset);
127	_patchROM(device, gbaset);
128}
129
130static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
131	struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
132	_unpatchROM(device, gbaset);
133	_removeBreakpoint(device, gbaset);
134}
135
136static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
137	uint32_t o1 = op1;
138	uint32_t o2 = op2;
139	char line[18] = "XXXXXXXX XXXXXXXX";
140	snprintf(line, sizeof(line), "%08X %08X", op1, op2);
141
142	int gsaP, rgsaP, parP, rparP;
143	int maxProbability = INT_MIN;
144	switch (set->gsaVersion) {
145	case 0:
146		// Try to detect GameShark version
147		GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
148		gsaP = GBACheatGameSharkProbability(o1, o2);
149		o1 = op1;
150		o2 = op2;
151		if (gsaP > maxProbability) {
152			maxProbability = gsaP;
153			GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1);
154		}
155
156		GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
157		parP = GBACheatProActionReplayProbability(o1, o2);
158		if (parP > maxProbability) {
159			maxProbability = parP;
160			GBACheatSetGameSharkVersion(set, GBA_GS_PARV3);
161		}
162
163		rgsaP = GBACheatGameSharkProbability(op1, op1);
164		if (rgsaP > maxProbability) {
165			maxProbability = rgsaP;
166			GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1_RAW);
167		}
168
169		rparP = GBACheatProActionReplayProbability(op1, op1);
170		if (rparP > maxProbability) {
171			maxProbability = rparP;
172			GBACheatSetGameSharkVersion(set, GBA_GS_PARV3_RAW);
173		}
174
175		if (set->gsaVersion < 3) {
176			return GBACheatAddGameShark(set, op1, op2);
177		} else {
178			return GBACheatAddProActionReplay(set, op1, op2);
179		}
180		break;
181	case 1:
182	case 2:
183		return GBACheatAddGameShark(set, o1, o2);
184	case 3:
185	case 4:
186		return GBACheatAddProActionReplay(set, o1, o2);
187	}
188	return false;
189}
190
191bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
192	uint32_t address;
193	uint8_t op;
194	uint32_t value = 0;
195	int width = 0;
196	const char* lineNext = hex32(line, &address);
197	if (!lineNext) {
198		return false;
199	}
200	if (lineNext[0] != ':') {
201		return false;
202	}
203	++lineNext;
204	while (width < 4) {
205		lineNext = hex8(lineNext, &op);
206		if (!lineNext) {
207			break;
208		}
209		value <<= 8;
210		value |= op;
211		++width;
212	}
213	if (width == 0 || width == 3) {
214		return false;
215	}
216
217	struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
218	cheat->address = address;
219	cheat->operandOffset = 0;
220	cheat->addressOffset = 0;
221	cheat->repeat = 1;
222	cheat->type = CHEAT_ASSIGN;
223	cheat->width = width;
224	cheat->operand = value;
225	return true;
226}
227
228bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) {
229	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
230	switch (type) {
231	case GBA_CHEAT_AUTODETECT:
232		break;
233	case GBA_CHEAT_CODEBREAKER:
234		return GBACheatAddCodeBreakerLine(cheats, line);
235	case GBA_CHEAT_GAMESHARK:
236		return GBACheatAddGameSharkLine(cheats, line);
237	case GBA_CHEAT_PRO_ACTION_REPLAY:
238		return GBACheatAddProActionReplayLine(cheats, line);
239	case GBA_CHEAT_VBA:
240		return GBACheatAddVBALine(cheats, line);
241	default:
242		return false;
243	}
244
245	uint32_t op1;
246	uint16_t op2;
247	uint16_t op3;
248	const char* lineNext = hex32(line, &op1);
249	if (!lineNext) {
250		return false;
251	}
252	if (lineNext[0] == ':') {
253		return GBACheatAddVBALine(cheats, line);
254	}
255	while (isspace((int) lineNext[0])) {
256		++lineNext;
257	}
258	lineNext = hex16(lineNext, &op2);
259	if (!lineNext) {
260		return false;
261	}
262	if (!lineNext[0] || isspace((int) lineNext[0])) {
263		return GBACheatAddCodeBreaker(cheats, op1, op2);
264	}
265	lineNext = hex16(lineNext, &op3);
266	if (!lineNext) {
267		return false;
268	}
269	uint32_t realOp2 = op2;
270	realOp2 <<= 16;
271	realOp2 |= op3;
272	return GBACheatAddAutodetect(cheats, op1, realOp2);
273}
274
275static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
276	struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
277	if (cheats->enabled) {
278		_patchROM(device, gbaset);
279	} else {
280		_unpatchROM(device, gbaset);
281	}
282}
283
284static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
285	struct GBACheatSet* newSet = (struct GBACheatSet*) set;
286	struct GBACheatSet* gbaset = (struct GBACheatSet*) oldSet;
287	newSet->gsaVersion = gbaset->gsaVersion;
288	memcpy(newSet->gsaSeeds, gbaset->gsaSeeds, sizeof(newSet->gsaSeeds));
289	newSet->cbRngState = gbaset->cbRngState;
290	newSet->cbMaster = gbaset->cbMaster;
291	memcpy(newSet->cbSeeds, gbaset->cbSeeds, sizeof(newSet->cbSeeds));
292	memcpy(newSet->cbTable, gbaset->cbTable, sizeof(newSet->cbTable));
293	if (gbaset->hook) {
294		if (newSet->hook) {
295			--newSet->hook->refs;
296			if (newSet->hook->refs == 0) {
297				free(newSet->hook);
298			}
299		}
300		newSet->hook = gbaset->hook;
301		++newSet->hook->refs;
302	}
303}
304
305static void GBACheatParseDirectives(struct mCheatSet* set, const struct StringList* directives) {
306	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
307	size_t d;
308	for (d = 0; d < StringListSize(directives); ++d) {
309		const char* directive = *StringListGetConstPointer(directives, d);
310		if (strcmp(directive, "GSAv1") == 0) {
311			GBACheatSetGameSharkVersion(cheats, GBA_GS_GSAV1);
312			continue;
313		}
314		if (strcmp(directive, "GSAv1 raw") == 0) {
315			GBACheatSetGameSharkVersion(cheats, GBA_GS_GSAV1_RAW);
316			continue;
317		}
318		if (strcmp(directive, "PARv3") == 0) {
319			GBACheatSetGameSharkVersion(cheats, GBA_GS_PARV3);
320			continue;
321		}
322		if (strcmp(directive, "PARv3 raw") == 0) {
323			GBACheatSetGameSharkVersion(cheats, GBA_GS_PARV3_RAW);
324			continue;
325		}
326	}
327}
328
329static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* directives) {
330	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
331
332	// TODO: Check previous directives
333	size_t d;
334	for (d = 0; d < StringListSize(directives); ++d) {
335		free(*StringListGetPointer(directives, d));
336	}
337	StringListClear(directives);
338
339	char** directive;
340	switch (cheats->gsaVersion) {
341	case 1:
342		directive = StringListAppend(directives);
343		*directive = strdup("GSAv1");
344		break;
345	case 2:
346		directive = StringListAppend(directives);
347		*directive = strdup("GSAv1 raw");
348		break;
349	case 3:
350		directive = StringListAppend(directives);
351		*directive = strdup("PARv3");
352		break;
353	case 4:
354		directive = StringListAppend(directives);
355		*directive = strdup("PARv3 raw");
356		break;
357	}
358}
359
360int GBACheatAddressIsReal(uint32_t address) {
361	switch (address >> BASE_OFFSET) {
362	case REGION_BIOS:
363		return -0x80;
364		break;
365	case REGION_WORKING_RAM:
366		if ((address & OFFSET_MASK) > SIZE_WORKING_RAM) {
367			return -0x40;
368		}
369		return 0x20;
370	case REGION_WORKING_IRAM:
371		if ((address & OFFSET_MASK) > SIZE_WORKING_IRAM) {
372			return -0x40;
373		}
374		return 0x20;
375	case REGION_IO:
376		if ((address & OFFSET_MASK) > SIZE_IO) {
377			return -0x80;
378		}
379		return 0x10;
380	case REGION_OAM:
381		if ((address & OFFSET_MASK) > SIZE_OAM) {
382			return -0x80;
383		}
384		return -0x8;
385	case REGION_VRAM:
386		if ((address & OFFSET_MASK) > SIZE_VRAM) {
387			return -0x80;
388		}
389		return -0x8;
390	case REGION_PALETTE_RAM:
391		if ((address & OFFSET_MASK) > SIZE_PALETTE_RAM) {
392			return -0x80;
393		}
394		return -0x8;
395	case REGION_CART0:
396	case REGION_CART0_EX:
397	case REGION_CART1:
398	case REGION_CART1_EX:
399	case REGION_CART2:
400	case REGION_CART2_EX:
401		return -0x8;
402	case REGION_CART_SRAM:
403	case REGION_CART_SRAM_MIRROR:
404		if ((address & OFFSET_MASK) > SIZE_CART_FLASH512) {
405			return -0x80;
406		}
407		return -0x8;
408	default:
409		return -0xC0;
410	}
411}