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