all repos — mgba @ 502fdfcfadb17dc90192eaa46fb92ccc10290fcc

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/gba.h"
  9#include "gba/io.h"
 10
 11const uint32_t GBA_CHEAT_DEVICE_ID = 0xABADC0DE;
 12
 13DEFINE_VECTOR(GBACheatList, struct GBACheat);
 14
 15static int32_t _read(struct ARMCore* cpu, uint32_t address, int width) {
 16	switch (width) {
 17	case 1:
 18		return cpu->memory.load8(cpu, address, 0);
 19	case 2:
 20		return cpu->memory.load16(cpu, address, 0);
 21	case 4:
 22		return cpu->memory.load32(cpu, address, 0);
 23	}
 24	return 0;
 25}
 26
 27static void _write(struct ARMCore* cpu, uint32_t address, int width, int32_t value) {
 28	switch (width) {
 29	case 1:
 30		cpu->memory.store8(cpu, address, value, 0);
 31		break;
 32	case 2:
 33		cpu->memory.store16(cpu, address, value, 0);
 34		break;
 35	case 4:
 36		cpu->memory.store32(cpu, address, value, 0);
 37		break;
 38	}
 39}
 40
 41static int _hexDigit(char digit) {
 42	switch (digit) {
 43	case '0':
 44	case '1':
 45	case '2':
 46	case '3':
 47	case '4':
 48	case '5':
 49	case '6':
 50	case '7':
 51	case '8':
 52	case '9':
 53		return digit - '0';
 54
 55	case 'a':
 56	case 'b':
 57	case 'c':
 58	case 'd':
 59	case 'e':
 60	case 'f':
 61		return digit - 'a' + 10;
 62
 63	case 'A':
 64	case 'B':
 65	case 'C':
 66	case 'D':
 67	case 'E':
 68	case 'F':
 69		return digit - 'A' + 10;
 70
 71	default:
 72		return -1;
 73	}
 74}
 75
 76static const char* _hex32(const char* line, uint32_t* out) {
 77	uint32_t value = 0;
 78	int i;
 79	for (i = 0; i < 8; ++i, ++line) {
 80		char digit = *line;
 81		value <<= 4;
 82		int nybble = _hexDigit(digit);
 83		if (nybble < 0) {
 84			return 0;
 85		}
 86		value |= nybble;
 87	}
 88	*out = value;
 89	return line;
 90}
 91
 92static const char* _hex16(const char* line, uint16_t* out) {
 93	uint16_t value = 0;
 94	int i;
 95	for (i = 0; i < 4; ++i, ++line) {
 96		char digit = *line;
 97		value <<= 4;
 98		int nybble = _hexDigit(digit);
 99		if (nybble < 0) {
100			return 0;
101		}
102		value |= nybble;
103	}
104	*out = value;
105	return line;
106}
107
108static void _addBreakpoint(struct GBACheatDevice* device) {
109	if (!device->cheats || !device->p) {
110		return;
111	}
112	GBASetBreakpoint(device->p, &device->d, device->cheats->hookAddress, device->cheats->hookMode, &device->cheats->patchedOpcode);
113}
114
115static void _removeBreakpoint(struct GBACheatDevice* device) {
116	if (!device->cheats || !device->p) {
117		return;
118	}
119	GBAClearBreakpoint(device->p, device->cheats->hookAddress, device->cheats->hookMode, device->cheats->patchedOpcode);
120}
121
122static void GBACheatDeviceInit(struct ARMCore*, struct ARMComponent*);
123static void GBACheatDeviceDeinit(struct ARMComponent*);
124
125void GBACheatDeviceCreate(struct GBACheatDevice* device) {
126	device->d.id = GBA_CHEAT_DEVICE_ID;
127	device->d.init = GBACheatDeviceInit;
128	device->d.deinit = GBACheatDeviceDeinit;
129}
130
131void GBACheatSetInit(struct GBACheatSet* set) {
132	set->hookAddress = 0;
133	set->hookMode = MODE_THUMB;
134	GBACheatListInit(&set->list, 4);
135	set->incompleteCheat = 0;
136	set->patchedOpcode = 0;
137}
138
139void GBACheatSetDeinit(struct GBACheatSet* set) {
140	GBACheatListDeinit(&set->list);
141}
142
143void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice* device) {
144	if (gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE]) {
145		ARMHotplugDetach(gba->cpu, GBA_COMPONENT_CHEAT_DEVICE);
146	}
147	gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE] = &device->d;
148	ARMHotplugAttach(gba->cpu, GBA_COMPONENT_CHEAT_DEVICE);
149}
150
151void GBACheatInstallSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
152	_removeBreakpoint(device);
153	device->cheats = cheats;
154	_addBreakpoint(device);
155}
156
157bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
158	enum GBACodeBreakerType type = op1 >> 28;
159	struct GBACheat* cheat = 0;
160
161	if (cheats->incompleteCheat) {
162		cheats->incompleteCheat->repeat = op1 & 0xFFFF;
163		cheats->incompleteCheat->addressOffset = op2;
164		cheats->incompleteCheat->operandOffset = 0;
165		cheats->incompleteCheat = 0;
166		return true;
167	}
168
169	switch (type) {
170	case CB_GAME_ID:
171		// TODO: Run checksum
172		return true;
173	case CB_HOOK:
174		if (cheats->hookAddress) {
175			return false;
176		}
177		cheats->hookAddress = BASE_CART0 | (op1 & (SIZE_CART0 - 1));
178		cheats->hookMode = MODE_THUMB;
179		return true;
180	case CB_OR_2:
181		cheat = GBACheatListAppend(&cheats->list);
182		cheat->type = CHEAT_OR;
183		cheat->width = 2;
184		break;
185	case CB_ASSIGN_1:
186		cheat = GBACheatListAppend(&cheats->list);
187		cheat->type = CHEAT_ASSIGN;
188		cheat->width = 1;
189		break;
190	case CB_FILL:
191		cheat = GBACheatListAppend(&cheats->list);
192		cheat->type = CHEAT_ASSIGN;
193		cheat->width = 2;
194		cheats->incompleteCheat = cheat;
195		break;
196	case CB_FILL_8:
197		GBALog(0, GBA_LOG_STUB, "[Cheat] CodeBreaker code %08X %04X not supported", op1, op2);
198		return false;
199	case CB_AND_2:
200		cheat = GBACheatListAppend(&cheats->list);
201		cheat->type = CHEAT_AND;
202		cheat->width = 2;
203		break;
204	case CB_IF_EQ:
205		cheat = GBACheatListAppend(&cheats->list);
206		cheat->type = CHEAT_IF_EQ;
207		cheat->width = 2;
208		break;
209	case CB_ASSIGN_2:
210		cheat = GBACheatListAppend(&cheats->list);
211		cheat->type = CHEAT_ASSIGN;
212		cheat->width = 2;
213		break;
214	case CB_ENCRYPT:
215		GBALog(0, GBA_LOG_STUB, "[Cheat] CodeBreaker encryption not supported");
216		return false;
217	case CB_IF_NE:
218		cheat = GBACheatListAppend(&cheats->list);
219		cheat->type = CHEAT_IF_NE;
220		cheat->width = 2;
221		break;
222	case CB_IF_GT:
223		cheat = GBACheatListAppend(&cheats->list);
224		cheat->type = CHEAT_IF_GT;
225		cheat->width = 2;
226		break;
227	case CB_IF_LT:
228		cheat = GBACheatListAppend(&cheats->list);
229		cheat->type = CHEAT_IF_LT;
230		cheat->width = 2;
231		break;
232	case CB_IF_SPECIAL:
233		switch (op1 & 0x0FFFFFFF) {
234		case 0x20:
235			cheat = GBACheatListAppend(&cheats->list);
236			cheat->type = CHEAT_IF_AND;
237			cheat->width = 2;
238			cheat->address = BASE_IO | REG_JOYSTAT;
239			cheat->operand = op2;
240			cheat->repeat = 1;
241			return true;
242		default:
243			GBALog(0, GBA_LOG_STUB, "[Cheat] CodeBreaker code %08X %04X not supported", op1, op2);
244			return false;
245		}
246	case CB_ADD_2:
247		cheat = GBACheatListAppend(&cheats->list);
248		cheat->type = CHEAT_ADD;
249		cheat->width = 2;
250		break;
251	case CB_IF_AND:
252		cheat = GBACheatListAppend(&cheats->list);
253		cheat->type = CHEAT_IF_AND;
254		cheat->width = 2;
255		break;
256	}
257
258	cheat->address = op1 & 0x0FFFFFFF;
259	cheat->operand = op2;
260	cheat->repeat = 1;
261	return true;
262}
263
264bool GBACheatAddCodeBreakerLine(struct GBACheatSet* cheats, const char* line) {
265	uint32_t op1;
266	uint16_t op2;
267	line = _hex32(line, &op1);
268	if (!line) {
269		return false;
270	}
271	while (*line == ' ') {
272		++line;
273	}
274	line = _hex16(line, &op2);
275	if (!line) {
276		return false;
277	}
278	return GBACheatAddCodeBreaker(cheats, op1, op2);
279}
280
281void GBACheatRefresh(struct GBACheatDevice* device) {
282	bool condition = true;
283	int conditionRemaining = 0;
284
285	size_t nCodes = GBACheatListSize(&device->cheats->list);
286	size_t i;
287	for (i = 0; i < nCodes; ++i) {
288		if (conditionRemaining > 0) {
289			--conditionRemaining;
290			if (!condition) {
291				continue;
292			}
293		} else {
294			condition = true;
295		}
296		struct GBACheat* cheat = GBACheatListGetPointer(&device->cheats->list, i);
297		int32_t value = 0;
298		int32_t operand = cheat->operand;
299		uint32_t operationsRemaining = cheat->repeat;
300		uint32_t address = cheat->address;
301		bool performAssignment = false;
302		for (; operationsRemaining; --operationsRemaining) {
303			switch (cheat->type) {
304			case CHEAT_ASSIGN:
305				value = operand;
306				performAssignment = true;
307				break;
308			case CHEAT_AND:
309				value = _read(device->p->cpu, address, cheat->width) & operand;
310				performAssignment = true;
311				break;
312			case CHEAT_ADD:
313				value = _read(device->p->cpu, address, cheat->width) + operand;
314				performAssignment = true;
315				break;
316			case CHEAT_OR:
317				value = _read(device->p->cpu, address, cheat->width) | operand;
318				performAssignment = true;
319				break;
320			case CHEAT_IF_EQ:
321				condition = _read(device->p->cpu, address, cheat->width) == operand;
322				conditionRemaining = cheat->repeat;
323				break;
324			case CHEAT_IF_NE:
325				condition = _read(device->p->cpu, address, cheat->width) != operand;
326				conditionRemaining = cheat->repeat;
327				break;
328			case CHEAT_IF_LT:
329				condition = _read(device->p->cpu, address, cheat->width) < operand;
330				conditionRemaining = cheat->repeat;
331				break;
332			case CHEAT_IF_GT:
333				condition = _read(device->p->cpu, address, cheat->width) > operand;
334				conditionRemaining = cheat->repeat;
335				break;
336			case CHEAT_IF_AND:
337				condition = _read(device->p->cpu, address, cheat->width) & operand;
338				conditionRemaining = cheat->repeat;
339				break;
340			}
341
342			if (performAssignment) {
343				_write(device->p->cpu, address, cheat->width, value);
344			}
345
346			address += cheat->addressOffset;
347			operand += cheat->operandOffset;
348		}
349	}
350}
351
352void GBACheatDeviceInit(struct ARMCore* cpu, struct ARMComponent* component) {
353	struct GBACheatDevice* device = (struct GBACheatDevice*) component;
354	device->p = (struct GBA*) cpu->master;
355	_addBreakpoint(device);
356}
357
358void GBACheatDeviceDeinit(struct ARMComponent* component) {
359	struct GBACheatDevice* device = (struct GBACheatDevice*) component;
360	_removeBreakpoint(device);
361}