all repos — mgba @ 67905d281bfecbb06f51f2ca5ac939df378734a5

mGBA Game Boy Advance Emulator

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}