all repos — mgba @ 44bbb9d1bba866b7428d78931c2bf66d042d5cfa

mGBA Game Boy Advance Emulator

src/core/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 "util/string.h"
 10#include "util/vfs.h"
 11
 12#define MAX_LINE_LENGTH 128
 13
 14const uint32_t M_CHEAT_DEVICE_ID = 0xABADC0DE;
 15
 16mLOG_DEFINE_CATEGORY(CHEATS, "Cheats");
 17
 18DEFINE_VECTOR(mCheatList, struct mCheat);
 19DEFINE_VECTOR(mCheatSets, struct mCheatSet*);
 20DEFINE_VECTOR(StringList, char*);
 21
 22static int32_t _readMem(struct mCore* core, uint32_t address, int width) {
 23	switch (width) {
 24	case 1:
 25		return core->busRead8(core, address);
 26	case 2:
 27		return core->busRead16(core, address);
 28	case 4:
 29		return core->busRead32(core, address);
 30	}
 31	return 0;
 32}
 33
 34static void _writeMem(struct mCore* core, uint32_t address, int width, int32_t value) {
 35	switch (width) {
 36	case 1:
 37		core->busWrite8(core, address, value);
 38		break;
 39	case 2:
 40		core->busWrite16(core, address, value);
 41		break;
 42	case 4:
 43		core->busWrite32(core, address, value);
 44		break;
 45	}
 46}
 47
 48static void mCheatDeviceInit(void*, struct mCPUComponent*);
 49static void mCheatDeviceDeinit(struct mCPUComponent*);
 50
 51void mCheatDeviceCreate(struct mCheatDevice* device) {
 52	device->d.id = M_CHEAT_DEVICE_ID;
 53	device->d.init = mCheatDeviceInit;
 54	device->d.deinit = mCheatDeviceDeinit;
 55	mCheatSetsInit(&device->cheats, 4);
 56}
 57
 58void mCheatDeviceDestroy(struct mCheatDevice* device) {
 59	mCheatDeviceClear(device);
 60	mCheatSetsDeinit(&device->cheats);
 61}
 62
 63void mCheatDeviceClear(struct mCheatDevice* device) {
 64	size_t i;
 65	for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
 66		struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
 67		mCheatSetDeinit(set);
 68	}
 69	mCheatSetsClear(&device->cheats);
 70}
 71
 72void mCheatSetInit(struct mCheatSet* set, const char* name) {
 73	mCheatListInit(&set->list, 4);
 74	StringListInit(&set->lines, 4);
 75	if (name) {
 76		set->name = strdup(name);
 77	} else {
 78		set->name = 0;
 79	}
 80	set->enabled = true;
 81}
 82
 83void mCheatSetDeinit(struct mCheatSet* set) {
 84	mCheatListDeinit(&set->list);
 85	size_t i;
 86	for (i = 0; i < StringListSize(&set->lines); ++i) {
 87		free(*StringListGetPointer(&set->lines, i));
 88	}
 89	if (set->name) {
 90		free(set->name);
 91	}
 92	set->deinit(set);
 93	free(set);
 94}
 95
 96void mCheatSetRename(struct mCheatSet* set, const char* name) {
 97	if (set->name) {
 98		free(set->name);
 99		set->name = NULL;
100	}
101	if (name) {
102		set->name = strdup(name);
103	}
104}
105
106bool mCheatAddLine(struct mCheatSet* set, const char* line, int type) {
107	if (!set->addLine(set, line, type)) {
108		return false;
109	}
110	*StringListAppend(&set->lines) = strdup(line);
111	return true;
112}
113
114void mCheatAddSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
115	*mCheatSetsAppend(&device->cheats) = cheats;
116	cheats->add(cheats, device);
117}
118
119void mCheatRemoveSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
120	size_t i;
121	for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
122		if (*mCheatSetsGetPointer(&device->cheats, i) == cheats) {
123			break;
124		}
125	}
126	if (i == mCheatSetsSize(&device->cheats)) {
127		return;
128	}
129	mCheatSetsShift(&device->cheats, i, 1);
130	cheats->remove(cheats, device);
131}
132
133bool mCheatParseFile(struct mCheatDevice* device, struct VFile* vf) {
134#warning Cheat loading is currently broken
135	return false;
136#if 0
137	char cheat[MAX_LINE_LENGTH];
138	struct mCheatSet* set = NULL;
139	struct mCheatSet* newSet;
140	bool nextDisabled = false;
141	void* directives = NULL;
142	while (true) {
143		size_t i = 0;
144		ssize_t bytesRead = vf->readline(vf, cheat, sizeof(cheat));
145		if (bytesRead == 0) {
146			break;
147		}
148		if (bytesRead < 0) {
149			return false;
150		}
151		while (isspace((int) cheat[i])) {
152			++i;
153		}
154		switch (cheat[i]) {
155		case '#':
156			do {
157				++i;
158			} while (isspace((int) cheat[i]));
159			cheat[strlen(cheat) - 1] = '\0'; // Remove trailing newline
160			newSet = device->createSet(device, &cheat[i]);
161			newSet->enabled = !nextDisabled;
162			nextDisabled = false;
163			if (set) {
164				mCheatAddSet(device, set);
165			}
166			if (set) {
167				newSet->copyProperties(newSet, set);
168			}
169			set = newSet;
170			break;
171		case '!':
172			do {
173				++i;
174			} while (isspace((int) cheat[i]));
175			if (strcasecmp(&cheat[i], "disabled") == 0) {
176				nextDisabled = true;
177				break;
178			}
179			if (strcasecmp(&cheat[i], "reset") == 0) {
180				directives = NULL;
181				break;
182			}
183			directives = set->parseDirective(set, &cheat[i], directives);
184			break;
185		default:
186			if (!set) {
187				set = device->createSet(device, NULL);
188				set->enabled = !nextDisabled;
189				nextDisabled = false;
190			}
191			mCheatAddLine(set, cheat);
192			break;
193		}
194	}
195	if (set) {
196		mCheatAddSet(device, set);
197	}
198	return true;
199#endif
200}
201
202bool mCheatSaveFile(struct mCheatDevice* device, struct VFile* vf) {
203#warning Cheat saving is currently broken
204	return false;
205#if 0
206	static const char lineStart[3] = "# ";
207	static const char lineEnd = '\n';
208	void* directives = NULL;
209
210	size_t i;
211	for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
212		struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
213		void* directives = set->dumpDirectives(set, vf, directives);
214		if (!set->enabled) {
215			static const char* disabledDirective = "!disabled\n";
216			vf->write(vf, disabledDirective, strlen(disabledDirective));
217		}
218
219		vf->write(vf, lineStart, 2);
220		if (set->name) {
221			vf->write(vf, set->name, strlen(set->name));
222		}
223		vf->write(vf, &lineEnd, 1);
224		size_t c;
225		for (c = 0; c < StringListSize(&set->lines); ++c) {
226			const char* line = *StringListGetPointer(&set->lines, c);
227			vf->write(vf, line, strlen(line));
228			vf->write(vf, &lineEnd, 1);
229		}
230	}
231	return true;
232#endif
233}
234
235void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
236	if (!cheats->enabled) {
237		return;
238	}
239	bool condition = true;
240	int conditionRemaining = 0;
241	int negativeConditionRemaining = 0;
242	cheats->refresh(cheats, device);
243
244	size_t nCodes = mCheatListSize(&cheats->list);
245	size_t i;
246	for (i = 0; i < nCodes; ++i) {
247		if (conditionRemaining > 0) {
248			--conditionRemaining;
249			if (!condition) {
250				continue;
251			}
252		} else if (negativeConditionRemaining > 0) {
253			conditionRemaining = negativeConditionRemaining - 1;
254			negativeConditionRemaining = 0;
255			condition = !condition;
256			if (!condition) {
257				continue;
258			}
259		} else {
260			condition = true;
261		}
262		struct mCheat* cheat = mCheatListGetPointer(&cheats->list, i);
263		int32_t value = 0;
264		int32_t operand = cheat->operand;
265		uint32_t operationsRemaining = cheat->repeat;
266		uint32_t address = cheat->address;
267		bool performAssignment = false;
268		for (; operationsRemaining; --operationsRemaining) {
269			switch (cheat->type) {
270			case CHEAT_ASSIGN:
271				value = operand;
272				performAssignment = true;
273				break;
274			case CHEAT_ASSIGN_INDIRECT:
275				value = operand;
276				address = _readMem(device->p, address + cheat->addressOffset, 4);
277				performAssignment = true;
278				break;
279			case CHEAT_AND:
280				value = _readMem(device->p, address, cheat->width) & operand;
281				performAssignment = true;
282				break;
283			case CHEAT_ADD:
284				value = _readMem(device->p, address, cheat->width) + operand;
285				performAssignment = true;
286				break;
287			case CHEAT_OR:
288				value = _readMem(device->p, address, cheat->width) | operand;
289				performAssignment = true;
290				break;
291			case CHEAT_IF_EQ:
292				condition = _readMem(device->p, address, cheat->width) == operand;
293				conditionRemaining = cheat->repeat;
294				negativeConditionRemaining = cheat->negativeRepeat;
295				break;
296			case CHEAT_IF_NE:
297				condition = _readMem(device->p, address, cheat->width) != operand;
298				conditionRemaining = cheat->repeat;
299				negativeConditionRemaining = cheat->negativeRepeat;
300				break;
301			case CHEAT_IF_LT:
302				condition = _readMem(device->p, address, cheat->width) < operand;
303				conditionRemaining = cheat->repeat;
304				negativeConditionRemaining = cheat->negativeRepeat;
305				break;
306			case CHEAT_IF_GT:
307				condition = _readMem(device->p, address, cheat->width) > operand;
308				conditionRemaining = cheat->repeat;
309				negativeConditionRemaining = cheat->negativeRepeat;
310				break;
311			case CHEAT_IF_ULT:
312				condition = (uint32_t) _readMem(device->p, address, cheat->width) < (uint32_t) operand;
313				conditionRemaining = cheat->repeat;
314				negativeConditionRemaining = cheat->negativeRepeat;
315				break;
316			case CHEAT_IF_UGT:
317				condition = (uint32_t) _readMem(device->p, address, cheat->width) > (uint32_t) operand;
318				conditionRemaining = cheat->repeat;
319				negativeConditionRemaining = cheat->negativeRepeat;
320				break;
321			case CHEAT_IF_AND:
322				condition = _readMem(device->p, address, cheat->width) & operand;
323				conditionRemaining = cheat->repeat;
324				negativeConditionRemaining = cheat->negativeRepeat;
325				break;
326			case CHEAT_IF_LAND:
327				condition = _readMem(device->p, address, cheat->width) && operand;
328				conditionRemaining = cheat->repeat;
329				negativeConditionRemaining = cheat->negativeRepeat;
330				break;
331			}
332
333			if (performAssignment) {
334				_writeMem(device->p, address, cheat->width, value);
335			}
336
337			address += cheat->addressOffset;
338			operand += cheat->operandOffset;
339		}
340	}
341}
342
343void mCheatDeviceInit(void* cpu, struct mCPUComponent* component) {
344	UNUSED(cpu);
345	struct mCheatDevice* device = (struct mCheatDevice*) component;
346	size_t i;
347	for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
348		struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
349		cheats->add(cheats, device);
350	}
351}
352
353void mCheatDeviceDeinit(struct mCPUComponent* component) {
354	struct mCheatDevice* device = (struct mCheatDevice*) component;
355	size_t i;
356	for (i = mCheatSetsSize(&device->cheats); i--;) {
357		struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
358		cheats->remove(cheats, device);
359	}
360}