all repos — mgba @ e12ca74d1e891349a01be852e0ff555529868e50

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