all repos — mgba @ 82f503bc4e8673d06ab49ae91d7cb781557efe57

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	switch (set->gsaVersion) {
142	case 0:
143		// Try to detect GameShark version
144		GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
145		if ((o1 & 0xF0000000) == 0xF0000000 && !(o2 & 0xFFFFFCFE)) {
146			GBACheatSetGameSharkVersion(set, 1);
147			return GBACheatAddGameSharkRaw(set, o1, o2);
148		}
149		o1 = op1;
150		o2 = op2;
151		GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
152		if ((o1 & 0xFE000000) == 0xC4000000 && !(o2 & 0xFFFF0000)) {
153			GBACheatSetGameSharkVersion(set, 3);
154			return GBACheatAddProActionReplayRaw(set, o1, o2);
155		}
156		break;
157	case 1:
158		GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
159		return GBACheatAddGameSharkRaw(set, o1, o2);
160	case 3:
161		GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
162		return GBACheatAddProActionReplayRaw(set, o1, o2);
163	}
164	return false;
165}
166
167bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
168	uint32_t address;
169	uint8_t op;
170	uint32_t value = 0;
171	int width = 0;
172	const char* lineNext = hex32(line, &address);
173	if (!lineNext) {
174		return false;
175	}
176	if (lineNext[0] != ':') {
177		return false;
178	}
179	++lineNext;
180	while (width < 4) {
181		lineNext = hex8(lineNext, &op);
182		if (!lineNext) {
183			break;
184		}
185		value <<= 8;
186		value |= op;
187		++width;
188	}
189	if (width == 0 || width == 3) {
190		return false;
191	}
192
193	struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
194	cheat->address = address;
195	cheat->operandOffset = 0;
196	cheat->addressOffset = 0;
197	cheat->repeat = 1;
198	cheat->type = CHEAT_ASSIGN;
199	cheat->width = width;
200	cheat->operand = value;
201	return true;
202}
203
204bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) {
205	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
206	switch (type) {
207	case GBA_CHEAT_AUTODETECT:
208		break;
209	case GBA_CHEAT_CODEBREAKER:
210		return GBACheatAddCodeBreakerLine(cheats, line);
211	case GBA_CHEAT_GAMESHARK:
212		return GBACheatAddGameSharkLine(cheats, line);
213	case GBA_CHEAT_PRO_ACTION_REPLAY:
214		return GBACheatAddProActionReplayLine(cheats, line);
215	case GBA_CHEAT_VBA:
216		return GBACheatAddVBALine(cheats, line);
217	default:
218		return false;
219	}
220
221	uint32_t op1;
222	uint16_t op2;
223	uint16_t op3;
224	const char* lineNext = hex32(line, &op1);
225	if (!lineNext) {
226		return false;
227	}
228	if (lineNext[0] == ':') {
229		return GBACheatAddVBALine(cheats, line);
230	}
231	while (isspace((int) lineNext[0])) {
232		++lineNext;
233	}
234	lineNext = hex16(lineNext, &op2);
235	if (!lineNext) {
236		return false;
237	}
238	if (!lineNext[0] || isspace((int) lineNext[0])) {
239		return GBACheatAddCodeBreaker(cheats, op1, op2);
240	}
241	lineNext = hex16(lineNext, &op3);
242	if (!lineNext) {
243		return false;
244	}
245	uint32_t realOp2 = op2;
246	realOp2 <<= 16;
247	realOp2 |= op3;
248	return GBACheatAddAutodetect(cheats, op1, realOp2);
249}
250
251static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
252	struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
253	_patchROM(device, gbaset);
254}
255
256static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
257	struct GBACheatSet* newSet = (struct GBACheatSet*) set;
258	struct GBACheatSet* gbaset = (struct GBACheatSet*) oldSet;
259	newSet->gsaVersion = gbaset->gsaVersion;
260	memcpy(newSet->gsaSeeds, gbaset->gsaSeeds, sizeof(newSet->gsaSeeds));
261	newSet->cbRngState = gbaset->cbRngState;
262	newSet->cbMaster = gbaset->cbMaster;
263	memcpy(newSet->cbSeeds, gbaset->cbSeeds, sizeof(newSet->cbSeeds));
264	memcpy(newSet->cbTable, gbaset->cbTable, sizeof(newSet->cbTable));
265	if (gbaset->hook) {
266		if (newSet->hook) {
267			--newSet->hook->refs;
268			if (newSet->hook->refs == 0) {
269				free(newSet->hook);
270			}
271		}
272		newSet->hook = gbaset->hook;
273		++newSet->hook->refs;
274	}
275}
276
277static void GBACheatParseDirectives(struct mCheatSet* set, const struct StringList* directives) {
278	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
279	size_t d;
280	for (d = 0; d < StringListSize(directives); ++d) {
281		const char* directive = *StringListGetConstPointer(directives, d);
282		if (strcmp(directive, "GSAv1") == 0) {
283			GBACheatSetGameSharkVersion(cheats, 1);
284			continue;
285		}
286		if (strcmp(directive, "PARv3") == 0) {
287			GBACheatSetGameSharkVersion(cheats, 3);
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	case 2:
307		directive = StringListAppend(directives);
308		*directive = strdup("GSAv1");
309		break;
310	case 3:
311	case 4:
312		directive = StringListAppend(directives);
313		*directive = strdup("PARv3");
314		break;
315	}
316}