src/gba/cheats.c (view raw)
1/* Copyright (c) 2013-2015 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 "gba/cheats/gameshark.h"
9#include "gba/cheats/parv3.h"
10#include "gba/gba.h"
11#include "util/string.h"
12
13#define MAX_LINE_LENGTH 128
14
15static void _addBreakpoint(struct mCheatDevice* device, struct GBACheatSet* cheats) {
16 if (!device->p || !cheats->hook) {
17 return;
18 }
19 ++cheats->hook->reentries;
20 if (cheats->hook->reentries > 1) {
21 return;
22 }
23 // TODO: Put back hooks
24}
25
26static void _removeBreakpoint(struct mCheatDevice* device, struct GBACheatSet* cheats) {
27 if (!device->p || !cheats->hook) {
28 return;
29 }
30 --cheats->hook->reentries;
31 if (cheats->hook->reentries > 0) {
32 return;
33 }
34 // TODO: Put back hooks
35}
36
37static void _patchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) {
38 if (!device->p) {
39 return;
40 }
41 int i;
42 for (i = 0; i < MAX_ROM_PATCHES; ++i) {
43 if (!cheats->romPatches[i].exists || cheats->romPatches[i].applied) {
44 continue;
45 }
46 GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].newValue, &cheats->romPatches[i].oldValue);
47 cheats->romPatches[i].applied = true;
48 }
49}
50
51static void _unpatchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) {
52 if (!device->p) {
53 return;
54 }
55 int i;
56 for (i = 0; i < MAX_ROM_PATCHES; ++i) {
57 if (!cheats->romPatches[i].exists || !cheats->romPatches[i].applied) {
58 continue;
59 }
60 GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].oldValue, 0);
61 cheats->romPatches[i].applied = false;
62 }
63}
64
65static void GBACheatSetDeinit(struct mCheatSet* set);
66static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device);
67static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device);
68static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device);
69static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet);
70static void GBACheatParseDirectives(struct mCheatSet*, const struct StringList* directives);
71static void GBACheatDumpDirectives(struct mCheatSet*, struct StringList* directives);
72static bool GBACheatAddLine(struct mCheatSet*, const char* line, int type);
73
74static struct mCheatSet* GBACheatSetCreate(struct mCheatDevice* device, const char* name) {
75 UNUSED(device);
76 struct GBACheatSet* set = malloc(sizeof(*set));
77 mCheatSetInit(&set->d, name);
78 set->incompleteCheat = 0;
79 set->incompletePatch = 0;
80 set->currentBlock = 0;
81 set->gsaVersion = 0;
82 set->cbRngState = 0;
83 set->cbMaster = 0;
84 set->remainingAddresses = 0;
85 set->hook = NULL;
86
87 set->d.deinit = GBACheatSetDeinit;
88 set->d.add = GBACheatAddSet;
89 set->d.remove = GBACheatRemoveSet;
90
91 set->d.addLine = GBACheatAddLine;
92 set->d.copyProperties = GBACheatSetCopyProperties;
93
94 set->d.parseDirectives = GBACheatParseDirectives;
95 set->d.dumpDirectives = GBACheatDumpDirectives;
96
97 set->d.refresh = GBACheatRefresh;
98
99 int i;
100 for (i = 0; i < MAX_ROM_PATCHES; ++i) {
101 set->romPatches[i].exists = false;
102 }
103 return &set->d;
104}
105
106struct mCheatDevice* GBACheatDeviceCreate(void) {
107 struct mCheatDevice* device = malloc(sizeof(*device));
108 mCheatDeviceCreate(device);
109 device->createSet = GBACheatSetCreate;
110 return device;
111}
112
113static void GBACheatSetDeinit(struct mCheatSet* set) {
114 struct GBACheatSet* gbaset = (struct GBACheatSet*) set;
115 if (gbaset->hook) {
116 --gbaset->hook->refs;
117 if (gbaset->hook->refs == 0) {
118 free(gbaset->hook);
119 }
120 }
121}
122
123static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
124 struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
125 _addBreakpoint(device, gbaset);
126 _patchROM(device, gbaset);
127}
128
129static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
130 struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
131 _unpatchROM(device, gbaset);
132 _removeBreakpoint(device, gbaset);
133}
134
135static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
136 uint32_t o1 = op1;
137 uint32_t o2 = op2;
138 char line[18] = "XXXXXXXX XXXXXXXX";
139 snprintf(line, sizeof(line), "%08X %08X", op1, op2);
140
141 switch (set->gsaVersion) {
142 case 0:
143 // Try to detect GameShark version
144 GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
145 if ((o1 & 0xF0000000) == 0xF0000000 && !(o2 & 0xFFFFFCFE)) {
146 GBACheatSetGameSharkVersion(set, 1);
147 return GBACheatAddGameSharkRaw(set, o1, o2);
148 }
149 o1 = op1;
150 o2 = op2;
151 GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
152 if ((o1 & 0xFE000000) == 0xC4000000 && !(o2 & 0xFFFF0000)) {
153 GBACheatSetGameSharkVersion(set, 3);
154 return GBACheatAddProActionReplayRaw(set, o1, o2);
155 }
156 break;
157 case 1:
158 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
159 return GBACheatAddGameSharkRaw(set, o1, o2);
160 case 3:
161 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
162 return GBACheatAddProActionReplayRaw(set, o1, o2);
163 }
164 return false;
165}
166
167bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
168 uint32_t address;
169 uint8_t op;
170 uint32_t value = 0;
171 int width = 0;
172 const char* lineNext = hex32(line, &address);
173 if (!lineNext) {
174 return false;
175 }
176 if (lineNext[0] != ':') {
177 return false;
178 }
179 ++lineNext;
180 while (width < 4) {
181 lineNext = hex8(lineNext, &op);
182 if (!lineNext) {
183 break;
184 }
185 value <<= 8;
186 value |= op;
187 ++width;
188 }
189 if (width == 0 || width == 3) {
190 return false;
191 }
192
193 struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
194 cheat->address = address;
195 cheat->operandOffset = 0;
196 cheat->addressOffset = 0;
197 cheat->repeat = 1;
198 cheat->type = CHEAT_ASSIGN;
199 cheat->width = width;
200 cheat->operand = value;
201 return true;
202}
203
204bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) {
205 struct GBACheatSet* cheats = (struct GBACheatSet*) set;
206 switch (type) {
207 case GBA_CHEAT_AUTODETECT:
208 break;
209 case GBA_CHEAT_CODEBREAKER:
210 return GBACheatAddCodeBreakerLine(cheats, line);
211 case GBA_CHEAT_GAMESHARK:
212 return GBACheatAddGameSharkLine(cheats, line);
213 case GBA_CHEAT_PRO_ACTION_REPLAY:
214 return GBACheatAddProActionReplayLine(cheats, line);
215 case GBA_CHEAT_VBA:
216 return GBACheatAddVBALine(cheats, line);
217 default:
218 return false;
219 }
220
221 uint32_t op1;
222 uint16_t op2;
223 uint16_t op3;
224 const char* lineNext = hex32(line, &op1);
225 if (!lineNext) {
226 return false;
227 }
228 if (lineNext[0] == ':') {
229 return GBACheatAddVBALine(cheats, line);
230 }
231 while (isspace((int) lineNext[0])) {
232 ++lineNext;
233 }
234 lineNext = hex16(lineNext, &op2);
235 if (!lineNext) {
236 return false;
237 }
238 if (!lineNext[0] || isspace((int) lineNext[0])) {
239 return GBACheatAddCodeBreaker(cheats, op1, op2);
240 }
241 lineNext = hex16(lineNext, &op3);
242 if (!lineNext) {
243 return false;
244 }
245 uint32_t realOp2 = op2;
246 realOp2 <<= 16;
247 realOp2 |= op3;
248 return GBACheatAddAutodetect(cheats, op1, realOp2);
249}
250
251static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
252 struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
253 _patchROM(device, gbaset);
254}
255
256static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
257 struct GBACheatSet* newSet = (struct GBACheatSet*) set;
258 struct GBACheatSet* gbaset = (struct GBACheatSet*) oldSet;
259 newSet->gsaVersion = gbaset->gsaVersion;
260 memcpy(newSet->gsaSeeds, gbaset->gsaSeeds, sizeof(newSet->gsaSeeds));
261 newSet->cbRngState = gbaset->cbRngState;
262 newSet->cbMaster = gbaset->cbMaster;
263 memcpy(newSet->cbSeeds, gbaset->cbSeeds, sizeof(newSet->cbSeeds));
264 memcpy(newSet->cbTable, gbaset->cbTable, sizeof(newSet->cbTable));
265 if (gbaset->hook) {
266 if (newSet->hook) {
267 --newSet->hook->refs;
268 if (newSet->hook->refs == 0) {
269 free(newSet->hook);
270 }
271 }
272 newSet->hook = gbaset->hook;
273 ++newSet->hook->refs;
274 }
275}
276
277static void GBACheatParseDirectives(struct mCheatSet* set, const struct StringList* directives) {
278 struct GBACheatSet* cheats = (struct GBACheatSet*) set;
279 size_t d;
280 for (d = 0; d < StringListSize(directives); ++d) {
281 const char* directive = *StringListGetConstPointer(directives, d);
282 if (strcmp(directive, "GSAv1") == 0) {
283 GBACheatSetGameSharkVersion(cheats, 1);
284 continue;
285 }
286 if (strcmp(directive, "PARv3") == 0) {
287 GBACheatSetGameSharkVersion(cheats, 3);
288 continue;
289 }
290 }
291}
292
293static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* directives) {
294 struct GBACheatSet* cheats = (struct GBACheatSet*) set;
295
296 // TODO: Check previous directives
297 size_t d;
298 for (d = 0; d < StringListSize(directives); ++d) {
299 free(*StringListGetPointer(directives, d));
300 }
301 StringListClear(directives);
302
303 char** directive = StringListAppend(directives);
304 switch (cheats->gsaVersion) {
305 case 1:
306 case 2:
307 *directive = strdup("GSAv1");
308 break;
309 case 3:
310 case 4:
311 *directive = strdup("PARv3");
312 break;
313 }
314}