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 != COMPLETE) {
201 struct mCheat* incompleteCheat = mCheatListGetPointer(&cheats->d.list, cheats->incompleteCheat);
202 incompleteCheat->repeat = op1 & 0xFFFF;
203 incompleteCheat->addressOffset = op2;
204 incompleteCheat->operandOffset = 0;
205 cheats->incompleteCheat = COMPLETE;
206 return true;
207 }
208
209 switch (type) {
210 case CB_GAME_ID:
211 // TODO: Run checksum
212 return true;
213 case CB_HOOK:
214 if (cheats->hook) {
215 return false;
216 }
217 cheats->hook = malloc(sizeof(*cheats->hook));
218 cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1));
219 cheats->hook->mode = MODE_THUMB;
220 cheats->hook->refs = 1;
221 cheats->hook->reentries = 0;
222 return true;
223 case CB_OR_2:
224 cheat = mCheatListAppend(&cheats->d.list);
225 cheat->type = CHEAT_OR;
226 cheat->width = 2;
227 break;
228 case CB_ASSIGN_1:
229 cheat = mCheatListAppend(&cheats->d.list);
230 cheat->type = CHEAT_ASSIGN;
231 cheat->width = 1;
232 break;
233 case CB_FILL:
234 cheat = mCheatListAppend(&cheats->d.list);
235 cheat->type = CHEAT_ASSIGN;
236 cheat->width = 2;
237 cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat);
238 break;
239 case CB_FILL_8:
240 mLOG(CHEATS, STUB, "CodeBreaker code %08X %04X not supported", op1, op2);
241 return false;
242 case CB_AND_2:
243 cheat = mCheatListAppend(&cheats->d.list);
244 cheat->type = CHEAT_AND;
245 cheat->width = 2;
246 break;
247 case CB_IF_EQ:
248 cheat = mCheatListAppend(&cheats->d.list);
249 cheat->type = CHEAT_IF_EQ;
250 cheat->width = 2;
251 break;
252 case CB_ASSIGN_2:
253 cheat = mCheatListAppend(&cheats->d.list);
254 cheat->type = CHEAT_ASSIGN;
255 cheat->width = 2;
256 break;
257 case CB_ENCRYPT:
258 _cbReseed(cheats, op1, op2);
259 return true;
260 case CB_IF_NE:
261 cheat = mCheatListAppend(&cheats->d.list);
262 cheat->type = CHEAT_IF_NE;
263 cheat->width = 2;
264 break;
265 case CB_IF_GT:
266 cheat = mCheatListAppend(&cheats->d.list);
267 cheat->type = CHEAT_IF_GT;
268 cheat->width = 2;
269 break;
270 case CB_IF_LT:
271 cheat = mCheatListAppend(&cheats->d.list);
272 cheat->type = CHEAT_IF_LT;
273 cheat->width = 2;
274 break;
275 case CB_IF_SPECIAL:
276 switch (op1 & 0x0FFFFFFF) {
277 case 0x20:
278 cheat = mCheatListAppend(&cheats->d.list);
279 cheat->type = CHEAT_IF_AND;
280 cheat->width = 2;
281 cheat->address = BASE_IO | REG_JOYSTAT;
282 cheat->operand = op2;
283 cheat->repeat = 1;
284 return true;
285 default:
286 mLOG(CHEATS, STUB, "CodeBreaker code %08X %04X not supported", op1, op2);
287 return false;
288 }
289 case CB_ADD_2:
290 cheat = mCheatListAppend(&cheats->d.list);
291 cheat->type = CHEAT_ADD;
292 cheat->width = 2;
293 break;
294 case CB_IF_AND:
295 cheat = mCheatListAppend(&cheats->d.list);
296 cheat->type = CHEAT_IF_AND;
297 cheat->width = 2;
298 break;
299 }
300
301 cheat->address = op1 & 0x0FFFFFFF;
302 cheat->operand = op2;
303 cheat->repeat = 1;
304 cheat->negativeRepeat = 0;
305 return true;
306}
307
308bool GBACheatAddCodeBreakerLine(struct GBACheatSet* cheats, const char* line) {
309 uint32_t op1;
310 uint16_t op2;
311 line = hex32(line, &op1);
312 if (!line) {
313 return false;
314 }
315 while (*line == ' ') {
316 ++line;
317 }
318 line = hex16(line, &op2);
319 if (!line) {
320 return false;
321 }
322 return GBACheatAddCodeBreaker(cheats, op1, op2);
323}