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}