all repos — mgba @ 199e3ef4ada7a49b400c5d613ad0522c6c8e427f

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