all repos — mgba @ 6442d17b4c0012a68683b052a0c1bbafd2318722

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		if (gbaset->hook && !gbaset->hook->reentries) {
280			_addBreakpoint(device, gbaset);
281		}
282	} else {
283		_unpatchROM(device, gbaset);
284	}
285}
286
287static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
288	struct GBACheatSet* newSet = (struct GBACheatSet*) set;
289	struct GBACheatSet* gbaset = (struct GBACheatSet*) oldSet;
290	newSet->gsaVersion = gbaset->gsaVersion;
291	memcpy(newSet->gsaSeeds, gbaset->gsaSeeds, sizeof(newSet->gsaSeeds));
292	newSet->cbRngState = gbaset->cbRngState;
293	newSet->cbMaster = gbaset->cbMaster;
294	memcpy(newSet->cbSeeds, gbaset->cbSeeds, sizeof(newSet->cbSeeds));
295	memcpy(newSet->cbTable, gbaset->cbTable, sizeof(newSet->cbTable));
296	if (gbaset->hook) {
297		if (newSet->hook) {
298			--newSet->hook->refs;
299			if (newSet->hook->refs == 0) {
300				free(newSet->hook);
301			}
302		}
303		newSet->hook = gbaset->hook;
304		++newSet->hook->refs;
305	}
306}
307
308static void GBACheatParseDirectives(struct mCheatSet* set, const struct StringList* directives) {
309	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
310	size_t d;
311	for (d = 0; d < StringListSize(directives); ++d) {
312		const char* directive = *StringListGetConstPointer(directives, d);
313		if (strcmp(directive, "GSAv1") == 0) {
314			GBACheatSetGameSharkVersion(cheats, GBA_GS_GSAV1);
315			continue;
316		}
317		if (strcmp(directive, "GSAv1 raw") == 0) {
318			GBACheatSetGameSharkVersion(cheats, GBA_GS_GSAV1_RAW);
319			continue;
320		}
321		if (strcmp(directive, "PARv3") == 0) {
322			GBACheatSetGameSharkVersion(cheats, GBA_GS_PARV3);
323			continue;
324		}
325		if (strcmp(directive, "PARv3 raw") == 0) {
326			GBACheatSetGameSharkVersion(cheats, GBA_GS_PARV3_RAW);
327			continue;
328		}
329	}
330}
331
332static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* directives) {
333	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
334
335	// TODO: Check previous directives
336	size_t d;
337	for (d = 0; d < StringListSize(directives); ++d) {
338		free(*StringListGetPointer(directives, d));
339	}
340	StringListClear(directives);
341
342	char** directive;
343	switch (cheats->gsaVersion) {
344	case 1:
345		directive = StringListAppend(directives);
346		*directive = strdup("GSAv1");
347		break;
348	case 2:
349		directive = StringListAppend(directives);
350		*directive = strdup("GSAv1 raw");
351		break;
352	case 3:
353		directive = StringListAppend(directives);
354		*directive = strdup("PARv3");
355		break;
356	case 4:
357		directive = StringListAppend(directives);
358		*directive = strdup("PARv3 raw");
359		break;
360	}
361}
362
363int GBACheatAddressIsReal(uint32_t address) {
364	switch (address >> BASE_OFFSET) {
365	case REGION_BIOS:
366		return -0x80;
367		break;
368	case REGION_WORKING_RAM:
369		if ((address & OFFSET_MASK) > SIZE_WORKING_RAM) {
370			return -0x40;
371		}
372		return 0x20;
373	case REGION_WORKING_IRAM:
374		if ((address & OFFSET_MASK) > SIZE_WORKING_IRAM) {
375			return -0x40;
376		}
377		return 0x20;
378	case REGION_IO:
379		if ((address & OFFSET_MASK) > SIZE_IO) {
380			return -0x80;
381		}
382		return 0x10;
383	case REGION_OAM:
384		if ((address & OFFSET_MASK) > SIZE_OAM) {
385			return -0x80;
386		}
387		return -0x8;
388	case REGION_VRAM:
389		if ((address & OFFSET_MASK) > SIZE_VRAM) {
390			return -0x80;
391		}
392		return -0x8;
393	case REGION_PALETTE_RAM:
394		if ((address & OFFSET_MASK) > SIZE_PALETTE_RAM) {
395			return -0x80;
396		}
397		return -0x8;
398	case REGION_CART0:
399	case REGION_CART0_EX:
400	case REGION_CART1:
401	case REGION_CART1_EX:
402	case REGION_CART2:
403	case REGION_CART2_EX:
404		return -0x8;
405	case REGION_CART_SRAM:
406	case REGION_CART_SRAM_MIRROR:
407		if ((address & OFFSET_MASK) > SIZE_CART_FLASH512) {
408			return -0x80;
409		}
410		return -0x8;
411	default:
412		return -0xC0;
413	}
414}