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