all repos — mgba @ 0298f25ea46b0eb0901becf0e9a6b478223fe39d

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