src/gba/cheats/codebreaker.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 "gba/cheats.h"
7
8#include "gba/gba.h"
9#include "gba/io.h"
10#include "util/string.h"
11
12static void _cbLoadByteswap(uint8_t* buffer, uint32_t op1, uint16_t op2) {
13 buffer[0] = op1 >> 24;
14 buffer[1] = op1 >> 16;
15 buffer[2] = op1 >> 8;
16 buffer[3] = op1;
17 buffer[4] = op2 >> 8;
18 buffer[5] = op2;
19}
20
21static void _cbStoreByteswap(uint8_t* buffer, uint32_t* op1, uint16_t* op2) {
22 *op1 = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
23 *op2 = (buffer[4] << 8) | buffer[5];
24}
25
26static void _cbDecrypt(struct GBACheatSet* cheats, uint32_t* op1, uint16_t* op2) {
27 uint8_t buffer[6];
28 int i;
29
30 _cbLoadByteswap(buffer, *op1, *op2);
31 for (i = sizeof(cheats->cbTable) - 1; i >= 0; --i) {
32 size_t offsetX = i >> 3;
33 size_t offsetY = cheats->cbTable[i] >> 3;
34 int bitX = i & 7;
35 int bitY = cheats->cbTable[i] & 7;
36
37 uint8_t x = (buffer[offsetX] >> bitX) & 1;
38 uint8_t y = (buffer[offsetY] >> bitY) & 1;
39 uint8_t x2 = buffer[offsetX] & ~(1 << bitX);
40 if (y) {
41 x2 |= 1 << bitX;
42 }
43 buffer[offsetX] = x2;
44
45 // This can't be moved earlier due to pointer aliasing
46 uint8_t y2 = buffer[offsetY] & ~(1 << bitY);
47 if (x) {
48 y2 |= 1 << bitY;
49 }
50 buffer[offsetY] = y2;
51 }
52 _cbStoreByteswap(buffer, op1, op2);
53
54 *op1 ^= cheats->cbSeeds[0];
55 *op2 ^= cheats->cbSeeds[1];
56
57 _cbLoadByteswap(buffer, *op1, *op2);
58 uint32_t master = cheats->cbMaster;
59 for (i = 0; i < 5; ++i) {
60 buffer[i] ^= (master >> 8) ^ buffer[i + 1];
61 }
62 buffer[5] ^= master >> 8;
63
64 for (i = 5; i > 0; --i) {
65 buffer[i] ^= master ^ buffer[i - 1];
66 }
67 buffer[0] ^= master;
68 _cbStoreByteswap(buffer, op1, op2);
69
70 *op1 ^= cheats->cbSeeds[2];
71 *op2 ^= cheats->cbSeeds[3];
72}
73
74static uint32_t _cbRand(struct GBACheatSet* cheats) {
75 // Roll LCG three times to get enough bits of entropy
76 uint32_t roll = cheats->cbRngState * 0x41C64E6D + 0x3039;
77 uint32_t roll2 = roll * 0x41C64E6D + 0x3039;
78 uint32_t roll3 = roll2 * 0x41C64E6D + 0x3039;
79 uint32_t mix = (roll << 14) & 0xC0000000;
80 mix |= (roll2 >> 1) & 0x3FFF8000;
81 mix |= (roll3 >> 16) & 0x7FFF;
82 cheats->cbRngState = roll3;
83 return mix;
84}
85
86static size_t _cbSwapIndex(struct GBACheatSet* cheats) {
87 uint32_t roll = _cbRand(cheats);
88 uint32_t count = sizeof(cheats->cbTable);
89
90 if (roll == count) {
91 roll = 0;
92 }
93
94 if (roll < count) {
95 return roll;
96 }
97
98 uint32_t bit = 1;
99
100 while (count < 0x10000000 && count < roll) {
101 count <<= 4;
102 bit <<= 4;
103 }
104
105 while (count < 0x80000000 && count < roll) {
106 count <<= 1;
107 bit <<= 1;
108 }
109
110 uint32_t mask;
111 while (true) {
112 mask = 0;
113 if (roll >= count) {
114 roll -= count;
115 }
116 if (roll >= count >> 1) {
117 roll -= count >> 1;
118 mask |= ROR(bit, 1);
119 }
120 if (roll >= count >> 2) {
121 roll -= count >> 2;
122 mask |= ROR(bit, 2);
123 }
124 if (roll >= count >> 3) {
125 roll -= count >> 3;
126 mask |= ROR(bit, 3);
127 }
128 if (!roll || !(bit >> 4)) {
129 break;
130 }
131 bit >>= 4;
132 count >>= 4;
133 }
134
135 mask &= 0xE0000000;
136 if (!mask || !(bit & 7)) {
137 return roll;
138 }
139
140 if (mask & ROR(bit, 3)) {
141 roll += count >> 3;
142 }
143 if (mask & ROR(bit, 2)) {
144 roll += count >> 2;
145 }
146 if (mask & ROR(bit, 1)) {
147 roll += count >> 1;
148 }
149
150 return roll;
151}
152
153static void _cbReseed(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
154 cheats->cbRngState = (op2 & 0xFF) ^ 0x1111;
155 size_t i;
156 // Populate the initial seed table
157 for (i = 0; i < sizeof(cheats->cbTable); ++i) {
158 cheats->cbTable[i] = i;
159 }
160 // Swap pseudo-random table entries based on the input code
161 for (i = 0; i < 0x50; ++i) {
162 size_t x = _cbSwapIndex(cheats);
163 size_t y = _cbSwapIndex(cheats);
164 uint8_t swap = cheats->cbTable[x];
165 cheats->cbTable[x] = cheats->cbTable[y];
166 cheats->cbTable[y] = swap;
167 }
168
169 // Spin the RNG some to make the initial seed
170 cheats->cbRngState = 0x4EFAD1C3;
171 for (i = 0; i < ((op1 >> 24) & 0xF); ++i) {
172 cheats->cbRngState = _cbRand(cheats);
173 }
174
175 cheats->cbSeeds[2] = _cbRand(cheats);
176 cheats->cbSeeds[3] = _cbRand(cheats);
177
178 cheats->cbRngState = (op2 >> 8) ^ 0xF254;
179 for (i = 0; i < (op2 >> 8); ++i) {
180 cheats->cbRngState = _cbRand(cheats);
181 }
182
183 cheats->cbSeeds[0] = _cbRand(cheats);
184 cheats->cbSeeds[1] = _cbRand(cheats);
185
186 cheats->cbMaster = op1;
187}
188
189bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
190 char line[14] = "XXXXXXXX XXXX";
191 snprintf(line, sizeof(line), "%08X %04X", op1, op2);
192
193 if (cheats->cbMaster) {
194 _cbDecrypt(cheats, &op1, &op2);
195 }
196
197 enum GBACodeBreakerType type = op1 >> 28;
198 struct mCheat* cheat = NULL;
199
200 if (cheats->incompleteCheat) {
201 cheats->incompleteCheat->repeat = op1 & 0xFFFF;
202 cheats->incompleteCheat->addressOffset = op2;
203 cheats->incompleteCheat->operandOffset = 0;
204 cheats->incompleteCheat = 0;
205 return true;
206 }
207
208 switch (type) {
209 case CB_GAME_ID:
210 // TODO: Run checksum
211 return true;
212 case CB_HOOK:
213 if (cheats->hook) {
214 return false;
215 }
216 cheats->hook = malloc(sizeof(*cheats->hook));
217 cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1));
218 cheats->hook->mode = MODE_THUMB;
219 cheats->hook->refs = 1;
220 cheats->hook->reentries = 0;
221 return true;
222 case CB_OR_2:
223 cheat = mCheatListAppend(&cheats->d.list);
224 cheat->type = CHEAT_OR;
225 cheat->width = 2;
226 break;
227 case CB_ASSIGN_1:
228 cheat = mCheatListAppend(&cheats->d.list);
229 cheat->type = CHEAT_ASSIGN;
230 cheat->width = 1;
231 break;
232 case CB_FILL:
233 cheat = mCheatListAppend(&cheats->d.list);
234 cheat->type = CHEAT_ASSIGN;
235 cheat->width = 2;
236 cheats->incompleteCheat = cheat;
237 break;
238 case CB_FILL_8:
239 mLOG(CHEATS, STUB, "CodeBreaker code %08X %04X not supported", op1, op2);
240 return false;
241 case CB_AND_2:
242 cheat = mCheatListAppend(&cheats->d.list);
243 cheat->type = CHEAT_AND;
244 cheat->width = 2;
245 break;
246 case CB_IF_EQ:
247 cheat = mCheatListAppend(&cheats->d.list);
248 cheat->type = CHEAT_IF_EQ;
249 cheat->width = 2;
250 break;
251 case CB_ASSIGN_2:
252 cheat = mCheatListAppend(&cheats->d.list);
253 cheat->type = CHEAT_ASSIGN;
254 cheat->width = 2;
255 break;
256 case CB_ENCRYPT:
257 _cbReseed(cheats, op1, op2);
258 return true;
259 case CB_IF_NE:
260 cheat = mCheatListAppend(&cheats->d.list);
261 cheat->type = CHEAT_IF_NE;
262 cheat->width = 2;
263 break;
264 case CB_IF_GT:
265 cheat = mCheatListAppend(&cheats->d.list);
266 cheat->type = CHEAT_IF_GT;
267 cheat->width = 2;
268 break;
269 case CB_IF_LT:
270 cheat = mCheatListAppend(&cheats->d.list);
271 cheat->type = CHEAT_IF_LT;
272 cheat->width = 2;
273 break;
274 case CB_IF_SPECIAL:
275 switch (op1 & 0x0FFFFFFF) {
276 case 0x20:
277 cheat = mCheatListAppend(&cheats->d.list);
278 cheat->type = CHEAT_IF_AND;
279 cheat->width = 2;
280 cheat->address = BASE_IO | REG_JOYSTAT;
281 cheat->operand = op2;
282 cheat->repeat = 1;
283 return true;
284 default:
285 mLOG(CHEATS, STUB, "CodeBreaker code %08X %04X not supported", op1, op2);
286 return false;
287 }
288 case CB_ADD_2:
289 cheat = mCheatListAppend(&cheats->d.list);
290 cheat->type = CHEAT_ADD;
291 cheat->width = 2;
292 break;
293 case CB_IF_AND:
294 cheat = mCheatListAppend(&cheats->d.list);
295 cheat->type = CHEAT_IF_AND;
296 cheat->width = 2;
297 break;
298 }
299
300 cheat->address = op1 & 0x0FFFFFFF;
301 cheat->operand = op2;
302 cheat->repeat = 1;
303 cheat->negativeRepeat = 0;
304 return true;
305}
306
307bool GBACheatAddCodeBreakerLine(struct GBACheatSet* cheats, const char* line) {
308 uint32_t op1;
309 uint16_t op2;
310 line = hex32(line, &op1);
311 if (!line) {
312 return false;
313 }
314 while (*line == ' ') {
315 ++line;
316 }
317 line = hex16(line, &op2);
318 if (!line) {
319 return false;
320 }
321 return GBACheatAddCodeBreaker(cheats, op1, op2);
322}