all repos — mgba @ 1f0f943526b06790aaf9dfaffa9538f21988af19

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 != 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}