all repos — mgba @ e8b7d180bfa016a487fb79329c23545a06e5c4b5

mGBA Game Boy Advance Emulator

src/gb/cheats.c (view raw)

  1/* Copyright (c) 2013-2016 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 "core/core.h"
  9#include "gb/memory.h"
 10#include "util/string.h"
 11
 12DEFINE_VECTOR(GBCheatPatchList, struct GBCheatPatch);
 13
 14static void _patchROM(struct mCheatDevice* device, struct GBCheatSet* cheats) {
 15	if (!device->p) {
 16		return;
 17	}
 18	size_t i;
 19	for (i = 0; i < GBCheatPatchListSize(&cheats->romPatches); ++i) {
 20		struct GBCheatPatch* patch = GBCheatPatchListGetPointer(&cheats->romPatches, i);
 21		if (patch->applied) {
 22			continue;
 23		}
 24		if (patch->checkByte) {
 25			// TODO: All segments
 26			int8_t value = GBView8(device->p->cpu, patch->address, 0);
 27			if (value != patch->oldValue) {
 28				continue;
 29			}
 30		}
 31		GBPatch8(device->p->cpu, patch->address, patch->newValue, &patch->oldValue);
 32		patch->applied = true;
 33	}
 34}
 35
 36static void _unpatchROM(struct mCheatDevice* device, struct GBCheatSet* cheats) {
 37	if (!device->p) {
 38		return;
 39	}
 40	size_t i;
 41	for (i = 0; i < GBCheatPatchListSize(&cheats->romPatches); ++i) {
 42		struct GBCheatPatch* patch = GBCheatPatchListGetPointer(&cheats->romPatches, i);
 43		if (!patch->applied) {
 44			continue;
 45		}
 46		GBPatch8(device->p->cpu, patch->address, patch->oldValue, &patch->newValue);
 47		patch->applied = false;
 48	}
 49}
 50
 51static void GBCheatSetDeinit(struct mCheatSet* set);
 52static void GBCheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device);
 53static void GBCheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device);
 54static void GBCheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device);
 55static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet);
 56static void GBCheatParseDirectives(struct mCheatSet* set, const struct StringList* directives);
 57static void GBCheatDumpDirectives(struct mCheatSet* set, struct StringList* directives);
 58static bool GBCheatAddLine(struct mCheatSet*, const char* line, int type);
 59
 60static struct mCheatSet* GBCheatSetCreate(struct mCheatDevice* device, const char* name) {
 61	UNUSED(device);
 62	struct GBCheatSet* set = malloc(sizeof(*set));
 63	mCheatSetInit(&set->d, name);
 64
 65	GBCheatPatchListInit(&set->romPatches, 0);
 66
 67	set->d.deinit = GBCheatSetDeinit;
 68	set->d.add = GBCheatAddSet;
 69	set->d.remove = GBCheatRemoveSet;
 70
 71	set->d.addLine = GBCheatAddLine;
 72	set->d.copyProperties = GBCheatSetCopyProperties;
 73
 74	set->d.parseDirectives = GBCheatParseDirectives;
 75	set->d.dumpDirectives = GBCheatDumpDirectives;
 76
 77	set->d.refresh = GBCheatRefresh;
 78	return &set->d;
 79}
 80
 81struct mCheatDevice* GBCheatDeviceCreate(void) {
 82	struct mCheatDevice* device = malloc(sizeof(*device));
 83	mCheatDeviceCreate(device);
 84	device->createSet = GBCheatSetCreate;
 85	return device;
 86}
 87
 88static void GBCheatSetDeinit(struct mCheatSet* set) {
 89	struct GBCheatSet* gbset = (struct GBCheatSet*) set;
 90	GBCheatPatchListDeinit(&gbset->romPatches);
 91}
 92
 93static void GBCheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
 94	struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
 95	_patchROM(device, gbset);
 96}
 97
 98static void GBCheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
 99	struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
100	_unpatchROM(device, gbset);
101}
102
103static bool GBCheatAddCodebreaker(struct GBCheatSet* cheats, uint16_t address, uint8_t data) {
104	struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
105	cheat->type = CHEAT_ASSIGN;
106	cheat->width = 1;
107	cheat->address = address;
108	cheat->operand = data;
109	cheat->repeat = 1;
110	cheat->negativeRepeat = 0;
111	return true;
112}
113
114static bool GBCheatAddGameShark(struct GBCheatSet* cheats, uint32_t op) {
115	return GBCheatAddCodebreaker(cheats, ((op & 0xFF) << 8) | ((op >> 8) & 0xFF), (op >> 16) & 0xFF);
116}
117
118static bool GBCheatAddGameSharkLine(struct GBCheatSet* cheats, const char* line) {
119	uint32_t op;
120	if (!hex32(line, &op)) {
121		return false;
122	}
123	return GBCheatAddGameShark(cheats, op);
124}
125
126static bool GBCheatAddGameGenieLine(struct GBCheatSet* cheats, const char* line) {
127	uint16_t op1;
128	uint16_t op2;
129	uint16_t op3 = 0x1000;
130	const char* lineNext = hex12(line, &op1);
131	if (!lineNext || lineNext[0] != '-') {
132		return false;
133	}
134	++lineNext;
135	lineNext = hex12(lineNext, &op2);
136	if (!lineNext) {
137		return false;
138	}
139	if (lineNext[0] == '-') {
140		++lineNext;
141		lineNext = hex12(lineNext, &op3);
142	}
143	if (!lineNext || lineNext[0]) {
144		return false;
145	}
146	uint16_t address = (op1 & 0xF) << 8;
147	address |= (op2 >> 4) & 0xFF;
148	address |= ((op2 & 0xF) ^ 0xF) << 12;
149	struct GBCheatPatch* patch = GBCheatPatchListAppend(&cheats->romPatches);
150	patch->address = address;
151	patch->newValue = op1 >> 4;
152	patch->applied = false;
153	if (op3 < 0x1000) {
154		uint32_t value = ((op3 & 0xF00) << 20) | (op3 & 0xF);
155		value = ROR(value, 2);
156		value |= value >> 24;
157		value ^= 0xBA;
158		patch->oldValue = value;
159		patch->checkByte = true;
160	} else {
161		patch->checkByte = false;
162	}
163	return true;
164}
165
166static bool GBCheatAddVBALine(struct GBCheatSet* cheats, const char* line) {
167	uint16_t address;
168	uint8_t value;
169	const char* lineNext = hex16(line, &address);
170	if (!lineNext && lineNext[0] != ':') {
171		return false;
172	}
173	if (!hex8(line, &value)) {
174		return false;
175	}
176	struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
177	cheat->type = CHEAT_ASSIGN;
178	cheat->width = 1;
179	cheat->address = address;
180	cheat->operand = value;
181	cheat->repeat = 1;
182	cheat->negativeRepeat = 0;
183	return true;
184}
185
186bool GBCheatAddLine(struct mCheatSet* set, const char* line, int type) {
187	struct GBCheatSet* cheats = (struct GBCheatSet*) set;
188	switch (type) {
189	case GB_CHEAT_AUTODETECT:
190		break;
191	case GB_CHEAT_GAME_GENIE:
192		return GBCheatAddGameGenieLine(cheats, line);
193	case GB_CHEAT_GAMESHARK:
194		return GBCheatAddGameSharkLine(cheats, line);
195	case GB_CHEAT_VBA:
196		return GBCheatAddVBALine(cheats, line);
197	default:
198		return false;
199	}
200
201	uint16_t op1;
202	uint8_t op2;
203	uint8_t op3;
204	bool codebreaker = false;
205	const char* lineNext = hex16(line, &op1);
206	if (!lineNext) {
207		return GBCheatAddGameGenieLine(cheats, line);
208	}
209	if (lineNext[0] == ':') {
210		return GBCheatAddVBALine(cheats, line);
211	}
212	lineNext = hex8(lineNext, &op2);
213	if (!lineNext) {
214		return false;
215	}
216	if (lineNext[0] == '-') {
217		codebreaker = true;
218		++lineNext;
219	}
220	lineNext = hex8(lineNext, &op3);
221	if (!lineNext) {
222		return false;
223	}
224	if (codebreaker) {
225		uint16_t address = (op1 << 8) | op2;
226		return GBCheatAddCodebreaker(cheats, address, op3);
227	} else {
228		uint32_t realOp = op1 << 16;
229		realOp |= op2 << 8;
230		realOp |= op3;
231		return GBCheatAddGameShark(cheats, realOp);
232	}
233}
234
235static void GBCheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
236	struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
237	_patchROM(device, gbset);
238}
239
240static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
241	UNUSED(set);
242	UNUSED(oldSet);
243}
244
245static void GBCheatParseDirectives(struct mCheatSet* set, const struct StringList* directives) {
246	UNUSED(set);
247	UNUSED(directives);
248}
249
250static void GBCheatDumpDirectives(struct mCheatSet* set, struct StringList* directives) {
251	UNUSED(set);
252	UNUSED(directives);
253}