all repos — mgba @ 845ecfe81e4458645688af34e347e99bb0b53ed0

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