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