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 bool GBACheatAddLine(struct mCheatSet*, const char* line, int type);
71
72static struct mCheatSet* GBACheatSetCreate(struct mCheatDevice* device, const char* name) {
73 UNUSED(device);
74 struct GBACheatSet* set = malloc(sizeof(*set));
75 mCheatSetInit(&set->d, name);
76 set->incompleteCheat = 0;
77 set->incompletePatch = 0;
78 set->currentBlock = 0;
79 set->gsaVersion = 0;
80 set->cbRngState = 0;
81 set->cbMaster = 0;
82 set->remainingAddresses = 0;
83 set->hook = NULL;
84
85 set->d.deinit = GBACheatSetDeinit;
86 set->d.add = GBACheatAddSet;
87 set->d.remove = GBACheatRemoveSet;
88
89 set->d.addLine = GBACheatAddLine;
90 set->d.copyProperties = GBACheatSetCopyProperties;
91
92 set->d.refresh = GBACheatRefresh;
93
94 int i;
95 for (i = 0; i < MAX_ROM_PATCHES; ++i) {
96 set->romPatches[i].exists = false;
97 }
98 return &set->d;
99}
100
101struct mCheatDevice* GBACheatDeviceCreate(void) {
102 struct mCheatDevice* device = malloc(sizeof(*device));
103 mCheatDeviceCreate(device);
104 device->createSet = GBACheatSetCreate;
105 return device;
106}
107
108static void GBACheatSetDeinit(struct mCheatSet* set) {
109 struct GBACheatSet* gbaset = (struct GBACheatSet*) set;
110 if (gbaset->hook) {
111 --gbaset->hook->refs;
112 if (gbaset->hook->refs == 0) {
113 free(gbaset->hook);
114 }
115 }
116}
117
118static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
119 struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
120 _addBreakpoint(device, gbaset);
121 _patchROM(device, gbaset);
122}
123
124static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
125 struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
126 _unpatchROM(device, gbaset);
127 _removeBreakpoint(device, gbaset);
128}
129
130static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
131 uint32_t o1 = op1;
132 uint32_t o2 = op2;
133 char line[18] = "XXXXXXXX XXXXXXXX";
134 snprintf(line, sizeof(line), "%08X %08X", op1, op2);
135
136 switch (set->gsaVersion) {
137 case 0:
138 // Try to detect GameShark version
139 GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
140 if ((o1 & 0xF0000000) == 0xF0000000 && !(o2 & 0xFFFFFCFE)) {
141 GBACheatSetGameSharkVersion(set, 1);
142 return GBACheatAddGameSharkRaw(set, o1, o2);
143 }
144 o1 = op1;
145 o2 = op2;
146 GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
147 if ((o1 & 0xFE000000) == 0xC4000000 && !(o2 & 0xFFFF0000)) {
148 GBACheatSetGameSharkVersion(set, 3);
149 return GBACheatAddProActionReplayRaw(set, o1, o2);
150 }
151 break;
152 case 1:
153 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
154 return GBACheatAddGameSharkRaw(set, o1, o2);
155 case 3:
156 GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
157 return GBACheatAddProActionReplayRaw(set, o1, o2);
158 }
159 return false;
160}
161
162bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
163 uint32_t address;
164 uint8_t op;
165 uint32_t value = 0;
166 int width = 0;
167 const char* lineNext = hex32(line, &address);
168 if (!lineNext) {
169 return false;
170 }
171 if (lineNext[0] != ':') {
172 return false;
173 }
174 ++lineNext;
175 while (width < 4) {
176 lineNext = hex8(lineNext, &op);
177 if (!lineNext) {
178 break;
179 }
180 value <<= 8;
181 value |= op;
182 ++width;
183 }
184 if (width == 0 || width == 3) {
185 return false;
186 }
187
188 struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
189 cheat->address = address;
190 cheat->operandOffset = 0;
191 cheat->addressOffset = 0;
192 cheat->repeat = 1;
193 cheat->type = CHEAT_ASSIGN;
194 cheat->width = width;
195 cheat->operand = value;
196 return true;
197}
198
199bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) {
200 struct GBACheatSet* cheats = (struct GBACheatSet*) set;
201 switch (type) {
202 case GBA_CHEAT_AUTODETECT:
203 break;
204 case GBA_CHEAT_CODEBREAKER:
205 return GBACheatAddCodeBreakerLine(cheats, line);
206 case GBA_CHEAT_GAMESHARK:
207 return GBACheatAddGameSharkLine(cheats, line);
208 case GBA_CHEAT_PRO_ACTION_REPLAY:
209 return GBACheatAddProActionReplayLine(cheats, line);
210 case GBA_CHEAT_VBA:
211 return GBACheatAddVBALine(cheats, line);
212 default:
213 return false;
214 }
215
216 uint32_t op1;
217 uint16_t op2;
218 uint16_t op3;
219 const char* lineNext = hex32(line, &op1);
220 if (!lineNext) {
221 return false;
222 }
223 if (lineNext[0] == ':') {
224 return GBACheatAddVBALine(cheats, line);
225 }
226 while (isspace((int) lineNext[0])) {
227 ++lineNext;
228 }
229 lineNext = hex16(lineNext, &op2);
230 if (!lineNext) {
231 return false;
232 }
233 if (!lineNext[0] || isspace((int) lineNext[0])) {
234 return GBACheatAddCodeBreaker(cheats, op1, op2);
235 }
236 lineNext = hex16(lineNext, &op3);
237 if (!lineNext) {
238 return false;
239 }
240 uint32_t realOp2 = op2;
241 realOp2 <<= 16;
242 realOp2 |= op3;
243 return GBACheatAddAutodetect(cheats, op1, realOp2);
244}
245
246static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
247 struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
248 _patchROM(device, gbaset);
249}
250
251static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
252 struct GBACheatSet* newSet = (struct GBACheatSet*) set;
253 struct GBACheatSet* gbaset = (struct GBACheatSet*) oldSet;
254 newSet->gsaVersion = gbaset->gsaVersion;
255 memcpy(newSet->gsaSeeds, gbaset->gsaSeeds, sizeof(newSet->gsaSeeds));
256 newSet->cbRngState = gbaset->cbRngState;
257 newSet->cbMaster = gbaset->cbMaster;
258 memcpy(newSet->cbSeeds, gbaset->cbSeeds, sizeof(newSet->cbSeeds));
259 memcpy(newSet->cbTable, gbaset->cbTable, sizeof(newSet->cbTable));
260 if (gbaset->hook) {
261 if (newSet->hook) {
262 --newSet->hook->refs;
263 if (newSet->hook->refs == 0) {
264 free(newSet->hook);
265 }
266 }
267 newSet->hook = gbaset->hook;
268 ++newSet->hook->refs;
269 }
270}