src/gb/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 "gb/memory.h"
10#include "util/string.h"
11
12DEFINE_VECTOR(GBCheatPatchList, struct GBCheatPatch);
13
14static void _patchROM(struct mCheatDevice* device, struct GBCheatSet* cheats) {
15 if (!device->p) {
16 return;
17 }
18 size_t i;
19 for (i = 0; i < GBCheatPatchListSize(&cheats->romPatches); ++i) {
20 struct GBCheatPatch* patch = GBCheatPatchListGetPointer(&cheats->romPatches, i);
21 if (patch->applied) {
22 continue;
23 }
24 if (patch->checkByte) {
25 // TODO: All segments
26 int8_t value = GBView8(device->p->cpu, patch->address, 0);
27 if (value != patch->oldValue) {
28 continue;
29 }
30 }
31 GBPatch8(device->p->cpu, patch->address, patch->newValue, &patch->oldValue);
32 patch->applied = true;
33 }
34}
35
36static void _unpatchROM(struct mCheatDevice* device, struct GBCheatSet* cheats) {
37 if (!device->p) {
38 return;
39 }
40 size_t i;
41 for (i = 0; i < GBCheatPatchListSize(&cheats->romPatches); ++i) {
42 struct GBCheatPatch* patch = GBCheatPatchListGetPointer(&cheats->romPatches, i);
43 if (!patch->applied) {
44 continue;
45 }
46 GBPatch8(device->p->cpu, patch->address, patch->oldValue, &patch->newValue);
47 patch->applied = false;
48 }
49}
50
51static void GBCheatSetDeinit(struct mCheatSet* set);
52static void GBCheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device);
53static void GBCheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device);
54static void GBCheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device);
55static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet);
56static void GBCheatParseDirectives(struct mCheatSet* set, const struct StringList* directives);
57static void GBCheatDumpDirectives(struct mCheatSet* set, struct StringList* directives);
58static bool GBCheatAddLine(struct mCheatSet*, const char* line, int type);
59
60static struct mCheatSet* GBCheatSetCreate(struct mCheatDevice* device, const char* name) {
61 UNUSED(device);
62 struct GBCheatSet* set = malloc(sizeof(*set));
63 mCheatSetInit(&set->d, name);
64
65 GBCheatPatchListInit(&set->romPatches, 0);
66
67 set->d.deinit = GBCheatSetDeinit;
68 set->d.add = GBCheatAddSet;
69 set->d.remove = GBCheatRemoveSet;
70
71 set->d.addLine = GBCheatAddLine;
72 set->d.copyProperties = GBCheatSetCopyProperties;
73
74 set->d.parseDirectives = GBCheatParseDirectives;
75 set->d.dumpDirectives = GBCheatDumpDirectives;
76
77 set->d.refresh = GBCheatRefresh;
78 return &set->d;
79}
80
81struct mCheatDevice* GBCheatDeviceCreate(void) {
82 struct mCheatDevice* device = malloc(sizeof(*device));
83 mCheatDeviceCreate(device);
84 device->createSet = GBCheatSetCreate;
85 return device;
86}
87
88static void GBCheatSetDeinit(struct mCheatSet* set) {
89 struct GBCheatSet* gbset = (struct GBCheatSet*) set;
90 GBCheatPatchListDeinit(&gbset->romPatches);
91}
92
93static void GBCheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
94 struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
95 _patchROM(device, gbset);
96}
97
98static void GBCheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
99 struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
100 _unpatchROM(device, gbset);
101}
102
103static bool GBCheatAddCodebreaker(struct GBCheatSet* cheats, uint16_t address, uint8_t data) {
104 struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
105 cheat->type = CHEAT_ASSIGN;
106 cheat->width = 1;
107 cheat->address = address;
108 cheat->operand = data;
109 cheat->repeat = 1;
110 cheat->negativeRepeat = 0;
111 return true;
112}
113
114static bool GBCheatAddGameShark(struct GBCheatSet* cheats, uint32_t op) {
115 return GBCheatAddCodebreaker(cheats, ((op & 0xFF) << 8) | ((op >> 8) & 0xFF), (op >> 16) & 0xFF);
116}
117
118static bool GBCheatAddGameSharkLine(struct GBCheatSet* cheats, const char* line) {
119 uint32_t op;
120 if (!hex32(line, &op)) {
121 return false;
122 }
123 return GBCheatAddGameShark(cheats, op);
124}
125
126static bool GBCheatAddGameGenieLine(struct GBCheatSet* cheats, const char* line) {
127 uint16_t op1;
128 uint16_t op2;
129 uint16_t op3 = 0x1000;
130 const char* lineNext = hex12(line, &op1);
131 if (!lineNext || lineNext[0] != '-') {
132 return false;
133 }
134 ++lineNext;
135 lineNext = hex12(lineNext, &op2);
136 if (!lineNext) {
137 return false;
138 }
139 if (lineNext[0] == '-') {
140 ++lineNext;
141 lineNext = hex12(lineNext, &op3);
142 }
143 if (!lineNext || lineNext[0]) {
144 return false;
145 }
146 uint16_t address = (op1 & 0xF) << 8;
147 address |= (op2 >> 4) & 0xFF;
148 address |= ((op2 & 0xF) ^ 0xF) << 12;
149 struct GBCheatPatch* patch = GBCheatPatchListAppend(&cheats->romPatches);
150 patch->address = address;
151 patch->newValue = op1 >> 4;
152 patch->applied = false;
153 if (op3 < 0x1000) {
154 uint32_t value = ((op3 & 0xF00) << 20) | (op3 & 0xF);
155 value = ROR(value, 2);
156 value |= value >> 24;
157 value ^= 0xBA;
158 patch->oldValue = value;
159 patch->checkByte = true;
160 } else {
161 patch->checkByte = false;
162 }
163 return true;
164}
165
166static bool GBCheatAddVBALine(struct GBCheatSet* cheats, const char* line) {
167 uint16_t address;
168 uint8_t value;
169 const char* lineNext = hex16(line, &address);
170 if (!lineNext && lineNext[0] != ':') {
171 return false;
172 }
173 if (!hex8(line, &value)) {
174 return false;
175 }
176 struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
177 cheat->type = CHEAT_ASSIGN;
178 cheat->width = 1;
179 cheat->address = address;
180 cheat->operand = value;
181 cheat->repeat = 1;
182 cheat->negativeRepeat = 0;
183 return true;
184}
185
186bool GBCheatAddLine(struct mCheatSet* set, const char* line, int type) {
187 struct GBCheatSet* cheats = (struct GBCheatSet*) set;
188 switch (type) {
189 case GB_CHEAT_AUTODETECT:
190 break;
191 case GB_CHEAT_GAME_GENIE:
192 return GBCheatAddGameGenieLine(cheats, line);
193 case GB_CHEAT_GAMESHARK:
194 return GBCheatAddGameSharkLine(cheats, line);
195 case GB_CHEAT_VBA:
196 return GBCheatAddVBALine(cheats, line);
197 default:
198 return false;
199 }
200
201 uint16_t op1;
202 uint8_t op2;
203 uint8_t op3;
204 bool codebreaker = false;
205 const char* lineNext = hex16(line, &op1);
206 if (!lineNext) {
207 return GBCheatAddGameGenieLine(cheats, line);
208 }
209 if (lineNext[0] == ':') {
210 return GBCheatAddVBALine(cheats, line);
211 }
212 lineNext = hex8(lineNext, &op2);
213 if (!lineNext) {
214 return false;
215 }
216 if (lineNext[0] == '-') {
217 codebreaker = true;
218 ++lineNext;
219 }
220 lineNext = hex8(lineNext, &op3);
221 if (!lineNext) {
222 return false;
223 }
224 if (codebreaker) {
225 uint16_t address = (op1 << 8) | op2;
226 return GBCheatAddCodebreaker(cheats, address, op3);
227 } else {
228 uint32_t realOp = op1 << 16;
229 realOp |= op2 << 8;
230 realOp |= op3;
231 return GBCheatAddGameShark(cheats, realOp);
232 }
233}
234
235static void GBCheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
236 struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
237 _patchROM(device, gbset);
238}
239
240static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
241 UNUSED(set);
242 UNUSED(oldSet);
243}
244
245static void GBCheatParseDirectives(struct mCheatSet* set, const struct StringList* directives) {
246 UNUSED(set);
247 UNUSED(directives);
248}
249
250static void GBCheatDumpDirectives(struct mCheatSet* set, struct StringList* directives) {
251 UNUSED(set);
252 UNUSED(directives);
253}