all repos — mgba @ fe354097f2596764d7072af8358586345a932291

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