all repos — mgba @ a59ffbc9a698ceda535dcff7f5cfb9004a6c2ae8

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