all repos — mgba @ 18c6e6c330749abe3105a24cbe6571a85bbb2de4

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