all repos — mgba @ 2e55bd098ac97fcb203634f5c65651695de6eb38

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 bool GBACheatAddLine(struct mCheatSet*, const char* line, int type);
 71
 72static struct mCheatSet* GBACheatSetCreate(struct mCheatDevice* device, const char* name) {
 73	UNUSED(device);
 74	struct GBACheatSet* set = malloc(sizeof(*set));
 75	mCheatSetInit(&set->d, name);
 76	set->incompleteCheat = 0;
 77	set->incompletePatch = 0;
 78	set->currentBlock = 0;
 79	set->gsaVersion = 0;
 80	set->cbRngState = 0;
 81	set->cbMaster = 0;
 82	set->remainingAddresses = 0;
 83	set->hook = NULL;
 84
 85	set->d.deinit = GBACheatSetDeinit;
 86	set->d.add = GBACheatAddSet;
 87	set->d.remove = GBACheatRemoveSet;
 88
 89	set->d.addLine = GBACheatAddLine;
 90	set->d.copyProperties = GBACheatSetCopyProperties;
 91
 92	set->d.refresh = GBACheatRefresh;
 93
 94	int i;
 95	for (i = 0; i < MAX_ROM_PATCHES; ++i) {
 96		set->romPatches[i].exists = false;
 97	}
 98	return &set->d;
 99}
100
101struct mCheatDevice* GBACheatDeviceCreate(void) {
102	struct mCheatDevice* device = malloc(sizeof(*device));
103	mCheatDeviceCreate(device);
104	device->createSet = GBACheatSetCreate;
105	return device;
106}
107
108static void GBACheatSetDeinit(struct mCheatSet* set) {
109	struct GBACheatSet* gbaset = (struct GBACheatSet*) set;
110	if (gbaset->hook) {
111		--gbaset->hook->refs;
112		if (gbaset->hook->refs == 0) {
113			free(gbaset->hook);
114		}
115	}
116}
117
118static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
119	struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
120	_addBreakpoint(device, gbaset);
121	_patchROM(device, gbaset);
122}
123
124static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
125	struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
126	_unpatchROM(device, gbaset);
127	_removeBreakpoint(device, gbaset);
128}
129
130static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
131	uint32_t o1 = op1;
132	uint32_t o2 = op2;
133	char line[18] = "XXXXXXXX XXXXXXXX";
134	snprintf(line, sizeof(line), "%08X %08X", op1, op2);
135
136	switch (set->gsaVersion) {
137	case 0:
138		// Try to detect GameShark version
139		GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
140		if ((o1 & 0xF0000000) == 0xF0000000 && !(o2 & 0xFFFFFCFE)) {
141			GBACheatSetGameSharkVersion(set, 1);
142			return GBACheatAddGameSharkRaw(set, o1, o2);
143		}
144		o1 = op1;
145		o2 = op2;
146		GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
147		if ((o1 & 0xFE000000) == 0xC4000000 && !(o2 & 0xFFFF0000)) {
148			GBACheatSetGameSharkVersion(set, 3);
149			return GBACheatAddProActionReplayRaw(set, o1, o2);
150		}
151		break;
152	case 1:
153		GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
154		return GBACheatAddGameSharkRaw(set, o1, o2);
155	case 3:
156		GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
157		return GBACheatAddProActionReplayRaw(set, o1, o2);
158	}
159	return false;
160}
161
162bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
163	uint32_t address;
164	uint8_t op;
165	uint32_t value = 0;
166	int width = 0;
167	const char* lineNext = hex32(line, &address);
168	if (!lineNext) {
169		return false;
170	}
171	if (lineNext[0] != ':') {
172		return false;
173	}
174	++lineNext;
175	while (width < 4) {
176		lineNext = hex8(lineNext, &op);
177		if (!lineNext) {
178			break;
179		}
180		value <<= 8;
181		value |= op;
182		++width;
183	}
184	if (width == 0 || width == 3) {
185		return false;
186	}
187
188	struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
189	cheat->address = address;
190	cheat->operandOffset = 0;
191	cheat->addressOffset = 0;
192	cheat->repeat = 1;
193	cheat->type = CHEAT_ASSIGN;
194	cheat->width = width;
195	cheat->operand = value;
196	return true;
197}
198
199bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) {
200	struct GBACheatSet* cheats = (struct GBACheatSet*) set;
201	switch (type) {
202	case GBA_CHEAT_AUTODETECT:
203		break;
204	case GBA_CHEAT_CODEBREAKER:
205		return GBACheatAddCodeBreakerLine(cheats, line);
206	case GBA_CHEAT_GAMESHARK:
207		return GBACheatAddGameSharkLine(cheats, line);
208	case GBA_CHEAT_PRO_ACTION_REPLAY:
209		return GBACheatAddProActionReplayLine(cheats, line);
210	case GBA_CHEAT_VBA:
211		return GBACheatAddVBALine(cheats, line);
212	default:
213		return false;
214	}
215
216	uint32_t op1;
217	uint16_t op2;
218	uint16_t op3;
219	const char* lineNext = hex32(line, &op1);
220	if (!lineNext) {
221		return false;
222	}
223	if (lineNext[0] == ':') {
224		return GBACheatAddVBALine(cheats, line);
225	}
226	while (isspace((int) lineNext[0])) {
227		++lineNext;
228	}
229	lineNext = hex16(lineNext, &op2);
230	if (!lineNext) {
231		return false;
232	}
233	if (!lineNext[0] || isspace((int) lineNext[0])) {
234		return GBACheatAddCodeBreaker(cheats, op1, op2);
235	}
236	lineNext = hex16(lineNext, &op3);
237	if (!lineNext) {
238		return false;
239	}
240	uint32_t realOp2 = op2;
241	realOp2 <<= 16;
242	realOp2 |= op3;
243	return GBACheatAddAutodetect(cheats, op1, realOp2);
244}
245
246static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
247	struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
248	_patchROM(device, gbaset);
249}
250
251static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
252	struct GBACheatSet* newSet = (struct GBACheatSet*) set;
253	struct GBACheatSet* gbaset = (struct GBACheatSet*) oldSet;
254	newSet->gsaVersion = gbaset->gsaVersion;
255	memcpy(newSet->gsaSeeds, gbaset->gsaSeeds, sizeof(newSet->gsaSeeds));
256	newSet->cbRngState = gbaset->cbRngState;
257	newSet->cbMaster = gbaset->cbMaster;
258	memcpy(newSet->cbSeeds, gbaset->cbSeeds, sizeof(newSet->cbSeeds));
259	memcpy(newSet->cbTable, gbaset->cbTable, sizeof(newSet->cbTable));
260	if (gbaset->hook) {
261		if (newSet->hook) {
262			--newSet->hook->refs;
263			if (newSet->hook->refs == 0) {
264				free(newSet->hook);
265			}
266		}
267		newSet->hook = gbaset->hook;
268		++newSet->hook->refs;
269	}
270}