all repos — mgba @ 32f7f35ee9088694c58cb22b0e3803684a42d8a1

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	device->autosave = false;
 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	char cheat[MAX_LINE_LENGTH];
135	struct mCheatSet* set = NULL;
136	struct mCheatSet* newSet;
137	bool nextDisabled = false;
138	struct StringList directives;
139	StringListInit(&directives, 4);
140
141	while (true) {
142		size_t i = 0;
143		ssize_t bytesRead = vf->readline(vf, cheat, sizeof(cheat));
144		rtrim(cheat);
145		if (bytesRead == 0) {
146			break;
147		}
148		if (bytesRead < 0) {
149			StringListDeinit(&directives);
150			return false;
151		}
152		while (isspace((int) cheat[i])) {
153			++i;
154		}
155		switch (cheat[i]) {
156		case '#':
157			do {
158				++i;
159			} while (isspace((int) cheat[i]));
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			newSet->parseDirectives(newSet, &directives);
170			set = newSet;
171			break;
172		case '!':
173			do {
174				++i;
175			} while (isspace((int) cheat[i]));
176			if (strcasecmp(&cheat[i], "disabled") == 0) {
177				nextDisabled = true;
178				break;
179			}
180			if (strcasecmp(&cheat[i], "reset") == 0) {
181				size_t d;
182				for (d = 0; d < StringListSize(&directives); ++d) {
183					free(*StringListGetPointer(&directives, d));
184				}
185				StringListClear(&directives);
186				break;
187			}
188			*StringListAppend(&directives) = strdup(&cheat[i]);
189			break;
190		default:
191			if (!set) {
192				set = device->createSet(device, NULL);
193				set->enabled = !nextDisabled;
194				nextDisabled = false;
195			}
196			mCheatAddLine(set, cheat, 0);
197			break;
198		}
199	}
200	if (set) {
201		mCheatAddSet(device, set);
202	}
203	size_t d;
204	for (d = 0; d < StringListSize(&directives); ++d) {
205		free(*StringListGetPointer(&directives, d));
206	}
207	StringListClear(&directives);
208	StringListDeinit(&directives);
209	return true;
210}
211
212bool mCheatSaveFile(struct mCheatDevice* device, struct VFile* vf) {
213	static const char lineStart[3] = "# ";
214	static const char lineEnd = '\n';
215	struct StringList directives;
216	StringListInit(&directives, 4);
217
218	size_t i;
219	for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
220		struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
221		set->dumpDirectives(set, &directives);
222		if (!set->enabled) {
223			static const char* disabledDirective = "!disabled\n";
224			vf->write(vf, disabledDirective, strlen(disabledDirective));
225		}
226		size_t d;
227		for (d = 0; d < StringListSize(&directives); ++d) {
228			char directive[64];
229			ssize_t len = snprintf(directive, sizeof(directive) - 1, "!%s\n", *StringListGetPointer(&directives, d));
230			if (len > 1) {
231				vf->write(vf, directive, (size_t) len > sizeof(directive) ? sizeof(directive) : len);
232			}
233		}
234
235		vf->write(vf, lineStart, 2);
236		if (set->name) {
237			vf->write(vf, set->name, strlen(set->name));
238		}
239		vf->write(vf, &lineEnd, 1);
240		size_t c;
241		for (c = 0; c < StringListSize(&set->lines); ++c) {
242			const char* line = *StringListGetPointer(&set->lines, c);
243			vf->write(vf, line, strlen(line));
244			vf->write(vf, &lineEnd, 1);
245		}
246	}
247	size_t d;
248	for (d = 0; d < StringListSize(&directives); ++d) {
249		free(*StringListGetPointer(&directives, d));
250	}
251	StringListClear(&directives);
252	StringListDeinit(&directives);
253	return true;
254}
255
256void mCheatAutosave(struct mCheatDevice* device) {
257	if (!device->autosave) {
258		return;
259	}
260	struct VFile* vf = mDirectorySetOpenSuffix(&device->p->dirs, device->p->dirs.cheats, ".cheats", O_WRONLY | O_CREAT | O_TRUNC);
261	mCheatSaveFile(device, vf);
262	vf->close(vf);
263}
264
265void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
266	if (!cheats->enabled) {
267		return;
268	}
269	cheats->refresh(cheats, device);
270
271	size_t elseLoc = 0;
272	size_t endLoc = 0;
273	size_t nCodes = mCheatListSize(&cheats->list);
274	size_t i;
275	for (i = 0; i < nCodes; ++i) {
276		struct mCheat* cheat = mCheatListGetPointer(&cheats->list, i);
277		int32_t value = 0;
278		int32_t operand = cheat->operand;
279		uint32_t operationsRemaining = cheat->repeat;
280		uint32_t address = cheat->address;
281		bool performAssignment = false;
282		bool condition = true;
283		int conditionRemaining = 0;
284		int negativeConditionRemaining = 0;
285
286		for (; operationsRemaining; --operationsRemaining) {
287			switch (cheat->type) {
288			case CHEAT_ASSIGN:
289				value = operand;
290				performAssignment = true;
291				break;
292			case CHEAT_ASSIGN_INDIRECT:
293				value = operand;
294				address = _readMem(device->p, address + cheat->addressOffset, 4);
295				performAssignment = true;
296				break;
297			case CHEAT_AND:
298				value = _readMem(device->p, address, cheat->width) & operand;
299				performAssignment = true;
300				break;
301			case CHEAT_ADD:
302				value = _readMem(device->p, address, cheat->width) + operand;
303				performAssignment = true;
304				break;
305			case CHEAT_OR:
306				value = _readMem(device->p, address, cheat->width) | operand;
307				performAssignment = true;
308				break;
309			case CHEAT_IF_EQ:
310				condition = _readMem(device->p, address, cheat->width) == operand;
311				conditionRemaining = cheat->repeat;
312				negativeConditionRemaining = cheat->negativeRepeat;
313				operationsRemaining = 1;
314				break;
315			case CHEAT_IF_NE:
316				condition = _readMem(device->p, address, cheat->width) != operand;
317				conditionRemaining = cheat->repeat;
318				negativeConditionRemaining = cheat->negativeRepeat;
319				operationsRemaining = 1;
320				break;
321			case CHEAT_IF_LT:
322				condition = _readMem(device->p, address, cheat->width) < operand;
323				conditionRemaining = cheat->repeat;
324				negativeConditionRemaining = cheat->negativeRepeat;
325				operationsRemaining = 1;
326				break;
327			case CHEAT_IF_GT:
328				condition = _readMem(device->p, address, cheat->width) > operand;
329				conditionRemaining = cheat->repeat;
330				negativeConditionRemaining = cheat->negativeRepeat;
331				operationsRemaining = 1;
332				break;
333			case CHEAT_IF_ULT:
334				condition = (uint32_t) _readMem(device->p, address, cheat->width) < (uint32_t) operand;
335				conditionRemaining = cheat->repeat;
336				negativeConditionRemaining = cheat->negativeRepeat;
337				operationsRemaining = 1;
338				break;
339			case CHEAT_IF_UGT:
340				condition = (uint32_t) _readMem(device->p, address, cheat->width) > (uint32_t) operand;
341				conditionRemaining = cheat->repeat;
342				negativeConditionRemaining = cheat->negativeRepeat;
343				operationsRemaining = 1;
344				break;
345			case CHEAT_IF_AND:
346				condition = _readMem(device->p, address, cheat->width) & operand;
347				conditionRemaining = cheat->repeat;
348				negativeConditionRemaining = cheat->negativeRepeat;
349				operationsRemaining = 1;
350				break;
351			case CHEAT_IF_LAND:
352				condition = _readMem(device->p, address, cheat->width) && operand;
353				conditionRemaining = cheat->repeat;
354				negativeConditionRemaining = cheat->negativeRepeat;
355				operationsRemaining = 1;
356				break;
357			case CHEAT_IF_NAND:
358				condition = !(_readMem(device->p, address, cheat->width) & operand);
359				conditionRemaining = cheat->repeat;
360				negativeConditionRemaining = cheat->negativeRepeat;
361				operationsRemaining = 1;
362				break;
363			}
364
365			if (performAssignment) {
366				_writeMem(device->p, address, cheat->width, value);
367			}
368
369			address += cheat->addressOffset;
370			operand += cheat->operandOffset;
371		}
372
373
374		if (elseLoc && i == elseLoc) {
375			i = endLoc;
376			endLoc = 0;
377		}
378		if (conditionRemaining > 0 && !condition) {
379			i += conditionRemaining;
380		} else if (negativeConditionRemaining > 0) {
381			elseLoc = i + conditionRemaining;
382			endLoc = elseLoc + negativeConditionRemaining;
383		}
384	}
385}
386
387void mCheatDeviceInit(void* cpu, struct mCPUComponent* component) {
388	UNUSED(cpu);
389	struct mCheatDevice* device = (struct mCheatDevice*) component;
390	size_t i;
391	for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
392		struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
393		cheats->add(cheats, device);
394	}
395}
396
397void mCheatDeviceDeinit(struct mCPUComponent* component) {
398	struct mCheatDevice* device = (struct mCheatDevice*) component;
399	size_t i;
400	for (i = mCheatSetsSize(&device->cheats); i--;) {
401		struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
402		cheats->remove(cheats, device);
403	}
404}