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