all repos — mgba @ 11b18311b7ae40b75098aef7be65da709358e635

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	device->buttonDown = false;
 56	mCheatSetsInit(&device->cheats, 4);
 57}
 58
 59void mCheatDeviceDestroy(struct mCheatDevice* device) {
 60	mCheatDeviceClear(device);
 61	mCheatSetsDeinit(&device->cheats);
 62}
 63
 64void mCheatDeviceClear(struct mCheatDevice* device) {
 65	size_t i;
 66	for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
 67		struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
 68		mCheatSetDeinit(set);
 69	}
 70	mCheatSetsClear(&device->cheats);
 71}
 72
 73void mCheatSetInit(struct mCheatSet* set, const char* name) {
 74	mCheatListInit(&set->list, 4);
 75	StringListInit(&set->lines, 4);
 76	if (name) {
 77		set->name = strdup(name);
 78	} else {
 79		set->name = 0;
 80	}
 81	set->enabled = true;
 82}
 83
 84void mCheatSetDeinit(struct mCheatSet* set) {
 85	mCheatListDeinit(&set->list);
 86	size_t i;
 87	for (i = 0; i < StringListSize(&set->lines); ++i) {
 88		free(*StringListGetPointer(&set->lines, i));
 89	}
 90	if (set->name) {
 91		free(set->name);
 92	}
 93	StringListDeinit(&set->lines);
 94	set->deinit(set);
 95	free(set);
 96}
 97
 98void mCheatSetRename(struct mCheatSet* set, const char* name) {
 99	if (set->name) {
100		free(set->name);
101		set->name = NULL;
102	}
103	if (name) {
104		set->name = strdup(name);
105	}
106}
107
108bool mCheatAddLine(struct mCheatSet* set, const char* line, int type) {
109	if (!set->addLine(set, line, type)) {
110		return false;
111	}
112	*StringListAppend(&set->lines) = strdup(line);
113	return true;
114}
115
116void mCheatAddSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
117	*mCheatSetsAppend(&device->cheats) = cheats;
118	cheats->add(cheats, device);
119}
120
121void mCheatRemoveSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
122	size_t i;
123	for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
124		if (*mCheatSetsGetPointer(&device->cheats, i) == cheats) {
125			break;
126		}
127	}
128	if (i == mCheatSetsSize(&device->cheats)) {
129		return;
130	}
131	mCheatSetsShift(&device->cheats, i, 1);
132	cheats->remove(cheats, device);
133}
134
135bool mCheatParseFile(struct mCheatDevice* device, struct VFile* vf) {
136	char cheat[MAX_LINE_LENGTH];
137	struct mCheatSet* set = NULL;
138	struct mCheatSet* newSet;
139	bool nextDisabled = false;
140	struct StringList directives;
141	StringListInit(&directives, 4);
142
143	while (true) {
144		size_t i = 0;
145		ssize_t bytesRead = vf->readline(vf, cheat, sizeof(cheat));
146		rtrim(cheat);
147		if (bytesRead == 0) {
148			break;
149		}
150		if (bytesRead < 0) {
151			StringListDeinit(&directives);
152			return false;
153		}
154		while (isspace((int) cheat[i])) {
155			++i;
156		}
157		switch (cheat[i]) {
158		case '#':
159			do {
160				++i;
161			} while (isspace((int) cheat[i]));
162			newSet = device->createSet(device, &cheat[i]);
163			newSet->enabled = !nextDisabled;
164			nextDisabled = false;
165			if (set) {
166				mCheatAddSet(device, set);
167			}
168			if (set) {
169				newSet->copyProperties(newSet, set);
170			}
171			newSet->parseDirectives(newSet, &directives);
172			set = newSet;
173			break;
174		case '!':
175			do {
176				++i;
177			} while (isspace((int) cheat[i]));
178			if (strcasecmp(&cheat[i], "disabled") == 0) {
179				nextDisabled = true;
180				break;
181			}
182			if (strcasecmp(&cheat[i], "reset") == 0) {
183				size_t d;
184				for (d = 0; d < StringListSize(&directives); ++d) {
185					free(*StringListGetPointer(&directives, d));
186				}
187				StringListClear(&directives);
188				break;
189			}
190			*StringListAppend(&directives) = strdup(&cheat[i]);
191			break;
192		default:
193			if (!set) {
194				set = device->createSet(device, NULL);
195				set->enabled = !nextDisabled;
196				nextDisabled = false;
197			}
198			mCheatAddLine(set, cheat, 0);
199			break;
200		}
201	}
202	if (set) {
203		mCheatAddSet(device, set);
204	}
205	size_t d;
206	for (d = 0; d < StringListSize(&directives); ++d) {
207		free(*StringListGetPointer(&directives, d));
208	}
209	StringListClear(&directives);
210	StringListDeinit(&directives);
211	return true;
212}
213
214bool mCheatSaveFile(struct mCheatDevice* device, struct VFile* vf) {
215	static const char lineStart[3] = "# ";
216	static const char lineEnd = '\n';
217	struct StringList directives;
218	StringListInit(&directives, 4);
219
220	size_t i;
221	for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
222		struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
223		set->dumpDirectives(set, &directives);
224		if (!set->enabled) {
225			static const char* disabledDirective = "!disabled\n";
226			vf->write(vf, disabledDirective, strlen(disabledDirective));
227		}
228		size_t d;
229		for (d = 0; d < StringListSize(&directives); ++d) {
230			char directive[64];
231			ssize_t len = snprintf(directive, sizeof(directive) - 1, "!%s\n", *StringListGetPointer(&directives, d));
232			if (len > 1) {
233				vf->write(vf, directive, (size_t) len > sizeof(directive) ? sizeof(directive) : (size_t) len);
234			}
235		}
236
237		vf->write(vf, lineStart, 2);
238		if (set->name) {
239			vf->write(vf, set->name, strlen(set->name));
240		}
241		vf->write(vf, &lineEnd, 1);
242		size_t c;
243		for (c = 0; c < StringListSize(&set->lines); ++c) {
244			const char* line = *StringListGetPointer(&set->lines, c);
245			vf->write(vf, line, strlen(line));
246			vf->write(vf, &lineEnd, 1);
247		}
248	}
249	size_t d;
250	for (d = 0; d < StringListSize(&directives); ++d) {
251		free(*StringListGetPointer(&directives, d));
252	}
253	StringListClear(&directives);
254	StringListDeinit(&directives);
255	return true;
256}
257
258#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
259void mCheatAutosave(struct mCheatDevice* device) {
260	if (!device->autosave) {
261		return;
262	}
263	struct VFile* vf = mDirectorySetOpenSuffix(&device->p->dirs, device->p->dirs.cheats, ".cheats", O_WRONLY | O_CREAT | O_TRUNC);
264	if (!vf) {
265		return;
266	}
267	mCheatSaveFile(device, vf);
268	vf->close(vf);
269}
270#endif
271
272void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
273	cheats->refresh(cheats, device);
274	if (!cheats->enabled) {
275		return;
276	}
277
278	size_t elseLoc = 0;
279	size_t endLoc = 0;
280	size_t nCodes = mCheatListSize(&cheats->list);
281	size_t i;
282	for (i = 0; i < nCodes; ++i) {
283		struct mCheat* cheat = mCheatListGetPointer(&cheats->list, i);
284		int32_t value = 0;
285		int32_t operand = cheat->operand;
286		uint32_t operationsRemaining = cheat->repeat;
287		uint32_t address = cheat->address;
288		bool performAssignment = false;
289		bool condition = true;
290		int conditionRemaining = 0;
291		int negativeConditionRemaining = 0;
292
293		for (; operationsRemaining; --operationsRemaining) {
294			switch (cheat->type) {
295			case CHEAT_ASSIGN:
296				value = operand;
297				performAssignment = true;
298				break;
299			case CHEAT_ASSIGN_INDIRECT:
300				value = operand;
301				address = _readMem(device->p, address + cheat->addressOffset, 4);
302				performAssignment = true;
303				break;
304			case CHEAT_AND:
305				value = _readMem(device->p, address, cheat->width) & operand;
306				performAssignment = true;
307				break;
308			case CHEAT_ADD:
309				value = _readMem(device->p, address, cheat->width) + operand;
310				performAssignment = true;
311				break;
312			case CHEAT_OR:
313				value = _readMem(device->p, address, cheat->width) | operand;
314				performAssignment = true;
315				break;
316			case CHEAT_IF_EQ:
317				condition = _readMem(device->p, address, cheat->width) == operand;
318				conditionRemaining = cheat->repeat;
319				negativeConditionRemaining = cheat->negativeRepeat;
320				operationsRemaining = 1;
321				break;
322			case CHEAT_IF_NE:
323				condition = _readMem(device->p, address, cheat->width) != operand;
324				conditionRemaining = cheat->repeat;
325				negativeConditionRemaining = cheat->negativeRepeat;
326				operationsRemaining = 1;
327				break;
328			case CHEAT_IF_LT:
329				condition = _readMem(device->p, address, cheat->width) < operand;
330				conditionRemaining = cheat->repeat;
331				negativeConditionRemaining = cheat->negativeRepeat;
332				operationsRemaining = 1;
333				break;
334			case CHEAT_IF_GT:
335				condition = _readMem(device->p, address, cheat->width) > operand;
336				conditionRemaining = cheat->repeat;
337				negativeConditionRemaining = cheat->negativeRepeat;
338				operationsRemaining = 1;
339				break;
340			case CHEAT_IF_ULT:
341				condition = (uint32_t) _readMem(device->p, address, cheat->width) < (uint32_t) operand;
342				conditionRemaining = cheat->repeat;
343				negativeConditionRemaining = cheat->negativeRepeat;
344				operationsRemaining = 1;
345				break;
346			case CHEAT_IF_UGT:
347				condition = (uint32_t) _readMem(device->p, address, cheat->width) > (uint32_t) operand;
348				conditionRemaining = cheat->repeat;
349				negativeConditionRemaining = cheat->negativeRepeat;
350				operationsRemaining = 1;
351				break;
352			case CHEAT_IF_AND:
353				condition = _readMem(device->p, address, cheat->width) & operand;
354				conditionRemaining = cheat->repeat;
355				negativeConditionRemaining = cheat->negativeRepeat;
356				operationsRemaining = 1;
357				break;
358			case CHEAT_IF_LAND:
359				condition = _readMem(device->p, address, cheat->width) && operand;
360				conditionRemaining = cheat->repeat;
361				negativeConditionRemaining = cheat->negativeRepeat;
362				operationsRemaining = 1;
363				break;
364			case CHEAT_IF_NAND:
365				condition = !(_readMem(device->p, address, cheat->width) & operand);
366				conditionRemaining = cheat->repeat;
367				negativeConditionRemaining = cheat->negativeRepeat;
368				operationsRemaining = 1;
369				break;
370			case CHEAT_IF_BUTTON:
371				condition = device->buttonDown;
372				conditionRemaining = cheat->repeat;
373				negativeConditionRemaining = cheat->negativeRepeat;
374				operationsRemaining = 1;
375				break;
376			}
377
378			if (performAssignment) {
379				_writeMem(device->p, address, cheat->width, value);
380			}
381
382			address += cheat->addressOffset;
383			operand += cheat->operandOffset;
384		}
385
386
387		if (elseLoc && i == elseLoc) {
388			i = endLoc;
389			endLoc = 0;
390		}
391		if (conditionRemaining > 0 && !condition) {
392			i += conditionRemaining;
393		} else if (negativeConditionRemaining > 0) {
394			elseLoc = i + conditionRemaining;
395			endLoc = elseLoc + negativeConditionRemaining;
396		}
397	}
398}
399
400void mCheatPressButton(struct mCheatDevice* device, bool down) {
401	device->buttonDown = down;
402}
403
404void mCheatDeviceInit(void* cpu, struct mCPUComponent* component) {
405	UNUSED(cpu);
406	struct mCheatDevice* device = (struct mCheatDevice*) component;
407	size_t i;
408	for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
409		struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
410		cheats->add(cheats, device);
411	}
412}
413
414void mCheatDeviceDeinit(struct mCPUComponent* component) {
415	struct mCheatDevice* device = (struct mCheatDevice*) component;
416	size_t i;
417	for (i = mCheatSetsSize(&device->cheats); i--;) {
418		struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
419		cheats->remove(cheats, device);
420	}
421}