all repos — mgba @ 9a9aba86e771785d71a96fc28b9d27f92cc6cc4b

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, parP;
143	switch (set->gsaVersion) {
144	case 0:
145		// Try to detect GameShark version
146		GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
147		gsaP = GBACheatGameSharkProbability(o1, o2);
148		o1 = op1;
149		o2 = op2;
150		GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
151		parP = GBACheatProActionReplayProbability(o1, o2);
152		o1 = op1;
153		o2 = op2;
154		if (gsaP > parP) {
155			GBACheatSetGameSharkVersion(set, 1);
156			GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
157			return GBACheatAddGameSharkRaw(set, o1, o2);
158		} else {
159			// If probabilities are equal, assume PARv3
160			GBACheatSetGameSharkVersion(set, 3);
161			GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
162			return GBACheatAddProActionReplayRaw(set, o1, o2);
163		}
164		break;
165	case 1:
166		GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
167		return GBACheatAddGameSharkRaw(set, o1, o2);
168	case 3:
169		GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
170		return GBACheatAddProActionReplayRaw(set, o1, o2);
171	}
172	return false;
173}
174
175bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
176	uint32_t address;
177	uint8_t op;
178	uint32_t value = 0;
179	int width = 0;
180	const char* lineNext = hex32(line, &address);
181	if (!lineNext) {
182		return false;
183	}
184	if (lineNext[0] != ':') {
185		return false;
186	}
187	++lineNext;
188	while (width < 4) {
189		lineNext = hex8(lineNext, &op);
190		if (!lineNext) {
191			break;
192		}
193		value <<= 8;
194		value |= op;
195		++width;
196	}
197	if (width == 0 || width == 3) {
198		return false;
199	}
200
201	struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
202	cheat->address = address;
203	cheat->operandOffset = 0;
204	cheat->addressOffset = 0;
205	cheat->repeat = 1;
206	cheat->type = CHEAT_ASSIGN;
207	cheat->width = width;
208	cheat->operand = value;
209	return true;
210}
211
212bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) {
213	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
214	switch (type) {
215	case GBA_CHEAT_AUTODETECT:
216		break;
217	case GBA_CHEAT_CODEBREAKER:
218		return GBACheatAddCodeBreakerLine(cheats, line);
219	case GBA_CHEAT_GAMESHARK:
220		return GBACheatAddGameSharkLine(cheats, line);
221	case GBA_CHEAT_PRO_ACTION_REPLAY:
222		return GBACheatAddProActionReplayLine(cheats, line);
223	case GBA_CHEAT_VBA:
224		return GBACheatAddVBALine(cheats, line);
225	default:
226		return false;
227	}
228
229	uint32_t op1;
230	uint16_t op2;
231	uint16_t op3;
232	const char* lineNext = hex32(line, &op1);
233	if (!lineNext) {
234		return false;
235	}
236	if (lineNext[0] == ':') {
237		return GBACheatAddVBALine(cheats, line);
238	}
239	while (isspace((int) lineNext[0])) {
240		++lineNext;
241	}
242	lineNext = hex16(lineNext, &op2);
243	if (!lineNext) {
244		return false;
245	}
246	if (!lineNext[0] || isspace((int) lineNext[0])) {
247		return GBACheatAddCodeBreaker(cheats, op1, op2);
248	}
249	lineNext = hex16(lineNext, &op3);
250	if (!lineNext) {
251		return false;
252	}
253	uint32_t realOp2 = op2;
254	realOp2 <<= 16;
255	realOp2 |= op3;
256	return GBACheatAddAutodetect(cheats, op1, realOp2);
257}
258
259static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
260	struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
261	_patchROM(device, gbaset);
262}
263
264static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
265	struct GBACheatSet* newSet = (struct GBACheatSet*) set;
266	struct GBACheatSet* gbaset = (struct GBACheatSet*) oldSet;
267	newSet->gsaVersion = gbaset->gsaVersion;
268	memcpy(newSet->gsaSeeds, gbaset->gsaSeeds, sizeof(newSet->gsaSeeds));
269	newSet->cbRngState = gbaset->cbRngState;
270	newSet->cbMaster = gbaset->cbMaster;
271	memcpy(newSet->cbSeeds, gbaset->cbSeeds, sizeof(newSet->cbSeeds));
272	memcpy(newSet->cbTable, gbaset->cbTable, sizeof(newSet->cbTable));
273	if (gbaset->hook) {
274		if (newSet->hook) {
275			--newSet->hook->refs;
276			if (newSet->hook->refs == 0) {
277				free(newSet->hook);
278			}
279		}
280		newSet->hook = gbaset->hook;
281		++newSet->hook->refs;
282	}
283}
284
285static void GBACheatParseDirectives(struct mCheatSet* set, const struct StringList* directives) {
286	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
287	size_t d;
288	for (d = 0; d < StringListSize(directives); ++d) {
289		const char* directive = *StringListGetConstPointer(directives, d);
290		if (strcmp(directive, "GSAv1") == 0) {
291			GBACheatSetGameSharkVersion(cheats, 1);
292			continue;
293		}
294		if (strcmp(directive, "PARv3") == 0) {
295			GBACheatSetGameSharkVersion(cheats, 3);
296			continue;
297		}
298	}
299}
300
301static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* directives) {
302	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
303
304	// TODO: Check previous directives
305	size_t d;
306	for (d = 0; d < StringListSize(directives); ++d) {
307		free(*StringListGetPointer(directives, d));
308	}
309	StringListClear(directives);
310
311	char** directive;
312	switch (cheats->gsaVersion) {
313	case 1:
314	case 2:
315		directive = StringListAppend(directives);
316		*directive = strdup("GSAv1");
317		break;
318	case 3:
319	case 4:
320		directive = StringListAppend(directives);
321		*directive = strdup("PARv3");
322		break;
323	}
324}
325
326int GBACheatAddressIsReal(uint32_t address) {
327	switch (address >> BASE_OFFSET) {
328	case REGION_BIOS:
329		return -0x80;
330		break;
331	case REGION_WORKING_RAM:
332		if ((address & OFFSET_MASK) > SIZE_WORKING_RAM) {
333			return -0x40;
334		}
335		return 0x20;
336	case REGION_WORKING_IRAM:
337		if ((address & OFFSET_MASK) > SIZE_WORKING_IRAM) {
338			return -0x40;
339		}
340		return 0x20;
341	case REGION_IO:
342		if ((address & OFFSET_MASK) > SIZE_IO) {
343			return -0x80;
344		}
345		return 0x10;
346	case REGION_OAM:
347		if ((address & OFFSET_MASK) > SIZE_OAM) {
348			return -0x80;
349		}
350		return -0x8;
351	case REGION_VRAM:
352		if ((address & OFFSET_MASK) > SIZE_VRAM) {
353			return -0x80;
354		}
355		return -0x8;
356	case REGION_PALETTE_RAM:
357		if ((address & OFFSET_MASK) > SIZE_PALETTE_RAM) {
358			return -0x80;
359		}
360		return -0x8;
361	case REGION_CART0:
362	case REGION_CART0_EX:
363	case REGION_CART1:
364	case REGION_CART1_EX:
365	case REGION_CART2:
366	case REGION_CART2_EX:
367		return -0x8;
368	case REGION_CART_SRAM:
369	case REGION_CART_SRAM_MIRROR:
370		if ((address & OFFSET_MASK) > SIZE_CART_SRAM) {
371			return -0x80;
372		}
373		return -0x8;
374	default:
375		return -0xC0;
376	}
377}